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