cli.rs

  1use clap::{Parser, Subcommand};
  2
  3#[derive(Parser)]
  4#[command(name = "td", version, about = "Todo tracker for AI agents")]
  5pub struct Cli {
  6    /// Output JSON
  7    #[arg(short = 'j', long = "json", global = true)]
  8    pub json: bool,
  9
 10    #[command(subcommand)]
 11    pub command: Command,
 12}
 13
 14#[derive(Subcommand)]
 15pub enum Command {
 16    /// Initialize .td directory
 17    Init {
 18        /// Add .td/ to .gitignore
 19        #[arg(long)]
 20        stealth: bool,
 21    },
 22
 23    /// Create a new task
 24    #[command(visible_alias = "add")]
 25    Create {
 26        /// Task title
 27        title: Option<String>,
 28
 29        /// Priority (low, medium, high)
 30        #[arg(short, long, default_value = "medium")]
 31        priority: String,
 32
 33        /// Effort (low, medium, high)
 34        #[arg(short, long, default_value = "medium")]
 35        effort: String,
 36
 37        /// Task type
 38        #[arg(short = 't', long = "type", default_value = "task")]
 39        task_type: String,
 40
 41        /// Description
 42        #[arg(short = 'd', long = "desc")]
 43        desc: Option<String>,
 44
 45        /// Parent task ID (creates a subtask)
 46        #[arg(long)]
 47        parent: Option<String>,
 48
 49        /// Labels (comma-separated)
 50        #[arg(short, long)]
 51        labels: Option<String>,
 52    },
 53
 54    /// List tasks
 55    #[command(visible_alias = "ls")]
 56    List {
 57        /// Filter by status
 58        #[arg(short, long)]
 59        status: Option<String>,
 60
 61        /// Filter by priority (low, medium, high)
 62        #[arg(short, long)]
 63        priority: Option<String>,
 64
 65        /// Filter by effort (low, medium, high)
 66        #[arg(short, long)]
 67        effort: Option<String>,
 68
 69        /// Filter by label
 70        #[arg(short, long)]
 71        label: Option<String>,
 72    },
 73
 74    /// Show task details
 75    Show {
 76        /// Task ID
 77        id: String,
 78    },
 79
 80    /// Append a work log entry to a task
 81    Log {
 82        /// Task ID
 83        id: String,
 84        /// Log entry body
 85        message: String,
 86    },
 87
 88    /// Update a task
 89    Update {
 90        /// Task ID
 91        id: String,
 92
 93        /// Set status
 94        #[arg(short, long)]
 95        status: Option<String>,
 96
 97        /// Set priority (low, medium, high)
 98        #[arg(short, long)]
 99        priority: Option<String>,
100
101        /// Set effort (low, medium, high)
102        #[arg(short, long)]
103        effort: Option<String>,
104
105        /// Set title
106        #[arg(short = 't', long)]
107        title: Option<String>,
108
109        /// Set description
110        #[arg(short = 'd', long = "desc")]
111        desc: Option<String>,
112    },
113
114    /// Mark task(s) as closed
115    #[command(visible_alias = "close")]
116    Done {
117        /// Task IDs
118        #[arg(required = true)]
119        ids: Vec<String>,
120    },
121
122    /// Reopen task(s)
123    Reopen {
124        /// Task IDs
125        #[arg(required = true)]
126        ids: Vec<String>,
127    },
128
129    /// Manage dependencies / blockers
130    Dep {
131        #[command(subcommand)]
132        action: DepAction,
133    },
134
135    /// Manage labels
136    Label {
137        #[command(subcommand)]
138        action: LabelAction,
139    },
140
141    /// Search tasks by title or description
142    Search {
143        /// Search query
144        query: String,
145    },
146
147    /// Show tasks with no open blockers
148    Ready,
149
150    /// Recommend next task(s) to work on
151    Next {
152        /// Scoring strategy: impact (default) or effort
153        #[arg(short, long, default_value = "impact")]
154        mode: String,
155
156        /// Show signal breakdown and equation
157        #[arg(short, long)]
158        verbose: bool,
159
160        /// Maximum number of results
161        #[arg(short = 'n', long = "limit", default_value = "5")]
162        limit: usize,
163    },
164
165    /// Show task statistics (always JSON)
166    Stats,
167
168    /// Vacuum the database
169    Compact,
170
171    /// Export tasks to JSONL (one JSON object per line)
172    Export,
173
174    /// Import tasks from a JSONL file
175    Import {
176        /// Path to JSONL file (- for stdin)
177        file: String,
178    },
179
180    /// Install the agent skill file (SKILL.md)
181    Skill {
182        /// Skills directory (writes managing-tasks-with-td/SKILL.md inside)
183        #[arg(long)]
184        dir: Option<String>,
185    },
186}
187
188#[derive(Subcommand)]
189pub enum DepAction {
190    /// Add a dependency (child is blocked by parent)
191    Add {
192        /// Task that is blocked
193        child: String,
194        /// Task that blocks it
195        parent: String,
196    },
197    /// Remove a dependency
198    Rm {
199        /// Task that was blocked
200        child: String,
201        /// Task that was blocking
202        parent: String,
203    },
204    /// Show child tasks
205    Tree {
206        /// Parent task ID
207        id: String,
208    },
209}
210
211#[derive(Subcommand)]
212pub enum LabelAction {
213    /// Add a label to a task
214    Add {
215        /// Task ID
216        id: String,
217        /// Label to add
218        label: String,
219    },
220    /// Remove a label from a task
221    Rm {
222        /// Task ID
223        id: String,
224        /// Label to remove
225        label: String,
226    },
227    /// List labels on a task
228    List {
229        /// Task ID
230        id: String,
231    },
232    /// List all distinct labels
233    ListAll,
234}