Skip to main content

codetether_agent/cli/
mod.rs

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