mps/cli.rs
1use clap::{Parser, Subcommand};
2
3#[derive(Parser)]
4#[command(name = "mps", version, about = "Plain-text personal productivity CLI")]
5pub struct Cli {
6 /// Path to config file (default: ~/.mps_config.yaml)
7 #[arg(long, global = true)]
8 pub config_path: Option<String>,
9
10 /// Recreate config even if it exists
11 #[arg(long, global = true, default_value_t = false)]
12 pub force: bool,
13
14 #[command(subcommand)]
15 pub command: Option<Commands>,
16}
17
18#[derive(Subcommand)]
19pub enum Commands {
20 /// Open a date's .mps file in $EDITOR (default: today)
21 Open {
22 /// Date to open (today, yesterday, YYYYMMDD, last friday, …)
23 datesign: Option<String>,
24 },
25
26 /// List elements for a date as an indented tree
27 List {
28 /// Date (default: today)
29 datesign: Option<String>,
30 /// Filter by element type: task, note, log, reminder
31 #[arg(short = 't', long)]
32 r#type: Option<String>,
33 /// Filter by tag name
34 #[arg(short = 'g', long)]
35 tag: Option<String>,
36 /// Filter tasks by status: open, done
37 #[arg(short = 's', long)]
38 status: Option<String>,
39 /// Show elements from SINCE up to DATESIGN
40 #[arg(short = 'S', long)]
41 since: Option<String>,
42 /// Show human-readable ref column (task-1, mps-1.2, …)
43 #[arg(short = 'r', long)]
44 refs: bool,
45 /// List elements across all dates in the archive
46 #[arg(short = 'a', long)]
47 all: bool,
48 /// Filter character entries by person name
49 #[arg(short = 'n', long)]
50 name: Option<String>,
51 },
52
53 /// Append an element to today's file without opening an editor
54 Append {
55 /// Element type: task, note, log, reminder (aliases from config are resolved)
56 kind: String,
57 /// Element body text
58 body: Vec<String>,
59 /// Comma-separated tags (e.g. work,backend)
60 #[arg(long)]
61 tags: Option<String>,
62 /// Task status: open (default) or done
63 #[arg(long)]
64 status: Option<String>,
65 /// Time for reminders (e.g. 5pm, 10:30)
66 #[arg(long)]
67 at: Option<String>,
68 /// Start time for logs (HH:MM)
69 #[arg(long)]
70 start_time: Option<String>,
71 /// End time for logs (HH:MM)
72 #[arg(long)]
73 end_time: Option<String>,
74 /// Person name for character entries
75 #[arg(short = 'n', long)]
76 name: Option<String>,
77 },
78
79 /// Update an element's attributes in-place
80 Update {
81 /// Element ref: human (task-1) or epoch (20260428.1)
82 ref_path: String,
83 /// Set task status: open or done
84 #[arg(long)]
85 status: Option<String>,
86 /// Set log start time (HH:MM)
87 #[arg(long = "start-time")]
88 start_time: Option<String>,
89 /// Set log end time (HH:MM)
90 #[arg(long = "end-time")]
91 end_time: Option<String>,
92 /// Set reminder time
93 #[arg(long)]
94 at: Option<String>,
95 /// Date context for human refs (default: today)
96 #[arg(short = 'd', long)]
97 date: Option<String>,
98 },
99
100 /// Edit an element's body text in $EDITOR
101 Edit {
102 /// Element ref: human (task-1) or epoch (20260428.1)
103 ref_path: String,
104 /// Date context for human refs (default: today)
105 #[arg(short = 'd', long)]
106 date: Option<String>,
107 },
108
109 /// Delete an element from its file
110 Delete {
111 /// Element ref: human (task-1) or epoch (20260428.1)
112 ref_path: String,
113 /// Date context for human refs (default: today)
114 #[arg(short = 'd', long)]
115 date: Option<String>,
116 /// Skip confirmation prompt
117 #[arg(short = 'y', long)]
118 yes: bool,
119 },
120
121 /// Mark a task as done (shorthand for update REFPATH --status done)
122 Done {
123 /// Element ref: human (task-1) or epoch (20260428.1)
124 ref_path: String,
125 /// Date context for human refs (default: today)
126 #[arg(short = 'd', long)]
127 date: Option<String>,
128 },
129
130 /// Full-text search across all .mps files
131 Search {
132 /// Search query
133 query: String,
134 /// Filter by element type
135 #[arg(short = 't', long)]
136 r#type: Option<String>,
137 /// Filter by tag
138 #[arg(short = 'g', long)]
139 tag: Option<String>,
140 /// Search from this date onward
141 #[arg(short = 'S', long)]
142 since: Option<String>,
143 /// Filter character entries by person name
144 #[arg(short = 'n', long)]
145 name: Option<String>,
146 },
147
148 /// Show element counts and log durations
149 Stats {
150 /// Date (default: today)
151 datesign: Option<String>,
152 /// Stats from SINCE up to DATESIGN
153 #[arg(short = 'S', long)]
154 since: Option<String>,
155 /// Stats across all dates in the archive
156 #[arg(short = 'a', long)]
157 all: bool,
158 },
159
160 /// Show tag usage frequency bar chart
161 Tags {
162 /// Date (default: today)
163 datesign: Option<String>,
164 /// Filter by element type
165 #[arg(short = 't', long)]
166 r#type: Option<String>,
167 /// Filter tasks by status
168 #[arg(short = 's', long)]
169 status: Option<String>,
170 /// Tags from SINCE up to DATESIGN
171 #[arg(short = 'S', long)]
172 since: Option<String>,
173 /// Count tags across all dates
174 #[arg(short = 'a', long)]
175 all: bool,
176 /// Filter character entries by person name
177 #[arg(short = 'n', long)]
178 name: Option<String>,
179 },
180
181 /// Export elements to JSON or CSV on stdout
182 Export {
183 /// Date (default: today)
184 datesign: Option<String>,
185 /// Output format: json (default), csv
186 #[arg(short = 'f', long, default_value = "json")]
187 format: String,
188 /// Filter by element type
189 #[arg(short = 't', long)]
190 r#type: Option<String>,
191 /// Export from SINCE up to DATESIGN
192 #[arg(short = 'S', long)]
193 since: Option<String>,
194 },
195
196 /// View or edit MPS configuration
197 Config {
198 /// Subcommand: show (default), edit, init, check
199 subcommand: Option<String>,
200 /// Skip network reachability check (used with 'check' subcommand)
201 #[arg(long)]
202 no_network: bool,
203 },
204
205 /// Run git commands inside the storage directory
206 Git {
207 /// Git subcommand and args (auto = full cycle, autocommit = stage+commit)
208 #[arg(trailing_var_arg = true)]
209 args: Vec<String>,
210 },
211
212 /// Stage, commit, pull, and push (equivalent to git auto)
213 Autogit,
214
215 /// Run any shell command inside the storage directory
216 Cmd {
217 /// Command and arguments
218 #[arg(trailing_var_arg = true)]
219 args: Vec<String>,
220 },
221
222 /// Check for due reminders and open tasks; send desktop notifications
223 Notify {
224 /// Show what would be sent without actually sending
225 #[arg(long)]
226 dry_run: bool,
227 /// Override the time window in minutes (default: from config)
228 #[arg(long)]
229 window: Option<u64>,
230 /// Fire even if already notified within the window
231 #[arg(long)]
232 force: bool,
233 },
234
235 /// Manage the background notification daemon (systemd user timer)
236 Daemon {
237 /// Subcommand: install, remove, status, run
238 subcommand: String,
239 },
240
241 /// Inspect or edit the .mps.meta sidecar config file
242 Meta {
243 /// Subcommand: show (default), clear, edit, sync up, sync down
244 #[arg(trailing_var_arg = true)]
245 args: Vec<String>,
246 },
247
248 /// Interactive LLM chat session with your mps journal as context
249 Chat {
250 /// OpenAI-compatible base URL (default: auto-detect :11434 or :8080)
251 #[arg(long)]
252 url: Option<String>,
253 /// Model name (default: llama3.2)
254 #[arg(long)]
255 model: Option<String>,
256 /// Bearer API key (empty = no auth)
257 #[arg(long)]
258 api_key: Option<String>,
259 /// Days of journal history to load (default: 7)
260 #[arg(long)]
261 context_days: Option<u64>,
262 /// Load journal from this date onward (overrides --context-days)
263 #[arg(long)]
264 since: Option<String>,
265 /// Load entire journal history
266 #[arg(long)]
267 all: bool,
268 /// Stream tokens as they arrive (default: true)
269 #[arg(long)]
270 stream: Option<bool>,
271 /// Named session: resume existing or start new
272 #[arg(long, short = 's')]
273 session_name: Option<String>,
274 /// Force new session even if a named session already exists
275 #[arg(long)]
276 new: bool,
277 /// List all saved sessions and exit
278 #[arg(long)]
279 list_sessions: bool,
280 },
281
282 /// Start the HTTP API server
283 Serve {
284 /// TCP port to listen on (default: 3000)
285 #[arg(short = 'p', long)]
286 port: Option<u16>,
287 /// Host address to bind (default: 127.0.0.1)
288 #[arg(long)]
289 host: Option<String>,
290 /// Bearer token for API auth (empty = no auth)
291 #[arg(short = 't', long)]
292 token: Option<String>,
293 },
294
295 /// Interactive first-run setup wizard — creates ~/.mps_config.yaml
296 Init,
297
298 /// Migrate legacy .ms / non-standard .mps files into the journal
299 Import {
300 /// Preview what would be imported without writing anything (default when no flag given)
301 #[arg(long)]
302 dry_run: bool,
303 /// Execute the import; rename originals to *.imported afterwards
304 #[arg(long)]
305 yes: bool,
306 /// Execute the import; delete originals afterwards
307 #[arg(long, name = "move")]
308 move_: bool,
309 },
310
311 /// Print version
312 Version,
313}