@propel-code/codegraph
v0.5.1
Published
Generate a local AI coding usage heatmap from Codex, Claude Code, Vibe, and Grok Code session data.
Downloads
822
Maintainers
Readme
codegraph
codegraph is a Bun + TypeScript CLI package for local AI coding usage heatmaps.
By default, codegraph writes a PNG heatmap.
For a persistent local view, codegraph --dashboard starts a live dashboard that refreshes every 5 minutes.
Supported providers
codegraph currently supports:
- Codex
- Claude Code
- Vibe
- Grok Code
- Propel Code
- merged
allview across all detected providers
By default, codegraph runs with --provider all.
If multiple providers have data in the requested window, the result is merged. If only one provider has data, the result falls back to that provider. If neither provider has data, the CLI exits with an error.
Install
Use without installing:
npx @propel-code/codegraph --helpInstall globally:
npm install -g @propel-code/codegraph
codegraph --helpUse with Bun:
bunx @propel-code/codegraph --helpFor local development, clone the repository and install dependencies:
git clone https://github.com/propel-gtm/codegraph.git
cd codegraph
bun installQuickstart
Generate the default YTD PNG using all available providers:
codegraphStart the persistent YTD dashboard on http://127.0.0.1:4269:
codegraph --dashboardStart the dashboard with a custom refresh cadence:
codegraph --dashboard --refresh-minutes 10Generate a merged last-30 PNG:
codegraph --last-30Generate a merged last-365 PNG:
codegraph --last-365Generate a rolling last-90-day PNG:
codegraph --last-90Generate a custom date-range PNG:
codegraph --start-date 2026-02-18 --end-date 2026-03-20Generate a specific calendar year:
codegraph --year 2025Generate Codex-only output:
codegraph --provider codexGenerate Claude-only output:
codegraph --provider claudeGenerate Vibe-only output:
codegraph --provider vibeGenerate Grok-only output:
codegraph --provider grokGenerate Propel-only output:
codegraph --provider propelGenerate JSON instead of the default PNG:
codegraph --format jsonGenerate SVG instead of the default PNG:
codegraph --format svgWrite to a custom file:
codegraph --provider all --year 2025 --output ./out/codegraph-2025.pngShow help:
codegraph --helpFixture-backed example
This repository includes a small checked-in fixture bundle under
test/fixtures plus a normalized example export at
examples/fixture-export-all.json.
The smoke test loads those fixtures through the same public summary and JSON export APIs the CLI uses, then validates the checked-in example file byte-for-byte after normalizing machine-specific fields such as absolute fixture paths and local timestamp formatting.
Excerpt:
{
"version": "0.3.0",
"generatedAt": "2026-03-05T00:00:00.000Z",
"summary": {
"provider": {
"id": "all",
"title": "Codex + Claude Code + Vibe + Grok Code"
},
"metrics": {
"input": 410,
"output": 135,
"total": 545
}
},
"spend": null
}The checked-in example keeps spend as null so it stays deterministic
without depending on cached or fetched pricing data.
CLI reference
codegraph [--ytd | --last-N | --year YYYY | --start-date YYYY-MM-DD --end-date YYYY-MM-DD] [--provider codex|claude|vibe|grok|propel|all] [--format svg|png|json] [--output PATH]
codegraph --dashboard [--ytd | --last-N | --year YYYY | --start-date YYYY-MM-DD --end-date YYYY-MM-DD] [--provider codex|claude|vibe|grok|propel|all] [--host HOST] [--port PORT] [--refresh-minutes MINUTES]Options:
--ytdRender from January 1 of the current year through today.--last-NRender a rollingN-day window through today. Examples:--last-30,--last-365.--year YYYYRender a specific calendar year.--start-date YYYY-MM-DDRender from an explicit start date. Requires--end-date.--end-date YYYY-MM-DDRender through an explicit end date. Requires--start-date.--provider codex|claude|vibe|grok|propel|allChoose a single provider or merge all available providers. Default isall.--dashboardStart a persistent local dashboard server instead of writing a file.--host HOSTDashboard bind host. Default is127.0.0.1.--port PORTDashboard bind port. Default is4269.--refresh-minutes MINUTESBrowser refresh cadence for dashboard mode. Default is5.--format svg|png|jsonOutput SVG, PNG, or JSON. Default is inferred from--output, otherwisepng.--output PATHOverride the output file location.--codex-home PATHOverride the Codex data directory.--claude-config-dir PATHOverride the Claude config directory.--vibe-home PATHOverride the Vibe home directory.--grok-home PATHOverride the Grok Code home directory.--propel-home PATHOverride the Propel Code home directory.--helpPrint usage information.
Rules:
- If no date mode is passed,
codegraphdefaults to YTD. --ytd,--last-N,--year, and explicit--start-date+--end-dateranges are mutually exclusive.--start-dateand--end-datemust be passed together.--dashboardcannot be combined with--formator--output.- If
--yearis the current year, the end date is clamped to today instead of rendering future empty days. - Default output names depend on both the date window and provider.
Dashboard mode
codegraph --dashboard starts a small local HTTP server and keeps running until you stop it.
Behavior:
- the browser view auto-refreshes every 5 minutes by default
- the server also refreshes its in-memory snapshot on the same cadence
- YTD, rolling
--last-N, and current-year dashboards recalculate their date window on refresh, so they roll forward without a restart Refresh nowforces an immediate reload without restarting the process/api/dashboardexposes JSON withrefreshError,refreshIntervalMs, and asnapshotpayload for local integrations
Default output files
Merged all output:
codegraph-ytd.pngcodegraph-ytd.svgcodegraph-ytd.jsoncodegraph-last-30.pngcodegraph-last-30.svgcodegraph-last-30.jsoncodegraph-2026-02-18-to-2026-03-20.pngcodegraph-last-365.pngcodegraph-last-365.svgcodegraph-last-365.jsoncodegraph-2025.pngcodegraph-2025.svgcodegraph-2025.json
Single-provider output adds the provider suffix:
codegraph-ytd-codex.pngcodegraph-ytd-codex.svgcodegraph-ytd-claude.pngcodegraph-ytd-claude.svgcodegraph-last-30-codex.jsoncodegraph-2026-02-18-to-2026-03-20-codex.jsoncodegraph-ytd-vibe.pngcodegraph-ytd-grok.pngcodegraph-last-365-codex.jsoncodegraph-2025-claude.pngcodegraph-2025-claude.svgcodegraph-2025-vibe.json
Data sources
Codex
codegraph reads Codex session files from:
$CODEX_HOME/sessions~/.codex/sessionsifCODEX_HOMEis not set
You can override that root with:
codegraph --provider codex --codex-home /path/to/.codexClaude Code
codegraph reads Claude Code session files from:
$CLAUDE_CONFIG_DIR/projects./.claude/projects~/.claude/projects~/.config/claude/projects
If present, codegraph also uses Claude session metadata from the matching
usage-data/session-meta directories as a fallback when a session is not represented
by a project log.
You can override that root with:
codegraph --provider claude --claude-config-dir /path/to/.claudeVibe
codegraph reads Vibe session metadata from:
$VIBE_HOME/logs/session./.vibe/logs/session~/.vibe/logs/session
You can override that root with:
codegraph --provider vibe --vibe-home /path/to/.vibeGrok Code
codegraph reads Grok Code session files from:
$GROK_HOME/sessions~/.grok-code/sessions
You can override that root with:
codegraph --provider grok --grok-home /path/to/.grok-codePropel Code
codegraph reads Propel Code audit events from:
$PROPEL_HOME/state.sqlite3~/.propel/state.sqlite3ifPROPEL_HOMEis not set
Only Propel audit events that include token usage metadata contribute to totals.
If a local state.sqlite3 only records model and provider metadata, codegraph
will skip those rows and Propel will not contribute usage in that window.
On runtimes without Node's built-in node:sqlite module, Propel support falls
back to the local sqlite3 command if it is installed.
You can override that root with:
codegraph --provider propel --propel-home /path/to/.propelAggregation behavior
Codex parsing
codegraph treats Codex event_msg records with payload.type === "token_count" as the source of truth.
Behavior:
- if
total_token_usageis present, it is treated as cumulative usage - repeated status events are de-duplicated by subtracting the previous cumulative total
- if
last_token_usageis present on the first relevant event, that value is used directly - model names are normalized to remove trailing date suffixes such as
-20251101
Claude Code parsing
codegraph reads Claude assistant message usage from message.usage.
Behavior:
inputincludesinput_tokens + cache_read_input_tokensoutputincludesoutput_tokens + cache_creation_input_tokens- cache tokens are preserved in
cache.inputandcache.output usage-data/session-meta/*.jsonis used as a fallback source when a session is missing fromprojects/- zero-token Claude records are ignored
- model names are normalized the same way as Codex names
Vibe parsing
codegraph reads Vibe session metadata from each session's meta.json.
Behavior:
inputusesstats.session_prompt_tokensoutputusesstats.session_completion_tokens- totals use
stats.session_total_llm_tokenswhen present - activity is attributed to
end_time, falling back tostart_time - model names come from
config.active_model
Propel Code parsing
codegraph reads Propel Code usage from audit_events.payload rows in state.sqlite3.
Behavior:
- only audit events with positive token usage metadata are counted
- OpenAI-style cache reads and cache writes are preserved separately in
cache.inputandcache.output - Claude-style cached reads and cache writes are folded into
inputandoutputto match the existing Claude spend model - rows without usage metadata are ignored
- model names are normalized the same way as Codex names
Merged provider behavior
When --provider all is used:
- daily totals are merged by date
- model totals are merged by model name
- last-30-day totals are recomputed from the merged daily rows
- parser stats are summed across providers
What the Heatmap Shows
- Monday-first contribution-style heatmap
- theoretical token spend using LiteLLM model pricing
- total tokens in the last 30 days
- cumulative input tokens
- cumulative output tokens
- most-used model
- latest model
- longest streak
- current streak
Pricing data is resolved in this order:
- bundled prices in the codebase for common supported models
- cached LiteLLM pricing in
~/.codegraph/litellm-pricing.json - LiteLLM's published model cost map only when a model is still unknown
JSON export shape
The JSON export contains:
versiongeneratedAtspendsummary.providersummary.startsummary.endsummary.daily[]summary.metricssummary.insightssummary.stats
summary.insights includes:
mostUsedModelrecentMostUsedModelThis now reflects the most recently used model, with total tokens for that model in the selected window.latestModelThis retains the raw timestamped latest-model record used to derive recency.streaks
spend includes:
totalUsdpricedModelsunpricedModels[]
If unpricedModels is non-empty, totalUsd reflects only the models with resolved pricing.
Each daily row contains:
dateinputoutputcache.inputcache.outputtotalbreakdown[]
Each summary.stats object contains:
sourceLabelsourcePaths[]filesScannedfilesFailedlinesScannedbadLineseventsConsumed
Project structure
src/cli.tsCLI argument parsing, provider selection, date-range selection, and file output.src/bin.tsNode-facing executable wrapper for published installs.src/codex.tsCodex session scanning and token aggregation.src/claude.tsClaude Code session scanning and token aggregation.src/vibe.tsVibe session scanning and token aggregation.src/grok.tsGrok Code session scanning and token aggregation.src/propel.tsPropel Codestate.sqlite3scanning and token aggregation.src/summary.tsShared daily/model aggregation and merged-summary utilities.src/update.tsPackage version checks for published CLI installs.src/heatmap.tsSVG rendering and PNG export.src/utils.tsShared date, formatting, and filesystem helpers.src/types.tsShared TypeScript types.tsconfig.build.jsonEmit configuration for the publishabledist/CLI build.
Development
Run tests:
bun testRun static typechecking:
bun run typecheckRun the CLI during development:
bun run start -- --provider all --ytdDisable update checks:
CODEGRAPH_DISABLE_UPDATE_CHECK=1 codegraph --helpVerification
Typical verification loop:
bun run typecheck
bun test
bun run build
npx @propel-code/codegraph --help
node dist/cli.js --provider codex --ytd
node dist/cli.js --provider all --last-30
node dist/cli.js --provider all --last-365
node dist/cli.js --provider claude --year 2025 --format json
node dist/cli.js --provider vibe --ytd --format json