Skip to main content

ralph/cli/run/
args.rs

1//! Clap argument definitions for `ralph run`.
2//!
3//! Responsibilities:
4//! - Define clap types for `run`, `run one`, `run loop`, `run resume`, and `run parallel`.
5//!
6//! Not handled here:
7//! - Dispatch logic.
8//! - Long-help text authoring.
9//!
10//! Invariants/assumptions:
11//! - Clap validation remains colocated with the flags it governs.
12
13use std::path::PathBuf;
14
15use clap::{Args, Subcommand};
16
17use super::help::{
18    PARALLEL_AFTER_LONG_HELP, PARALLEL_RETRY_AFTER_LONG_HELP, PARALLEL_STATUS_AFTER_LONG_HELP,
19    RESUME_AFTER_LONG_HELP, RUN_AFTER_LONG_HELP, RUN_LOOP_AFTER_LONG_HELP, RUN_ONE_AFTER_LONG_HELP,
20};
21
22#[derive(Args)]
23#[command(
24    about = "Run Ralph supervisor (executes queued tasks via codex/opencode/gemini/claude/cursor/kimi/pi)",
25    after_long_help = RUN_AFTER_LONG_HELP
26)]
27pub struct RunArgs {
28    #[command(subcommand)]
29    pub command: RunCommand,
30}
31
32#[derive(Subcommand)]
33pub enum RunCommand {
34    #[command(about = "Resume an interrupted session from where it left off", after_long_help = RESUME_AFTER_LONG_HELP)]
35    Resume(ResumeArgs),
36    #[command(about = "Run exactly one task (the first todo in the configured queue file)", after_long_help = RUN_ONE_AFTER_LONG_HELP)]
37    One(RunOneArgs),
38    #[command(about = "Run tasks repeatedly until no todo remain (or --max-tasks is reached)", after_long_help = RUN_LOOP_AFTER_LONG_HELP)]
39    Loop(RunLoopArgs),
40    #[command(
41        about = "Experimental: manage parallel mode operations",
42        after_long_help = PARALLEL_AFTER_LONG_HELP,
43        hide = true
44    )]
45    Parallel(ParallelArgs),
46}
47
48#[derive(Args)]
49pub struct ResumeArgs {
50    #[arg(long)]
51    pub force: bool,
52    #[arg(long)]
53    pub debug: bool,
54    #[arg(long)]
55    pub non_interactive: bool,
56    #[command(flatten)]
57    pub agent: crate::agent::RunAgentArgs,
58}
59
60#[derive(Args)]
61pub struct RunOneArgs {
62    #[arg(long)]
63    pub debug: bool,
64    #[arg(long)]
65    pub resume: bool,
66    #[arg(long, value_name = "TASK_ID")]
67    pub id: Option<String>,
68    #[arg(long)]
69    pub non_interactive: bool,
70    #[arg(long, conflicts_with = "parallel_worker")]
71    pub dry_run: bool,
72    #[arg(
73        long,
74        hide = true,
75        conflicts_with = "resume",
76        requires_all = ["id", "coordinator_queue_path", "coordinator_done_path", "parallel_target_branch"]
77    )]
78    pub parallel_worker: bool,
79    #[arg(
80        long,
81        hide = true,
82        value_name = "PATH",
83        requires_all = ["parallel_worker", "coordinator_done_path"]
84    )]
85    pub coordinator_queue_path: Option<PathBuf>,
86    #[arg(
87        long,
88        hide = true,
89        value_name = "PATH",
90        requires_all = ["parallel_worker", "coordinator_queue_path"]
91    )]
92    pub coordinator_done_path: Option<PathBuf>,
93    #[arg(
94        long,
95        hide = true,
96        value_name = "BRANCH",
97        requires_all = ["parallel_worker", "coordinator_queue_path", "coordinator_done_path"]
98    )]
99    pub parallel_target_branch: Option<String>,
100    #[command(flatten)]
101    pub agent: crate::agent::RunAgentArgs,
102}
103
104#[derive(Args)]
105pub struct RunLoopArgs {
106    #[arg(long, default_value_t = 0)]
107    pub max_tasks: u32,
108    #[arg(long)]
109    pub debug: bool,
110    #[arg(long)]
111    pub resume: bool,
112    #[arg(long)]
113    pub non_interactive: bool,
114    #[arg(long, conflicts_with = "parallel")]
115    pub dry_run: bool,
116    #[arg(
117        long,
118        value_parser = clap::value_parser!(u8).range(2..),
119        num_args = 0..=1,
120        default_missing_value = "2",
121        value_name = "N",
122    )]
123    pub parallel: Option<u8>,
124    #[arg(long, conflicts_with = "parallel")]
125    pub wait_when_blocked: bool,
126    #[arg(
127        long,
128        default_value_t = 1000,
129        value_parser = clap::value_parser!(u64).range(50..),
130        value_name = "MS"
131    )]
132    pub wait_poll_ms: u64,
133    #[arg(long, default_value_t = 0, value_name = "SECONDS")]
134    pub wait_timeout_seconds: u64,
135    #[arg(long)]
136    pub notify_when_unblocked: bool,
137    #[arg(long, alias = "continuous", conflicts_with = "parallel")]
138    pub wait_when_empty: bool,
139    #[arg(
140        long,
141        default_value_t = 30_000,
142        value_parser = clap::value_parser!(u64).range(50..),
143        value_name = "MS"
144    )]
145    pub empty_poll_ms: u64,
146    #[command(flatten)]
147    pub agent: crate::agent::RunAgentArgs,
148}
149
150#[derive(Args)]
151pub struct ParallelArgs {
152    #[command(subcommand)]
153    pub command: ParallelSubcommand,
154}
155
156#[derive(Subcommand)]
157pub enum ParallelSubcommand {
158    #[command(about = "Show status of parallel workers", after_long_help = PARALLEL_STATUS_AFTER_LONG_HELP)]
159    Status(ParallelStatusArgs),
160    #[command(about = "Retry a blocked or failed parallel worker", after_long_help = PARALLEL_RETRY_AFTER_LONG_HELP)]
161    Retry(ParallelRetryArgs),
162}
163
164#[derive(Args)]
165pub struct ParallelStatusArgs {
166    #[arg(long)]
167    pub json: bool,
168}
169
170#[derive(Args)]
171pub struct ParallelRetryArgs {
172    #[arg(long, value_name = "TASK_ID", required = true)]
173    pub task: String,
174}