How it works
vibestats tracks your Claude Code usage across machines and surfaces it as a public heatmap on your GitHub profile and on vibestats.dev.
Architecture
Claude Code sessions
│
▼ Stop/SessionStart hooks
Rust binary (vibestats)
│ reads ~/.claude/projects/**/*.jsonl
│ pushes per-machine daily JSON
▼
vibestats-data (private GitHub repo)
└── machines/year=.../day=.../machine_id=.../data.json
│
▼ daily GitHub Actions cron
stephenleo/vibestats@v1
(aggregate.py + generate_svg.py + update_readme.py)
│
├──► username/username/vibestats/heatmap.svg
│ └──► profile README (<!-- vibestats-start/end -->)
│
└──► username/username/vibestats/data.json
└──► vibestats.dev/[username] (client-side dashboard)
Data flow explained
-
Claude Code writes JSONL files — Every Claude Code session appends conversation events to
~/.claude/projects/**/*.jsonlon your local machine. -
Rust binary syncs on session end — The
vibestatsbinary is invoked via Stop and SessionStart hooks in~/.claude/settings.json. It reads the JSONL files and pushes per-machine daily JSON to your privatevibestats-dataGitHub repository. -
GitHub Actions aggregates daily — A scheduled cron workflow in
vibestats-datarunsstephenleo/vibestats@v1daily. It runsaggregate.pyto combine data across machines,generate_svg.pyto produce a heatmap SVG, andupdate_readme.pyto embed the heatmap in your GitHub profile README. -
Profile README is updated — The
update_readme.pyscript patches the<!-- vibestats-start -->/<!-- vibestats-end -->section in yourusername/usernameprofile README with the freshly generatedheatmap.svg. -
Dashboard served client-side —
vibestats.dev/[username]fetchesdata.jsonfrom yourusername/username/vibestats/folder and renders an interactive cal-heatmap in the browser — no server required.
Multi-machine support
Each machine writes to its own path in vibestats-data using a unique machine ID. The aggregation step merges data from all machines before generating the heatmap, so your activity from every machine is reflected in a single view.