agileplus-cli 0.3.0

Typed intent graphs for engineering work — convert prompts into validated DAGs of intents, features, tasks, tests, and edges, then store, query, and serve them from Rust, HTTP, or MCP.
use std::path::PathBuf;

use agileplus_sqlite::Storage;
use anyhow::{Context, Result};
use clap::Args;

/// Export all intent graphs from a SQLite database to individual JSON files.
#[derive(Debug, Args)]
#[command(name = "export-all")]
pub struct ExportAll {
    /// Path to the SQLite database.
    #[arg(short, long)]
    pub db: PathBuf,

    /// Output directory for exported JSON files.
    #[arg(short, long)]
    pub dir: PathBuf,
}

impl ExportAll {
    pub fn run(&self) -> Result<()> {
        let storage = Storage::open(&self.db)
            .with_context(|| format!("Failed to open storage at: {}", self.db.display()))?;

        let ids = storage
            .list_graphs()
            .with_context(|| "Failed to list graphs")?;

        if ids.is_empty() {
            println!("No graphs found in database.");
            return Ok(());
        }

        std::fs::create_dir_all(&self.dir)
            .with_context(|| format!("Failed to create output directory: {}", self.dir.display()))?;

        let mut count = 0;
        for id in &ids {
            let graph = storage
                .load_graph(id)
                .with_context(|| format!("Failed to load graph: {id}"))?
                .ok_or_else(|| anyhow::anyhow!("Graph {id} not found"))?;

            let json = serde_json::to_string_pretty(&graph)
                .with_context(|| format!("Failed to serialize graph: {id}"))?;

            let path = self.dir.join(format!("{id}.json"));
            std::fs::write(&path, &json)
                .with_context(|| format!("Failed to write: {}", path.display()))?;

            count += 1;
        }

        println!("Exported {count} graph(s) to {}", self.dir.display());
        Ok(())
    }
}