How it works
vibestats tracks your Claude Code and Codex usage across machines and surfaces it as a public heatmap on your GitHub profile and on vibestats.dev.
Architecture
Claude Code and Codex sessions
│
▼ Stop/SessionStart hooks
Rust binary (vibestats)
│ reads ~/.claude/projects/**/*.jsonl and ~/.codex/sessions/**/*.jsonl
│ pushes per-machine daily JSON
▼
vibestats-data (private GitHub repo)
└── machines/year=.../day=.../harness=.../machine_id=.../data.json
│
▼ daily GitHub Actions cron
stephenleo/vibestats@v2
(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
-
Local tools write usage state — Claude Code writes JSONL files under
~/.claude/projects/; Codex writes rollout JSONL files under~/.codex/sessions/. -
Rust binary syncs on session end — The
vibestatsbinary is invoked via Stop and SessionStart hooks. It reads all supported harnesses and pushes per-machine daily JSON to your privatevibestats-dataGitHub repository. -
GitHub Actions aggregates daily — A scheduled cron workflow in
vibestats-datarunsstephenleo/vibestats@v2daily. 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.
Long-term retention
By default, Claude Code deletes local session transcripts in ~/.claude/projects/ after 30 days (controlled by the cleanupPeriodDays setting). vibestats persists aggregated daily stats to your private vibestats-data repo on every session, before that cleanup fires. The sync is non-destructive by design: a date that's been pruned locally never overwrites or deletes the remote archive (see the invariant comment in src/sync.rs). Once a day's stats are in vibestats-data, they stay there indefinitely.
That means:
- You keep Claude Code's privacy default — local transcripts continue to auto-expire as Anthropic intends, and only small JSON aggregates leave your machine.
- Your usage history survives machine wipes, reinstalls, and any local cleanup.
- vibestats stores aggregates only — tokens, sessions, minutes, and model names. No prompt or response content is ever synced.