//! agent-manager — a CLI to manage local AI coding agents. //! //! The crate is organised around a few core concepts: //! //! * cli — clap definitions for the am command line interface. //! * config — YAML schema, loading, validation, includes, path expansion. //! * catalog — the merged agent catalog (embedded defaults + user config). //! * state — the local installation database (JSON file). //! * process — background/foreground process management (PID bookkeeping). //! * installers — one installer per method (npm, pip, cargo, go, bun, curl, //! binary, git). //! * commands — one module per CLI command. pub mod app; pub mod agent_config; pub mod automation; pub mod backup; pub mod catalog; pub mod costs; pub mod templates; pub mod catalog_remote; pub mod cli; pub mod commands; pub mod config; pub mod context; pub mod dashboard; pub mod deps; pub mod doctor; pub mod download; pub mod events; pub mod help; pub mod history; pub mod hooks; pub mod i18n; pub mod installers; pub mod lab; pub mod models; pub mod nav; pub mod output; pub mod probe; pub mod providers; pub mod projects; pub mod ps; pub mod process; pub mod repl; pub mod runner; pub mod secrets; pub mod sessions; pub mod shell; pub mod state; pub mod sync; pub mod playbook; pub mod plugins; pub mod tables; pub mod theme; pub mod toolchain; pub mod version; pub mod web; use clap::Parser; /// CLI entry point used by the am binary. Returns the process exit code. /// /// The whole run happens on a dedicated thread with a large stack: clap's /// derive code for the full command tree (40+ subcommands) uses deep stack /// frames in debug builds, which overflows the Windows main-thread default /// (1 MB). An 8 MB thread keeps every build mode on every platform safe. pub fn main_entry() -> i32 { std::thread::Builder::new() .name("am-main".to_string()) .stack_size(8 * 1024 * 1024) .spawn(real_main) .expect("cannot spawn the am main thread") .join() .unwrap_or(1) } /// The actual entry point, running on the big-stack thread. fn real_main() -> i32 { // Enable ANSI escape sequences on Windows consoles (best effort): the // colored crate and the theme palettes both emit raw VT codes. #[cfg(windows)] let _ = colored::control::set_virtual_terminal(true); // Nushell-style help: 'am -h', 'am install -h', 'am ls --help', ... all // show the custom help before clap gets a chance to parse the line. let args: Vec = std::env::args().collect(); if let Some(code) = help::intercept(&args) { return code; } let cli = cli::Cli::parse(); // Issue #74: resolve the output language before anything else so even // config errors are localized; the REPL can switch it at runtime. i18n::set_lang(i18n::resolve(cli.lang)); match app::App::from_cli(cli) { Ok(app) => { // Issue #53: enforce the session retention at every startup // (silent; the primary event journal is never touched). let days = app .config .settings .sessions_retention_days .unwrap_or(sessions::DEFAULT_RETENTION_DAYS); if let Err(e) = sessions::purge_expired(&app, days) { app.log.verbose(&format!("session retention skipped: {e:#}")); } match commands::execute(&app) { Ok(code) => code, Err(err) => { if app.json() { let payload = serde_json::json!({ "error": format!("{:#}", err), }); let _ = serde_json::to_writer_pretty(std::io::stdout(), &payload); let _ = writeln!(std::io::stdout()); } else { eprintln!("{} {}", "error:".red().bold(), format!("{:#}", err)); } 1 } } } Err(err) => { // Config could not even be loaded: fall back to plain stderr output. eprintln!("{} {}", "error:".red().bold(), format!("{:#}", err)); 1 } } } #[allow(unused_imports)] use colored::Colorize; use std::io::Write;