1mod aging;
17mod archive;
18mod burndown;
19mod dashboard;
20mod explain;
21mod export;
22mod graph;
23mod history;
24mod import;
25mod issue;
26mod list;
27mod next;
28mod next_id;
29mod prune;
30mod repair;
31mod schema;
32mod search;
33mod shared;
34mod show;
35mod sort;
36mod stats;
37mod stop;
38mod tree;
39mod unlock;
40mod validate;
41
42use anyhow::Result;
43use clap::{Args, Subcommand};
44
45use crate::config;
46
47pub use aging::QueueAgingArgs;
48pub use archive::QueueArchiveArgs;
49pub use burndown::QueueBurndownArgs;
50pub use dashboard::QueueDashboardArgs;
51pub use explain::QueueExplainArgs;
52pub use export::QueueExportArgs;
53pub use graph::QueueGraphArgs;
54pub use history::QueueHistoryArgs;
55pub use import::QueueImportArgs;
56pub use issue::{
57 QueueIssueArgs, QueueIssueCommand, QueueIssuePublishArgs, QueueIssuePublishManyArgs,
58};
59pub use list::QueueListArgs;
60pub use next::QueueNextArgs;
61pub use next_id::QueueNextIdArgs;
62pub use prune::QueuePruneArgs;
63pub use repair::RepairArgs;
64pub use search::QueueSearchArgs;
65pub use shared::{
66 QueueExportFormat, QueueImportFormat, QueueListFormat, QueueListSortBy, QueueReportFormat,
67 QueueShowFormat, QueueSortBy, QueueSortOrder, StatusArg,
68};
69pub use show::QueueShowArgs;
70pub(crate) use show::show_task;
71pub use sort::QueueSortArgs;
72pub use stats::QueueStatsArgs;
73pub use tree::QueueTreeArgs;
74pub use unlock::QueueUnlockArgs;
75
76pub fn handle_queue(cmd: QueueCommand, force: bool) -> Result<()> {
77 let resolved = config::resolve_from_cwd()?;
78 match cmd {
79 QueueCommand::Validate => validate::handle(&resolved),
80 QueueCommand::Next(args) => next::handle(&resolved, args),
81 QueueCommand::NextId(args) => next_id::handle(&resolved, args),
82 QueueCommand::Show(args) => show::handle(&resolved, args),
83 QueueCommand::List(args) => list::handle(&resolved, args),
84 QueueCommand::Search(args) => search::handle(&resolved, args),
85 QueueCommand::Archive(args) => archive::handle(&resolved, force, args),
86 QueueCommand::Repair(args) => repair::handle(&resolved, force, args),
87 QueueCommand::Unlock(args) => unlock::handle(&resolved, args),
88 QueueCommand::Sort(args) => sort::handle(&resolved, force, args),
89 QueueCommand::Stats(args) => stats::handle(&resolved, args),
90 QueueCommand::History(args) => history::handle(&resolved, args),
91 QueueCommand::Burndown(args) => burndown::handle(&resolved, args),
92 QueueCommand::Aging(args) => aging::handle(&resolved, args),
93 QueueCommand::Schema => schema::handle(),
94 QueueCommand::Prune(args) => prune::handle(&resolved, force, args),
95 QueueCommand::Graph(args) => graph::handle(&resolved, args),
96 QueueCommand::Export(args) => export::handle(&resolved, args),
97 QueueCommand::Import(args) => import::handle(&resolved, force, args),
98 QueueCommand::Stop => stop::handle(&resolved),
99 QueueCommand::Explain(args) => explain::handle(&resolved, args),
100 QueueCommand::Tree(args) => tree::handle(&resolved, args),
101 QueueCommand::Dashboard(args) => dashboard::handle(&resolved, args),
102 QueueCommand::Issue(args) => issue::handle(&resolved, force, args),
103 }
104}
105
106#[derive(Args)]
107#[command(
108 about = "Inspect and manage the task queue",
109 after_long_help = "Examples:\n ralph queue list\n ralph queue list --status todo --tag rust\n ralph queue show RQ-0008\n ralph queue next --with-title\n ralph queue next-id\n ralph queue archive"
110)]
111pub struct QueueArgs {
112 #[command(subcommand)]
113 pub command: QueueCommand,
114}
115
116#[derive(Subcommand)]
117pub enum QueueCommand {
118 #[command(
120 after_long_help = "Continuation workflow:\n - This command is read-only.\n - If the queue is valid, Ralph tells you whether continuation is ready, waiting, or blocked.\n - If recoverable issues are present, Ralph explains the blocking state and the next repair/undo steps.\n - Warnings are preserved as partial value; they do not force manual queue surgery.\n\nExamples:\n ralph queue validate\n ralph --verbose queue validate\n ralph queue repair --dry-run\n ralph queue repair\n ralph undo --dry-run"
121 )]
122 Validate,
123
124 #[command(
126 after_long_help = "Prune removes old tasks from .ralph/done.jsonc while preserving recent history.\n\nSafety:\n --keep-last always protects the N most recently completed tasks (by completed_at).\n If no filters are provided, all tasks are pruned except those protected by --keep-last.\n Missing or invalid completed_at timestamps are treated as oldest for keep-last ordering\n but do NOT match the age filter (safety-first).\n\nExamples:\n ralph queue prune --dry-run --age 30 --status rejected\n ralph queue prune --keep-last 100\n ralph queue prune --age 90\n ralph queue prune --age 30 --status done --keep-last 50"
127 )]
128 Prune(QueuePruneArgs),
129
130 #[command(
132 after_long_help = "Examples:\n ralph queue next\n ralph queue next --with-title\n ralph queue next --with-eta\n ralph queue next --with-title --with-eta\n ralph queue next --explain\n ralph queue next --explain --with-title"
133 )]
134 Next(QueueNextArgs),
135
136 #[command(
138 after_long_help = "Examples:\n ralph queue next-id\n ralph queue next-id --count 5\n ralph queue next-id -n 3\n ralph --verbose queue next-id"
139 )]
140 NextId(QueueNextIdArgs),
141
142 Show(QueueShowArgs),
144
145 List(QueueListArgs),
147
148 #[command(
150 after_long_help = "Examples:\n ralph queue search \"authentication\"\n ralph queue search \"RQ-\\d{4}\" --regex\n ralph queue search \"TODO\" --match-case\n ralph queue search \"fix\" --status todo --tag rust\n ralph queue search \"refactor\" --scope crates/ralph --tag rust\n ralph queue search \"auth bug\" --fuzzy\n ralph queue search \"fuzzy search\" --fuzzy --match-case"
151 )]
152 Search(QueueSearchArgs),
153
154 #[command(
156 after_long_help = "Examples:\n ralph queue archive\n ralph queue archive --dry-run"
157 )]
158 Archive(QueueArchiveArgs),
159
160 #[command(
162 after_long_help = "Continuation workflow:\n - Use --dry-run first to preview recoverable fixes.\n - Applying the repair creates an undo checkpoint before queue files are rewritten.\n - This command is for normal continuation, not manual emergency surgery.\n\nExamples:\n ralph queue repair --dry-run\n ralph queue repair\n ralph undo --dry-run"
163 )]
164 Repair(RepairArgs),
165
166 #[command(after_long_help = "Safely remove the queue lock directory.\n\n\
168Safety:\n - Checks if the lock holder process is still running\n - Blocks if process is active (override with --force)\n - Requires confirmation in interactive mode (bypass with --yes)\n\n\
169Examples:\n ralph queue unlock --dry-run\n ralph queue unlock --yes\n ralph queue unlock --force --yes")]
170 Unlock(QueueUnlockArgs),
171
172 #[command(
174 after_long_help = "Examples:\n ralph queue sort\n ralph queue sort --order descending\n ralph queue sort --order ascending"
175 )]
176 Sort(QueueSortArgs),
177
178 #[command(
180 after_long_help = "Examples:\n ralph queue stats\n ralph queue stats --tag rust --tag cli\n ralph queue stats --format json"
181 )]
182 Stats(QueueStatsArgs),
183
184 #[command(after_long_help = "Examples:\n ralph queue history\n ralph queue history --days 14")]
186 History(QueueHistoryArgs),
187
188 #[command(
190 after_long_help = "Examples:\n ralph queue burndown\n ralph queue burndown --days 30"
191 )]
192 Burndown(QueueBurndownArgs),
193
194 #[command(
196 after_long_help = "Examples:\n ralph queue aging\n ralph queue aging --format json\n ralph queue aging --status todo --status doing"
197 )]
198 Aging(QueueAgingArgs),
199
200 #[command(after_long_help = "Example:\n ralph queue schema")]
202 Schema,
203
204 #[command(
206 after_long_help = "Examples:\n ralph queue graph\n ralph queue graph --task RQ-0001\n ralph queue graph --format dot\n ralph queue graph --critical\n ralph queue graph --reverse --task RQ-0001"
207 )]
208 Graph(QueueGraphArgs),
209
210 #[command(
212 after_long_help = "Examples:\n ralph queue export\n ralph queue export --format csv --output tasks.csv\n ralph queue export --format json --status done\n ralph queue export --format tsv --tag rust --tag cli\n ralph queue export --format md --status todo\n ralph queue export --format gh --id-pattern RQ-0001\n ralph queue export --include-archive --format csv\n ralph queue export --format csv --created-after 2026-01-01"
213 )]
214 Export(QueueExportArgs),
215
216 #[command(
218 after_long_help = "Examples:\n ralph queue import --format json < tasks.json\n ralph queue import --format csv tasks.csv\n ralph queue import --format tsv --on-duplicate rename tasks.tsv\n ralph queue import --format json --dry-run < tasks.json"
219 )]
220 Import(QueueImportArgs),
221
222 #[command(
224 after_long_help = "Examples:\n ralph queue stop\n\nNotes:\n - This creates a stop signal file that the run loop checks between tasks.\n - Sequential mode: exits between tasks (current task completes, then exits).\n - Parallel mode: stops scheduling new tasks; waits for in-flight tasks to complete.\n - The stop signal is automatically cleared when the loop honors the request.\n - To force immediate termination, use Ctrl+C in the running loop."
225 )]
226 Stop,
227
228 #[command(
230 after_long_help = "Examples:\n ralph queue explain\n ralph queue explain --format json\n ralph queue explain --include-draft\n ralph queue explain --format json --include-draft"
231 )]
232 Explain(QueueExplainArgs),
233
234 #[command(
236 after_long_help = "Examples:\n ralph queue tree\n ralph queue tree --include-done\n ralph queue tree --root RQ-0001\n ralph queue tree --max-depth 25"
237 )]
238 Tree(QueueTreeArgs),
239
240 #[command(
242 after_long_help = "Examples:\n ralph queue dashboard\n ralph queue dashboard --days 30\n ralph queue dashboard --days 7\n\n\
243The dashboard command returns all analytics data in a single JSON payload for GUI clients.\n\
244Each section includes a 'status' field ('ok' or 'unavailable') for graceful partial failure handling."
245 )]
246 Dashboard(QueueDashboardArgs),
247
248 #[command(
250 after_long_help = "Examples:\n ralph queue issue publish RQ-0655\n ralph queue issue publish RQ-0655 --dry-run\n ralph queue issue publish RQ-0655 --label bug --assignee @me\n ralph queue issue publish RQ-0655 --repo owner/repo\n ralph queue issue publish-many --status todo --tag bug --dry-run\n ralph queue issue publish-many --status todo --execute --force"
251 )]
252 Issue(QueueIssueArgs),
253}
254
255#[cfg(test)]
256mod tests;