mod checkpoint;
mod commands;
mod config;
mod github_api;
mod harnesses;
mod hooks;
mod logger;
mod sync;
use clap::{Parser, Subcommand};
fn harness_arg_parser() -> clap::builder::PossibleValuesParser {
let mut values: Vec<&'static str> = vec!["all"];
for id in harnesses::ids() {
values.push(id);
}
clap::builder::PossibleValuesParser::new(values)
}
fn parse_harness_selection(value: &str) -> Option<&'static dyn harnesses::Harness> {
if value == "all" {
None
} else {
harnesses::by_id(value)
}
}
#[derive(Parser)]
#[command(
name = "vibestats",
version,
about = "Track your Claude Code and Codex session activity"
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Sync {
#[arg(long)]
backfill: bool,
#[arg(
long,
value_parser = harness_arg_parser(),
default_value = "all"
)]
harness: String,
#[arg(long, hide = true)]
quiet: bool,
},
Status,
Machines {
#[command(subcommand)]
subcommand: MachinesSubcommand,
},
Auth,
Uninstall,
SessionStart,
}
#[derive(Subcommand)]
enum MachinesSubcommand {
List,
Remove {
machine_id: String,
#[arg(long)]
purge_history: bool,
},
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Sync {
backfill,
harness,
quiet,
} => commands::sync::run(backfill, parse_harness_selection(&harness), quiet),
Commands::Status => commands::status::run(),
Commands::Machines { subcommand } => match subcommand {
MachinesSubcommand::List => commands::machines::list(),
MachinesSubcommand::Remove {
machine_id,
purge_history,
} => commands::machines::remove(&machine_id, purge_history),
},
Commands::Auth => commands::auth::run(),
Commands::Uninstall => commands::uninstall::run(),
Commands::SessionStart => {
hooks::session_start::run();
std::process::exit(0);
}
}
}