//! `ralph queue ...` command group: Clap types and handler facade.
//!
//! Responsibilities:
//! - Define clap structures for queue-related subcommands.
//! - Route queue subcommands to their specific handlers.
//! - Re-export argument types used by queue commands.
//!
//! Not handled here:
//! - Queue persistence and locking semantics (see `crate::queue` and `crate::lock`).
//! - Task execution or runner behavior.
//!
//! Invariants/assumptions:
//! - Configuration is resolved from the current working directory.
//! - Queue state changes occur within the subcommand handlers.
mod aging;
mod archive;
mod burndown;
mod dashboard;
mod explain;
mod export;
mod graph;
mod history;
mod import;
mod issue;
mod list;
mod next;
mod next_id;
mod prune;
mod repair;
mod schema;
mod search;
mod shared;
mod show;
mod sort;
mod stats;
mod stop;
mod tree;
mod unlock;
mod validate;
use anyhow::Result;
use clap::{Args, Subcommand};
use crate::config;
pub use aging::QueueAgingArgs;
pub use archive::QueueArchiveArgs;
pub use burndown::QueueBurndownArgs;
pub use dashboard::QueueDashboardArgs;
pub use explain::QueueExplainArgs;
pub use export::QueueExportArgs;
pub use graph::QueueGraphArgs;
pub use history::QueueHistoryArgs;
pub use import::QueueImportArgs;
pub use issue::{
QueueIssueArgs, QueueIssueCommand, QueueIssuePublishArgs, QueueIssuePublishManyArgs,
};
pub use list::QueueListArgs;
pub use next::QueueNextArgs;
pub use next_id::QueueNextIdArgs;
pub use prune::QueuePruneArgs;
pub use repair::RepairArgs;
pub use search::QueueSearchArgs;
pub use shared::{
QueueExportFormat, QueueImportFormat, QueueListFormat, QueueListSortBy, QueueReportFormat,
QueueShowFormat, QueueSortBy, QueueSortOrder, StatusArg,
};
pub use show::QueueShowArgs;
pub(crate) use show::show_task;
pub use sort::QueueSortArgs;
pub use stats::QueueStatsArgs;
pub use tree::QueueTreeArgs;
pub use unlock::QueueUnlockArgs;
pub fn handle_queue(cmd: QueueCommand, force: bool) -> Result<()> {
let resolved = config::resolve_from_cwd()?;
match cmd {
QueueCommand::Validate => validate::handle(&resolved),
QueueCommand::Next(args) => next::handle(&resolved, args),
QueueCommand::NextId(args) => next_id::handle(&resolved, args),
QueueCommand::Show(args) => show::handle(&resolved, args),
QueueCommand::List(args) => list::handle(&resolved, args),
QueueCommand::Search(args) => search::handle(&resolved, args),
QueueCommand::Archive(args) => archive::handle(&resolved, force, args),
QueueCommand::Repair(args) => repair::handle(&resolved, force, args),
QueueCommand::Unlock(args) => unlock::handle(&resolved, args),
QueueCommand::Sort(args) => sort::handle(&resolved, force, args),
QueueCommand::Stats(args) => stats::handle(&resolved, args),
QueueCommand::History(args) => history::handle(&resolved, args),
QueueCommand::Burndown(args) => burndown::handle(&resolved, args),
QueueCommand::Aging(args) => aging::handle(&resolved, args),
QueueCommand::Schema => schema::handle(),
QueueCommand::Prune(args) => prune::handle(&resolved, force, args),
QueueCommand::Graph(args) => graph::handle(&resolved, args),
QueueCommand::Export(args) => export::handle(&resolved, args),
QueueCommand::Import(args) => import::handle(&resolved, force, args),
QueueCommand::Stop => stop::handle(&resolved),
QueueCommand::Explain(args) => explain::handle(&resolved, args),
QueueCommand::Tree(args) => tree::handle(&resolved, args),
QueueCommand::Dashboard(args) => dashboard::handle(&resolved, args),
QueueCommand::Issue(args) => issue::handle(&resolved, force, args),
}
}
#[derive(Args)]
#[command(
about = "Inspect and manage the task queue",
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"
)]
pub struct QueueArgs {
#[command(subcommand)]
pub command: QueueCommand,
}
#[derive(Subcommand)]
pub enum QueueCommand {
/// Inspect whether Ralph can safely continue from the current queue state.
#[command(
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"
)]
Validate,
/// Prune tasks from the done archive based on age, status, or keep-last rules.
#[command(
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"
)]
Prune(QueuePruneArgs),
/// Print the next todo task (ID by default).
#[command(
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"
)]
Next(QueueNextArgs),
/// Print the next available task ID (across queue + done archive).
#[command(
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"
)]
NextId(QueueNextIdArgs),
/// Show a task by ID.
Show(QueueShowArgs),
/// List tasks in queue order.
List(QueueListArgs),
/// Search tasks by content (title, evidence, plan, notes, request, tags, scope, custom fields).
#[command(
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"
)]
Search(QueueSearchArgs),
/// Move completed tasks from queue.jsonc to done.jsonc.
#[command(
after_long_help = "Examples:\n ralph queue archive\n ralph queue archive --dry-run"
)]
Archive(QueueArchiveArgs),
/// Normalize recoverable queue issues so Ralph can continue safely.
#[command(
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"
)]
Repair(RepairArgs),
/// Safely remove the queue lock file with process detection.
#[command(after_long_help = "Safely remove the queue lock directory.\n\n\
Safety:\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\
Examples:\n ralph queue unlock --dry-run\n ralph queue unlock --yes\n ralph queue unlock --force --yes")]
Unlock(QueueUnlockArgs),
/// Sort tasks by priority (reorders the queue file).
#[command(
after_long_help = "Examples:\n ralph queue sort\n ralph queue sort --order descending\n ralph queue sort --order ascending"
)]
Sort(QueueSortArgs),
/// Show task statistics (completion rate, avg duration, tag breakdown).
#[command(
after_long_help = "Examples:\n ralph queue stats\n ralph queue stats --tag rust --tag cli\n ralph queue stats --format json"
)]
Stats(QueueStatsArgs),
/// Show task history timeline (creation/completion events by day).
#[command(after_long_help = "Examples:\n ralph queue history\n ralph queue history --days 14")]
History(QueueHistoryArgs),
/// Show burndown chart of remaining tasks over time.
#[command(
after_long_help = "Examples:\n ralph queue burndown\n ralph queue burndown --days 30"
)]
Burndown(QueueBurndownArgs),
/// Show task aging buckets to identify stale work.
#[command(
after_long_help = "Examples:\n ralph queue aging\n ralph queue aging --format json\n ralph queue aging --status todo --status doing"
)]
Aging(QueueAgingArgs),
/// Print the JSON schema for the queue file.
#[command(after_long_help = "Example:\n ralph queue schema")]
Schema,
/// Visualize task dependencies as a graph.
#[command(
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"
)]
Graph(QueueGraphArgs),
/// Export task data to CSV, TSV, JSON, Markdown, or GitHub issue format.
#[command(
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"
)]
Export(QueueExportArgs),
/// Import tasks from CSV, TSV, or JSON.
#[command(
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"
)]
Import(QueueImportArgs),
/// Request graceful stop of a running loop after current task completes.
#[command(
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."
)]
Stop,
/// Explain why tasks are (not) runnable.
#[command(
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"
)]
Explain(QueueExplainArgs),
/// Render a parent/child hierarchy tree (based on parent_id).
#[command(
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"
)]
Tree(QueueTreeArgs),
/// Aggregated dashboard for analytics UI (combines stats, burndown, history, productivity).
#[command(
after_long_help = "Examples:\n ralph queue dashboard\n ralph queue dashboard --days 30\n ralph queue dashboard --days 7\n\n\
The dashboard command returns all analytics data in a single JSON payload for GUI clients.\n\
Each section includes a 'status' field ('ok' or 'unavailable') for graceful partial failure handling."
)]
Dashboard(QueueDashboardArgs),
/// Publish tasks to GitHub Issues.
#[command(
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"
)]
Issue(QueueIssueArgs),
}
#[cfg(test)]
mod tests;