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::{
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    /// Inspect whether Ralph can safely continue from the current queue state.
119    #[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    /// Prune tasks from the done archive based on age, status, or keep-last rules.
125    #[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    /// Print the next todo task (ID by default).
131    #[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    /// Print the next available task ID (across queue + done archive).
137    #[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 a task by ID.
143    Show(QueueShowArgs),
144
145    /// List tasks in queue order.
146    List(QueueListArgs),
147
148    /// Search tasks by content (title, evidence, plan, notes, request, tags, scope, custom fields).
149    #[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    /// Move completed tasks from queue.jsonc to done.jsonc.
155    #[command(
156        after_long_help = "Examples:\n  ralph queue archive\n  ralph queue archive --dry-run"
157    )]
158    Archive(QueueArchiveArgs),
159
160    /// Normalize recoverable queue issues so Ralph can continue safely.
161    #[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    /// Safely remove the queue lock file with process detection.
167    #[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    /// Sort tasks by priority (reorders the queue file).
173    #[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    /// Show task statistics (completion rate, avg duration, tag breakdown).
179    #[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    /// Show task history timeline (creation/completion events by day).
185    #[command(after_long_help = "Examples:\n ralph queue history\n ralph queue history --days 14")]
186    History(QueueHistoryArgs),
187
188    /// Show burndown chart of remaining tasks over time.
189    #[command(
190        after_long_help = "Examples:\n ralph queue burndown\n ralph queue burndown --days 30"
191    )]
192    Burndown(QueueBurndownArgs),
193
194    /// Show task aging buckets to identify stale work.
195    #[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    /// Print the JSON schema for the queue file.
201    #[command(after_long_help = "Example:\n ralph queue schema")]
202    Schema,
203
204    /// Visualize task dependencies as a graph.
205    #[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    /// Export task data to CSV, TSV, JSON, Markdown, or GitHub issue format.
211    #[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    /// Import tasks from CSV, TSV, or JSON.
217    #[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    /// Request graceful stop of a running loop after current task completes.
223    #[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    /// Explain why tasks are (not) runnable.
229    #[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    /// Render a parent/child hierarchy tree (based on parent_id).
235    #[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    /// Aggregated dashboard for analytics UI (combines stats, burndown, history, productivity).
241    #[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    /// Publish tasks to GitHub Issues.
249    #[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;