tasks-cli-rs 0.2.0

Markdown-based TODO task management CLI: each task is a Markdown file with YAML front matter
use std::path::PathBuf;

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "tasks", version, about = "Markdown-based TODO task management CLI")]
pub struct Cli {
    #[command(subcommand)]
    pub command: Command,
}

#[derive(Subcommand)]
pub enum Command {
    /// Show version and config info
    Info,
    /// Manage task libraries
    Lib {
        #[command(subcommand)]
        command: LibCommand,
    },
    /// Create a new task
    New {
        title: String,
        /// Priority: low | medium | high | urgent
        #[arg(short, long)]
        priority: Option<String>,
        /// Tags (repeatable)
        #[arg(short, long = "tag")]
        tags: Vec<String>,
        /// Due date (YYYY-MM-DD)
        #[arg(short, long)]
        due: Option<String>,
        /// Subdirectory inside the library to place the task in
        #[arg(long)]
        dir: Option<String>,
        /// Create from a template (see 'tasks template list')
        #[arg(long)]
        template: Option<String>,
    },
    /// Manage task templates
    Template {
        #[command(subcommand)]
        command: TemplateCommand,
    },
    /// List tasks in the active library
    List {
        /// Only list tasks under this subdirectory
        #[arg(long)]
        dir: Option<String>,
        /// Filter by status
        #[arg(short, long)]
        status: Option<String>,
        /// Filter by tag
        #[arg(short, long)]
        tag: Option<String>,
        /// Filter by priority
        #[arg(short, long)]
        priority: Option<String>,
        /// Due on or before this date (YYYY-MM-DD)
        #[arg(long)]
        due_before: Option<String>,
        /// Due on or after this date (YYYY-MM-DD)
        #[arg(long)]
        due_after: Option<String>,
        /// Suppress the non-task file warning
        #[arg(short, long)]
        quiet: bool,
    },
    /// Convert plain markdown files into task files (adds front matter)
    Adopt {
        /// Specific files to adopt; scans the whole library when omitted
        paths: Vec<String>,
        /// Adopt everything without prompting
        #[arg(long)]
        all: bool,
        /// Show what would happen without touching files
        #[arg(long)]
        dry_run: bool,
        /// Also rename adopted files per filename_format
        #[arg(long)]
        rename: bool,
    },
    /// Search tasks by text in title and body
    Search {
        query: String,
        /// Filter by status
        #[arg(short, long)]
        status: Option<String>,
        /// Filter by tag
        #[arg(short, long)]
        tag: Option<String>,
    },
    /// Manage tags
    Tag {
        #[command(subcommand)]
        command: TagCommand,
    },
    /// Show task details
    Show { id: String },
    /// Delete a task
    Delete {
        id: String,
        /// Skip confirmation
        #[arg(short, long)]
        force: bool,
    },
    /// Mark a task as started (in_progress)
    Start { id: String },
    /// Mark a task as done
    Done { id: String },
    /// Cancel a task
    Cancel { id: String },
    /// Set an arbitrary status: todo | in_progress | blocked | in_review | done | cancelled
    Status { id: String, status: String },
    /// Modify task fields
    Set {
        id: String,
        #[arg(long)]
        title: Option<String>,
        /// Priority: low | medium | high | urgent
        #[arg(long)]
        priority: Option<String>,
        /// Due date (YYYY-MM-DD); pass "none" to clear
        #[arg(long)]
        due: Option<String>,
        /// Add a tag (repeatable)
        #[arg(long = "tag-add")]
        tag_add: Vec<String>,
        /// Remove a tag (repeatable)
        #[arg(long = "tag-remove")]
        tag_remove: Vec<String>,
    },
    /// Manage sub-task steps
    Step {
        #[command(subcommand)]
        command: StepCommand,
    },
    /// Show tasks as a Kanban board grouped by status
    Board {
        /// Only include tasks under this subdirectory
        #[arg(long)]
        dir: Option<String>,
        /// Filter by tag
        #[arg(short, long)]
        tag: Option<String>,
        /// Comma-separated status columns (default: todo,in_progress,done)
        #[arg(long)]
        columns: Option<String>,
    },
    /// Tasks due soon
    Remind {
        /// Window like "7d" or "7" (days); default 3d
        #[arg(long)]
        within: Option<String>,
        /// Show only overdue tasks
        #[arg(long)]
        overdue: bool,
        /// One-line summary (for shell prompt / cron)
        #[arg(long)]
        summary: bool,
    },
    /// List overdue tasks
    Overdue,
    /// Task statistics for the active library
    Stats,
    /// Generate shell completions (bash, zsh, fish, elvish, powershell)
    Completions { shell: String },
    /// Open a task file (or the library root with --lib) in an editor
    Edit {
        /// Task id; omit when using --lib
        id: Option<String>,
        /// Editor command to use
        #[arg(long)]
        editor: Option<String>,
        /// Open the library root directory instead of a task
        #[arg(long = "lib")]
        lib: bool,
    },
}

#[derive(Subcommand)]
pub enum TemplateCommand {
    /// List templates (library templates shadow global ones)
    List,
    /// Print a template's content
    Show { name: String },
    /// Create a template and open it in the editor
    New {
        name: String,
        /// Create in the current library instead of globally
        #[arg(long)]
        lib: bool,
        /// Editor command to use
        #[arg(long)]
        editor: Option<String>,
    },
    /// Edit a template
    Edit {
        name: String,
        /// Editor command to use
        #[arg(long)]
        editor: Option<String>,
    },
    /// Remove a template
    Remove { name: String },
}

#[derive(Subcommand)]
pub enum StepCommand {
    /// Add a step to a task
    Add {
        id: String,
        title: String,
        /// Due date (YYYY-MM-DD)
        #[arg(long)]
        due: Option<String>,
        /// Step ids this step depends on (comma-separated, e.g. s1,s2)
        #[arg(long)]
        depends_on: Option<String>,
    },
    /// Mark a step as done
    Done { id: String, step: String },
    /// Set a step's status
    Status { id: String, step: String, status: String },
    /// Remove a step from a task
    Remove { id: String, step: String },
}

#[derive(Subcommand)]
pub enum TagCommand {
    /// Add tags to a task
    Add {
        id: String,
        #[arg(required = true)]
        tags: Vec<String>,
    },
    /// Remove tags from a task
    Remove {
        id: String,
        #[arg(required = true)]
        tags: Vec<String>,
    },
    /// List all tags used in the active library
    List,
}

#[derive(Subcommand)]
pub enum LibCommand {
    /// Register a new library (creates the directory if needed)
    Add { name: String, path: PathBuf },
    /// List all registered libraries
    List,
    /// Switch the active library
    Use { name: String },
    /// Show the currently active library
    Current,
    /// Unregister a library (does not delete files)
    Remove { name: String },
}