Skip to main content

talon_cli/
lib.rs

1//! CLI process boundary for Talon.
2
3pub mod agent_contract;
4mod banner;
5pub mod cli;
6pub mod command;
7pub mod config;
8pub mod exit_codes;
9pub mod mcp;
10pub mod output;
11pub mod platform;
12mod spinner;
13mod telemetry;
14
15/// Embedded skill contract printed by `talon --skill`.
16pub const SKILL_MD: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/embedded/SKILL.md"));
17
18/// Runs the CLI and returns a process exit code.
19#[must_use]
20pub async fn run() -> u8 {
21    let cli = cli::parse_or_exit();
22    platform::start();
23
24    if cli.skill {
25        return output::write_stdout_bytes(SKILL_MD.as_bytes());
26    }
27
28    banner::eprint_fancy_prelude_for_run(&cli);
29
30    match command::run(&cli).await {
31        Ok(()) => exit_codes::SUCCESS,
32        Err(error) => {
33            // When JSON output was requested, emit a structured error envelope so the caller
34            // always receives machine-readable output — even on failure (Decision 8).
35            if cli.json || cli.agent {
36                let action = cli.command.as_ref().map_or("unknown", |cmd| match cmd {
37                    cli::Commands::Mcp => "mcp",
38                    cli::Commands::Init(_) => "init",
39                    cli::Commands::Sync(_) => "sync",
40                    cli::Commands::Status(_) => "status",
41                    cli::Commands::Search(_) => "search",
42                    cli::Commands::Ask(_) => "ask",
43                    cli::Commands::Read(_) => "read",
44                    cli::Commands::Related(_) => "related",
45                    cli::Commands::Meta(_) => "meta",
46                    cli::Commands::Changes(_) => "changes",
47                    cli::Commands::Inspect(_) => "inspect",
48                    cli::Commands::Recall(_) => "recall",
49                    cli::Commands::Secrets(_) => "secrets",
50                });
51                let envelope = talon_core::TalonEnvelope::err(
52                    action,
53                    talon_core::ErrorEnvelope {
54                        code: talon_core::ErrorCode::Internal,
55                        message: format!("{error:#}"),
56                        detail: None,
57                    },
58                );
59                let mode = if cli.agent {
60                    output::OutputMode::Agent
61                } else {
62                    output::OutputMode::JsonPretty
63                };
64                let _ = output::emit_response(&envelope, mode);
65            } else {
66                if banner::should_clear_fancy_prelude(&cli) {
67                    banner::clear_fancy_prelude();
68                }
69                eprintln!("Error: {error:#}");
70            }
71            exit_codes::GENERIC_ERROR
72        }
73    }
74}