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}