1use clap::{Parser, Subcommand, ValueEnum, CommandFactory};
2use std::path::PathBuf;
3
4#[derive(Parser)]
5#[command(name = "tempo")]
6#[command(about = "Automatic project time tracking CLI tool")]
7#[command(version = env!("CARGO_PKG_VERSION"))]
8#[command(author = "Tempo Contributors")]
9pub struct Cli {
10 #[command(subcommand)]
11 pub command: Commands,
12
13 #[arg(long, short, help = "Path to config file")]
14 pub config: Option<PathBuf>,
15
16 #[arg(long, short, help = "Verbose output")]
17 pub verbose: bool,
18}
19
20#[derive(Subcommand)]
21pub enum Commands {
22 #[command(about = "Start the daemon")]
23 Start,
24
25 #[command(about = "Stop the daemon")]
26 Stop,
27
28 #[command(about = "Restart the daemon")]
29 Restart,
30
31 #[command(about = "Check daemon status")]
32 Status,
33
34 #[command(about = "Initialize a project for tracking")]
35 Init {
36 #[arg(help = "Project name")]
37 name: Option<String>,
38
39 #[arg(long, help = "Project path")]
40 path: Option<PathBuf>,
41
42 #[arg(long, help = "Project description")]
43 description: Option<String>,
44 },
45
46 #[command(about = "List projects")]
47 List {
48 #[arg(long, help = "Include archived projects")]
49 all: bool,
50
51 #[arg(long, help = "Filter by tag")]
52 tag: Option<String>,
53 },
54
55 #[command(about = "Generate time reports")]
56 Report {
57 #[arg(help = "Project name or ID")]
58 project: Option<String>,
59
60 #[arg(long, help = "Start date (YYYY-MM-DD)")]
61 from: Option<String>,
62
63 #[arg(long, help = "End date (YYYY-MM-DD)")]
64 to: Option<String>,
65
66 #[arg(long, help = "Export format (csv, json)")]
67 format: Option<String>,
68
69 #[arg(long, help = "Group by (day, week, month, project)")]
70 group: Option<String>,
71 },
72
73 #[command(about = "Project management")]
74 Project {
75 #[command(subcommand)]
76 action: ProjectAction,
77 },
78
79 #[command(about = "Session management")]
80 Session {
81 #[command(subcommand)]
82 action: SessionAction,
83 },
84
85 #[command(about = "Tag management")]
86 Tag {
87 #[command(subcommand)]
88 action: TagAction,
89 },
90
91 #[command(about = "Configuration management")]
92 Config {
93 #[command(subcommand)]
94 action: ConfigAction,
95 },
96
97 #[command(about = "Interactive dashboard")]
98 Dashboard,
99
100 #[command(about = "Interactive project and session viewer")]
101 Tui,
102
103 #[command(about = "Interactive timer with visual progress")]
104 Timer,
105
106 #[command(about = "Browse session history")]
107 History,
108
109 #[command(about = "Goal management")]
110 Goal {
111 #[command(subcommand)]
112 action: GoalAction,
113 },
114
115 #[command(about = "Productivity insights and analytics")]
116 Insights {
117 #[arg(long, help = "Period (daily, weekly, monthly)")]
118 period: Option<String>,
119
120 #[arg(long, help = "Project name or ID")]
121 project: Option<String>,
122 },
123
124 #[command(about = "Weekly or monthly summary")]
125 Summary {
126 #[arg(help = "Period type (week, month)")]
127 period: String,
128
129 #[arg(long, help = "Start date (YYYY-MM-DD)")]
130 from: Option<String>,
131 },
132
133 #[command(about = "Compare projects")]
134 Compare {
135 #[arg(help = "Project names or IDs (comma-separated)")]
136 projects: String,
137
138 #[arg(long, help = "Start date (YYYY-MM-DD)")]
139 from: Option<String>,
140
141 #[arg(long, help = "End date (YYYY-MM-DD)")]
142 to: Option<String>,
143 },
144
145 #[command(about = "Show database pool statistics")]
146 PoolStats,
147
148 #[command(about = "Time estimation tracking")]
149 Estimate {
150 #[command(subcommand)]
151 action: EstimateAction,
152 },
153
154 #[command(about = "Git branch tracking")]
155 Branch {
156 #[command(subcommand)]
157 action: BranchAction,
158 },
159
160 #[command(about = "Project templates")]
161 Template {
162 #[command(subcommand)]
163 action: TemplateAction,
164 },
165
166 #[command(about = "Workspace management")]
167 Workspace {
168 #[command(subcommand)]
169 action: WorkspaceAction,
170 },
171
172 #[command(about = "Calendar integration")]
173 Calendar {
174 #[command(subcommand)]
175 action: CalendarAction,
176 },
177
178 #[command(about = "Issue tracker integration")]
179 Issue {
180 #[command(subcommand)]
181 action: IssueAction,
182 },
183
184 #[command(about = "Client reporting")]
185 Client {
186 #[command(subcommand)]
187 action: ClientAction,
188 },
189
190 #[command(about = "Generate shell completions", hide = true)]
191 Completions {
192 #[arg(help = "Shell to generate completions for")]
193 shell: Shell,
194 },
195}
196
197#[derive(Subcommand)]
198pub enum ProjectAction {
199 #[command(about = "Archive a project")]
200 Archive {
201 #[arg(help = "Project name or ID")]
202 project: String,
203 },
204
205 #[command(about = "Unarchive a project")]
206 Unarchive {
207 #[arg(help = "Project name or ID")]
208 project: String,
209 },
210
211 #[command(about = "Update project path")]
212 UpdatePath {
213 #[arg(help = "Project name or ID")]
214 project: String,
215
216 #[arg(help = "New path")]
217 path: PathBuf,
218 },
219
220 #[command(about = "Add tag to project")]
221 AddTag {
222 #[arg(help = "Project name or ID")]
223 project: String,
224
225 #[arg(help = "Tag name")]
226 tag: String,
227 },
228
229 #[command(about = "Remove tag from project")]
230 RemoveTag {
231 #[arg(help = "Project name or ID")]
232 project: String,
233
234 #[arg(help = "Tag name")]
235 tag: String,
236 },
237}
238
239#[derive(Subcommand)]
240pub enum SessionAction {
241 #[command(about = "Start tracking time for current project")]
242 Start {
243 #[arg(long, help = "Project name or path")]
244 project: Option<String>,
245
246 #[arg(long, help = "Session context")]
247 context: Option<String>,
248 },
249
250 #[command(about = "Stop current session")]
251 Stop,
252
253 #[command(about = "Pause current session")]
254 Pause,
255
256 #[command(about = "Resume paused session")]
257 Resume,
258
259 #[command(about = "Show current session")]
260 Current,
261
262 #[command(about = "List recent sessions")]
263 List {
264 #[arg(long, help = "Number of sessions to show")]
265 limit: Option<usize>,
266
267 #[arg(long, help = "Project filter")]
268 project: Option<String>,
269 },
270
271 #[command(about = "Edit a session")]
272 Edit {
273 #[arg(help = "Session ID")]
274 id: i64,
275
276 #[arg(long, help = "New start time")]
277 start: Option<String>,
278
279 #[arg(long, help = "New end time")]
280 end: Option<String>,
281
282 #[arg(long, help = "New project")]
283 project: Option<String>,
284
285 #[arg(long, help = "Edit reason")]
286 reason: Option<String>,
287 },
288
289 #[command(about = "Delete a session")]
290 Delete {
291 #[arg(help = "Session ID")]
292 id: i64,
293
294 #[arg(long, help = "Force deletion without confirmation")]
295 force: bool,
296 },
297
298 #[command(about = "Merge multiple sessions into one")]
299 Merge {
300 #[arg(help = "Session IDs to merge (comma-separated)")]
301 session_ids: String,
302
303 #[arg(long, help = "Target project for merged session")]
304 project: Option<String>,
305
306 #[arg(long, help = "Notes for the merged session")]
307 notes: Option<String>,
308 },
309
310 #[command(about = "Split a session into multiple sessions")]
311 Split {
312 #[arg(help = "Session ID to split")]
313 session_id: i64,
314
315 #[arg(help = "Split points (comma-separated times like '10:30,11:45')")]
316 split_times: String,
317
318 #[arg(long, help = "Notes for each split session (comma-separated)")]
319 notes: Option<String>,
320 },
321}
322
323#[derive(Subcommand)]
324pub enum TagAction {
325 #[command(about = "Create a new tag")]
326 Create {
327 #[arg(help = "Tag name")]
328 name: String,
329
330 #[arg(long, help = "Tag color")]
331 color: Option<String>,
332
333 #[arg(long, help = "Tag description")]
334 description: Option<String>,
335 },
336
337 #[command(about = "List all tags")]
338 List,
339
340 #[command(about = "Delete a tag")]
341 Delete {
342 #[arg(help = "Tag name")]
343 name: String,
344 },
345}
346
347#[derive(Subcommand)]
348pub enum ConfigAction {
349 #[command(about = "Show current configuration")]
350 Show,
351
352 #[command(about = "Set configuration value")]
353 Set {
354 #[arg(help = "Configuration key")]
355 key: String,
356
357 #[arg(help = "Configuration value")]
358 value: String,
359 },
360
361 #[command(about = "Get configuration value")]
362 Get {
363 #[arg(help = "Configuration key")]
364 key: String,
365 },
366
367 #[command(about = "Reset configuration to defaults")]
368 Reset,
369}
370
371#[derive(Subcommand)]
372pub enum GoalAction {
373 #[command(about = "Create a new goal")]
374 Create {
375 #[arg(help = "Goal name")]
376 name: String,
377
378 #[arg(help = "Target hours")]
379 target_hours: f64,
380
381 #[arg(long, help = "Project name or ID")]
382 project: Option<String>,
383
384 #[arg(long, help = "Goal description")]
385 description: Option<String>,
386
387 #[arg(long, help = "Start date (YYYY-MM-DD)")]
388 start_date: Option<String>,
389
390 #[arg(long, help = "End date (YYYY-MM-DD)")]
391 end_date: Option<String>,
392 },
393
394 #[command(about = "List goals")]
395 List {
396 #[arg(long, help = "Project name or ID")]
397 project: Option<String>,
398 },
399
400 #[command(about = "Update goal progress")]
401 Update {
402 #[arg(help = "Goal ID")]
403 id: i64,
404
405 #[arg(help = "Hours to add")]
406 hours: f64,
407 },
408}
409
410#[derive(Subcommand)]
411pub enum EstimateAction {
412 #[command(about = "Create a time estimate")]
413 Create {
414 #[arg(help = "Project name or ID")]
415 project: String,
416
417 #[arg(help = "Task name")]
418 task: String,
419
420 #[arg(help = "Estimated hours")]
421 hours: f64,
422
423 #[arg(long, help = "Due date (YYYY-MM-DD)")]
424 due_date: Option<String>,
425 },
426
427 #[command(about = "Record actual time")]
428 Record {
429 #[arg(help = "Estimate ID")]
430 id: i64,
431
432 #[arg(help = "Actual hours")]
433 hours: f64,
434 },
435
436 #[command(about = "List estimates")]
437 List {
438 #[arg(help = "Project name or ID")]
439 project: String,
440 },
441}
442
443#[derive(Subcommand)]
444pub enum BranchAction {
445 #[command(about = "List git branches for a project")]
446 List {
447 #[arg(help = "Project name or ID")]
448 project: String,
449 },
450
451 #[command(about = "Show branch statistics")]
452 Stats {
453 #[arg(help = "Project name or ID")]
454 project: String,
455
456 #[arg(long, help = "Branch name")]
457 branch: Option<String>,
458 },
459}
460
461#[derive(Subcommand)]
462pub enum TemplateAction {
463 #[command(about = "Create a new project template")]
464 Create {
465 #[arg(help = "Template name")]
466 name: String,
467
468 #[arg(long, help = "Template description")]
469 description: Option<String>,
470
471 #[arg(long, help = "Default tags (comma-separated)")]
472 tags: Option<String>,
473
474 #[arg(long, help = "Workspace path for template")]
475 workspace_path: Option<PathBuf>,
476 },
477
478 #[command(about = "List all templates")]
479 List,
480
481 #[command(about = "Delete a template")]
482 Delete {
483 #[arg(help = "Template name or ID")]
484 template: String,
485 },
486
487 #[command(about = "Use a template to initialize a project")]
488 Use {
489 #[arg(help = "Template name or ID")]
490 template: String,
491
492 #[arg(help = "Project name")]
493 project_name: String,
494
495 #[arg(long, help = "Project path")]
496 path: Option<PathBuf>,
497 },
498}
499
500#[derive(Subcommand)]
501pub enum WorkspaceAction {
502 #[command(about = "Create a new workspace")]
503 Create {
504 #[arg(help = "Workspace name")]
505 name: String,
506
507 #[arg(long, help = "Workspace description")]
508 description: Option<String>,
509
510 #[arg(long, help = "Workspace path")]
511 path: Option<PathBuf>,
512 },
513
514 #[command(about = "List all workspaces")]
515 List,
516
517 #[command(about = "Add project to workspace")]
518 AddProject {
519 #[arg(help = "Workspace name or ID")]
520 workspace: String,
521
522 #[arg(help = "Project name or ID")]
523 project: String,
524 },
525
526 #[command(about = "Remove project from workspace")]
527 RemoveProject {
528 #[arg(help = "Workspace name or ID")]
529 workspace: String,
530
531 #[arg(help = "Project name or ID")]
532 project: String,
533 },
534
535 #[command(about = "List projects in workspace")]
536 Projects {
537 #[arg(help = "Workspace name or ID")]
538 workspace: String,
539 },
540
541 #[command(about = "Delete a workspace")]
542 Delete {
543 #[arg(help = "Workspace name or ID")]
544 workspace: String,
545 },
546}
547
548#[derive(Subcommand)]
549pub enum CalendarAction {
550 #[command(about = "Add a calendar event")]
551 Add {
552 #[arg(help = "Event name")]
553 name: String,
554
555 #[arg(help = "Start time (YYYY-MM-DD HH:MM)")]
556 start: String,
557
558 #[arg(long, help = "End time (YYYY-MM-DD HH:MM)")]
559 end: Option<String>,
560
561 #[arg(long, help = "Event type (meeting, focus_block, deadline)")]
562 event_type: Option<String>,
563
564 #[arg(long, help = "Project name or ID")]
565 project: Option<String>,
566
567 #[arg(long, help = "Event description")]
568 description: Option<String>,
569 },
570
571 #[command(about = "List calendar events")]
572 List {
573 #[arg(long, help = "Start date (YYYY-MM-DD)")]
574 from: Option<String>,
575
576 #[arg(long, help = "End date (YYYY-MM-DD)")]
577 to: Option<String>,
578
579 #[arg(long, help = "Project name or ID")]
580 project: Option<String>,
581 },
582
583 #[command(about = "Delete a calendar event")]
584 Delete {
585 #[arg(help = "Event ID")]
586 id: i64,
587 },
588}
589
590#[derive(Subcommand)]
591pub enum IssueAction {
592 #[command(about = "Sync issues from external tracker")]
593 Sync {
594 #[arg(help = "Project name or ID")]
595 project: String,
596
597 #[arg(long, help = "Issue tracker type (jira, github, gitlab)")]
598 tracker_type: Option<String>,
599 },
600
601 #[command(about = "List issues for a project")]
602 List {
603 #[arg(help = "Project name or ID")]
604 project: String,
605
606 #[arg(long, help = "Filter by status")]
607 status: Option<String>,
608 },
609
610 #[command(about = "Link session to issue")]
611 Link {
612 #[arg(help = "Session ID")]
613 session_id: i64,
614
615 #[arg(help = "Issue ID (external ID like JIRA-123)")]
616 issue_id: String,
617 },
618}
619
620#[derive(Subcommand)]
621pub enum ClientAction {
622 #[command(about = "Generate a client report")]
623 Generate {
624 #[arg(help = "Client name")]
625 client: String,
626
627 #[arg(help = "Start date (YYYY-MM-DD)")]
628 from: String,
629
630 #[arg(help = "End date (YYYY-MM-DD)")]
631 to: String,
632
633 #[arg(long, help = "Project filter (comma-separated)")]
634 projects: Option<String>,
635
636 #[arg(long, help = "Output format (json, csv, markdown)")]
637 format: Option<String>,
638 },
639
640 #[command(about = "List all client reports")]
641 List {
642 #[arg(long, help = "Client name filter")]
643 client: Option<String>,
644 },
645
646 #[command(about = "View a specific report")]
647 View {
648 #[arg(help = "Report ID")]
649 id: i64,
650 },
651}
652
653#[derive(ValueEnum, Clone, Debug)]
654pub enum Shell {
655 Bash,
656 Zsh,
657 Fish,
658 PowerShell,
659}
660
661impl Cli {
662 pub fn generate_completions(shell: Shell) {
663 use clap_complete::{generate, shells};
664 use std::io;
665
666 let mut cmd = Self::command();
667 match shell {
668 Shell::Bash => generate(shells::Bash, &mut cmd, "tempo", &mut io::stdout()),
669 Shell::Zsh => generate(shells::Zsh, &mut cmd, "tempo", &mut io::stdout()),
670 Shell::Fish => generate(shells::Fish, &mut cmd, "tempo", &mut io::stdout()),
671 Shell::PowerShell => generate(shells::PowerShell, &mut cmd, "tempo", &mut io::stdout()),
672 }
673 }
674}