Files
agent-manager/src/lib.rs
T

131 lines
4.2 KiB
Rust

//! 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 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 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;
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<String> = 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;