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}