gobby-code 0.6.1

Fast Rust CLI for Gobby's code index — AST-aware search, symbol navigation, and dependency graph
use gobby_core::project::{find_project_root, read_project_id};

use crate::config::Context;
use crate::db;
use crate::index::indexer;

pub fn run(
    ctx: &Context,
    path: Option<String>,
    files: Option<Vec<String>>,
    full: bool,
) -> anyhow::Result<()> {
    // Resolve root, project_id, and DB connection — re-resolve if path
    // belongs to a different project than the CWD-derived context.
    let (root, project_id, conn) = match path.as_deref() {
        Some(p) => {
            let target = std::path::PathBuf::from(p);
            let target_root = find_project_root(&target).unwrap_or_else(|| target.clone());
            if target_root != ctx.project_root {
                // Path belongs to a different project — re-resolve everything
                let db_path = crate::config::resolve_db_path(&target_root)?;
                let project_id = read_project_id(&target_root)
                    .or_else(|_| crate::project::read_gcode_json(&target_root))
                    .unwrap_or_else(|_| crate::project::generate_project_id(&target_root));
                if !ctx.quiet {
                    eprintln!(
                        "Warning: path '{}' belongs to project {} (not {}), re-resolving context",
                        p,
                        &project_id[..8],
                        &ctx.project_id[..8]
                    );
                }
                let conn = db::open_readwrite(&db_path)?;
                (target_root, project_id, conn)
            } else {
                let conn = db::open_readwrite(&ctx.db_path)?;
                (target, ctx.project_id.clone(), conn)
            }
        }
        None => {
            let conn = db::open_readwrite(&ctx.db_path)?;
            (ctx.project_root.clone(), ctx.project_id.clone(), conn)
        }
    };

    // Auto-init: ensure identity file exists before indexing
    crate::project::ensure_gcode_json(&root)?;

    if let Some(file_list) = files {
        let result = indexer::index_files(&conn, &root, &project_id, &file_list)?;
        if !ctx.quiet {
            eprintln!(
                "Indexed {} files, {} symbols in {}ms",
                result.files_indexed, result.symbols_found, result.duration_ms
            );
        }
    } else {
        let result = indexer::index_directory(&conn, &root, &project_id, !full, ctx.quiet)?;
        if !ctx.quiet {
            eprintln!(
                "Indexed {} files ({} skipped), {} symbols in {}ms",
                result.files_indexed,
                result.files_skipped,
                result.symbols_found,
                result.duration_ms
            );
        }
    }

    Ok(())
}