Skip to main content

codetether_agent/cli/
mod.rs

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