kodegraf-cli 0.1.0

Structural code intelligence for AI coding assistants — CLI
mod commands;

use clap::Parser;

#[derive(Parser)]
#[command(
    name = "kodegraf",
    version,
    about = "Structural code intelligence for AI coding assistants",
    long_about = "Kodegraf builds a knowledge graph of your codebase, validates AI-written code \
                  at write-time, and learns from failure patterns to prevent recurring mistakes."
)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(clap::Subcommand)]
enum Commands {
    /// Initialize Kodegraf in the current project
    Init,
    /// Build the knowledge graph (full parse)
    Build,
    /// Incrementally update the graph (changed files only)
    Update {
        /// Fast mode: skip edge resolution, only re-parse changed files. For hook usage.
        #[arg(long)]
        fast: bool,
    },
    /// Show graph statistics
    Status,
    /// Find a symbol by name
    Find {
        /// Symbol name to search for
        query: String,
    },
    /// Show dependencies of a file
    Deps {
        /// File path to check
        file: String,
    },
    /// Show what depends on a file (reverse dependencies)
    Dependents {
        /// File path to check
        file: String,
    },
    /// Show blast radius for current changes
    Impact,
    /// Validate a file against the graph (DiffGuard)
    Check {
        /// File path to validate
        file: String,
    },
    /// Show quality insights and failure patterns
    Insights {
        /// Output as JSON
        #[arg(long)]
        json: bool,
    },
    /// Auto-detect and configure AI coding platforms
    Install {
        /// Target a specific platform
        #[arg(long)]
        platform: Option<String>,
        /// Preview changes without writing
        #[arg(long)]
        dry_run: bool,
    },
    /// Run evaluation benchmarks (token efficiency, search quality, performance)
    Eval {
        /// Output as JSON instead of markdown
        #[arg(long)]
        json: bool,
    },
    /// Generate CLAUDE.md rules from failure patterns
    GenerateRules,
    /// Start the MCP server
    Serve,
    /// Print graph stats for session start (used by Claude Code hooks)
    SessionGreeting,
    /// Hook subcommands (for PostToolUse and SessionStart events)
    Hook {
        #[command(subcommand)]
        event: HookEvent,
    },
}

#[derive(clap::Subcommand)]
enum HookEvent {
    /// PostToolUse hook — validates AI-written code
    PostToolUse,
    /// SessionStart hook — update graph if HEAD changed
    SessionStart,
}

fn main() -> anyhow::Result<()> {
    let cli = Cli::parse();

    match cli.command {
        Commands::Init => commands::init::run(),
        Commands::Build => commands::build::run(),
        Commands::Status => commands::status::run(),
        Commands::Update { fast } => {
            if fast {
                commands::build::run_fast_update()
            } else {
                commands::build::run_update()
            }
        }
        Commands::Find { query } => commands::find::run(&query),
        Commands::Deps { file } => commands::deps::run(&file),
        Commands::Dependents { file } => commands::dependents::run(&file),
        Commands::Impact => commands::impact::run(),
        Commands::Check { file } => commands::check::run(&file),
        Commands::Insights { json } => commands::insights::run(json),
        Commands::Eval { json } => commands::eval::run(json),
        Commands::GenerateRules => commands::generate_rules::run(),
        Commands::Install { platform, dry_run } => {
            commands::install::run(platform.as_deref(), dry_run)
        }
        Commands::Serve => {
            // The MCP server is the kodegraf-mcp binary. When invoked via CLI,
            // we exec the MCP binary. In practice, AI platforms call kodegraf-mcp directly.
            eprintln!("Starting MCP server over stdio...");
            eprintln!("(Press Ctrl+C to stop, or configure your AI tool to call `kodegraf-mcp` directly)");
            let status = std::process::Command::new("kodegraf-mcp")
                .stdin(std::process::Stdio::inherit())
                .stdout(std::process::Stdio::inherit())
                .stderr(std::process::Stdio::inherit())
                .status();
            match status {
                Ok(s) if s.success() => Ok(()),
                Ok(s) => anyhow::bail!("MCP server exited with status: {}", s),
                Err(e) => anyhow::bail!("Failed to start MCP server: {}. Is kodegraf-mcp in your PATH?", e),
            }
        }
        Commands::SessionGreeting => commands::session_greeting::run(),
        Commands::Hook { event } => match event {
            HookEvent::PostToolUse => commands::hook::run_post_tool_use(),
            HookEvent::SessionStart => commands::hook::run_session_start(),
        },
    }
}