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