Skip to main content

agent_id_cli/
cli.rs

1use crate::activity::ActivityStateValue;
2
3use clap::{Args, Command, CommandFactory, Parser, Subcommand};
4
5#[derive(Debug, Parser)]
6#[command(
7    name = "agent-id",
8    version,
9    about = "Portable identity registry for coding-agent sessions",
10    long_about = "agent-id assigns permanent human-readable names to stable agent-harness session IDs."
11)]
12pub struct Cli {
13    #[command(subcommand)]
14    pub command: Commands,
15}
16
17#[derive(Debug, Subcommand)]
18pub enum Commands {
19    /// Register a new permanent identity for a session
20    Register(RegisterArgs),
21    /// Look up the identity already registered for a session
22    Lookup(LookupArgs),
23    /// Look up the identity for AGENT_ID_SESSION_ID
24    Current(CurrentArgs),
25    /// Set or clear the current-work summary for a registered session
26    Annotate(AnnotateArgs),
27    /// List identity assignments; stopped assignments are hidden by default
28    Discover(DiscoverArgs),
29    /// Remove identity assignments older than a cutoff
30    Prune(PruneArgs),
31    /// Output the agent-facing identity workflow manual
32    Prime(PrimeArgs),
33}
34
35#[derive(Debug, Args)]
36pub struct CurrentArgs {
37    /// Print the complete assignment as JSON
38    #[arg(long)]
39    pub json: bool,
40}
41
42#[derive(Debug, Args)]
43pub struct RegisterArgs {
44    /// Harness session ID; falls back to AGENT_ID_SESSION_ID
45    #[arg(value_name = "SESSION_ID", conflicts_with = "session_id")]
46    pub session: Option<String>,
47
48    /// Explicit harness session ID
49    #[arg(long = "session-id", value_name = "ID")]
50    pub session_id: Option<String>,
51
52    /// Prefer this family name when allocating the identity
53    #[arg(long, value_name = "NAME")]
54    pub family: Option<String>,
55
56    /// Computer realm; falls back to AGENT_REALM or config
57    #[arg(long, value_name = "NAME")]
58    pub realm: Option<String>,
59
60    /// Print the complete assignment as JSON
61    #[arg(long)]
62    pub json: bool,
63}
64
65impl RegisterArgs {
66    pub fn explicit_session(&self) -> Option<&str> {
67        self.session.as_deref().or(self.session_id.as_deref())
68    }
69}
70
71#[derive(Debug, Args)]
72pub struct LookupArgs {
73    /// Session ID, canonical name, or slug; falls back to AGENT_ID_SESSION_ID
74    #[arg(value_name = "IDENTIFIER", conflicts_with = "session_id")]
75    pub input: Option<String>,
76
77    /// Explicit session ID
78    #[arg(long = "session-id", value_name = "ID")]
79    pub session_id: Option<String>,
80
81    /// Print the complete assignment as JSON
82    #[arg(long)]
83    pub json: bool,
84}
85
86impl LookupArgs {
87    pub fn explicit_input(&self) -> Option<&str> {
88        self.input.as_deref().or(self.session_id.as_deref())
89    }
90}
91
92#[derive(Debug, Args)]
93pub struct AnnotateArgs {
94    /// Harness session ID; falls back to AGENT_ID_SESSION_ID
95    #[arg(value_name = "SESSION_ID", conflicts_with = "session_id")]
96    pub session: Option<String>,
97
98    /// Explicit harness session ID
99    #[arg(long = "session-id", value_name = "ID")]
100    pub session_id: Option<String>,
101
102    /// Set the concise current-work summary
103    #[arg(long, value_name = "TEXT", conflicts_with = "clear_summary")]
104    pub summary: Option<String>,
105
106    /// Remove the current-work summary
107    #[arg(long, conflicts_with = "summary")]
108    pub clear_summary: bool,
109
110    /// Set the activity state
111    #[arg(long, value_name = "VALUE", conflicts_with = "clear_state")]
112    pub state: Option<ActivityStateValue>,
113
114    /// Remove the activity state
115    #[arg(long, conflicts_with = "state")]
116    pub clear_state: bool,
117
118    /// Set the current working directory
119    #[arg(long, value_name = "PATH", conflicts_with = "clear_cwd")]
120    pub cwd: Option<String>,
121
122    /// Remove the current working directory
123    #[arg(long, conflicts_with = "cwd")]
124    pub clear_cwd: bool,
125
126    /// Set namespaced extension metadata from OWNER=JSON
127    #[arg(long = "extension", value_name = "OWNER=JSON")]
128    pub extensions: Vec<String>,
129
130    /// Remove one namespaced extension metadata value
131    #[arg(long = "clear-extension", value_name = "OWNER")]
132    pub clear_extensions: Vec<String>,
133
134    /// Print the complete assignment as JSON
135    #[arg(long)]
136    pub json: bool,
137}
138
139impl AnnotateArgs {
140    pub fn explicit_session(&self) -> Option<&str> {
141        self.session.as_deref().or(self.session_id.as_deref())
142    }
143}
144
145#[derive(Debug, Args)]
146pub struct DiscoverArgs {
147    /// Maximum records to print; zero prints all records
148    #[arg(long, default_value_t = 20)]
149    pub limit: usize,
150
151    /// Only include records updated within this many hours
152    #[arg(long, value_name = "HOURS")]
153    pub recent: Option<i64>,
154
155    /// Only include records in this realm
156    #[arg(long, value_name = "NAME")]
157    pub realm: Option<String>,
158
159    /// Include stopped assignments
160    #[arg(long)]
161    pub all: bool,
162
163    /// Print the complete assignments as JSON
164    #[arg(long)]
165    pub json: bool,
166}
167
168#[derive(Debug, Args)]
169pub struct PruneArgs {
170    /// Remove records with updated_at before this RFC 3339 timestamp
171    #[arg(long, value_name = "TIMESTAMP", required = true)]
172    pub before: String,
173
174    /// Preview matching records without deleting them
175    #[arg(long)]
176    pub dry_run: bool,
177
178    /// Print the prune report as JSON
179    #[arg(long)]
180    pub json: bool,
181}
182
183#[derive(Debug, Args)]
184#[command(about = "Output the agent-facing identity workflow manual")]
185pub struct PrimeArgs {
186    /// Output only the workflow prelude
187    #[arg(long)]
188    pub prelude: bool,
189
190    /// Wrap the documentation in a JSON object
191    #[arg(long)]
192    pub json: bool,
193}
194
195pub fn build_cli() -> Command {
196    Cli::command()
197}