codebase-graph 1.1.5

Native codebaseGraph CLI and MCP server for local code knowledge graphs.
use super::{
    build::{run_materialize, run_plan},
    format::top_level_help,
    graph::{
        run_graph_architecture_queries, run_graph_context, run_graph_health, run_graph_query,
        run_graph_query_helpers, run_graph_schema, run_graph_search,
    },
    mcp::{run_mcp_command, serve_mcp_http, serve_mcp_stdio, McpHttpOptions, McpServeOptions},
    reinstall::run_reinstall,
    setup::run_setup,
    uninstall::run_uninstall,
    watch::run_watch,
};
use std::{
    env,
    io::{self, Write},
};

pub fn run_from_env() -> Result<(), String> {
    let args: Vec<String> = env::args().skip(1).collect();
    run_process_args(args)
}

pub fn run_process_args(args: Vec<String>) -> Result<(), String> {
    if args.is_empty() {
        return run(args, &mut io::stdout());
    }
    if args.first().map(String::as_str) == Some("mcp") {
        match args.get(1).map(String::as_str) {
            Some("start") => {
                let options = McpServeOptions::parse(&args[2..])?;
                return serve_mcp_stdio(&options, io::stdin().lock(), &mut io::stdout());
            }
            Some("http") => {
                let options = McpHttpOptions::parse(&args[2..])?;
                return serve_mcp_http(&options);
            }
            _ => {}
        }
    }
    run(args, &mut io::stdout())
}

pub fn run<I, S, W>(args: I, stdout: &mut W) -> Result<(), String>
where
    I: IntoIterator<Item = S>,
    S: Into<String>,
    W: Write,
{
    let args: Vec<String> = args.into_iter().map(Into::into).collect();
    match args.first().map(String::as_str) {
        Some("-h" | "--help") => {
            writeln!(stdout, "{}", top_level_help()).map_err(|error| error.to_string())?;
            Ok(())
        }
        Some("install") => run_setup(&args[1..], stdout),
        Some("reinstall") => run_reinstall(&args[1..], stdout),
        Some("uninstall") => run_uninstall(&args[1..], stdout),
        Some("build") => run_materialize(&args[1..], stdout),
        Some("plan") => run_plan(&args[1..], stdout),
        Some("watch") => run_watch(&args[1..], stdout),
        Some("check-health") => run_graph_health(&args[1..], stdout),
        Some("schema") => run_graph_schema(&args[1..], stdout),
        Some("query-helpers") => run_graph_query_helpers(&args[1..], stdout),
        Some("codebase-architecture-queries") => run_graph_architecture_queries(&args[1..], stdout),
        Some("codebase-search") => run_graph_search(&args[1..], stdout),
        Some("codebase-context") => run_graph_context(&args[1..], stdout),
        Some("graph-query") => run_graph_query(&args[1..], stdout),
        Some("mcp") => run_mcp_command(&args[1..], stdout),
        Some(command) => Err(format!(
            "unknown command: {command}\n\n{}",
            top_level_help()
        )),
        None => {
            writeln!(stdout, "{}", top_level_help()).map_err(|error| error.to_string())?;
            Ok(())
        }
    }
}

pub fn error_exit_code(error: &str) -> i32 {
    if error.starts_with("graph_query is read-only; blocked keyword:")
        || error.starts_with("graph_query accepts one read-only statement at a time")
        || error.starts_with("graph_query requires a non-empty statement")
        || error.starts_with("graph_query parameters must be a JSON object")
        || error.starts_with("graph-query --parameters must be a JSON object")
        || error.starts_with("failed to resolve repo root")
        || error.starts_with("Repository root may not be inside")
        || error.starts_with("unknown install option:")
        || error.starts_with("unknown reinstall option:")
        || error.starts_with("--mcp-client must be")
        || error.starts_with("--mcp-client requires")
        || error.starts_with("--instructions-target must be")
        || error.starts_with("--instructions-target requires")
    {
        2
    } else {
        1
    }
}