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 level (1=high, 2=medium, 3=low)
30 #[arg(short, long, default_value_t = 2)]
31 priority: i32,
32
33 /// Task type
34 #[arg(short = 't', long = "type", default_value = "task")]
35 task_type: String,
36
37 /// Description
38 #[arg(short = 'd', long = "desc")]
39 desc: Option<String>,
40
41 /// Parent task ID (creates a subtask)
42 #[arg(long)]
43 parent: Option<String>,
44
45 /// Labels (comma-separated)
46 #[arg(short, long)]
47 labels: Option<String>,
48 },
49
50 /// List tasks
51 #[command(visible_alias = "ls")]
52 List {
53 /// Filter by status
54 #[arg(short, long)]
55 status: Option<String>,
56
57 /// Filter by priority
58 #[arg(short, long)]
59 priority: Option<i32>,
60
61 /// Filter by label
62 #[arg(short, long)]
63 label: Option<String>,
64 },
65
66 /// Show task details
67 Show {
68 /// Task ID
69 id: String,
70 },
71
72 /// Update a task
73 Update {
74 /// Task ID
75 id: String,
76
77 /// Set status
78 #[arg(short, long)]
79 status: Option<String>,
80
81 /// Set priority
82 #[arg(short, long)]
83 priority: Option<i32>,
84
85 /// Set title
86 #[arg(short = 't', long)]
87 title: Option<String>,
88
89 /// Set description
90 #[arg(short = 'd', long = "desc")]
91 desc: Option<String>,
92 },
93
94 /// Mark task(s) as closed
95 #[command(visible_alias = "close")]
96 Done {
97 /// Task IDs
98 #[arg(required = true)]
99 ids: Vec<String>,
100 },
101
102 /// Reopen task(s)
103 Reopen {
104 /// Task IDs
105 #[arg(required = true)]
106 ids: Vec<String>,
107 },
108
109 /// Manage dependencies / blockers
110 Dep {
111 #[command(subcommand)]
112 action: DepAction,
113 },
114
115 /// Manage labels
116 Label {
117 #[command(subcommand)]
118 action: LabelAction,
119 },
120
121 /// Search tasks by title or description
122 Search {
123 /// Search query
124 query: String,
125 },
126
127 /// Show tasks with no open blockers
128 Ready,
129
130 /// Show task statistics (always JSON)
131 Stats,
132
133 /// Vacuum the database
134 Compact,
135
136 /// Export tasks to JSONL (one JSON object per line)
137 Export,
138
139 /// Import tasks from a JSONL file
140 Import {
141 /// Path to JSONL file (- for stdin)
142 file: String,
143 },
144
145 /// Install the agent skill file (SKILL.md)
146 Skill {
147 /// Skills directory (writes managing-tasks-with-td/SKILL.md inside)
148 #[arg(long)]
149 dir: Option<String>,
150 },
151}
152
153#[derive(Subcommand)]
154pub enum DepAction {
155 /// Add a dependency (child is blocked by parent)
156 Add {
157 /// Task that is blocked
158 child: String,
159 /// Task that blocks it
160 parent: String,
161 },
162 /// Remove a dependency
163 Rm {
164 /// Task that was blocked
165 child: String,
166 /// Task that was blocking
167 parent: String,
168 },
169 /// Show child tasks
170 Tree {
171 /// Parent task ID
172 id: String,
173 },
174}
175
176#[derive(Subcommand)]
177pub enum LabelAction {
178 /// Add a label to a task
179 Add {
180 /// Task ID
181 id: String,
182 /// Label to add
183 label: String,
184 },
185 /// Remove a label from a task
186 Rm {
187 /// Task ID
188 id: String,
189 /// Label to remove
190 label: String,
191 },
192 /// List labels on a task
193 List {
194 /// Task ID
195 id: String,
196 },
197 /// List all distinct labels
198 ListAll,
199}