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    /// Delete task(s)
123    Rm {
124        /// Skip warnings about dependents becoming unblocked
125        #[arg(short, long)]
126        force: bool,
127
128        /// Delete the whole subtree (task and descendants)
129        #[arg(short = 'r', long)]
130        recursive: bool,
131
132        /// Task IDs
133        #[arg(required = true)]
134        ids: Vec<String>,
135    },
136
137    /// Reopen task(s)
138    Reopen {
139        /// Task IDs
140        #[arg(required = true)]
141        ids: Vec<String>,
142    },
143
144    /// Manage dependencies / blockers
145    Dep {
146        #[command(subcommand)]
147        action: DepAction,
148    },
149
150    /// Manage labels
151    Label {
152        #[command(subcommand)]
153        action: LabelAction,
154    },
155
156    /// Search tasks by title or description
157    Search {
158        /// Search query
159        query: String,
160    },
161
162    /// Show tasks with no open blockers
163    Ready,
164
165    /// Recommend next task(s) to work on
166    Next {
167        /// Scoring strategy: impact (default) or effort
168        #[arg(short, long, default_value = "impact")]
169        mode: String,
170
171        /// Show signal breakdown and equation
172        #[arg(short, long)]
173        verbose: bool,
174
175        /// Maximum number of results
176        #[arg(short = 'n', long = "limit", default_value = "5")]
177        limit: usize,
178    },
179
180    /// Show task statistics (always JSON)
181    Stats,
182
183    /// Vacuum the database
184    Compact,
185
186    /// Export tasks to JSONL (one JSON object per line)
187    Export,
188
189    /// Import tasks from a JSONL file
190    Import {
191        /// Path to JSONL file (- for stdin)
192        file: String,
193    },
194
195    /// Install the agent skill file (SKILL.md)
196    Skill {
197        /// Skills directory (writes managing-tasks-with-td/SKILL.md inside)
198        #[arg(long)]
199        dir: Option<String>,
200    },
201}
202
203#[derive(Subcommand)]
204pub enum DepAction {
205    /// Add a dependency (child is blocked by parent)
206    Add {
207        /// Task that is blocked
208        child: String,
209        /// Task that blocks it
210        parent: String,
211    },
212    /// Remove a dependency
213    Rm {
214        /// Task that was blocked
215        child: String,
216        /// Task that was blocking
217        parent: String,
218    },
219    /// Show child tasks
220    Tree {
221        /// Parent task ID
222        id: String,
223    },
224}
225
226#[derive(Subcommand)]
227pub enum LabelAction {
228    /// Add a label to a task
229    Add {
230        /// Task ID
231        id: String,
232        /// Label to add
233        label: String,
234    },
235    /// Remove a label from a task
236    Rm {
237        /// Task ID
238        id: String,
239        /// Label to remove
240        label: String,
241    },
242    /// List labels on a task
243    List {
244        /// Task ID
245        id: String,
246    },
247    /// List all distinct labels
248    ListAll,
249}