Skip to main content

codetether_agent/cli/
mod.rs

1//! CLI command definitions and handlers
2
3pub mod auth;
4pub mod config;
5pub mod run;
6
7use clap::{Parser, Subcommand};
8use std::path::PathBuf;
9
10/// CodeTether Agent - A2A-native AI coding agent
11///
12/// By default, runs as an A2A worker connecting to the CodeTether server.
13/// Use the 'tui' subcommand for interactive terminal mode.
14#[derive(Parser, Debug)]
15#[command(name = "codetether")]
16#[command(version, about, long_about = None)]
17pub struct Cli {
18    /// Project directory to operate on
19    #[arg(global = true)]
20    pub project: Option<PathBuf>,
21
22    /// Print logs to stderr
23    #[arg(long, global = true)]
24    pub print_logs: bool,
25
26    /// Log level
27    #[arg(long, global = true, value_parser = ["DEBUG", "INFO", "WARN", "ERROR"])]
28    pub log_level: Option<String>,
29
30    // Default A2A args (when no subcommand)
31    /// A2A server URL (default mode)
32    #[arg(short, long, env = "CODETETHER_SERVER")]
33    pub server: Option<String>,
34
35    /// Authentication token
36    #[arg(short, long, env = "CODETETHER_TOKEN")]
37    pub token: Option<String>,
38
39    /// Worker name
40    #[arg(short, long, env = "CODETETHER_WORKER_NAME")]
41    pub name: Option<String>,
42
43    #[command(subcommand)]
44    pub command: Option<Command>,
45}
46
47#[derive(Subcommand, Debug)]
48pub enum Command {
49    /// Start interactive terminal UI
50    Tui(TuiArgs),
51
52    /// Start a headless API server
53    Serve(ServeArgs),
54
55    /// Run with a message (non-interactive)
56    Run(RunArgs),
57
58    /// Authenticate provider credentials and store in Vault
59    Auth(AuthArgs),
60
61    /// Manage configuration
62    Config(ConfigArgs),
63
64    /// A2A worker mode (explicit - also the default)
65    Worker(A2aArgs),
66
67    /// Execute task with parallel sub-agents (swarm mode)
68    Swarm(SwarmArgs),
69
70    /// Analyze large content with RLM (Recursive Language Model)
71    Rlm(RlmArgs),
72
73    /// Autonomous PRD-driven agent loop (Ralph)
74    Ralph(RalphArgs),
75
76    /// Model Context Protocol (MCP) server/client
77    Mcp(McpArgs),
78
79    /// Show telemetry and execution statistics
80    Stats(StatsArgs),
81
82    /// Clean up orphaned worktrees and branches from failed Ralph runs
83    Cleanup(CleanupArgs),
84
85    /// List available models from all configured providers
86    Models(ModelsArgs),
87}
88
89#[derive(Parser, Debug)]
90pub struct AuthArgs {
91    #[command(subcommand)]
92    pub command: AuthCommand,
93}
94
95#[derive(Subcommand, Debug)]
96pub enum AuthCommand {
97    /// Authenticate with GitHub Copilot using device flow
98    Copilot(CopilotAuthArgs),
99}
100
101#[derive(Parser, Debug)]
102pub struct CopilotAuthArgs {
103    /// GitHub Enterprise URL or domain (e.g. company.ghe.com)
104    #[arg(long)]
105    pub enterprise_url: Option<String>,
106
107    /// GitHub OAuth app client ID for Copilot device flow
108    #[arg(long, env = "CODETETHER_COPILOT_OAUTH_CLIENT_ID")]
109    pub client_id: Option<String>,
110}
111
112#[derive(Parser, Debug)]
113pub struct TuiArgs {
114    /// Project directory
115    pub project: Option<PathBuf>,
116}
117
118#[derive(Parser, Debug)]
119pub struct ServeArgs {
120    /// Port to listen on
121    #[arg(short, long, default_value = "4096")]
122    pub port: u16,
123
124    /// Hostname to bind to
125    #[arg(long, default_value = "127.0.0.1")]
126    pub hostname: String,
127
128    /// Enable mDNS discovery
129    #[arg(long)]
130    pub mdns: bool,
131}
132
133#[derive(Parser, Debug)]
134pub struct RunArgs {
135    /// Message to send (can be multiple words, quoted or unquoted)
136    pub message: String,
137
138    /// Continue the last session
139    #[arg(short, long)]
140    pub continue_session: bool,
141
142    /// Session ID to continue
143    #[arg(short, long)]
144    pub session: Option<String>,
145
146    /// Model to use (provider/model format)
147    #[arg(short, long)]
148    pub model: Option<String>,
149
150    /// Agent to use
151    #[arg(long)]
152    pub agent: Option<String>,
153
154    /// Output format
155    #[arg(long, default_value = "default", value_parser = ["default", "json"])]
156    pub format: String,
157
158    /// Files to attach
159    #[arg(short, long)]
160    pub file: Vec<PathBuf>,
161}
162
163#[derive(Parser, Debug, Clone)]
164pub struct A2aArgs {
165    /// A2A server URL
166    #[arg(short, long, env = "CODETETHER_SERVER")]
167    pub server: String,
168
169    /// Authentication token
170    #[arg(short, long, env = "CODETETHER_TOKEN")]
171    pub token: Option<String>,
172
173    /// Worker name
174    #[arg(short, long, env = "CODETETHER_WORKER_NAME")]
175    pub name: Option<String>,
176
177    /// Comma-separated list of codebase paths
178    #[arg(short, long)]
179    pub codebases: Option<String>,
180
181    /// Auto-approve policy: all, safe (read-only), none
182    #[arg(long, default_value = "safe", value_parser = ["all", "safe", "none"])]
183    pub auto_approve: String,
184
185    /// Email for task completion reports
186    #[arg(short, long)]
187    pub email: Option<String>,
188
189    /// Push notification endpoint URL
190    #[arg(long)]
191    pub push_url: Option<String>,
192}
193
194#[derive(Parser, Debug)]
195pub struct ConfigArgs {
196    /// Show current configuration
197    #[arg(long)]
198    pub show: bool,
199
200    /// Initialize default configuration
201    #[arg(long)]
202    pub init: bool,
203
204    /// Set a configuration value
205    #[arg(long)]
206    pub set: Option<String>,
207}
208
209#[derive(Parser, Debug)]
210pub struct SwarmArgs {
211    /// Task to execute with swarm
212    pub task: String,
213
214    /// Model to use (provider/model format, e.g. openrouter/z-ai/glm-4.7)
215    #[arg(short, long)]
216    pub model: Option<String>,
217
218    /// Decomposition strategy: auto, domain, data, stage, none
219    #[arg(short = 's', long, default_value = "auto")]
220    pub strategy: String,
221
222    /// Maximum number of concurrent sub-agents
223    #[arg(long, default_value = "100")]
224    pub max_subagents: usize,
225
226    /// Maximum steps per sub-agent
227    #[arg(long, default_value = "100")]
228    pub max_steps: usize,
229
230    /// Timeout per sub-agent (seconds)
231    #[arg(long, default_value = "300")]
232    pub timeout: u64,
233
234    /// Output as JSON
235    #[arg(long)]
236    pub json: bool,
237}
238
239#[derive(Parser, Debug)]
240pub struct RlmArgs {
241    /// Query to answer about the content
242    pub query: String,
243
244    /// File paths to analyze
245    #[arg(short, long)]
246    pub file: Vec<PathBuf>,
247
248    /// Direct content to analyze (use - for stdin)
249    #[arg(long)]
250    pub content: Option<String>,
251
252    /// Content type hint: code, logs, conversation, documents, auto
253    #[arg(long, default_value = "auto")]
254    pub content_type: String,
255
256    /// Maximum tokens for output
257    #[arg(long, default_value = "4000")]
258    pub max_tokens: usize,
259
260    /// Output as JSON
261    #[arg(long)]
262    pub json: bool,
263
264    /// Enable verbose output (shows context summary)
265    #[arg(short, long)]
266    pub verbose: bool,
267}
268
269#[derive(Parser, Debug)]
270pub struct RalphArgs {
271    /// Action to perform
272    #[arg(value_parser = ["run", "status", "create-prd"])]
273    pub action: String,
274
275    /// Path to prd.json file
276    #[arg(short, long, default_value = "prd.json")]
277    pub prd: PathBuf,
278
279    /// Feature name (for create-prd)
280    #[arg(short, long)]
281    pub feature: Option<String>,
282
283    /// Project name (for create-prd)
284    #[arg(long = "project-name")]
285    pub project_name: Option<String>,
286
287    /// Maximum iterations
288    #[arg(long, default_value = "10")]
289    pub max_iterations: usize,
290
291    /// Model to use
292    #[arg(short, long)]
293    pub model: Option<String>,
294
295    /// Output as JSON
296    #[arg(long)]
297    pub json: bool,
298}
299
300#[derive(Parser, Debug)]
301pub struct McpArgs {
302    /// Action to perform
303    #[arg(value_parser = ["serve", "connect", "list-tools", "call"])]
304    pub action: String,
305
306    /// Command to spawn for connecting to MCP server
307    #[arg(short, long)]
308    pub command: Option<String>,
309
310    /// Server name for registry
311    #[arg(long)]
312    pub server_name: Option<String>,
313
314    /// Tool name for call action
315    #[arg(long)]
316    pub tool: Option<String>,
317
318    /// JSON arguments for tool call
319    #[arg(long)]
320    pub arguments: Option<String>,
321
322    /// Output as JSON
323    #[arg(long)]
324    pub json: bool,
325}
326
327#[derive(Parser, Debug)]
328pub struct StatsArgs {
329    /// Show tool execution history
330    #[arg(short, long)]
331    pub tools: bool,
332
333    /// Show file change history
334    #[arg(short, long)]
335    pub files: bool,
336
337    /// Show token usage
338    #[arg(long)]
339    pub tokens: bool,
340
341    /// Filter by tool name
342    #[arg(long)]
343    pub tool: Option<String>,
344
345    /// Filter by file path
346    #[arg(long)]
347    pub file: Option<String>,
348
349    /// Number of recent entries to show
350    #[arg(short, long, default_value = "20")]
351    pub limit: usize,
352
353    /// Output as JSON
354    #[arg(long)]
355    pub json: bool,
356
357    /// Show all/summary (default shows summary)
358    #[arg(long)]
359    pub all: bool,
360}
361
362#[derive(Parser, Debug)]
363pub struct CleanupArgs {
364    /// Dry run - show what would be cleaned up without deleting
365    #[arg(short, long)]
366    pub dry_run: bool,
367
368    /// Clean up worktrees only (not branches)
369    #[arg(long)]
370    pub worktrees_only: bool,
371
372    /// Output as JSON
373    #[arg(long)]
374    pub json: bool,
375}
376
377#[derive(Parser, Debug)]
378pub struct ModelsArgs {
379    /// Filter by provider name
380    #[arg(short, long)]
381    pub provider: Option<String>,
382
383    /// Output as JSON
384    #[arg(long)]
385    pub json: bool,
386}