Skip to main content

qualifier/cli/
mod.rs

1use clap::{Parser, Subcommand};
2
3pub mod commands;
4pub mod config;
5pub mod output;
6pub mod span_context;
7
8// Clap doesn't natively group subcommands into headed sections in the
9// parent --help, so we render the Commands block ourselves via a custom
10// help_template. If you add, rename, or remove a subcommand, update
11// HELP_TEMPLATE to match — the Commands enum below is still the source
12// of truth for parsing.
13const HELP_TEMPLATE: &str = "\
14{about-with-newline}
15{usage-heading} {usage}
16
17For AI agents:
18  agents     Self-contained guide for AI coding agents (start here)
19
20Record observations:
21  record     Record an annotation: `qualifier record <kind> <location> [message]`
22  reply      Reply to an existing record (id-prefix or location)
23  resolve    Resolve (close) an existing record (id-prefix or location)
24  emit       Emit a raw record of any type
25
26Inspect annotations:
27  show       Show annotations for an artifact
28  ls         List artifacts by kind
29  praise     Show who annotated an artifact and why (alias: blame)
30  review     Check freshness of annotations against current code
31
32Maintain:
33  compact    Compact a .qual file
34
35Other:
36  haiku      Print a random qualifier haiku
37  help       Print this message or the help of the given subcommand(s)
38
39Run `qualifier <COMMAND> --help` for command-specific options.
40
41Options:
42{options}
43";
44
45#[derive(Parser)]
46#[command(
47    name = "qualifier",
48    version,
49    about = "Deterministic quality annotations for software artifacts",
50    help_template = HELP_TEMPLATE
51)]
52pub struct Cli {
53    #[command(subcommand)]
54    pub command: Commands,
55}
56
57#[derive(Subcommand)]
58pub enum Commands {
59    /// Self-contained guide for AI coding agents (start here)
60    Agents(commands::agents::Args),
61
62    /// Record an annotation: `qualifier record <kind> <location> [message]`
63    Record(Box<commands::record::Args>),
64    /// Reply to an existing record (id-prefix or location)
65    Reply(commands::reply::Args),
66    /// Resolve (close) an existing record (id-prefix or location)
67    Resolve(commands::resolve::Args),
68    /// Emit a raw record of any type: `qualifier emit <type> <subject> --body '<JSON>'`
69    Emit(commands::emit::Args),
70
71    /// Show annotations for an artifact
72    Show(commands::show::Args),
73    /// List artifacts by kind
74    Ls(commands::ls::Args),
75    /// Show who annotated an artifact and why
76    #[command(alias = "blame")]
77    Praise(commands::praise::Args),
78    /// Check freshness of annotations against current code
79    Review(commands::freshness::Args),
80
81    /// Compact a .qual file
82    Compact(commands::compact::Args),
83
84    /// Print a random qualifier haiku
85    Haiku,
86}
87
88pub fn run() {
89    // Detect if the user typed "blame" so we can print a hint
90    let used_blame_alias = std::env::args().nth(1).is_some_and(|arg| arg == "blame");
91
92    let cli = Cli::parse();
93
94    if used_blame_alias {
95        eprintln!(
96            "hint: the command is \"praise\" \u{2014} qualifier tracks who helped, not who to blame"
97        );
98    }
99
100    let result: crate::Result<()> = match cli.command {
101        Commands::Agents(args) => commands::agents::run(args),
102        Commands::Record(args) => commands::record::run(*args),
103        Commands::Reply(args) => commands::reply::run(args),
104        Commands::Resolve(args) => commands::resolve::run(args),
105        Commands::Emit(args) => commands::emit::run(args),
106        Commands::Show(args) => commands::show::run(args),
107        Commands::Ls(args) => commands::ls::run(args),
108        Commands::Compact(args) => commands::compact::run(args),
109        Commands::Haiku => {
110            commands::haiku::run();
111            Ok(())
112        }
113        Commands::Praise(args) => commands::praise::run(args),
114        Commands::Review(args) => commands::freshness::run(args),
115    };
116
117    if let Err(e) = result {
118        eprintln!("qualifier: {e}");
119        std::process::exit(1);
120    }
121}