Skip to main content

codetether_agent/cli/
mod.rs

1//! CLI command definitions and handlers
2
3pub mod auth;
4pub mod browserctl;
5pub mod clipboard;
6pub mod config;
7pub mod context;
8pub mod go_ralph;
9pub mod oracle;
10pub mod run;
11pub mod search;
12pub mod search_render;
13
14use clap::{Parser, Subcommand};
15use std::path::PathBuf;
16
17/// CodeTether Agent - A2A-native AI coding agent
18///
19/// By default, runs as an A2A worker connecting to the CodeTether server.
20/// Use the 'tui' subcommand for interactive terminal mode.
21#[derive(Parser, Debug)]
22#[command(name = "codetether")]
23#[command(version, about, long_about = None)]
24pub struct Cli {
25    /// Project directory to operate on
26    #[arg(global = true, last = true)]
27    pub project: Option<PathBuf>,
28
29    /// Print logs to stderr
30    #[arg(long, global = true)]
31    pub print_logs: bool,
32
33    /// Log level
34    #[arg(long, global = true, value_parser = ["DEBUG", "INFO", "WARN", "ERROR"])]
35    pub log_level: Option<String>,
36
37    // Default A2A args (when no subcommand)
38    /// A2A server URL (default mode)
39    #[arg(short, long, env = "CODETETHER_SERVER")]
40    pub server: Option<String>,
41
42    /// Authentication token
43    #[arg(short, long, env = "CODETETHER_TOKEN")]
44    pub token: Option<String>,
45
46    /// Worker name
47    #[arg(short, long, env = "CODETETHER_WORKER_NAME")]
48    pub name: Option<String>,
49
50    #[command(subcommand)]
51    pub command: Option<Command>,
52}
53
54#[derive(Subcommand, Debug)]
55pub enum Command {
56    /// Start interactive terminal UI
57    Tui(TuiArgs),
58
59    /// Start a headless API server
60    Serve(ServeArgs),
61
62    /// Run with a message (non-interactive)
63    Run(RunArgs),
64
65    /// Create or update GitHub pull requests with CodeTether provenance
66    Pr(PrArgs),
67
68    /// Authenticate provider credentials and store in Vault
69    Auth(AuthArgs),
70
71    /// Manage configuration
72    Config(ConfigArgs),
73
74    /// Convert clipboard images into terminal-pasteable CodeTether input
75    Clipboard(clipboard::ClipboardArgs),
76
77    /// Browse or reset the active session context
78    Context(ContextArgs),
79
80    /// A2A worker mode (explicit - also the default)
81    Worker(A2aArgs),
82
83    /// Internal command: Git credential helper for worker-managed repositories.
84    #[command(hide = true)]
85    GitCredentialHelper(GitCredentialHelperArgs),
86
87    /// Spawn an A2A agent runtime with auto card registration and peer discovery
88    Spawn(SpawnArgs),
89
90    /// Execute task with parallel sub-agents (swarm mode)
91    Swarm(SwarmArgs),
92
93    /// Internal command: execute one swarm subtask payload.
94    #[command(hide = true)]
95    SwarmSubagent(SwarmSubagentArgs),
96
97    /// Analyze large content with RLM (Recursive Language Model)
98    Rlm(RlmArgs),
99
100    /// Deterministic oracle utilities (validate/sync)
101    Oracle(OracleArgs),
102
103    /// Autonomous PRD-driven agent loop (Ralph)
104    Ralph(RalphArgs),
105
106    /// Model Context Protocol (MCP) server/client
107    Mcp(McpArgs),
108
109    /// Show telemetry and execution statistics
110    Stats(StatsArgs),
111
112    /// Clean up orphaned worktrees and branches from failed Ralph runs
113    Cleanup(CleanupArgs),
114
115    /// List available models from all configured providers
116    Models(ModelsArgs),
117
118    /// Build a persistent codebase index for faster workspace introspection
119    Index(IndexArgs),
120
121    /// Run benchmark suite against models using Ralph PRDs
122    Benchmark(BenchmarkArgs),
123
124    /// Moltbook — social network for AI agents
125    Moltbook(MoltbookArgs),
126
127    /// Manage OKRs (Objectives and Key Results)
128    Okr(OkrArgs),
129
130    /// OKR-governed autonomous opportunity scanner/executor
131    Forage(ForageArgs),
132
133    /// LLM-routed search across grep/glob/web/memory/RLM backends
134    Search(SearchArgs),
135
136    /// Control a local Chromium browser via DevTools (alias: browser)
137    #[command(name = "browserctl", visible_alias = "browser")]
138    Browserctl(browserctl::BrowserCtlArgs),
139}
140
141#[derive(Parser, Debug)]
142pub struct AuthArgs {
143    #[command(subcommand)]
144    pub command: AuthCommand,
145}
146
147#[derive(Subcommand, Debug)]
148pub enum AuthCommand {
149    /// Authenticate with GitHub Copilot using device flow
150    Copilot(CopilotAuthArgs),
151
152    /// Authenticate OpenAI Codex with a ChatGPT subscription via OAuth (Plus/Pro/Team/Enterprise)
153    Codex(CodexAuthArgs),
154
155    /// Import browser cookies (Netscape format) into Vault for a provider
156    Cookies(CookieAuthArgs),
157
158    /// Register a new CodeTether account with email/password
159    Register(RegisterAuthArgs),
160
161    /// Login to a CodeTether server with email/password
162    Login(LoginAuthArgs),
163}
164
165#[derive(Parser, Debug)]
166pub struct RegisterAuthArgs {
167    /// CodeTether server URL (e.g., https://api.codetether.run)
168    #[arg(short, long, env = "CODETETHER_SERVER")]
169    pub server: String,
170
171    /// Email address
172    #[arg(short, long)]
173    pub email: Option<String>,
174
175    /// First name (optional)
176    #[arg(long)]
177    pub first_name: Option<String>,
178
179    /// Last name (optional)
180    #[arg(long)]
181    pub last_name: Option<String>,
182
183    /// Referral source (optional)
184    #[arg(long)]
185    pub referral_source: Option<String>,
186}
187
188#[derive(Parser, Debug)]
189pub struct LoginAuthArgs {
190    /// CodeTether server URL (e.g., https://api.codetether.io)
191    #[arg(short, long, env = "CODETETHER_SERVER")]
192    pub server: String,
193
194    /// Email address
195    #[arg(short, long)]
196    pub email: Option<String>,
197}
198
199#[derive(Parser, Debug)]
200pub struct CopilotAuthArgs {
201    /// GitHub Enterprise URL or domain (e.g. company.ghe.com)
202    #[arg(long)]
203    pub enterprise_url: Option<String>,
204
205    /// GitHub OAuth app client ID for Copilot device flow
206    #[arg(long, env = "CODETETHER_COPILOT_OAUTH_CLIENT_ID")]
207    pub client_id: Option<String>,
208}
209
210#[derive(Parser, Debug)]
211pub struct CodexAuthArgs {
212    /// Use ChatGPT device-code authentication flow (recommended for remote SSH sessions)
213    #[arg(long, default_value_t = false)]
214    pub device_code: bool,
215}
216
217#[derive(Parser, Debug)]
218pub struct CookieAuthArgs {
219    /// Provider ID to store under (e.g. nextdoor-web, gemini-web)
220    #[arg(long, default_value = "nextdoor-web")]
221    pub provider: String,
222
223    /// Path to Netscape cookies export file
224    #[arg(long)]
225    pub file: PathBuf,
226
227    /// Keep all valid cookies instead of filtering to auth-focused names
228    #[arg(long, default_value_t = false)]
229    pub keep_all: bool,
230}
231
232#[derive(Parser, Debug)]
233pub struct TuiArgs {
234    /// Project directory
235    pub project: Option<PathBuf>,
236
237    /// Allow network access in sandboxed commands
238    #[arg(long)]
239    pub allow_network: bool,
240
241    /// Disable the built-in A2A peer endpoint. By default the TUI binds an
242    /// A2A peer with auto-port, auto-name, and mDNS-based peer discovery —
243    /// other CodeTether processes on the same host or LAN find each other
244    /// without flags. Pass `--no-a2a` for purely interactive mode.
245    #[arg(long = "no-a2a", action = clap::ArgAction::SetFalse, default_value_t = true)]
246    pub a2a: bool,
247
248    /// Override the auto-picked A2A port. By default the OS assigns one
249    /// (port 0). Specify a port for stable curl-able URLs.
250    #[arg(long)]
251    pub a2a_port: Option<u16>,
252
253    /// A2A bind hostname. The default (`0.0.0.0`) provides the intended
254    /// zero-config path: auto-port, auto-name, and mDNS discovery without
255    /// extra flags. Use `127.0.0.1` to make the peer loopback-only.
256    #[arg(long, default_value = "0.0.0.0")]
257    pub a2a_hostname: String,
258
259    /// Public URL published in the agent card.
260    #[arg(long)]
261    pub a2a_public_url: Option<String>,
262
263    /// Override the auto-picked agent name (default: <host>-<repo>-<pid>).
264    #[arg(long)]
265    pub a2a_name: Option<String>,
266
267    /// Optional description for the A2A card.
268    #[arg(long)]
269    pub a2a_description: Option<String>,
270
271    /// Explicit peer seed URLs (in addition to mDNS-discovered peers).
272    /// Useful for cross-host setups where mDNS isn't routable.
273    #[arg(long, value_delimiter = ',', env = "CODETETHER_A2A_PEERS")]
274    pub a2a_peer: Vec<String>,
275
276    /// Discovery interval for explicit --a2a-peer seeds, in seconds.
277    /// Clamped to ≥ 5. (mDNS is event-driven, not polled.)
278    #[arg(long, default_value = "15")]
279    pub a2a_discovery_interval_secs: u64,
280
281    /// Disable auto-intro to newly discovered peers.
282    #[arg(long = "a2a-no-auto-introduce", action = clap::ArgAction::SetFalse, default_value_t = true)]
283    pub a2a_auto_introduce: bool,
284
285    /// Disable mDNS-based peer discovery. Without mDNS, only explicit
286    /// --a2a-peer seeds are discovered. mDNS is on by default for true P2P.
287    #[arg(long = "a2a-no-mdns", action = clap::ArgAction::SetFalse, default_value_t = true)]
288    pub a2a_mdns: bool,
289}
290
291#[derive(Parser, Debug)]
292pub struct ServeArgs {
293    /// Port to listen on
294    #[arg(short, long, default_value = "4096")]
295    pub port: u16,
296
297    /// Hostname to bind to
298    #[arg(long, default_value = "127.0.0.1")]
299    pub hostname: String,
300
301    /// Enable mDNS discovery
302    #[arg(long)]
303    pub mdns: bool,
304}
305
306#[derive(Parser, Debug)]
307pub struct RunArgs {
308    /// Message to send (can be multiple words, quoted or unquoted)
309    pub message: String,
310
311    /// Continue the last session
312    #[arg(short, long)]
313    pub continue_session: bool,
314
315    /// Session ID to continue
316    #[arg(short, long)]
317    pub session: Option<String>,
318
319    /// Model to use (provider/model format)
320    #[arg(short, long)]
321    pub model: Option<String>,
322
323    /// Agent to use
324    #[arg(long)]
325    pub agent: Option<String>,
326
327    /// Output format
328    #[arg(long, default_value = "default", value_parser = ["default", "json"])]
329    pub format: String,
330
331    /// Files to attach
332    #[arg(short, long)]
333    pub file: Vec<PathBuf>,
334
335    /// Import and continue a Codex CLI session by ID
336    #[arg(long)]
337    pub codex_session: Option<String>,
338
339    /// Maximum agentic loop steps (default: 250, minimum: 1)
340    #[arg(long)]
341    pub max_steps: Option<usize>,
342
343    /// Number of parallel speculative branches to race (1-8, default: 1).
344    /// When > 1, enables many-worlds speculative dev via the collapse controller.
345    #[arg(long, default_value = "1")]
346    pub branches: usize,
347
348    /// Optional comma-separated strategy prompts for speculative branches
349    /// (e.g. "planner,testfirst,minimal,refactor"). Defaults to built-in rotation.
350    #[arg(long, value_delimiter = ',')]
351    pub strategies: Vec<String>,
352}
353
354#[derive(Parser, Debug)]
355pub struct PrArgs {
356    #[command(subcommand)]
357    pub command: PrCommand,
358}
359
360#[derive(Subcommand, Debug)]
361pub enum PrCommand {
362    /// Create or update a pull request for the current branch
363    Create(CreatePrArgs),
364}
365
366#[derive(Parser, Debug, Clone)]
367pub struct CreatePrArgs {
368    /// Pull request title
369    #[arg(long)]
370    pub title: String,
371
372    /// Optional pull request body
373    #[arg(long)]
374    pub body: Option<String>,
375
376    /// Optional path to a pull request body file
377    #[arg(long)]
378    pub body_file: Option<PathBuf>,
379
380    /// Base branch
381    #[arg(long, default_value = "main")]
382    pub base: String,
383
384    /// Head branch override (defaults to current branch)
385    #[arg(long)]
386    pub head: Option<String>,
387
388    /// Create the PR as draft
389    #[arg(long, default_value_t = false)]
390    pub draft: bool,
391
392    /// Emit JSON instead of KEY=VALUE lines
393    #[arg(long, default_value_t = false)]
394    pub json: bool,
395
396    /// Project directory override
397    #[arg(long)]
398    pub project: Option<PathBuf>,
399}
400
401/// Arguments for standalone worker HTTP server (testing/debugging)
402#[derive(Parser, Debug, Clone)]
403pub struct WorkerServerArgs {
404    /// Hostname to bind
405    #[arg(long, default_value = "0.0.0.0")]
406    pub hostname: String,
407
408    /// Port to bind
409    #[arg(short, long, default_value = "8080")]
410    pub port: u16,
411}
412
413#[derive(Parser, Debug, Clone)]
414pub struct A2aArgs {
415    /// A2A server URL
416    #[arg(short, long, env = "CODETETHER_SERVER", default_value = crate::a2a::worker::DEFAULT_A2A_SERVER_URL)]
417    pub server: String,
418
419    /// Authentication token
420    #[arg(short, long, env = "CODETETHER_TOKEN")]
421    pub token: Option<String>,
422
423    /// Worker name
424    #[arg(short, long, env = "CODETETHER_WORKER_NAME")]
425    pub name: Option<String>,
426
427    /// Comma-separated list of workspace paths (alias: --codebases)
428    #[arg(short, long, visible_alias = "codebases")]
429    pub workspaces: Option<String>,
430
431    /// Maximum number of tasks this worker will process concurrently
432    #[arg(
433        long,
434        env = "CODETETHER_WORKER_MAX_CONCURRENT_TASKS",
435        default_value_t = 4
436    )]
437    pub max_concurrent_tasks: usize,
438
439    /// Auto-approve policy: all, safe (read-only), none
440    #[arg(long, default_value = "safe", value_parser = ["all", "safe", "none"])]
441    pub auto_approve: String,
442
443    /// Email for task completion reports
444    #[arg(short, long)]
445    pub email: Option<String>,
446
447    /// Push notification endpoint URL
448    #[arg(long)]
449    pub push_url: Option<String>,
450
451    /// Hostname to bind the worker HTTP server
452    #[arg(long, default_value = "0.0.0.0", env = "CODETETHER_WORKER_HOST")]
453    pub hostname: String,
454
455    /// Port for the worker HTTP server (for health/readiness probes)
456    #[arg(long, default_value = "8080", env = "CODETETHER_WORKER_PORT")]
457    pub port: u16,
458
459    /// Public base URL advertised for this worker's HTTP interfaces.
460    #[arg(long, env = "CODETETHER_WORKER_PUBLIC_URL")]
461    pub public_url: Option<String>,
462
463    /// Disable the worker HTTP server (for environments without K8s)
464    #[arg(long, env = "CODETETHER_WORKER_HTTP_DISABLED")]
465    pub no_http_server: bool,
466}
467
468#[derive(Parser, Debug, Clone)]
469pub struct GitCredentialHelperArgs {
470    /// Workspace/codebase ID for which Git credentials should be minted.
471    #[arg(long = "workspace-id", alias = "codebase-id")]
472    pub workspace_id: String,
473
474    /// CodeTether server URL.
475    #[arg(long, env = "CODETETHER_SERVER")]
476    pub server: Option<String>,
477
478    /// CodeTether bearer token.
479    #[arg(long, env = "CODETETHER_TOKEN")]
480    pub token: Option<String>,
481
482    /// Worker ID requesting the credential.
483    #[arg(long, env = "CODETETHER_WORKER_ID")]
484    pub worker_id: Option<String>,
485
486    /// Git helper operation (get/store/erase). Git passes this positionally.
487    pub operation: Option<String>,
488}
489
490#[derive(Parser, Debug, Clone)]
491pub struct SpawnArgs {
492    /// Agent name. Defaults to <host>-<repo>-<short-pid>.
493    #[arg(short, long)]
494    pub name: Option<String>,
495
496    /// Hostname to bind. 127.0.0.1 (default) is loopback-only and SAFE
497    /// (the peer is unreachable from outside the host) but mDNS multicast
498    /// does NOT traverse the Linux `lo` interface, so same-host peers
499    /// cannot find each other via mDNS at this binding. Use 0.0.0.0 to
500    /// be reachable on the LAN AND to enable mDNS auto-discovery between
501    /// same-host agents (multicast loops back through the real interface).
502    #[arg(long, default_value = "127.0.0.1")]
503    pub hostname: String,
504
505    /// Port to bind. 0 (default) lets the OS pick an available port.
506    /// Specify a port if you need a stable URL for curl scripts.
507    #[arg(short, long, default_value = "0")]
508    pub port: u16,
509
510    /// Public URL published in the agent card (defaults to http://<hostname>:<port>).
511    #[arg(long)]
512    pub public_url: Option<String>,
513
514    /// Optional custom agent description for the card
515    #[arg(short, long)]
516    pub description: Option<String>,
517
518    /// Explicit peer seed URLs (in addition to mDNS-discovered peers).
519    /// Useful for cross-host setups where mDNS isn't routable.
520    #[arg(long, value_delimiter = ',', env = "CODETETHER_A2A_PEERS")]
521    pub peer: Vec<String>,
522
523    /// Discovery interval for explicit --peer seeds, in seconds. Clamped
524    /// to ≥ 5. (mDNS discovery is event-driven, not polled.)
525    #[arg(long, default_value = "15")]
526    pub discovery_interval_secs: u64,
527
528    /// Disable sending an automatic intro message to newly discovered peers
529    #[arg(long = "no-auto-introduce", action = clap::ArgAction::SetFalse, default_value_t = true)]
530    pub auto_introduce: bool,
531
532    /// Disable mDNS announce + browse. Without mDNS, only explicit
533    /// --peer seeds are discovered. mDNS is on by default for true P2P.
534    #[arg(long = "no-mdns", action = clap::ArgAction::SetFalse, default_value_t = true)]
535    pub mdns: bool,
536}
537
538#[derive(Parser, Debug)]
539pub struct ConfigArgs {
540    /// Show current configuration
541    #[arg(long)]
542    pub show: bool,
543
544    /// Initialize default configuration
545    #[arg(long)]
546    pub init: bool,
547
548    /// Set a configuration value
549    #[arg(long)]
550    pub set: Option<String>,
551}
552
553#[derive(Parser, Debug)]
554pub struct ContextArgs {
555    #[command(subcommand)]
556    pub command: ContextCommand,
557}
558
559#[derive(Subcommand, Debug)]
560pub enum ContextCommand {
561    /// Emit a [CONTEXT RESET] marker into the latest session
562    Reset(ContextResetArgs),
563    /// Browse the latest session as virtual turn files
564    Browse(ContextBrowseArgs),
565}
566
567#[derive(Parser, Debug)]
568pub struct ContextResetArgs {
569    /// Optional summary for the reset marker
570    #[arg(long)]
571    pub summary: Option<String>,
572}
573
574#[derive(Parser, Debug)]
575pub struct ContextBrowseArgs {
576    #[command(subcommand)]
577    pub command: Option<ContextBrowseCommand>,
578}
579
580#[derive(Subcommand, Debug)]
581pub enum ContextBrowseCommand {
582    /// List virtual paths for each turn in the latest session
583    List,
584    /// Show one turn as markdown-like text
585    ShowTurn(ContextShowTurnArgs),
586}
587
588#[derive(Parser, Debug)]
589pub struct ContextShowTurnArgs {
590    /// Zero-based turn index
591    pub turn: usize,
592}
593
594#[derive(Parser, Debug)]
595pub struct SwarmArgs {
596    /// Task to execute with swarm
597    pub task: String,
598
599    /// Model to use (provider/model format, e.g. zai/glm-5 or openrouter/z-ai/glm-5)
600    #[arg(short, long)]
601    pub model: Option<String>,
602
603    /// Decomposition strategy: auto, domain, data, stage, none
604    #[arg(short = 's', long, default_value = "auto")]
605    pub strategy: String,
606
607    /// Maximum number of concurrent sub-agents
608    #[arg(long, default_value = "100")]
609    pub max_subagents: usize,
610
611    /// Maximum steps per sub-agent
612    #[arg(long, default_value = "100")]
613    pub max_steps: usize,
614
615    /// Timeout per sub-agent (seconds)
616    #[arg(long, default_value = "300")]
617    pub timeout: u64,
618
619    /// Output as JSON
620    #[arg(long)]
621    pub json: bool,
622
623    /// Sub-agent execution mode: local | k8s
624    #[arg(long, default_value = "local", value_parser = ["local", "k8s", "kubernetes", "kubernetes-pod", "pod"])]
625    pub execution_mode: String,
626
627    /// Maximum concurrent Kubernetes sub-agent pods when execution mode is k8s.
628    #[arg(long, default_value = "8")]
629    pub k8s_pod_budget: usize,
630
631    /// Optional image override for Kubernetes sub-agent pods.
632    #[arg(long)]
633    pub k8s_image: Option<String>,
634}
635
636#[derive(Parser, Debug)]
637pub struct SwarmSubagentArgs {
638    /// Base64 payload for the remote subtask (JSON encoded)
639    #[arg(long)]
640    pub payload_base64: Option<String>,
641
642    /// Read payload from an environment variable
643    #[arg(long, default_value = "CODETETHER_SWARM_SUBTASK_PAYLOAD")]
644    pub payload_env: String,
645}
646
647#[derive(Parser, Debug)]
648pub struct RlmArgs {
649    /// Query to answer about the content
650    pub query: String,
651
652    /// Model to use (provider/model format)
653    #[arg(short, long)]
654    pub model: Option<String>,
655
656    /// File paths to analyze
657    #[arg(short, long)]
658    pub file: Vec<PathBuf>,
659
660    /// Direct content to analyze (use - for stdin)
661    #[arg(long)]
662    pub content: Option<String>,
663
664    /// Content type hint: code, logs, conversation, documents, auto
665    #[arg(long, default_value = "auto")]
666    pub content_type: String,
667
668    /// Maximum tokens for output
669    #[arg(long, default_value = "4000")]
670    pub max_tokens: usize,
671
672    /// Output as JSON
673    #[arg(long)]
674    pub json: bool,
675
676    /// Enable verbose output (shows context summary)
677    #[arg(short, long)]
678    pub verbose: bool,
679
680    /// Deprecated no-op: oracle verification is now always enabled by default
681    #[arg(long)]
682    pub oracle_verify: bool,
683
684    /// Disable deterministic oracle verification (emergency opt-out)
685    #[arg(long)]
686    pub no_oracle_verify: bool,
687
688    /// Number of independent runs for semantic consensus verification
689    #[arg(long, default_value = "1")]
690    pub consensus_runs: usize,
691
692    /// Consensus threshold for semantic verification (1.0 = unanimous)
693    #[arg(long, default_value = "1.0")]
694    pub consensus_threshold: f32,
695
696    /// Temperature for RLM generation (defaults: 0.3 single run, 0.75 consensus)
697    #[arg(long)]
698    pub analysis_temperature: Option<f32>,
699
700    /// Directory to write split oracle JSONL datasets
701    #[arg(long)]
702    pub oracle_out_dir: Option<PathBuf>,
703
704    /// Output prefix for oracle split JSONL files
705    #[arg(long, default_value = "rlm_oracle")]
706    pub oracle_prefix: String,
707}
708
709#[derive(Parser, Debug)]
710pub struct OracleArgs {
711    #[command(subcommand)]
712    pub command: OracleCommand,
713}
714
715#[derive(Subcommand, Debug)]
716pub enum OracleCommand {
717    /// Validate a FINAL(JSON) payload deterministically against source content
718    Validate(OracleValidateArgs),
719    /// Sync pending oracle spool files to MinIO/S3
720    Sync(OracleSyncArgs),
721}
722
723#[derive(Parser, Debug)]
724pub struct OracleValidateArgs {
725    /// Query associated with the payload (used as trace prompt)
726    #[arg(long)]
727    pub query: String,
728
729    /// Source file to validate against
730    #[arg(short, long)]
731    pub file: Option<PathBuf>,
732
733    /// Source content to validate against (use - for stdin)
734    #[arg(long)]
735    pub content: Option<String>,
736
737    /// FINAL(JSON) payload string
738    #[arg(long)]
739    pub payload: Option<String>,
740
741    /// Path to a file containing FINAL(JSON) payload
742    #[arg(long)]
743    pub payload_file: Option<PathBuf>,
744
745    /// Output as JSON
746    #[arg(long)]
747    pub json: bool,
748
749    /// Persist validated record to oracle storage pipeline
750    #[arg(long)]
751    pub persist: bool,
752}
753
754#[derive(Parser, Debug)]
755pub struct OracleSyncArgs {
756    /// Output as JSON
757    #[arg(long)]
758    pub json: bool,
759}
760
761#[derive(Parser, Debug)]
762pub struct RalphArgs {
763    /// Action to perform
764    #[arg(value_parser = ["run", "status", "create-prd"])]
765    pub action: String,
766
767    /// Path to prd.json file
768    #[arg(short, long, default_value = "prd.json")]
769    pub prd: PathBuf,
770
771    /// Feature name (for create-prd)
772    #[arg(short, long)]
773    pub feature: Option<String>,
774
775    /// Project name (for create-prd)
776    #[arg(long = "project-name")]
777    pub project_name: Option<String>,
778
779    /// Maximum iterations
780    #[arg(long, default_value = "10")]
781    pub max_iterations: usize,
782
783    /// Model to use
784    #[arg(short, long)]
785    pub model: Option<String>,
786
787    /// Output as JSON
788    #[arg(long)]
789    pub json: bool,
790}
791
792#[derive(Parser, Debug)]
793pub struct McpArgs {
794    /// Action to perform
795    #[arg(value_parser = ["serve", "connect", "list-tools", "call"])]
796    pub action: String,
797
798    /// Command to spawn for connecting to MCP server (flag form)
799    #[arg(short, long)]
800    pub command: Option<String>,
801
802    /// Server name for registry
803    #[arg(long)]
804    pub server_name: Option<String>,
805
806    /// Tool name for call action
807    #[arg(long)]
808    pub tool: Option<String>,
809
810    /// JSON arguments for tool call
811    #[arg(long)]
812    pub arguments: Option<String>,
813
814    /// Output as JSON
815    #[arg(long)]
816    pub json: bool,
817
818    /// Resolve a worker's first-class bus connection via the control plane.
819    #[arg(long)]
820    pub worker_id: Option<String>,
821
822    /// Resolve the owning worker via a registered workspace ID.
823    #[arg(long)]
824    pub workspace_id: Option<String>,
825
826    /// URL of the CodeTether HTTP server's bus SSE endpoint.
827    /// When set, the MCP server connects to the agent bus and exposes
828    /// bus_events, bus_status, and ralph_status tools plus codetether:// resources.
829    /// Example: http://localhost:8001/v1/bus/stream
830    #[arg(long)]
831    pub bus_url: Option<String>,
832
833    /// Command (and args) to spawn for connecting to MCP server (positional form).
834    /// Example: `codetether mcp connect npx -y @modelcontextprotocol/server-filesystem /path`
835    #[arg(allow_hyphen_values = true)]
836    pub command_args: Vec<String>,
837}
838
839#[derive(Parser, Debug)]
840pub struct StatsArgs {
841    /// Show tool execution history
842    #[arg(short, long)]
843    pub tools: bool,
844
845    /// Show file change history
846    #[arg(short, long)]
847    pub files: bool,
848
849    /// Show token usage
850    #[arg(long)]
851    pub tokens: bool,
852
853    /// Filter by tool name
854    #[arg(long)]
855    pub tool: Option<String>,
856
857    /// Filter by file path
858    #[arg(long)]
859    pub file: Option<String>,
860
861    /// Number of recent entries to show
862    #[arg(short, long, default_value = "20")]
863    pub limit: usize,
864
865    /// Output as JSON
866    #[arg(long)]
867    pub json: bool,
868
869    /// Show all/summary (default shows summary)
870    #[arg(long)]
871    pub all: bool,
872}
873
874#[derive(Parser, Debug)]
875pub struct CleanupArgs {
876    /// Dry run - show what would be cleaned up without deleting
877    #[arg(short, long)]
878    pub dry_run: bool,
879
880    /// Clean up worktrees only (not branches)
881    #[arg(long)]
882    pub worktrees_only: bool,
883
884    /// Output as JSON
885    #[arg(long)]
886    pub json: bool,
887}
888
889#[derive(Parser, Debug)]
890pub struct ModelsArgs {
891    /// Filter by provider name
892    #[arg(short, long)]
893    pub provider: Option<String>,
894
895    /// Output as JSON
896    #[arg(long)]
897    pub json: bool,
898}
899
900#[derive(Parser, Debug)]
901pub struct IndexArgs {
902    /// Root directory to index (defaults to current directory)
903    #[arg(short, long)]
904    pub path: Option<PathBuf>,
905
906    /// Output file for index JSON (defaults to CODETETHER_DATA_DIR/indexes/...)
907    #[arg(short, long)]
908    pub output: Option<PathBuf>,
909
910    /// Maximum file size to index in KiB
911    #[arg(long, default_value = "1024")]
912    pub max_file_size_kib: u64,
913
914    /// Local open-source embedding model identifier
915    #[arg(long, default_value = "hash-v1")]
916    pub embedding_model: String,
917
918    /// Embedding provider ID (`local`, `huggingface`, etc.)
919    #[arg(long, default_value = "local")]
920    pub embedding_provider: String,
921
922    /// Embedding vector dimensions
923    #[arg(long, default_value = "384")]
924    pub embedding_dimensions: usize,
925
926    /// Number of documents per embedding batch
927    #[arg(long, default_value = "32")]
928    pub embedding_batch_size: usize,
929
930    /// Maximum retry attempts for a failed remote embedding batch
931    #[arg(long, default_value = "3")]
932    pub embedding_max_retries: u32,
933
934    /// Initial retry backoff in milliseconds for remote embedding failures
935    #[arg(long, default_value = "250")]
936    pub embedding_retry_initial_ms: u64,
937
938    /// Maximum retry backoff in milliseconds for remote embedding failures
939    #[arg(long, default_value = "2000")]
940    pub embedding_retry_max_ms: u64,
941
942    /// Max characters per file used for embedding input
943    #[arg(long, default_value = "8000")]
944    pub embedding_input_chars: usize,
945
946    /// Include hidden files/directories (dotfiles)
947    #[arg(long, default_value_t = false)]
948    pub include_hidden: bool,
949
950    /// Output result as JSON
951    #[arg(long)]
952    pub json: bool,
953}
954
955#[derive(Parser, Debug)]
956pub struct MoltbookArgs {
957    #[command(subcommand)]
958    pub command: MoltbookCommand,
959}
960
961#[derive(Subcommand, Debug)]
962pub enum MoltbookCommand {
963    /// Register a new agent on Moltbook
964    Register(MoltbookRegisterArgs),
965
966    /// Check claim status
967    Status,
968
969    /// View your Moltbook profile
970    Profile,
971
972    /// Update your profile description
973    UpdateProfile(MoltbookUpdateProfileArgs),
974
975    /// Create a post (defaults to m/general)
976    Post(MoltbookPostArgs),
977
978    /// Post a CodeTether introduction to Moltbook
979    Intro,
980
981    /// Run a heartbeat — check feed, show recent posts
982    Heartbeat,
983
984    /// Comment on a Moltbook post
985    Comment(MoltbookCommentArgs),
986
987    /// Search Moltbook posts and comments
988    Search(MoltbookSearchArgs),
989}
990
991#[derive(Parser, Debug)]
992pub struct MoltbookRegisterArgs {
993    /// Agent name to register on Moltbook
994    pub name: String,
995
996    /// Optional extra description (CodeTether branding is always included)
997    #[arg(short, long)]
998    pub description: Option<String>,
999}
1000
1001#[derive(Parser, Debug)]
1002pub struct MoltbookUpdateProfileArgs {
1003    /// Extra description to append
1004    #[arg(short, long)]
1005    pub description: Option<String>,
1006}
1007
1008#[derive(Parser, Debug)]
1009pub struct MoltbookPostArgs {
1010    /// Post title
1011    pub title: String,
1012
1013    /// Post content
1014    #[arg(short, long)]
1015    pub content: String,
1016
1017    /// Submolt to post in
1018    #[arg(short, long, default_value = "general")]
1019    pub submolt: String,
1020}
1021
1022#[derive(Parser, Debug)]
1023pub struct MoltbookCommentArgs {
1024    /// Post ID to comment on
1025    pub post_id: String,
1026
1027    /// Comment content
1028    pub content: String,
1029}
1030
1031#[derive(Parser, Debug)]
1032pub struct MoltbookSearchArgs {
1033    /// Search query
1034    pub query: String,
1035
1036    /// Max results
1037    #[arg(short, long, default_value = "10")]
1038    pub limit: usize,
1039}
1040
1041#[derive(Parser, Debug)]
1042pub struct BenchmarkArgs {
1043    /// Directory containing benchmark PRD files
1044    #[arg(long, default_value = "benchmarks")]
1045    pub prd_dir: String,
1046
1047    /// Models to benchmark (comma-separated, format: provider:model)
1048    #[arg(short, long, value_delimiter = ',')]
1049    pub models: Vec<String>,
1050
1051    /// Only run PRDs matching this tier (1, 2, or 3)
1052    #[arg(long)]
1053    pub tier: Option<u8>,
1054
1055    /// Run model×PRD combos in parallel
1056    #[arg(long)]
1057    pub parallel: bool,
1058
1059    /// Maximum iterations per story
1060    #[arg(long, default_value = "10")]
1061    pub max_iterations: usize,
1062
1063    /// Timeout per story in seconds
1064    #[arg(long, default_value = "300")]
1065    pub story_timeout: u64,
1066
1067    /// Output file path
1068    #[arg(short, long, default_value = "benchmark_results.json")]
1069    pub output: String,
1070
1071    /// Cost ceiling per run in USD (prevents runaway spending)
1072    #[arg(long, default_value = "50.0")]
1073    pub cost_ceiling: f64,
1074
1075    /// Submit results to this API URL
1076    #[arg(long)]
1077    pub submit_url: Option<String>,
1078
1079    /// API key for submitting results (Bearer token)
1080    #[arg(long, env = "BENCHMARK_API_KEY")]
1081    pub submit_key: Option<String>,
1082
1083    /// Output as JSON to stdout
1084    #[arg(long)]
1085    pub json: bool,
1086}
1087
1088#[derive(Parser, Debug)]
1089pub struct OkrArgs {
1090    /// Action to perform
1091    #[arg(value_parser = ["list", "status", "create", "runs", "export", "stats", "report"])]
1092    pub action: String,
1093
1094    /// OKR ID (UUID)
1095    #[arg(short, long)]
1096    pub id: Option<String>,
1097
1098    /// OKR title (for create)
1099    #[arg(short, long)]
1100    pub title: Option<String>,
1101
1102    /// OKR description (for create)
1103    #[arg(short, long)]
1104    pub description: Option<String>,
1105
1106    /// Target value for key result (for create)
1107    #[arg(long)]
1108    pub target: Option<f64>,
1109
1110    /// Unit for key result (for create)
1111    #[arg(long, default_value = "%")]
1112    pub unit: String,
1113
1114    /// Filter by status: draft, active, completed, cancelled, on_hold
1115    #[arg(long)]
1116    pub status: Option<String>,
1117
1118    /// Filter by owner
1119    #[arg(long)]
1120    pub owner: Option<String>,
1121
1122    /// Output as JSON
1123    #[arg(long)]
1124    pub json: bool,
1125
1126    /// Include evidence links in output
1127    #[arg(long)]
1128    pub evidence: bool,
1129}
1130
1131#[derive(Parser, Debug)]
1132#[command(
1133    about = "LLM-routed search across grep/glob/web/memory/RLM backends",
1134    long_about = "Uses the configured LLM router (default zai/glm-5.1) to pick the best search backend for your query, runs it, and returns normalized results.",
1135    after_long_help = "Examples:\n  codetether search \"where is fn main\"\n  codetether search --json \"how to center a div\"\n  codetether search --top-n 3 --router-model zai/glm-5.1 \"latest rust async trait docs\""
1136)]
1137pub struct SearchArgs {
1138    /// Natural-language search query
1139    pub query: String,
1140
1141    /// Maximum number of backends to run
1142    #[arg(long, default_value = "1")]
1143    pub top_n: usize,
1144
1145    /// Override router model (default zai/glm-5.1 or CODETETHER_SEARCH_ROUTER_MODEL)
1146    #[arg(long)]
1147    pub router_model: Option<String>,
1148
1149    /// Emit structured JSON instead of human output
1150    #[arg(long)]
1151    pub json: bool,
1152}
1153
1154#[derive(Parser, Debug)]
1155#[command(
1156    about = "OKR-governed autonomous opportunity scanner/executor",
1157    long_about = "Scan OKRs/KRs for high-priority opportunities and optionally execute the top selections with the build agent.",
1158    after_long_help = "Examples:\n  codetether forage --top 5\n  codetether forage --loop --interval-secs 600\n  codetether forage --loop --execute --top 1 --interval-secs 600 --max-cycles 48 --run-timeout-secs 900 --model minimax/MiniMax-M2.5\n  codetether forage --loop --execute --execution-engine swarm --swarm-max-subagents 8 --swarm-strategy auto --model zai/glm-5"
1159)]
1160pub struct ForageArgs {
1161    /// Show top-N opportunities each cycle
1162    #[arg(long, default_value = "3")]
1163    pub top: usize,
1164
1165    /// Keep running continuously
1166    #[arg(long = "loop")]
1167    pub loop_mode: bool,
1168
1169    /// Seconds between loop cycles
1170    #[arg(long, default_value = "120")]
1171    pub interval_secs: u64,
1172
1173    /// Maximum cycles when looping (0 = unlimited)
1174    #[arg(long, default_value = "0")]
1175    pub max_cycles: usize,
1176
1177    /// Execute selected opportunities via `codetether run`
1178    #[arg(long)]
1179    pub execute: bool,
1180
1181    /// Disable S3/MinIO archival requirement (for local-only execution)
1182    #[arg(long)]
1183    pub no_s3: bool,
1184
1185    /// Moonshot mission statement(s) used as a strategic rubric for prioritization.
1186    ///
1187    /// Repeat the flag to provide multiple missions.
1188    #[arg(long = "moonshot")]
1189    pub moonshots: Vec<String>,
1190
1191    /// Optional file containing moonshot mission statements (JSON array or newline-delimited text).
1192    #[arg(long)]
1193    pub moonshot_file: Option<PathBuf>,
1194
1195    /// Require minimum moonshot alignment for an opportunity to be selected.
1196    #[arg(long)]
1197    pub moonshot_required: bool,
1198
1199    /// Minimum moonshot alignment ratio when `--moonshot-required` is enabled.
1200    #[arg(long, default_value = "0.10")]
1201    pub moonshot_min_alignment: f64,
1202
1203    /// Execution engine for `--execute`: run | swarm | go
1204    #[arg(long, default_value = "run", value_parser = ["run", "swarm", "go"])]
1205    pub execution_engine: String,
1206
1207    /// Timeout per execution task in seconds (only used with --execute)
1208    #[arg(long, default_value = "900")]
1209    pub run_timeout_secs: u64,
1210
1211    /// Stop immediately on execution error/timeout (default: continue loop)
1212    #[arg(long)]
1213    pub fail_fast: bool,
1214
1215    /// Swarm decomposition strategy when --execution-engine=swarm
1216    #[arg(long, default_value = "auto", value_parser = ["auto", "domain", "data", "stage", "none"])]
1217    pub swarm_strategy: String,
1218
1219    /// Maximum concurrent sub-agents when --execution-engine=swarm
1220    #[arg(long, default_value = "8")]
1221    pub swarm_max_subagents: usize,
1222
1223    /// Maximum steps per sub-agent when --execution-engine=swarm
1224    #[arg(long, default_value = "100")]
1225    pub swarm_max_steps: usize,
1226
1227    /// Timeout per sub-agent in seconds when --execution-engine=swarm
1228    #[arg(long, default_value = "300")]
1229    pub swarm_subagent_timeout_secs: u64,
1230
1231    /// Optional model override for execution mode
1232    #[arg(short, long)]
1233    pub model: Option<String>,
1234
1235    /// Output as JSON
1236    #[arg(long)]
1237    pub json: bool,
1238}