1pub mod cli;
2pub mod collect;
3pub mod queue;
4pub mod queue_path;
5
6use clap::{Arg, ArgAction, Args, Command, CommandFactory, Parser, Subcommand};
7use std::path::PathBuf;
8
9#[derive(Parser)]
10#[command(
11 name = "sq",
12 version,
13 about = "Lightweight task-list CLI with structured sources",
14 long_about = "sq is a lightweight task-list CLI with structured sources.\n\nIt manages tasks in a JSONL file. You can use it directly from the shell or instruct agents to manage them for you."
15)]
16pub struct Cli {
17 #[arg(
19 short = 'q',
20 long = "queue",
21 value_name = "PATH",
22 global = true,
23 display_order = 900
24 )]
25 pub queue: Option<PathBuf>,
26
27 #[command(subcommand)]
28 pub command: Commands,
29}
30
31pub fn build_cli() -> Command {
32 let mut cmd = Cli::command()
33 .propagate_version(true)
34 .disable_version_flag(true)
35 .arg(
36 Arg::new("version")
37 .short('v')
38 .long("version")
39 .help("Print version")
40 .action(ArgAction::Version)
41 .global(true),
42 );
43 let root_help = crate::cli::help::root_after_help(cmd.get_styles());
44 cmd = cmd.after_help(root_help);
45
46 cmd = cmd.mut_subcommand("collect", |subcmd| {
47 let help = crate::cli::commands::collect::after_help(subcmd.get_styles());
48 subcmd.after_help(help)
49 });
50
51 cmd = cmd.mut_subcommand("add", |subcmd| {
52 let help = crate::cli::commands::add::after_help(subcmd.get_styles());
53 subcmd.after_help(help)
54 });
55
56 cmd = cmd.mut_subcommand("edit", |subcmd| {
57 let help = crate::cli::commands::edit::after_help(subcmd.get_styles());
58 subcmd.after_help(help)
59 });
60
61 cmd = cmd.mut_subcommand("list", |subcmd| {
62 let help = crate::cli::commands::list::after_help(subcmd.get_styles());
63 subcmd.after_help(help)
64 });
65
66 cmd = cmd.mut_subcommand("rm", |subcmd| {
67 let help = crate::cli::commands::rm::after_help(subcmd.get_styles());
68 subcmd.after_help(help)
69 });
70
71 cmd = cmd.mut_subcommand("close", |subcmd| {
72 let help = crate::cli::commands::status::close_after_help(subcmd.get_styles());
73 subcmd.long_about("").after_help(help)
74 });
75
76 cmd
77}
78
79#[derive(Subcommand)]
80pub enum Commands {
81 Add(AddArgs),
83 Collect(CollectArgs),
85 List(ListArgs),
87 Show(ShowArgs),
89 Edit(EditArgs),
91 Close(StatusArgs),
93 Rm(RmArgs),
95 Prime,
97}
98
99#[derive(Parser)]
100pub struct AddArgs {
101 #[arg(long = "title", value_name = "TITLE", display_order = 1)]
103 pub title: Option<String>,
104
105 #[arg(long = "description", value_name = "TEXT", display_order = 2)]
107 pub description: Option<String>,
108
109 #[arg(long = "priority", value_name = "PRIORITY", display_order = 3)]
111 pub priority: Option<String>,
112
113 #[arg(long = "diff", value_name = "PATH", display_order = 10)]
115 pub diff: Vec<String>,
116
117 #[arg(long = "file", value_name = "PATH", display_order = 11)]
119 pub file: Vec<String>,
120
121 #[arg(long = "text", value_name = "STRING", display_order = 12)]
123 pub text: Vec<String>,
124
125 #[arg(long = "directory", value_name = "PATH", display_order = 13)]
127 pub directory: Vec<String>,
128
129 #[arg(long = "stdin", value_name = "TYPE", display_order = 14)]
131 pub stdin: Option<String>,
132
133 #[arg(long = "metadata", value_name = "JSON", display_order = 15)]
135 pub metadata: Option<String>,
136
137 #[arg(long = "blocked-by", value_name = "IDS", display_order = 16)]
139 pub blocked_by: Option<String>,
140
141 #[arg(long = "json", display_order = 17)]
143 pub json: bool,
144}
145
146#[derive(Args)]
147#[command(about = "Collect tasks from stdin")]
148pub struct CollectArgs {
149 #[arg(long = "title", value_name = "TITLE", display_order = 1)]
151 pub title: Option<String>,
152
153 #[arg(long = "description", value_name = "TEXT", display_order = 2)]
155 pub description: Option<String>,
156
157 #[arg(long = "priority", value_name = "PRIORITY", display_order = 3)]
159 pub priority: Option<String>,
160
161 #[arg(long = "by-file", display_order = 10)]
163 pub by_file: bool,
164
165 #[arg(long = "stdin-format", value_name = "FORMAT", display_order = 11)]
167 pub stdin_format: Option<String>,
168
169 #[arg(long = "title-template", value_name = "TEMPLATE", display_order = 12)]
171 pub title_template: Option<String>,
172
173 #[arg(long = "metadata", value_name = "JSON", display_order = 13)]
175 pub metadata: Option<String>,
176
177 #[arg(long = "blocked-by", value_name = "IDS", display_order = 14)]
179 pub blocked_by: Option<String>,
180
181 #[arg(long = "json", display_order = 15)]
183 pub json: bool,
184}
185
186#[derive(Parser)]
187pub struct ListArgs {
188 #[arg(long = "status", value_name = "STATUS", display_order = 1)]
190 pub status: Option<String>,
191
192 #[arg(long = "all", display_order = 2)]
194 pub all: bool,
195
196 #[arg(long = "priority", value_name = "PRIORITY", display_order = 3)]
198 pub priority: Vec<String>,
199
200 #[arg(long = "ready", display_order = 4)]
202 pub ready: bool,
203
204 #[arg(long = "json", display_order = 10)]
206 pub json: bool,
207
208 #[arg(long = "filter", value_name = "EXPR", display_order = 11)]
210 pub filter: Option<String>,
211
212 #[arg(long = "sort", value_name = "PATH", display_order = 12)]
214 pub sort: Option<String>,
215
216 #[arg(long = "reverse", display_order = 13)]
218 pub reverse: bool,
219}
220
221#[derive(Parser)]
222pub struct ShowArgs {
223 pub id: Option<String>,
225
226 #[arg(long = "json")]
228 pub json: bool,
229}
230
231#[derive(Parser)]
232pub struct EditArgs {
233 pub id: Option<String>,
235
236 #[arg(long = "set-title", value_name = "TITLE", display_order = 1)]
238 pub set_title: Option<String>,
239
240 #[arg(long = "set-description", value_name = "TEXT", display_order = 2)]
242 pub set_description: Option<String>,
243
244 #[arg(long = "set-status", value_name = "STATUS", display_order = 3)]
246 pub set_status: Option<String>,
247
248 #[arg(long = "set-priority", value_name = "PRIORITY", display_order = 4)]
250 pub set_priority: Option<String>,
251
252 #[arg(long = "clear-priority", display_order = 5)]
254 pub clear_priority: bool,
255
256 #[arg(long = "add-diff", value_name = "PATH", display_order = 10)]
258 pub add_diff: Vec<String>,
259
260 #[arg(long = "add-file", value_name = "PATH", display_order = 11)]
262 pub add_file: Vec<String>,
263
264 #[arg(long = "add-text", value_name = "STRING", display_order = 12)]
266 pub add_text: Vec<String>,
267
268 #[arg(long = "add-directory", value_name = "PATH", display_order = 13)]
270 pub add_directory: Vec<String>,
271
272 #[arg(long = "rm-source", value_name = "INDEX", display_order = 14)]
274 pub rm_source: Vec<usize>,
275
276 #[arg(long = "set-metadata", value_name = "JSON", display_order = 15)]
278 pub set_metadata: Option<String>,
279
280 #[arg(long = "merge-metadata", value_name = "JSON", display_order = 16)]
282 pub merge_metadata: Option<String>,
283
284 #[arg(long = "set-blocked-by", value_name = "IDS", display_order = 17)]
286 pub set_blocked_by: Option<String>,
287
288 #[arg(long = "json", display_order = 18)]
290 pub json: bool,
291}
292
293#[derive(Parser)]
294pub struct StatusArgs {
295 pub id: Option<String>,
297
298 #[arg(long = "json")]
300 pub json: bool,
301}
302
303#[derive(Parser)]
304pub struct RmArgs {
305 pub id: Option<String>,
307
308 #[arg(long = "json")]
310 pub json: bool,
311}