mod commands;
mod config;
mod db;
mod index;
mod models;
mod neo4j;
mod output;
mod progress;
mod project;
mod savings;
mod schema;
mod search;
mod secrets;
mod skill;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "gcode", version, about = "Fast code index CLI for Gobby")]
struct Cli {
#[arg(long, global = true)]
project: Option<String>,
#[arg(long, global = true, default_value = "json")]
format: output::Format,
#[arg(long, global = true)]
quiet: bool,
#[arg(long, global = true)]
verbose: bool,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Init,
Index {
path: Option<String>,
#[arg(long, num_args = 1..)]
files: Option<Vec<String>>,
#[arg(long)]
full: bool,
},
Status,
Invalidate {
#[arg(long)]
force: bool,
},
Search {
query: String,
#[arg(long, default_value = "10")]
limit: usize,
#[arg(long, default_value = "0")]
offset: usize,
#[arg(long)]
kind: Option<String>,
},
SearchText {
query: String,
#[arg(long, default_value = "10")]
limit: usize,
#[arg(long, default_value = "0")]
offset: usize,
},
SearchContent {
query: String,
#[arg(long, default_value = "10")]
limit: usize,
#[arg(long, default_value = "0")]
offset: usize,
},
Outline { file: String },
Symbol { id: String },
Symbols { ids: Vec<String> },
Tree,
Callers {
symbol_name: String,
#[arg(long, default_value = "10")]
limit: usize,
#[arg(long, default_value = "0")]
offset: usize,
},
Usages {
symbol_name: String,
#[arg(long, default_value = "10")]
limit: usize,
#[arg(long, default_value = "0")]
offset: usize,
},
Imports { file: String },
BlastRadius {
target: String,
#[arg(long, default_value = "3")]
depth: usize,
},
Summary { symbol_id: String },
RepoOutline,
Projects,
Prune {
#[arg(long)]
force: bool,
},
}
fn main() -> anyhow::Result<()> {
#[cfg(target_os = "macos")]
unsafe {
std::env::set_var("GGML_METAL_TENSOR_ENABLE", "1")
};
let cli = Cli::parse();
search::semantic::configure_logging(cli.verbose);
match &cli.command {
Command::Init => {
let root = match &cli.project {
Some(p) => std::path::PathBuf::from(p).canonicalize()?,
None => config::detect_project_root()?,
};
return commands::init::run(&root, cli.format, cli.quiet);
}
Command::Projects => {
return commands::status::projects(cli.format);
}
Command::Prune { force } => {
return commands::status::prune(*force);
}
_ => {}
}
let ctx = config::Context::resolve(cli.project.as_deref(), cli.quiet)?;
let result = match cli.command {
Command::Init | Command::Projects | Command::Prune { .. } => unreachable!(),
Command::Index { path, files, full } => commands::index::run(&ctx, path, files, full),
Command::Status => commands::status::run(&ctx, cli.format),
Command::Invalidate { force } => commands::status::invalidate(&ctx, force),
Command::Search {
query,
limit,
offset,
kind,
} => commands::search::search(
&ctx,
&query,
limit,
offset,
kind.as_deref(),
cli.format,
cli.verbose,
),
Command::SearchText {
query,
limit,
offset,
} => commands::search::search_text(&ctx, &query, limit, offset, cli.format),
Command::SearchContent {
query,
limit,
offset,
} => commands::search::search_content(&ctx, &query, limit, offset, cli.format),
Command::Outline { file } => {
commands::symbols::outline(&ctx, &file, cli.format, cli.verbose)
}
Command::Symbol { id } => commands::symbols::symbol(&ctx, &id, cli.format),
Command::Symbols { ids } => commands::symbols::symbols(&ctx, &ids, cli.format),
Command::Tree => commands::symbols::tree(&ctx, cli.format),
Command::Callers {
symbol_name,
limit,
offset,
} => commands::graph::callers(&ctx, &symbol_name, limit, offset, cli.format),
Command::Usages {
symbol_name,
limit,
offset,
} => commands::graph::usages(&ctx, &symbol_name, limit, offset, cli.format),
Command::Imports { file } => commands::graph::imports(&ctx, &file, cli.format),
Command::BlastRadius { target, depth } => {
commands::graph::blast_radius(&ctx, &target, depth, cli.format)
}
Command::Summary { symbol_id } => commands::summary::summary(&ctx, &symbol_id, cli.format),
Command::RepoOutline => commands::summary::repo_outline(&ctx, cli.format),
};
search::semantic::shutdown();
result
}