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    /// Find registered identities for related work; 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 OMP lifecycle state signal
111    #[arg(long, value_name = "VALUE", conflicts_with = "clear_state")]
112    pub state: Option<ActivityStateValue>,
113
114    /// Remove the OMP lifecycle state signal
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)]
146#[command(
147    about = "Find identity assignments for related work",
148    after_help = "Neighbor selection: prefer an agent whose current work is related to the request. Do not contact arbitrary agents merely because they are idle or discoverable. If no suitable neighbor is apparent, do not broadcast; ask the user or report that no suitable neighbor was found. An explicit recipient chosen by the user takes precedence."
149)]
150pub struct DiscoverArgs {
151    /// Maximum records to print; zero prints all records
152    #[arg(long, default_value_t = 20)]
153    pub limit: usize,
154
155    /// Only include records updated within this many hours
156    #[arg(long, value_name = "HOURS")]
157    pub recent: Option<i64>,
158
159    /// Only include records in this realm
160    #[arg(long, value_name = "NAME")]
161    pub realm: Option<String>,
162
163    /// Include stopped assignments
164    #[arg(long)]
165    pub all: bool,
166
167    /// Print the complete assignments as JSON
168    #[arg(long)]
169    pub json: bool,
170}
171
172#[derive(Debug, Args)]
173pub struct PruneArgs {
174    /// Remove records with updated_at before this RFC 3339 timestamp
175    #[arg(long, value_name = "TIMESTAMP", required = true)]
176    pub before: String,
177
178    /// Preview matching records without deleting them
179    #[arg(long)]
180    pub dry_run: bool,
181
182    /// Print the prune report as JSON
183    #[arg(long)]
184    pub json: bool,
185}
186
187#[derive(Debug, Args)]
188#[command(about = "Output the agent-facing identity workflow manual")]
189pub struct PrimeArgs {
190    /// Output only the workflow prelude
191    #[arg(long)]
192    pub prelude: bool,
193
194    /// Wrap the documentation in a JSON object
195    #[arg(long)]
196    pub json: bool,
197}
198
199pub fn build_cli() -> Command {
200    Cli::command()
201}