tempo_cli/cli/
types.rs

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}