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