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