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    /// Run benchmark suite against models using Ralph PRDs
89    Benchmark(BenchmarkArgs),
90
91    /// Moltbook — social network for AI agents
92    Moltbook(MoltbookArgs),
93}
94
95#[derive(Parser, Debug)]
96pub struct AuthArgs {
97    #[command(subcommand)]
98    pub command: AuthCommand,
99}
100
101#[derive(Subcommand, Debug)]
102pub enum AuthCommand {
103    /// Authenticate with GitHub Copilot using device flow
104    Copilot(CopilotAuthArgs),
105
106    /// Login to a CodeTether server with email/password
107    Login(LoginAuthArgs),
108}
109
110#[derive(Parser, Debug)]
111pub struct LoginAuthArgs {
112    /// CodeTether server URL (e.g., https://api.codetether.io)
113    #[arg(short, long, env = "CODETETHER_SERVER")]
114    pub server: String,
115
116    /// Email address
117    #[arg(short, long)]
118    pub email: Option<String>,
119}
120
121#[derive(Parser, Debug)]
122pub struct CopilotAuthArgs {
123    /// GitHub Enterprise URL or domain (e.g. company.ghe.com)
124    #[arg(long)]
125    pub enterprise_url: Option<String>,
126
127    /// GitHub OAuth app client ID for Copilot device flow
128    #[arg(long, env = "CODETETHER_COPILOT_OAUTH_CLIENT_ID")]
129    pub client_id: Option<String>,
130}
131
132#[derive(Parser, Debug)]
133pub struct TuiArgs {
134    /// Project directory
135    pub project: Option<PathBuf>,
136}
137
138#[derive(Parser, Debug)]
139pub struct ServeArgs {
140    /// Port to listen on
141    #[arg(short, long, default_value = "4096")]
142    pub port: u16,
143
144    /// Hostname to bind to
145    #[arg(long, default_value = "127.0.0.1")]
146    pub hostname: String,
147
148    /// Enable mDNS discovery
149    #[arg(long)]
150    pub mdns: bool,
151}
152
153#[derive(Parser, Debug)]
154pub struct RunArgs {
155    /// Message to send (can be multiple words, quoted or unquoted)
156    pub message: String,
157
158    /// Continue the last session
159    #[arg(short, long)]
160    pub continue_session: bool,
161
162    /// Session ID to continue
163    #[arg(short, long)]
164    pub session: Option<String>,
165
166    /// Model to use (provider/model format)
167    #[arg(short, long)]
168    pub model: Option<String>,
169
170    /// Agent to use
171    #[arg(long)]
172    pub agent: Option<String>,
173
174    /// Output format
175    #[arg(long, default_value = "default", value_parser = ["default", "json"])]
176    pub format: String,
177
178    /// Files to attach
179    #[arg(short, long)]
180    pub file: Vec<PathBuf>,
181}
182
183#[derive(Parser, Debug, Clone)]
184pub struct A2aArgs {
185    /// A2A server URL
186    #[arg(short, long, env = "CODETETHER_SERVER")]
187    pub server: String,
188
189    /// Authentication token
190    #[arg(short, long, env = "CODETETHER_TOKEN")]
191    pub token: Option<String>,
192
193    /// Worker name
194    #[arg(short, long, env = "CODETETHER_WORKER_NAME")]
195    pub name: Option<String>,
196
197    /// Comma-separated list of codebase paths
198    #[arg(short, long)]
199    pub codebases: Option<String>,
200
201    /// Auto-approve policy: all, safe (read-only), none
202    #[arg(long, default_value = "safe", value_parser = ["all", "safe", "none"])]
203    pub auto_approve: String,
204
205    /// Email for task completion reports
206    #[arg(short, long)]
207    pub email: Option<String>,
208
209    /// Push notification endpoint URL
210    #[arg(long)]
211    pub push_url: Option<String>,
212}
213
214#[derive(Parser, Debug)]
215pub struct ConfigArgs {
216    /// Show current configuration
217    #[arg(long)]
218    pub show: bool,
219
220    /// Initialize default configuration
221    #[arg(long)]
222    pub init: bool,
223
224    /// Set a configuration value
225    #[arg(long)]
226    pub set: Option<String>,
227}
228
229#[derive(Parser, Debug)]
230pub struct SwarmArgs {
231    /// Task to execute with swarm
232    pub task: String,
233
234    /// Model to use (provider/model format, e.g. openrouter/z-ai/glm-4.7)
235    #[arg(short, long)]
236    pub model: Option<String>,
237
238    /// Decomposition strategy: auto, domain, data, stage, none
239    #[arg(short = 's', long, default_value = "auto")]
240    pub strategy: String,
241
242    /// Maximum number of concurrent sub-agents
243    #[arg(long, default_value = "100")]
244    pub max_subagents: usize,
245
246    /// Maximum steps per sub-agent
247    #[arg(long, default_value = "100")]
248    pub max_steps: usize,
249
250    /// Timeout per sub-agent (seconds)
251    #[arg(long, default_value = "300")]
252    pub timeout: u64,
253
254    /// Output as JSON
255    #[arg(long)]
256    pub json: bool,
257}
258
259#[derive(Parser, Debug)]
260pub struct RlmArgs {
261    /// Query to answer about the content
262    pub query: String,
263
264    /// File paths to analyze
265    #[arg(short, long)]
266    pub file: Vec<PathBuf>,
267
268    /// Direct content to analyze (use - for stdin)
269    #[arg(long)]
270    pub content: Option<String>,
271
272    /// Content type hint: code, logs, conversation, documents, auto
273    #[arg(long, default_value = "auto")]
274    pub content_type: String,
275
276    /// Maximum tokens for output
277    #[arg(long, default_value = "4000")]
278    pub max_tokens: usize,
279
280    /// Output as JSON
281    #[arg(long)]
282    pub json: bool,
283
284    /// Enable verbose output (shows context summary)
285    #[arg(short, long)]
286    pub verbose: bool,
287}
288
289#[derive(Parser, Debug)]
290pub struct RalphArgs {
291    /// Action to perform
292    #[arg(value_parser = ["run", "status", "create-prd"])]
293    pub action: String,
294
295    /// Path to prd.json file
296    #[arg(short, long, default_value = "prd.json")]
297    pub prd: PathBuf,
298
299    /// Feature name (for create-prd)
300    #[arg(short, long)]
301    pub feature: Option<String>,
302
303    /// Project name (for create-prd)
304    #[arg(long = "project-name")]
305    pub project_name: Option<String>,
306
307    /// Maximum iterations
308    #[arg(long, default_value = "10")]
309    pub max_iterations: usize,
310
311    /// Model to use
312    #[arg(short, long)]
313    pub model: Option<String>,
314
315    /// Output as JSON
316    #[arg(long)]
317    pub json: bool,
318}
319
320#[derive(Parser, Debug)]
321pub struct McpArgs {
322    /// Action to perform
323    #[arg(value_parser = ["serve", "connect", "list-tools", "call"])]
324    pub action: String,
325
326    /// Command to spawn for connecting to MCP server
327    #[arg(short, long)]
328    pub command: Option<String>,
329
330    /// Server name for registry
331    #[arg(long)]
332    pub server_name: Option<String>,
333
334    /// Tool name for call action
335    #[arg(long)]
336    pub tool: Option<String>,
337
338    /// JSON arguments for tool call
339    #[arg(long)]
340    pub arguments: Option<String>,
341
342    /// Output as JSON
343    #[arg(long)]
344    pub json: bool,
345}
346
347#[derive(Parser, Debug)]
348pub struct StatsArgs {
349    /// Show tool execution history
350    #[arg(short, long)]
351    pub tools: bool,
352
353    /// Show file change history
354    #[arg(short, long)]
355    pub files: bool,
356
357    /// Show token usage
358    #[arg(long)]
359    pub tokens: bool,
360
361    /// Filter by tool name
362    #[arg(long)]
363    pub tool: Option<String>,
364
365    /// Filter by file path
366    #[arg(long)]
367    pub file: Option<String>,
368
369    /// Number of recent entries to show
370    #[arg(short, long, default_value = "20")]
371    pub limit: usize,
372
373    /// Output as JSON
374    #[arg(long)]
375    pub json: bool,
376
377    /// Show all/summary (default shows summary)
378    #[arg(long)]
379    pub all: bool,
380}
381
382#[derive(Parser, Debug)]
383pub struct CleanupArgs {
384    /// Dry run - show what would be cleaned up without deleting
385    #[arg(short, long)]
386    pub dry_run: bool,
387
388    /// Clean up worktrees only (not branches)
389    #[arg(long)]
390    pub worktrees_only: bool,
391
392    /// Output as JSON
393    #[arg(long)]
394    pub json: bool,
395}
396
397#[derive(Parser, Debug)]
398pub struct ModelsArgs {
399    /// Filter by provider name
400    #[arg(short, long)]
401    pub provider: Option<String>,
402
403    /// Output as JSON
404    #[arg(long)]
405    pub json: bool,
406}
407
408#[derive(Parser, Debug)]
409pub struct MoltbookArgs {
410    #[command(subcommand)]
411    pub command: MoltbookCommand,
412}
413
414#[derive(Subcommand, Debug)]
415pub enum MoltbookCommand {
416    /// Register a new agent on Moltbook
417    Register(MoltbookRegisterArgs),
418
419    /// Check claim status
420    Status,
421
422    /// View your Moltbook profile
423    Profile,
424
425    /// Update your profile description
426    UpdateProfile(MoltbookUpdateProfileArgs),
427
428    /// Create a post (defaults to m/general)
429    Post(MoltbookPostArgs),
430
431    /// Post a CodeTether introduction to Moltbook
432    Intro,
433
434    /// Run a heartbeat — check feed, show recent posts
435    Heartbeat,
436
437    /// Comment on a Moltbook post
438    Comment(MoltbookCommentArgs),
439
440    /// Search Moltbook posts and comments
441    Search(MoltbookSearchArgs),
442}
443
444#[derive(Parser, Debug)]
445pub struct MoltbookRegisterArgs {
446    /// Agent name to register on Moltbook
447    pub name: String,
448
449    /// Optional extra description (CodeTether branding is always included)
450    #[arg(short, long)]
451    pub description: Option<String>,
452}
453
454#[derive(Parser, Debug)]
455pub struct MoltbookUpdateProfileArgs {
456    /// Extra description to append
457    #[arg(short, long)]
458    pub description: Option<String>,
459}
460
461#[derive(Parser, Debug)]
462pub struct MoltbookPostArgs {
463    /// Post title
464    pub title: String,
465
466    /// Post content
467    #[arg(short, long)]
468    pub content: String,
469
470    /// Submolt to post in
471    #[arg(short, long, default_value = "general")]
472    pub submolt: String,
473}
474
475#[derive(Parser, Debug)]
476pub struct MoltbookCommentArgs {
477    /// Post ID to comment on
478    pub post_id: String,
479
480    /// Comment content
481    pub content: String,
482}
483
484#[derive(Parser, Debug)]
485pub struct MoltbookSearchArgs {
486    /// Search query
487    pub query: String,
488
489    /// Max results
490    #[arg(short, long, default_value = "10")]
491    pub limit: usize,
492}
493
494#[derive(Parser, Debug)]
495pub struct BenchmarkArgs {
496    /// Directory containing benchmark PRD files
497    #[arg(long, default_value = "benchmarks")]
498    pub prd_dir: String,
499
500    /// Models to benchmark (comma-separated, format: provider:model)
501    #[arg(short, long, value_delimiter = ',')]
502    pub models: Vec<String>,
503
504    /// Only run PRDs matching this tier (1, 2, or 3)
505    #[arg(long)]
506    pub tier: Option<u8>,
507
508    /// Run model×PRD combos in parallel
509    #[arg(long)]
510    pub parallel: bool,
511
512    /// Maximum iterations per story
513    #[arg(long, default_value = "10")]
514    pub max_iterations: usize,
515
516    /// Timeout per story in seconds
517    #[arg(long, default_value = "300")]
518    pub story_timeout: u64,
519
520    /// Output file path
521    #[arg(short, long, default_value = "benchmark_results.json")]
522    pub output: String,
523
524    /// Cost ceiling per run in USD (prevents runaway spending)
525    #[arg(long, default_value = "50.0")]
526    pub cost_ceiling: f64,
527
528    /// Submit results to this API URL (e.g. https://opencode.ai/bench/submission)
529    #[arg(long)]
530    pub submit_url: Option<String>,
531
532    /// API key for submitting results (Bearer token)
533    #[arg(long, env = "BENCHMARK_API_KEY")]
534    pub submit_key: Option<String>,
535
536    /// Output as JSON to stdout
537    #[arg(long)]
538    pub json: bool,
539}