Skip to main content

agent_runtime/
lib.rs

1//! `agent-runtime` CLI library. Render / install / doctor /
2//! audit-drift for graysurf/agent-runtime-kit.
3//!
4//! ## Determinism contract (Resolved Decision #9)
5//!
6//! Render output must be a pure function of the source-root contents:
7//! no wall-clock time, no hash-randomized iteration. The crate-wide
8//! `#![deny(...)]` attribute below pairs with `clippy.toml` to make
9//! `std::collections::HashMap`, `std::time::SystemTime::now`, and
10//! `chrono::Utc::now` build failures. The single sanctioned time
11//! value is `render::time::source_commit_timestamp()`. Helpers under
12//! `render::helpers` are the only sanctioned `HashMap` site — Tera's
13//! `Function` trait forces the signature, and the lint is silenced
14//! exactly there.
15//!
16//! Source: `agent-runtime-kit/docs/source/inventory-target-architecture.md`
17//! → Resolved Decision #9.
18#![deny(clippy::disallowed_types, clippy::disallowed_methods)]
19
20use clap::{Parser, Subcommand};
21use std::process::ExitCode;
22
23pub mod audit_drift;
24pub mod commands;
25pub mod doctor;
26pub mod gc_backups;
27pub mod install;
28pub mod live_surface;
29pub mod managed_block;
30pub mod prune_stale;
31pub mod purge_state;
32pub mod render;
33pub mod restore_backups;
34pub mod uninstall;
35
36#[derive(Parser)]
37#[command(
38    name = "agent-runtime",
39    version,
40    long_version = nils_build_info::long_version(env!("CARGO_PKG_VERSION")),
41    about = "Render / install / doctor / audit-drift for graysurf/agent-runtime-kit."
42)]
43pub struct Cli {
44    #[command(subcommand)]
45    pub command: Command,
46}
47
48#[derive(Subcommand)]
49pub enum Command {
50    /// Render `core/` + `targets/<product>/` into `build/<product>/`.
51    Render(commands::render::RenderArgs),
52    /// Activate rendered output against a product's runtime home.
53    Install(commands::install::InstallArgs),
54    /// Remove installed renderer output from a product's runtime home.
55    Uninstall(commands::uninstall::UninstallArgs),
56    /// Diagnose host setup, runtime roots, and required CLI floors.
57    Doctor(commands::doctor::DoctorArgs),
58    /// Preview or apply the host bootstrap phases for Codex and Claude.
59    BootstrapHost(commands::bootstrap_host::BootstrapHostArgs),
60    /// Detect source-vs-rendered, rendered-vs-live, and unsafe drift.
61    AuditDrift(commands::audit_drift::AuditDriftArgs),
62    /// Prune old backups under `<state_home>/backups/`.
63    GcBackups(commands::gc_backups::GcBackupsArgs),
64    /// List the skills an `install` would activate for a product.
65    ListSkills(commands::list_skills::ListSkillsArgs),
66    /// Render standardized PR / MR bodies for forge-cli create flows.
67    PrBody(commands::pr_body::PrBodyArgs),
68    /// Remove stale managed runtime-home surfaces.
69    PruneStale(commands::prune_stale::PruneStaleArgs),
70    /// Restore a runtime home from a recorded backup snapshot.
71    RestoreBackups(commands::restore_backups::RestoreBackupsArgs),
72    /// Purge runtime-managed state (use with caution).
73    PurgeState(commands::purge_state::PurgeStateArgs),
74}
75
76impl Command {
77    fn name(&self) -> &'static str {
78        match self {
79            Command::Render(_) => "render",
80            Command::Install(_) => "install",
81            Command::Uninstall(_) => "uninstall",
82            Command::Doctor(_) => "doctor",
83            Command::BootstrapHost(_) => "bootstrap-host",
84            Command::AuditDrift(_) => "audit-drift",
85            Command::GcBackups(_) => "gc-backups",
86            Command::ListSkills(_) => "list-skills",
87            Command::PrBody(_) => "pr-body",
88            Command::PruneStale(_) => "prune-stale",
89            Command::RestoreBackups(_) => "restore-backups",
90            Command::PurgeState(_) => "purge-state",
91        }
92    }
93}
94
95pub fn run() -> ExitCode {
96    let cli = Cli::parse();
97    let name = cli.command.name();
98    match cli.command {
99        Command::Render(args) => match commands::render::run(args) {
100            Ok(code) => ExitCode::from(code),
101            Err(err) => {
102                eprintln!("agent-runtime render: {err:#}");
103                ExitCode::from(2)
104            }
105        },
106        Command::AuditDrift(args) => match commands::audit_drift::run(args) {
107            Ok(code) => ExitCode::from(code),
108            Err(err) => {
109                eprintln!("agent-runtime audit-drift: {err:#}");
110                ExitCode::from(2)
111            }
112        },
113        Command::BootstrapHost(args) => match commands::bootstrap_host::run(args) {
114            Ok(code) => ExitCode::from(code),
115            Err(err) => {
116                eprintln!("agent-runtime bootstrap-host: {err:#}");
117                ExitCode::from(2)
118            }
119        },
120        Command::Install(args) => match commands::install::run(args) {
121            Ok(code) => ExitCode::from(code),
122            Err(err) => {
123                eprintln!("agent-runtime install: {err:#}");
124                ExitCode::from(2)
125            }
126        },
127        Command::Uninstall(args) => match commands::uninstall::run(args) {
128            Ok(code) => ExitCode::from(code),
129            Err(err) => {
130                eprintln!("agent-runtime uninstall: {err:#}");
131                ExitCode::from(2)
132            }
133        },
134        Command::RestoreBackups(args) => match commands::restore_backups::run(args) {
135            Ok(code) => ExitCode::from(code),
136            Err(err) => {
137                eprintln!("agent-runtime restore-backups: {err:#}");
138                ExitCode::from(2)
139            }
140        },
141        Command::PurgeState(args) => match commands::purge_state::run(args) {
142            Ok(code) => ExitCode::from(code),
143            Err(err) => {
144                eprintln!("agent-runtime purge-state: {err:#}");
145                ExitCode::from(2)
146            }
147        },
148        Command::GcBackups(args) => match commands::gc_backups::run(args) {
149            Ok(code) => ExitCode::from(code),
150            Err(err) => {
151                eprintln!("agent-runtime gc-backups: {err:#}");
152                ExitCode::from(2)
153            }
154        },
155        Command::ListSkills(args) => match commands::list_skills::run(args) {
156            Ok(code) => ExitCode::from(code),
157            Err(err) => {
158                eprintln!("agent-runtime list-skills: {err:#}");
159                ExitCode::from(2)
160            }
161        },
162        Command::PrBody(args) => match commands::pr_body::run(args) {
163            Ok(code) => ExitCode::from(code),
164            Err(err) => {
165                eprintln!("agent-runtime pr-body: {err:#}");
166                ExitCode::from(2)
167            }
168        },
169        Command::PruneStale(args) => match commands::prune_stale::run(args) {
170            Ok(code) => ExitCode::from(code),
171            Err(err) => {
172                eprintln!("agent-runtime prune-stale: {err:#}");
173                ExitCode::from(2)
174            }
175        },
176        Command::Doctor(args) => match commands::doctor::run(args) {
177            Ok(code) => ExitCode::from(code),
178            Err(err) => {
179                eprintln!("agent-runtime {name}: {err:#}");
180                ExitCode::from(2)
181            }
182        },
183    }
184}