use anyhow::Result;
use super::*;
pub(super) async fn dispatch_command(command: Commands, output: OutputConfig) -> Result<()> {
match command {
Commands::Init(args) => init::run(args, output).await,
Commands::Connect(args) => connect::run(args, output).await,
Commands::Index(args) => index::run(args, output).await,
Commands::Pack(args) => pack::run(args, output).await,
Commands::IndexBead(args) => index_bead::run(args, output).await,
Commands::Calibrate(args) => calibrate::run(args, output).await,
Commands::Search(args) => search::run(args, output).await,
Commands::Context(args) => context::run(args, output).await,
Commands::Deps(args) => deps::run(args, output).await,
Commands::Feedback(args) => feedback::run(args, output).await,
Commands::Bead(args) => bead::run(args, output).await,
Commands::Reconcile(args) => reconcile::run(args, output).await,
Commands::Predict(args) => predict::run(args, output).await,
Commands::Grep(args) => grep::run(args, output).await,
Commands::Refs(args) => refs::run(args, output).await,
Commands::Related(args) => related::run(args, output).await,
Commands::Coverage(args) => coverage::run(args, output).await,
Commands::History(args) => history::run(args, output).await,
Commands::Log(args) => log::run(args, output).await,
Commands::Hotspots(args) => hotspots::run(args, output).await,
Commands::Impact(args) => impact::run(args, output).await,
Commands::Review(args) => review::run(args, output).await,
Commands::Similar(args) => similar::run(args, output).await,
Commands::Status(args) => status::run(args, output).await,
Commands::Serve(args) => serve::run(args, output).await,
Commands::Benchmark(args) => benchmark::run(args, output).await,
Commands::Watch(args) => watch::run(args, output).await,
Commands::Completions(args) => {
completions::run(args);
Ok(())
}
Commands::Hook(args) => hook::run(args, output).await,
Commands::Tour(args) => tour::run(args, output).await,
Commands::Purge(args) => purge::run(args, output).await,
Commands::Prime(args) => prime::run(args, output).await,
Commands::Tag(args) => tag::run(args, output).await,
Commands::Bundle(args) => bundle::run(args, output).await,
Commands::Bc(args) => breadcrumb::run(args, output),
Commands::Mark(args) => breadcrumb::run_mark(args, output),
Commands::Recall(args) => breadcrumb::run_recall(args, output),
Commands::Ontology(args) => ontology::run(args, output).await,
Commands::Run(_) => anyhow::bail!("Nested run commands are not supported"),
Commands::External(_) => anyhow::bail!("External command dispatch failed"),
}
}
pub(super) async fn dispatch_external(args: &[String], output: &OutputConfig) -> Result<()> {
let name = args
.first()
.ok_or_else(|| anyhow::anyhow!("No command specified"))?;
let kv_params: Vec<(&str, String)> = args[1..]
.iter()
.filter_map(|arg| {
let (k, v) = arg.split_once('=')?;
Some((k, v.to_string()))
})
.collect();
if let Some(repo_root) = find_bobbin_root() {
let commands = crate::commands::load_commands(&repo_root).unwrap_or_default();
if let Some(def) = commands.get(name.as_str()) {
let mut full_args = vec!["bobbin".to_string()];
if output.json {
full_args.push("--json".to_string());
}
if output.quiet {
full_args.push("--quiet".to_string());
}
if output.verbose {
full_args.push("--verbose".to_string());
}
if let Some(ref server) = output.server {
full_args.push("--server".to_string());
full_args.push(server.clone());
}
full_args.push(def.command.clone());
full_args.extend(def.args.iter().cloned());
for arg in &args[1..] {
if let Some((key, value)) = arg.split_once('=') {
if key == "q" {
full_args.push(value.to_string());
} else {
full_args.push(format!("--{}", key));
full_args.push(value.to_string());
}
} else {
full_args.push(arg.clone());
}
}
let resolved = Cli::try_parse_from(&full_args).map_err(|e| anyhow::anyhow!("{}", e))?;
let resolved_output = OutputConfig {
json: resolved.json,
quiet: resolved.quiet,
verbose: resolved.verbose,
server: resolved.server,
role: crate::access::RepoFilter::resolve_role(resolved.role.as_deref()),
};
return dispatch_command(resolved.command, resolved_output).await;
}
}
let server_url = output.server.as_ref().ok_or_else(|| {
anyhow::anyhow!(
"Unknown command '{}'. Not found in local commands.toml and no server configured.\n\
Hint: set BOBBIN_SERVER or use --server to enable HTTP commands.",
name
)
})?;
let client = crate::http::client::Client::new(server_url);
let params: Vec<(&str, String)> = kv_params;
let result = client.invoke_command(name, ¶ms).await?;
if output.json {
println!("{}", serde_json::to_string(&result)?);
} else {
println!("{}", serde_json::to_string_pretty(&result)?);
}
Ok(())
}
pub(super) fn resolve_server_url(cli_server: Option<String>) -> Option<String> {
use crate::config::Config;
if let Some(ref url) = cli_server {
if url.is_empty() {
return None;
}
return cli_server;
}
if let Some(repo_root) = find_bobbin_root() {
let config_path = Config::config_path(&repo_root);
if let Ok(config) = Config::load(&config_path) {
if let Some(ref url) = config.server.url {
if url.is_empty() {
return None;
}
return config.server.url;
}
}
}
let global = Config::load_global();
global.server.url.filter(|u| !u.is_empty())
}