Skip to main content

tftio_cli_common/
meta.rs

1//! Canonical metadata command surface shared across workspace binaries.
2
3use clap::Subcommand;
4use clap_complete::Shell;
5
6use crate::{JsonOutput, StandardCommand, StandardCommandMap, agent::AgentSubcommand};
7
8/// Canonical metadata commands exposed by the shared CLI contract.
9#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
10pub enum MetaCommand {
11    /// Show version information.
12    Version {
13        /// Emit JSON instead of plain text.
14        #[arg(long)]
15        json: bool,
16    },
17    /// Show license text.
18    License,
19    /// Generate shell completion scripts.
20    Completions {
21        /// Shell to generate completions for.
22        shell: Shell,
23    },
24    /// Run health checks.
25    Doctor {
26        /// Emit JSON instead of plain text.
27        #[arg(long)]
28        json: bool,
29    },
30    /// Inspect or emit agent-skill artifacts for this tool.
31    Agent {
32        /// Agent-skill subcommand.
33        #[command(subcommand)]
34        command: AgentSubcommand,
35    },
36}
37
38impl StandardCommandMap for MetaCommand {
39    fn to_standard_command(&self, _output: JsonOutput) -> StandardCommand {
40        match self {
41            Self::Version { json } => StandardCommand::Version {
42                output: JsonOutput::from_flag(*json),
43            },
44            Self::License => StandardCommand::License,
45            Self::Completions { shell } => StandardCommand::Completions { shell: *shell },
46            Self::Doctor { json } => StandardCommand::Doctor {
47                output: JsonOutput::from_flag(*json),
48            },
49            Self::Agent { command } => StandardCommand::Agent {
50                command: command.clone(),
51            },
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use clap::{Parser, Subcommand};
59
60    use super::*;
61    use crate::{JsonOutput, StandardCommand, map_standard_command};
62
63    #[derive(Debug, Parser)]
64    #[command(name = "tool")]
65    struct Cli {
66        #[command(subcommand)]
67        command: Command,
68    }
69
70    #[derive(Debug, Subcommand)]
71    enum Command {
72        /// Canonical metadata commands.
73        Meta {
74            #[command(subcommand)]
75            command: MetaCommand,
76        },
77    }
78
79    fn parse_meta(argv: &[&str]) -> MetaCommand {
80        match Cli::parse_from(argv).command {
81            Command::Meta { command } => command,
82        }
83    }
84
85    #[test]
86    fn parses_meta_version_json() {
87        let command = parse_meta(&["tool", "meta", "version", "--json"]);
88        assert_eq!(command, MetaCommand::Version { json: true });
89        assert_eq!(
90            map_standard_command(&command, JsonOutput::Text),
91            StandardCommand::Version {
92                output: JsonOutput::Json
93            }
94        );
95    }
96
97    #[test]
98    fn parses_meta_license() {
99        let command = parse_meta(&["tool", "meta", "license"]);
100        assert_eq!(command, MetaCommand::License);
101        assert_eq!(
102            map_standard_command(&command, JsonOutput::Text),
103            StandardCommand::License
104        );
105    }
106
107    #[test]
108    fn parses_meta_completions() {
109        let command = parse_meta(&["tool", "meta", "completions", "bash"]);
110        assert_eq!(command, MetaCommand::Completions { shell: Shell::Bash });
111        assert_eq!(
112            map_standard_command(&command, JsonOutput::Text),
113            StandardCommand::Completions { shell: Shell::Bash }
114        );
115    }
116
117    #[test]
118    fn parses_meta_doctor_json() {
119        let command = parse_meta(&["tool", "meta", "doctor", "--json"]);
120        assert_eq!(command, MetaCommand::Doctor { json: true });
121        assert_eq!(
122            map_standard_command(&command, JsonOutput::Text),
123            StandardCommand::Doctor {
124                output: JsonOutput::Json
125            }
126        );
127    }
128
129    #[test]
130    fn rejects_meta_update() {
131        let result = Cli::try_parse_from(["tool", "meta", "update"]);
132        assert!(result.is_err(), "meta update must no longer parse");
133    }
134}