Skip to main content

ralph/cli/queue/
mod.rs

1//! `ralph queue ...` command group: Clap types and handler facade.
2//!
3//! Responsibilities:
4//! - Define clap structures for queue-related subcommands.
5//! - Route queue subcommands to their specific handlers.
6//! - Re-export argument types used by queue commands.
7//!
8//! Not handled here:
9//! - Queue persistence and locking semantics (see `crate::queue` and `crate::lock`).
10//! - Task execution or runner behavior.
11//!
12//! Invariants/assumptions:
13//! - Configuration is resolved from the current working directory.
14//! - Queue state changes occur within the subcommand handlers.
15
16mod 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::QueueIssueArgs;
57pub use list::QueueListArgs;
58pub use next::QueueNextArgs;
59pub use next_id::QueueNextIdArgs;
60pub use prune::QueuePruneArgs;
61pub use repair::RepairArgs;
62pub use search::QueueSearchArgs;
63pub use shared::{
64    QueueExportFormat, QueueImportFormat, QueueListFormat, QueueListSortBy, QueueReportFormat,
65    QueueShowFormat, QueueSortBy, QueueSortOrder, StatusArg,
66};
67pub use show::QueueShowArgs;
68pub(crate) use show::show_task;
69pub use sort::QueueSortArgs;
70pub use stats::QueueStatsArgs;
71pub use tree::QueueTreeArgs;
72pub use unlock::QueueUnlockArgs;
73
74pub fn handle_queue(cmd: QueueCommand, force: bool) -> Result<()> {
75    let resolved = config::resolve_from_cwd()?;
76    match cmd {
77        QueueCommand::Validate => validate::handle(&resolved),
78        QueueCommand::Next(args) => next::handle(&resolved, args),
79        QueueCommand::NextId(args) => next_id::handle(&resolved, args),
80        QueueCommand::Show(args) => show::handle(&resolved, args),
81        QueueCommand::List(args) => list::handle(&resolved, args),
82        QueueCommand::Search(args) => search::handle(&resolved, args),
83        QueueCommand::Archive(args) => archive::handle(&resolved, force, args),
84        QueueCommand::Repair(args) => repair::handle(&resolved, force, args),
85        QueueCommand::Unlock(args) => unlock::handle(&resolved, args),
86        QueueCommand::Sort(args) => sort::handle(&resolved, force, args),
87        QueueCommand::Stats(args) => stats::handle(&resolved, args),
88        QueueCommand::History(args) => history::handle(&resolved, args),
89        QueueCommand::Burndown(args) => burndown::handle(&resolved, args),
90        QueueCommand::Aging(args) => aging::handle(&resolved, args),
91        QueueCommand::Schema => schema::handle(),
92        QueueCommand::Prune(args) => prune::handle(&resolved, force, args),
93        QueueCommand::Graph(args) => graph::handle(&resolved, args),
94        QueueCommand::Export(args) => export::handle(&resolved, args),
95        QueueCommand::Import(args) => import::handle(&resolved, force, args),
96        QueueCommand::Stop => stop::handle(&resolved),
97        QueueCommand::Explain(args) => explain::handle(&resolved, args),
98        QueueCommand::Tree(args) => tree::handle(&resolved, args),
99        QueueCommand::Dashboard(args) => dashboard::handle(&resolved, args),
100        QueueCommand::Issue(args) => issue::handle(&resolved, force, args),
101    }
102}
103
104#[derive(Args)]
105#[command(
106    about = "Inspect and manage the task queue",
107    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"
108)]
109pub struct QueueArgs {
110    #[command(subcommand)]
111    pub command: QueueCommand,
112}
113
114#[derive(Subcommand)]
115pub enum QueueCommand {
116    /// Validate the active queue (and done archive if present).
117    #[command(
118        after_long_help = "Examples:\n ralph queue validate\n ralph --verbose queue validate"
119    )]
120    Validate,
121
122    /// Prune tasks from the done archive based on age, status, or keep-last rules.
123    #[command(
124        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"
125    )]
126    Prune(QueuePruneArgs),
127
128    /// Print the next todo task (ID by default).
129    #[command(
130        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"
131    )]
132    Next(QueueNextArgs),
133
134    /// Print the next available task ID (across queue + done archive).
135    #[command(
136        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"
137    )]
138    NextId(QueueNextIdArgs),
139
140    /// Show a task by ID.
141    Show(QueueShowArgs),
142
143    /// List tasks in queue order.
144    List(QueueListArgs),
145
146    /// Search tasks by content (title, evidence, plan, notes, request, tags, scope, custom fields).
147    #[command(
148        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"
149    )]
150    Search(QueueSearchArgs),
151
152    /// Move completed tasks from queue.jsonc to done.jsonc.
153    #[command(
154        after_long_help = "Examples:\n  ralph queue archive\n  ralph queue archive --dry-run"
155    )]
156    Archive(QueueArchiveArgs),
157
158    /// Repair the queue and done files (fix missing fields, duplicates, timestamps).
159    #[command(after_long_help = "Example:\n ralph queue repair\n ralph queue repair --dry-run")]
160    Repair(RepairArgs),
161
162    /// Safely remove the queue lock file with process detection.
163    #[command(after_long_help = "Safely remove the queue lock directory.\n\n\
164Safety:\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\
165Examples:\n  ralph queue unlock --dry-run\n  ralph queue unlock --yes\n  ralph queue unlock --force --yes")]
166    Unlock(QueueUnlockArgs),
167
168    /// Sort tasks by priority (reorders the queue file).
169    #[command(
170        after_long_help = "Examples:\n ralph queue sort\n ralph queue sort --order descending\n ralph queue sort --order ascending"
171    )]
172    Sort(QueueSortArgs),
173
174    /// Show task statistics (completion rate, avg duration, tag breakdown).
175    #[command(
176        after_long_help = "Examples:\n ralph queue stats\n ralph queue stats --tag rust --tag cli\n ralph queue stats --format json"
177    )]
178    Stats(QueueStatsArgs),
179
180    /// Show task history timeline (creation/completion events by day).
181    #[command(after_long_help = "Examples:\n ralph queue history\n ralph queue history --days 14")]
182    History(QueueHistoryArgs),
183
184    /// Show burndown chart of remaining tasks over time.
185    #[command(
186        after_long_help = "Examples:\n ralph queue burndown\n ralph queue burndown --days 30"
187    )]
188    Burndown(QueueBurndownArgs),
189
190    /// Show task aging buckets to identify stale work.
191    #[command(
192        after_long_help = "Examples:\n  ralph queue aging\n  ralph queue aging --format json\n  ralph queue aging --status todo --status doing"
193    )]
194    Aging(QueueAgingArgs),
195
196    /// Print the JSON schema for the queue file.
197    #[command(after_long_help = "Example:\n ralph queue schema")]
198    Schema,
199
200    /// Visualize task dependencies as a graph.
201    #[command(
202        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"
203    )]
204    Graph(QueueGraphArgs),
205
206    /// Export task data to CSV, TSV, JSON, Markdown, or GitHub issue format.
207    #[command(
208        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"
209    )]
210    Export(QueueExportArgs),
211
212    /// Import tasks from CSV, TSV, or JSON.
213    #[command(
214        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"
215    )]
216    Import(QueueImportArgs),
217
218    /// Request graceful stop of a running loop after current task completes.
219    #[command(
220        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."
221    )]
222    Stop,
223
224    /// Explain why tasks are (not) runnable.
225    #[command(
226        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"
227    )]
228    Explain(QueueExplainArgs),
229
230    /// Render a parent/child hierarchy tree (based on parent_id).
231    #[command(
232        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"
233    )]
234    Tree(QueueTreeArgs),
235
236    /// Aggregated dashboard for analytics UI (combines stats, burndown, history, productivity).
237    #[command(
238        after_long_help = "Examples:\n  ralph queue dashboard\n  ralph queue dashboard --days 30\n  ralph queue dashboard --days 7\n\n\
239The dashboard command returns all analytics data in a single JSON payload for GUI clients.\n\
240Each section includes a 'status' field ('ok' or 'unavailable') for graceful partial failure handling."
241    )]
242    Dashboard(QueueDashboardArgs),
243
244    /// Publish tasks to GitHub Issues.
245    #[command(
246        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"
247    )]
248    Issue(QueueIssueArgs),
249}
250
251#[cfg(test)]
252mod tests;