Skip to main content

codetether_agent/cli/
mod.rs

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