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