1use clap::{Parser, Subcommand};
2
3pub mod commands;
4pub mod config;
5pub mod output;
6pub mod span_context;
7
8const 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 Agents(commands::agents::Args),
61
62 Record(Box<commands::record::Args>),
64 Reply(commands::reply::Args),
66 Resolve(commands::resolve::Args),
68 Emit(commands::emit::Args),
70
71 Show(commands::show::Args),
73 Ls(commands::ls::Args),
75 #[command(alias = "blame")]
77 Praise(commands::praise::Args),
78 Review(commands::freshness::Args),
80
81 Compact(commands::compact::Args),
83
84 Haiku,
86}
87
88pub fn run() {
89 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}