use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "tempo")]
#[command(about = "Automatic project time tracking CLI tool")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(author = "Tempo Contributors")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(long, short, help = "Path to config file")]
pub config: Option<PathBuf>,
#[arg(long, short, help = "Verbose output")]
pub verbose: bool,
}
#[derive(Subcommand)]
pub enum Commands {
#[command(about = "Start the daemon")]
Start,
#[command(about = "Stop the daemon")]
Stop,
#[command(about = "Restart the daemon")]
Restart,
#[command(about = "Check daemon status")]
Status,
#[command(about = "Initialize a project for tracking")]
Init {
#[arg(help = "Project name")]
name: Option<String>,
#[arg(long, help = "Project path")]
path: Option<PathBuf>,
#[arg(long, help = "Project description")]
description: Option<String>,
},
#[command(about = "List projects")]
List {
#[arg(long, help = "Include archived projects")]
all: bool,
#[arg(long, help = "Filter by tag")]
tag: Option<String>,
},
#[command(about = "Generate time reports")]
Report {
#[arg(help = "Project name or ID")]
project: Option<String>,
#[arg(long, help = "Start date (YYYY-MM-DD)")]
from: Option<String>,
#[arg(long, help = "End date (YYYY-MM-DD)")]
to: Option<String>,
#[arg(long, help = "Export format (csv, json)")]
format: Option<String>,
#[arg(long, help = "Group by (day, week, month, project)")]
group: Option<String>,
},
#[command(about = "Project management")]
Project {
#[command(subcommand)]
action: ProjectAction,
},
#[command(about = "Session management")]
Session {
#[command(subcommand)]
action: SessionAction,
},
#[command(about = "Tag management")]
Tag {
#[command(subcommand)]
action: TagAction,
},
#[command(about = "Configuration management")]
Config {
#[command(subcommand)]
action: ConfigAction,
},
#[command(about = "Interactive dashboard")]
Dashboard,
#[command(about = "Interactive project and session viewer")]
Tui,
#[command(about = "Interactive timer with visual progress")]
Timer,
#[command(about = "Browse session history")]
History,
#[command(about = "Goal management")]
Goal {
#[command(subcommand)]
action: GoalAction,
},
#[command(about = "Productivity insights and analytics")]
Insights {
#[arg(long, help = "Period (daily, weekly, monthly)")]
period: Option<String>,
#[arg(long, help = "Project name or ID")]
project: Option<String>,
},
#[command(about = "Weekly or monthly summary")]
Summary {
#[arg(help = "Period type (week, month)")]
period: String,
#[arg(long, help = "Start date (YYYY-MM-DD)")]
from: Option<String>,
},
#[command(about = "Compare projects")]
Compare {
#[arg(help = "Project names or IDs (comma-separated)")]
projects: String,
#[arg(long, help = "Start date (YYYY-MM-DD)")]
from: Option<String>,
#[arg(long, help = "End date (YYYY-MM-DD)")]
to: Option<String>,
},
#[command(about = "Show database pool statistics")]
PoolStats,
#[command(about = "Time estimation tracking")]
Estimate {
#[command(subcommand)]
action: EstimateAction,
},
#[command(about = "Git branch tracking")]
Branch {
#[command(subcommand)]
action: BranchAction,
},
#[command(about = "Project templates")]
Template {
#[command(subcommand)]
action: TemplateAction,
},
#[command(about = "Workspace management")]
Workspace {
#[command(subcommand)]
action: WorkspaceAction,
},
#[command(about = "Calendar integration")]
Calendar {
#[command(subcommand)]
action: CalendarAction,
},
#[command(about = "Issue tracker integration")]
Issue {
#[command(subcommand)]
action: IssueAction,
},
#[command(about = "Client reporting")]
Client {
#[command(subcommand)]
action: ClientAction,
},
#[command(about = "Update tempo to the latest version")]
Update {
#[arg(long, help = "Check for updates without installing")]
check: bool,
#[arg(long, help = "Force update even if current version is latest")]
force: bool,
#[arg(long, help = "Show detailed update information")]
verbose: bool,
},
#[command(about = "Generate shell completions", hide = true)]
Completions {
#[arg(help = "Shell to generate completions for")]
shell: Shell,
},
#[command(about = "Display file contents")]
Cat {
#[arg(help = "Files to display")]
files: Vec<PathBuf>,
#[arg(short = 'A', long, help = "Equivalent to -vET")]
show_all: bool,
#[arg(short = 'b', long, help = "Number nonblank output lines, overrides -n")]
number_nonblank: bool,
#[arg(short = 'e', help = "Equivalent to -vE")]
show_ends: bool,
#[arg(short = 'E', long, help = "Display $ at end of each line")]
show_ends_only: bool,
#[arg(short = 'n', long, help = "Number all output lines")]
number: bool,
#[arg(short = 's', long, help = "Suppress repeated empty output lines")]
squeeze_blank: bool,
#[arg(short = 't', help = "Equivalent to -vT")]
show_tabs: bool,
#[arg(short = 'T', long, help = "Display TAB characters as ^I")]
show_tabs_only: bool,
#[arg(
short = 'v',
long,
help = "Use ^ and M- notation, except for LFD and TAB"
)]
show_nonprinting: bool,
#[arg(long, help = "Output version information and exit")]
version: bool,
},
#[command(about = "Seed database with dummy data")]
Seed,
}
#[derive(Subcommand)]
pub enum ProjectAction {
#[command(about = "Archive a project")]
Archive {
#[arg(help = "Project name or ID")]
project: String,
},
#[command(about = "Unarchive a project")]
Unarchive {
#[arg(help = "Project name or ID")]
project: String,
},
#[command(about = "Update project path")]
UpdatePath {
#[arg(help = "Project name or ID")]
project: String,
#[arg(help = "New path")]
path: PathBuf,
},
#[command(about = "Add tag to project")]
AddTag {
#[arg(help = "Project name or ID")]
project: String,
#[arg(help = "Tag name")]
tag: String,
},
#[command(about = "Remove tag from project")]
RemoveTag {
#[arg(help = "Project name or ID")]
project: String,
#[arg(help = "Tag name")]
tag: String,
},
}
#[derive(Subcommand)]
pub enum SessionAction {
#[command(about = "Start tracking time for current project")]
Start {
#[arg(long, help = "Project name or path")]
project: Option<String>,
#[arg(long, help = "Session context")]
context: Option<String>,
},
#[command(about = "Stop current session")]
Stop,
#[command(about = "Pause current session")]
Pause,
#[command(about = "Resume paused session")]
Resume,
#[command(about = "Show current session")]
Current,
#[command(about = "List recent sessions")]
List {
#[arg(long, help = "Number of sessions to show")]
limit: Option<usize>,
#[arg(long, help = "Project filter")]
project: Option<String>,
},
#[command(about = "Edit a session")]
Edit {
#[arg(help = "Session ID")]
id: i64,
#[arg(long, help = "New start time")]
start: Option<String>,
#[arg(long, help = "New end time")]
end: Option<String>,
#[arg(long, help = "New project")]
project: Option<String>,
#[arg(long, help = "Edit reason")]
reason: Option<String>,
},
#[command(about = "Delete a session")]
Delete {
#[arg(help = "Session ID")]
id: i64,
#[arg(long, help = "Force deletion without confirmation")]
force: bool,
},
#[command(about = "Merge multiple sessions into one")]
Merge {
#[arg(help = "Session IDs to merge (comma-separated)")]
session_ids: String,
#[arg(long, help = "Target project for merged session")]
project: Option<String>,
#[arg(long, help = "Notes for the merged session")]
notes: Option<String>,
},
#[command(about = "Split a session into multiple sessions")]
Split {
#[arg(help = "Session ID to split")]
session_id: i64,
#[arg(help = "Split points (comma-separated times like '10:30,11:45')")]
split_times: String,
#[arg(long, help = "Notes for each split session (comma-separated)")]
notes: Option<String>,
},
}
#[derive(Subcommand)]
pub enum TagAction {
#[command(about = "Create a new tag")]
Create {
#[arg(help = "Tag name")]
name: String,
#[arg(long, help = "Tag color")]
color: Option<String>,
#[arg(long, help = "Tag description")]
description: Option<String>,
},
#[command(about = "List all tags")]
List,
#[command(about = "Delete a tag")]
Delete {
#[arg(help = "Tag name")]
name: String,
},
}
#[derive(Subcommand)]
pub enum ConfigAction {
#[command(about = "Show current configuration")]
Show,
#[command(about = "Set configuration value")]
Set {
#[arg(help = "Configuration key")]
key: String,
#[arg(help = "Configuration value")]
value: String,
},
#[command(about = "Get configuration value")]
Get {
#[arg(help = "Configuration key")]
key: String,
},
#[command(about = "Reset configuration to defaults")]
Reset,
}
#[derive(Subcommand)]
pub enum GoalAction {
#[command(about = "Create a new goal")]
Create {
#[arg(help = "Goal name")]
name: String,
#[arg(help = "Target hours")]
target_hours: f64,
#[arg(long, help = "Project name or ID")]
project: Option<String>,
#[arg(long, help = "Goal description")]
description: Option<String>,
#[arg(long, help = "Start date (YYYY-MM-DD)")]
start_date: Option<String>,
#[arg(long, help = "End date (YYYY-MM-DD)")]
end_date: Option<String>,
},
#[command(about = "List goals")]
List {
#[arg(long, help = "Project name or ID")]
project: Option<String>,
},
#[command(about = "Update goal progress")]
Update {
#[arg(help = "Goal ID")]
id: i64,
#[arg(help = "Hours to add")]
hours: f64,
},
}
#[derive(Subcommand)]
pub enum EstimateAction {
#[command(about = "Create a time estimate")]
Create {
#[arg(help = "Project name or ID")]
project: String,
#[arg(help = "Task name")]
task: String,
#[arg(help = "Estimated hours")]
hours: f64,
#[arg(long, help = "Due date (YYYY-MM-DD)")]
due_date: Option<String>,
},
#[command(about = "Record actual time")]
Record {
#[arg(help = "Estimate ID")]
id: i64,
#[arg(help = "Actual hours")]
hours: f64,
},
#[command(about = "List estimates")]
List {
#[arg(help = "Project name or ID")]
project: String,
},
}
#[derive(Subcommand)]
pub enum BranchAction {
#[command(about = "List git branches for a project")]
List {
#[arg(help = "Project name or ID")]
project: String,
},
#[command(about = "Show branch statistics")]
Stats {
#[arg(help = "Project name or ID")]
project: String,
#[arg(long, help = "Branch name")]
branch: Option<String>,
},
}
#[derive(Subcommand)]
pub enum TemplateAction {
#[command(about = "Create a new project template")]
Create {
#[arg(help = "Template name")]
name: String,
#[arg(long, help = "Template description")]
description: Option<String>,
#[arg(long, help = "Default tags (comma-separated)")]
tags: Option<String>,
#[arg(long, help = "Workspace path for template")]
workspace_path: Option<PathBuf>,
},
#[command(about = "List all templates")]
List,
#[command(about = "Delete a template")]
Delete {
#[arg(help = "Template name or ID")]
template: String,
},
#[command(about = "Use a template to initialize a project")]
Use {
#[arg(help = "Template name or ID")]
template: String,
#[arg(help = "Project name")]
project_name: String,
#[arg(long, help = "Project path")]
path: Option<PathBuf>,
},
}
#[derive(Subcommand)]
pub enum WorkspaceAction {
#[command(about = "Create a new workspace")]
Create {
#[arg(help = "Workspace name")]
name: String,
#[arg(long, help = "Workspace description")]
description: Option<String>,
#[arg(long, help = "Workspace path")]
path: Option<PathBuf>,
},
#[command(about = "List all workspaces")]
List,
#[command(about = "Add project to workspace")]
AddProject {
#[arg(help = "Workspace name or ID")]
workspace: String,
#[arg(help = "Project name or ID")]
project: String,
},
#[command(about = "Remove project from workspace")]
RemoveProject {
#[arg(help = "Workspace name or ID")]
workspace: String,
#[arg(help = "Project name or ID")]
project: String,
},
#[command(about = "List projects in workspace")]
Projects {
#[arg(help = "Workspace name or ID")]
workspace: String,
},
#[command(about = "Delete a workspace")]
Delete {
#[arg(help = "Workspace name or ID")]
workspace: String,
},
}
#[derive(Subcommand)]
pub enum CalendarAction {
#[command(about = "Add a calendar event")]
Add {
#[arg(help = "Event name")]
name: String,
#[arg(help = "Start time (YYYY-MM-DD HH:MM)")]
start: String,
#[arg(long, help = "End time (YYYY-MM-DD HH:MM)")]
end: Option<String>,
#[arg(long, help = "Event type (meeting, focus_block, deadline)")]
event_type: Option<String>,
#[arg(long, help = "Project name or ID")]
project: Option<String>,
#[arg(long, help = "Event description")]
description: Option<String>,
},
#[command(about = "List calendar events")]
List {
#[arg(long, help = "Start date (YYYY-MM-DD)")]
from: Option<String>,
#[arg(long, help = "End date (YYYY-MM-DD)")]
to: Option<String>,
#[arg(long, help = "Project name or ID")]
project: Option<String>,
},
#[command(about = "Delete a calendar event")]
Delete {
#[arg(help = "Event ID")]
id: i64,
},
}
#[derive(Subcommand)]
pub enum IssueAction {
#[command(about = "Sync issues from external tracker")]
Sync {
#[arg(help = "Project name or ID")]
project: String,
#[arg(long, help = "Issue tracker type (jira, github, gitlab)")]
tracker_type: Option<String>,
},
#[command(about = "List issues for a project")]
List {
#[arg(help = "Project name or ID")]
project: String,
#[arg(long, help = "Filter by status")]
status: Option<String>,
},
#[command(about = "Link session to issue")]
Link {
#[arg(help = "Session ID")]
session_id: i64,
#[arg(help = "Issue ID (external ID like JIRA-123)")]
issue_id: String,
},
}
#[derive(Subcommand)]
pub enum ClientAction {
#[command(about = "Generate a client report")]
Generate {
#[arg(help = "Client name")]
client: String,
#[arg(help = "Start date (YYYY-MM-DD)")]
from: String,
#[arg(help = "End date (YYYY-MM-DD)")]
to: String,
#[arg(long, help = "Project filter (comma-separated)")]
projects: Option<String>,
#[arg(long, help = "Output format (json, csv, markdown)")]
format: Option<String>,
},
#[command(about = "List all client reports")]
List {
#[arg(long, help = "Client name filter")]
client: Option<String>,
},
#[command(about = "View a specific report")]
View {
#[arg(help = "Report ID")]
id: i64,
},
}
#[derive(ValueEnum, Clone, Debug)]
pub enum Shell {
Bash,
Zsh,
Fish,
PowerShell,
}
impl Cli {
pub fn generate_completions(shell: Shell) {
use clap_complete::{generate, shells};
use std::io;
let mut cmd = Self::command();
match shell {
Shell::Bash => generate(shells::Bash, &mut cmd, "tempo", &mut io::stdout()),
Shell::Zsh => generate(shells::Zsh, &mut cmd, "tempo", &mut io::stdout()),
Shell::Fish => generate(shells::Fish, &mut cmd, "tempo", &mut io::stdout()),
Shell::PowerShell => generate(shells::PowerShell, &mut cmd, "tempo", &mut io::stdout()),
}
}
}