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
86#[derive(Parser, Debug)]
87pub struct AuthArgs {
88    #[command(subcommand)]
89    pub command: AuthCommand,
90}
91
92#[derive(Subcommand, Debug)]
93pub enum AuthCommand {
94    /// Authenticate with GitHub Copilot using device flow
95    Copilot(CopilotAuthArgs),
96}
97
98#[derive(Parser, Debug)]
99pub struct CopilotAuthArgs {
100    /// GitHub Enterprise URL or domain (e.g. company.ghe.com)
101    #[arg(long)]
102    pub enterprise_url: Option<String>,
103
104    /// GitHub OAuth app client ID for Copilot device flow
105    #[arg(long, env = "CODETETHER_COPILOT_OAUTH_CLIENT_ID")]
106    pub client_id: Option<String>,
107}
108
109#[derive(Parser, Debug)]
110pub struct TuiArgs {
111    /// Project directory
112    pub project: Option<PathBuf>,
113}
114
115#[derive(Parser, Debug)]
116pub struct ServeArgs {
117    /// Port to listen on
118    #[arg(short, long, default_value = "4096")]
119    pub port: u16,
120
121    /// Hostname to bind to
122    #[arg(long, default_value = "127.0.0.1")]
123    pub hostname: String,
124
125    /// Enable mDNS discovery
126    #[arg(long)]
127    pub mdns: bool,
128}
129
130#[derive(Parser, Debug)]
131pub struct RunArgs {
132    /// Message to send (can be multiple words, quoted or unquoted)
133    pub message: String,
134
135    /// Continue the last session
136    #[arg(short, long)]
137    pub continue_session: bool,
138
139    /// Session ID to continue
140    #[arg(short, long)]
141    pub session: Option<String>,
142
143    /// Model to use (provider/model format)
144    #[arg(short, long)]
145    pub model: Option<String>,
146
147    /// Agent to use
148    #[arg(long)]
149    pub agent: Option<String>,
150
151    /// Output format
152    #[arg(long, default_value = "default", value_parser = ["default", "json"])]
153    pub format: String,
154
155    /// Files to attach
156    #[arg(short, long)]
157    pub file: Vec<PathBuf>,
158}
159
160#[derive(Parser, Debug, Clone)]
161pub struct A2aArgs {
162    /// A2A server URL
163    #[arg(short, long, env = "CODETETHER_SERVER")]
164    pub server: String,
165
166    /// Authentication token
167    #[arg(short, long, env = "CODETETHER_TOKEN")]
168    pub token: Option<String>,
169
170    /// Worker name
171    #[arg(short, long, env = "CODETETHER_WORKER_NAME")]
172    pub name: Option<String>,
173
174    /// Comma-separated list of codebase paths
175    #[arg(short, long)]
176    pub codebases: Option<String>,
177
178    /// Auto-approve policy: all, safe (read-only), none
179    #[arg(long, default_value = "safe", value_parser = ["all", "safe", "none"])]
180    pub auto_approve: String,
181
182    /// Email for task completion reports
183    #[arg(short, long)]
184    pub email: Option<String>,
185
186    /// Push notification endpoint URL
187    #[arg(long)]
188    pub push_url: Option<String>,
189}
190
191#[derive(Parser, Debug)]
192pub struct ConfigArgs {
193    /// Show current configuration
194    #[arg(long)]
195    pub show: bool,
196
197    /// Initialize default configuration
198    #[arg(long)]
199    pub init: bool,
200
201    /// Set a configuration value
202    #[arg(long)]
203    pub set: Option<String>,
204}
205
206#[derive(Parser, Debug)]
207pub struct SwarmArgs {
208    /// Task to execute with swarm
209    pub task: String,
210
211    /// Model to use (provider/model format, e.g. openrouter/stepfun/step-3.5-flash:free)
212    #[arg(short, long)]
213    pub model: Option<String>,
214
215    /// Decomposition strategy: auto, domain, data, stage, none
216    #[arg(short = 's', long, default_value = "auto")]
217    pub strategy: String,
218
219    /// Maximum number of concurrent sub-agents
220    #[arg(long, default_value = "100")]
221    pub max_subagents: usize,
222
223    /// Maximum steps per sub-agent
224    #[arg(long, default_value = "100")]
225    pub max_steps: usize,
226
227    /// Timeout per sub-agent (seconds)
228    #[arg(long, default_value = "300")]
229    pub timeout: u64,
230
231    /// Output as JSON
232    #[arg(long)]
233    pub json: bool,
234}
235
236#[derive(Parser, Debug)]
237pub struct RlmArgs {
238    /// Query to answer about the content
239    pub query: String,
240
241    /// File paths to analyze
242    #[arg(short, long)]
243    pub file: Vec<PathBuf>,
244
245    /// Direct content to analyze (use - for stdin)
246    #[arg(long)]
247    pub content: Option<String>,
248
249    /// Content type hint: code, logs, conversation, documents, auto
250    #[arg(long, default_value = "auto")]
251    pub content_type: String,
252
253    /// Maximum tokens for output
254    #[arg(long, default_value = "4000")]
255    pub max_tokens: usize,
256
257    /// Output as JSON
258    #[arg(long)]
259    pub json: bool,
260
261    /// Enable verbose output (shows context summary)
262    #[arg(short, long)]
263    pub verbose: bool,
264}
265
266#[derive(Parser, Debug)]
267pub struct RalphArgs {
268    /// Action to perform
269    #[arg(value_parser = ["run", "status", "create-prd"])]
270    pub action: String,
271
272    /// Path to prd.json file
273    #[arg(short, long, default_value = "prd.json")]
274    pub prd: PathBuf,
275
276    /// Feature name (for create-prd)
277    #[arg(short, long)]
278    pub feature: Option<String>,
279
280    /// Project name (for create-prd)
281    #[arg(long = "project-name")]
282    pub project_name: Option<String>,
283
284    /// Maximum iterations
285    #[arg(long, default_value = "10")]
286    pub max_iterations: usize,
287
288    /// Model to use
289    #[arg(short, long)]
290    pub model: Option<String>,
291
292    /// Output as JSON
293    #[arg(long)]
294    pub json: bool,
295}
296
297#[derive(Parser, Debug)]
298pub struct McpArgs {
299    /// Action to perform
300    #[arg(value_parser = ["serve", "connect", "list-tools", "call"])]
301    pub action: String,
302
303    /// Command to spawn for connecting to MCP server
304    #[arg(short, long)]
305    pub command: Option<String>,
306
307    /// Server name for registry
308    #[arg(long)]
309    pub server_name: Option<String>,
310
311    /// Tool name for call action
312    #[arg(long)]
313    pub tool: Option<String>,
314
315    /// JSON arguments for tool call
316    #[arg(long)]
317    pub arguments: Option<String>,
318
319    /// Output as JSON
320    #[arg(long)]
321    pub json: bool,
322}
323
324#[derive(Parser, Debug)]
325pub struct StatsArgs {
326    /// Show tool execution history
327    #[arg(short, long)]
328    pub tools: bool,
329
330    /// Show file change history
331    #[arg(short, long)]
332    pub files: bool,
333
334    /// Show token usage
335    #[arg(long)]
336    pub tokens: bool,
337
338    /// Filter by tool name
339    #[arg(long)]
340    pub tool: Option<String>,
341
342    /// Filter by file path
343    #[arg(long)]
344    pub file: Option<String>,
345
346    /// Number of recent entries to show
347    #[arg(short, long, default_value = "20")]
348    pub limit: usize,
349
350    /// Output as JSON
351    #[arg(long)]
352    pub json: bool,
353
354    /// Show all/summary (default shows summary)
355    #[arg(long)]
356    pub all: bool,
357}
358
359#[derive(Parser, Debug)]
360pub struct CleanupArgs {
361    /// Dry run - show what would be cleaned up without deleting
362    #[arg(short, long)]
363    pub dry_run: bool,
364
365    /// Clean up worktrees only (not branches)
366    #[arg(long)]
367    pub worktrees_only: bool,
368
369    /// Output as JSON
370    #[arg(long)]
371    pub json: bool,
372}