-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.rs
More file actions
97 lines (87 loc) · 3.67 KB
/
Copy pathmain.rs
File metadata and controls
97 lines (87 loc) · 3.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! greeter -- minimal agent-friendly CLI.
//!
//! Demonstrates every pattern from the agent-cli-framework:
//! - Modular structure (cli, config, error, output, commands/)
//! - JSON envelope on stdout, coloured table on TTY
//! - Semantic exit codes (0-4)
//! - `--quiet` to suppress informational output
//! - `agent-info` for machine-readable capability discovery
//! - `config show/path` for configuration management
//! - `skill install` to register with AI agent platforms
//! - `update` for distribution-aware update checks
//! - Rich help: Tips + Examples via `after_long_help`
//! - `doctor` for structured dependency diagnostics
//! - Tested duplicate guard primitive for expensive operations
mod cli;
mod commands;
mod config;
mod error;
mod guard;
mod output;
use clap::Parser;
use cli::{Cli, Commands, ConfigAction, SkillAction};
use output::{Ctx, Format};
/// Pre-scan argv for --json before clap parses. This ensures --json is
/// honored even on help, version, and parse-error paths where clap hasn't
/// populated the Cli struct yet.
fn has_json_flag() -> bool {
std::env::args_os()
.take_while(|a| a != "--")
.any(|a| a == "--json")
}
fn main() {
let json_flag = has_json_flag();
// Use try_parse so clap errors go through the JSON envelope instead of
// printing human-only text that breaks agent pipelines.
let cli = match Cli::try_parse() {
Ok(cli) => cli,
Err(e) => {
// Help and --version are NOT errors. Exit 0.
if matches!(
e.kind(),
clap::error::ErrorKind::DisplayHelp | clap::error::ErrorKind::DisplayVersion
) {
let format = Format::detect(json_flag);
match format {
Format::Json => {
if let Err(err) = output::print_help_json(e) {
output::print_error(format, &err);
std::process::exit(err.exit_code());
}
std::process::exit(0);
}
Format::Human => e.exit(), // clap prints coloured help, exits 0
}
}
// Actual parse errors -- always exit 3, never let clap own the exit.
let format = Format::detect(json_flag);
output::print_clap_error(format, &e);
std::process::exit(3);
}
};
let ctx = Ctx::new(cli.json, cli.quiet);
// Config is loaded lazily -- only commands that need it call config::load().
// This ensures agent-info, config path, skill, and hello always work,
// even when config.toml is malformed.
let result = match cli.command {
Commands::Hello { name, style } => commands::hello::run(ctx, name, style),
Commands::AgentInfo { command } => commands::agent_info::run(command.as_deref()),
Commands::Skill { action } => match action {
SkillAction::Install => commands::skill::install(ctx),
SkillAction::Status => commands::skill::status(ctx),
},
Commands::Config { action } => match action {
ConfigAction::Show => config::load().and_then(|cfg| commands::config::show(ctx, &cfg)),
ConfigAction::Path => commands::config::path(ctx),
},
Commands::Doctor => commands::doctor::run(ctx),
Commands::Update { check, force } => {
config::load().and_then(|cfg| commands::update::run(ctx, check, force, &cfg))
}
Commands::Contract { code } => commands::contract::run(ctx, code),
};
if let Err(e) = result {
output::print_error(ctx.format, &e);
std::process::exit(e.exit_code());
}
}