1use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
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 = "Update tempo to the latest version")]
191 Update {
192 #[arg(long, help = "Check for updates without installing")]
193 check: bool,
194
195 #[arg(long, help = "Force update even if current version is latest")]
196 force: bool,
197
198 #[arg(long, help = "Show detailed update information")]
199 verbose: bool,
200 },
201
202 #[command(about = "Generate shell completions", hide = true)]
203 Completions {
204 #[arg(help = "Shell to generate completions for")]
205 shell: Shell,
206 },
207
208 #[command(about = "Display file contents")]
209 Cat {
210 #[arg(help = "Files to display")]
211 files: Vec<PathBuf>,
212
213 #[arg(short = 'A', long, help = "Equivalent to -vET")]
214 show_all: bool,
215
216 #[arg(short = 'b', long, help = "Number nonblank output lines, overrides -n")]
217 number_nonblank: bool,
218
219 #[arg(short = 'e', help = "Equivalent to -vE")]
220 show_ends: bool,
221
222 #[arg(short = 'E', long, help = "Display $ at end of each line")]
223 show_ends_only: bool,
224
225 #[arg(short = 'n', long, help = "Number all output lines")]
226 number: bool,
227
228 #[arg(short = 's', long, help = "Suppress repeated empty output lines")]
229 squeeze_blank: bool,
230
231 #[arg(short = 't', help = "Equivalent to -vT")]
232 show_tabs: bool,
233
234 #[arg(short = 'T', long, help = "Display TAB characters as ^I")]
235 show_tabs_only: bool,
236
237 #[arg(short = 'v', long, help = "Use ^ and M- notation, except for LFD and TAB")]
238 show_nonprinting: bool,
239
240 #[arg(long, help = "Output version information and exit")]
241 version: bool,
242 },
243}
244
245#[derive(Subcommand)]
246pub enum ProjectAction {
247 #[command(about = "Archive a project")]
248 Archive {
249 #[arg(help = "Project name or ID")]
250 project: String,
251 },
252
253 #[command(about = "Unarchive a project")]
254 Unarchive {
255 #[arg(help = "Project name or ID")]
256 project: String,
257 },
258
259 #[command(about = "Update project path")]
260 UpdatePath {
261 #[arg(help = "Project name or ID")]
262 project: String,
263
264 #[arg(help = "New path")]
265 path: PathBuf,
266 },
267
268 #[command(about = "Add tag to project")]
269 AddTag {
270 #[arg(help = "Project name or ID")]
271 project: String,
272
273 #[arg(help = "Tag name")]
274 tag: String,
275 },
276
277 #[command(about = "Remove tag from project")]
278 RemoveTag {
279 #[arg(help = "Project name or ID")]
280 project: String,
281
282 #[arg(help = "Tag name")]
283 tag: String,
284 },
285}
286
287#[derive(Subcommand)]
288pub enum SessionAction {
289 #[command(about = "Start tracking time for current project")]
290 Start {
291 #[arg(long, help = "Project name or path")]
292 project: Option<String>,
293
294 #[arg(long, help = "Session context")]
295 context: Option<String>,
296 },
297
298 #[command(about = "Stop current session")]
299 Stop,
300
301 #[command(about = "Pause current session")]
302 Pause,
303
304 #[command(about = "Resume paused session")]
305 Resume,
306
307 #[command(about = "Show current session")]
308 Current,
309
310 #[command(about = "List recent sessions")]
311 List {
312 #[arg(long, help = "Number of sessions to show")]
313 limit: Option<usize>,
314
315 #[arg(long, help = "Project filter")]
316 project: Option<String>,
317 },
318
319 #[command(about = "Edit a session")]
320 Edit {
321 #[arg(help = "Session ID")]
322 id: i64,
323
324 #[arg(long, help = "New start time")]
325 start: Option<String>,
326
327 #[arg(long, help = "New end time")]
328 end: Option<String>,
329
330 #[arg(long, help = "New project")]
331 project: Option<String>,
332
333 #[arg(long, help = "Edit reason")]
334 reason: Option<String>,
335 },
336
337 #[command(about = "Delete a session")]
338 Delete {
339 #[arg(help = "Session ID")]
340 id: i64,
341
342 #[arg(long, help = "Force deletion without confirmation")]
343 force: bool,
344 },
345
346 #[command(about = "Merge multiple sessions into one")]
347 Merge {
348 #[arg(help = "Session IDs to merge (comma-separated)")]
349 session_ids: String,
350
351 #[arg(long, help = "Target project for merged session")]
352 project: Option<String>,
353
354 #[arg(long, help = "Notes for the merged session")]
355 notes: Option<String>,
356 },
357
358 #[command(about = "Split a session into multiple sessions")]
359 Split {
360 #[arg(help = "Session ID to split")]
361 session_id: i64,
362
363 #[arg(help = "Split points (comma-separated times like '10:30,11:45')")]
364 split_times: String,
365
366 #[arg(long, help = "Notes for each split session (comma-separated)")]
367 notes: Option<String>,
368 },
369}
370
371#[derive(Subcommand)]
372pub enum TagAction {
373 #[command(about = "Create a new tag")]
374 Create {
375 #[arg(help = "Tag name")]
376 name: String,
377
378 #[arg(long, help = "Tag color")]
379 color: Option<String>,
380
381 #[arg(long, help = "Tag description")]
382 description: Option<String>,
383 },
384
385 #[command(about = "List all tags")]
386 List,
387
388 #[command(about = "Delete a tag")]
389 Delete {
390 #[arg(help = "Tag name")]
391 name: String,
392 },
393}
394
395#[derive(Subcommand)]
396pub enum ConfigAction {
397 #[command(about = "Show current configuration")]
398 Show,
399
400 #[command(about = "Set configuration value")]
401 Set {
402 #[arg(help = "Configuration key")]
403 key: String,
404
405 #[arg(help = "Configuration value")]
406 value: String,
407 },
408
409 #[command(about = "Get configuration value")]
410 Get {
411 #[arg(help = "Configuration key")]
412 key: String,
413 },
414
415 #[command(about = "Reset configuration to defaults")]
416 Reset,
417}
418
419#[derive(Subcommand)]
420pub enum GoalAction {
421 #[command(about = "Create a new goal")]
422 Create {
423 #[arg(help = "Goal name")]
424 name: String,
425
426 #[arg(help = "Target hours")]
427 target_hours: f64,
428
429 #[arg(long, help = "Project name or ID")]
430 project: Option<String>,
431
432 #[arg(long, help = "Goal description")]
433 description: Option<String>,
434
435 #[arg(long, help = "Start date (YYYY-MM-DD)")]
436 start_date: Option<String>,
437
438 #[arg(long, help = "End date (YYYY-MM-DD)")]
439 end_date: Option<String>,
440 },
441
442 #[command(about = "List goals")]
443 List {
444 #[arg(long, help = "Project name or ID")]
445 project: Option<String>,
446 },
447
448 #[command(about = "Update goal progress")]
449 Update {
450 #[arg(help = "Goal ID")]
451 id: i64,
452
453 #[arg(help = "Hours to add")]
454 hours: f64,
455 },
456}
457
458#[derive(Subcommand)]
459pub enum EstimateAction {
460 #[command(about = "Create a time estimate")]
461 Create {
462 #[arg(help = "Project name or ID")]
463 project: String,
464
465 #[arg(help = "Task name")]
466 task: String,
467
468 #[arg(help = "Estimated hours")]
469 hours: f64,
470
471 #[arg(long, help = "Due date (YYYY-MM-DD)")]
472 due_date: Option<String>,
473 },
474
475 #[command(about = "Record actual time")]
476 Record {
477 #[arg(help = "Estimate ID")]
478 id: i64,
479
480 #[arg(help = "Actual hours")]
481 hours: f64,
482 },
483
484 #[command(about = "List estimates")]
485 List {
486 #[arg(help = "Project name or ID")]
487 project: String,
488 },
489}
490
491#[derive(Subcommand)]
492pub enum BranchAction {
493 #[command(about = "List git branches for a project")]
494 List {
495 #[arg(help = "Project name or ID")]
496 project: String,
497 },
498
499 #[command(about = "Show branch statistics")]
500 Stats {
501 #[arg(help = "Project name or ID")]
502 project: String,
503
504 #[arg(long, help = "Branch name")]
505 branch: Option<String>,
506 },
507}
508
509#[derive(Subcommand)]
510pub enum TemplateAction {
511 #[command(about = "Create a new project template")]
512 Create {
513 #[arg(help = "Template name")]
514 name: String,
515
516 #[arg(long, help = "Template description")]
517 description: Option<String>,
518
519 #[arg(long, help = "Default tags (comma-separated)")]
520 tags: Option<String>,
521
522 #[arg(long, help = "Workspace path for template")]
523 workspace_path: Option<PathBuf>,
524 },
525
526 #[command(about = "List all templates")]
527 List,
528
529 #[command(about = "Delete a template")]
530 Delete {
531 #[arg(help = "Template name or ID")]
532 template: String,
533 },
534
535 #[command(about = "Use a template to initialize a project")]
536 Use {
537 #[arg(help = "Template name or ID")]
538 template: String,
539
540 #[arg(help = "Project name")]
541 project_name: String,
542
543 #[arg(long, help = "Project path")]
544 path: Option<PathBuf>,
545 },
546}
547
548#[derive(Subcommand)]
549pub enum WorkspaceAction {
550 #[command(about = "Create a new workspace")]
551 Create {
552 #[arg(help = "Workspace name")]
553 name: String,
554
555 #[arg(long, help = "Workspace description")]
556 description: Option<String>,
557
558 #[arg(long, help = "Workspace path")]
559 path: Option<PathBuf>,
560 },
561
562 #[command(about = "List all workspaces")]
563 List,
564
565 #[command(about = "Add project to workspace")]
566 AddProject {
567 #[arg(help = "Workspace name or ID")]
568 workspace: String,
569
570 #[arg(help = "Project name or ID")]
571 project: String,
572 },
573
574 #[command(about = "Remove project from workspace")]
575 RemoveProject {
576 #[arg(help = "Workspace name or ID")]
577 workspace: String,
578
579 #[arg(help = "Project name or ID")]
580 project: String,
581 },
582
583 #[command(about = "List projects in workspace")]
584 Projects {
585 #[arg(help = "Workspace name or ID")]
586 workspace: String,
587 },
588
589 #[command(about = "Delete a workspace")]
590 Delete {
591 #[arg(help = "Workspace name or ID")]
592 workspace: String,
593 },
594}
595
596#[derive(Subcommand)]
597pub enum CalendarAction {
598 #[command(about = "Add a calendar event")]
599 Add {
600 #[arg(help = "Event name")]
601 name: String,
602
603 #[arg(help = "Start time (YYYY-MM-DD HH:MM)")]
604 start: String,
605
606 #[arg(long, help = "End time (YYYY-MM-DD HH:MM)")]
607 end: Option<String>,
608
609 #[arg(long, help = "Event type (meeting, focus_block, deadline)")]
610 event_type: Option<String>,
611
612 #[arg(long, help = "Project name or ID")]
613 project: Option<String>,
614
615 #[arg(long, help = "Event description")]
616 description: Option<String>,
617 },
618
619 #[command(about = "List calendar events")]
620 List {
621 #[arg(long, help = "Start date (YYYY-MM-DD)")]
622 from: Option<String>,
623
624 #[arg(long, help = "End date (YYYY-MM-DD)")]
625 to: Option<String>,
626
627 #[arg(long, help = "Project name or ID")]
628 project: Option<String>,
629 },
630
631 #[command(about = "Delete a calendar event")]
632 Delete {
633 #[arg(help = "Event ID")]
634 id: i64,
635 },
636}
637
638#[derive(Subcommand)]
639pub enum IssueAction {
640 #[command(about = "Sync issues from external tracker")]
641 Sync {
642 #[arg(help = "Project name or ID")]
643 project: String,
644
645 #[arg(long, help = "Issue tracker type (jira, github, gitlab)")]
646 tracker_type: Option<String>,
647 },
648
649 #[command(about = "List issues for a project")]
650 List {
651 #[arg(help = "Project name or ID")]
652 project: String,
653
654 #[arg(long, help = "Filter by status")]
655 status: Option<String>,
656 },
657
658 #[command(about = "Link session to issue")]
659 Link {
660 #[arg(help = "Session ID")]
661 session_id: i64,
662
663 #[arg(help = "Issue ID (external ID like JIRA-123)")]
664 issue_id: String,
665 },
666}
667
668#[derive(Subcommand)]
669pub enum ClientAction {
670 #[command(about = "Generate a client report")]
671 Generate {
672 #[arg(help = "Client name")]
673 client: String,
674
675 #[arg(help = "Start date (YYYY-MM-DD)")]
676 from: String,
677
678 #[arg(help = "End date (YYYY-MM-DD)")]
679 to: String,
680
681 #[arg(long, help = "Project filter (comma-separated)")]
682 projects: Option<String>,
683
684 #[arg(long, help = "Output format (json, csv, markdown)")]
685 format: Option<String>,
686 },
687
688 #[command(about = "List all client reports")]
689 List {
690 #[arg(long, help = "Client name filter")]
691 client: Option<String>,
692 },
693
694 #[command(about = "View a specific report")]
695 View {
696 #[arg(help = "Report ID")]
697 id: i64,
698 },
699}
700
701#[derive(ValueEnum, Clone, Debug)]
702pub enum Shell {
703 Bash,
704 Zsh,
705 Fish,
706 PowerShell,
707}
708
709impl Cli {
710 pub fn generate_completions(shell: Shell) {
711 use clap_complete::{generate, shells};
712 use std::io;
713
714 let mut cmd = Self::command();
715 match shell {
716 Shell::Bash => generate(shells::Bash, &mut cmd, "tempo", &mut io::stdout()),
717 Shell::Zsh => generate(shells::Zsh, &mut cmd, "tempo", &mut io::stdout()),
718 Shell::Fish => generate(shells::Fish, &mut cmd, "tempo", &mut io::stdout()),
719 Shell::PowerShell => generate(shells::PowerShell, &mut cmd, "tempo", &mut io::stdout()),
720 }
721 }
722}