How the `/caveman-stats` Command Calculates Token Savings and Costs
The /caveman-stats command analyzes local Claude session logs to attribute output tokens to specific Caveman modes, applies compression benchmarks to estimate saved tokens, and converts those savings into USD using model-specific pricing tables.
The /caveman-stats command is a slash command for Claude Code that provides detailed analytics on token usage and cost savings when using the Caveman mode. According to the JuliusBrussee/caveman source code, the calculation involves parsing session logs, attributing tokens to specific modes based on timestamps, and applying compression ratios to estimate financial savings. The implementation lives primarily in src/hooks/caveman-stats.js.
Parsing the Session Log
Locating the Session File
The hook first identifies the Claude session file to analyze. It receives the path via the --session-file flag (injected by the mode tracker) or discovers the most recent session automatically using findRecentSession().
According to lines 55-75 of src/hooks/caveman-stats.js, the discovery logic scans the .claude/projects/ directory for recent session.jsonl files.
Extracting Token Counts
Once located, parseSession() walks each line of the JSONL file to aggregate raw usage statistics. Based on lines 95-100, the parser extracts:
outputTokensfromusage.output_tokenscacheReadTokensfromusage.cache_read_input_tokensturnsincremented per assistant messagemodelfrom the firstmessage.modelencounteredmessagesarray with timestamps and token counts
// Excerpt from src/hooks/caveman-stats.js#L95-L100
outputTokens += usage.output_tokens || 0;
cacheReadTokens += usage.cache_read_input_tokens || 0;
Mode Attribution and Token Mapping
Caveman can run in different modes (full, lite, etc.). The command must attribute token usage to the correct mode based on when messages were generated, not just the current mode flag.
Tracking Mode Transitions
The system tracks mode changes via the .caveman-active flag file and a transition log (.caveman-mode-log.jsonl). The hook reads these files via readModeLog() to build a timeline of mode switches.
Attribution Logic
The attributeByMode() function (lines 84-90) matches each message's timestamp to the most recent mode transition. It returns an attribution map keyed by mode, plus unknownTokens for messages that cannot be matched.
// From src/hooks/caveman-stats.js#L84-L90
const attribution = attributeByMode({
messages: parsed.messages,
modeLog,
mode,
flagMtimeMs,
outputTokens: parsed.outputTokens,
});
This ensures that if you switched from lite to full mid-session, tokens are correctly attributed to each respective mode.
Calculating Token Savings
Compression Benchmarks
The savings calculation relies on hard-coded compression ratios stored in the COMPRESSION map at lines 19-20. Currently, only the full mode has a defined benchmark of 65% savings (a ratio of 0.65).
The Savings Formula
The deriveSavings() function (lines 44-47) applies these ratios to estimate how many tokens would have been generated without Caveman:
// From src/hooks/caveman-stats.js#L44-L47
estSavedTokens += Math.round(tokens / (1 - ratio)) - tokens;
This formula calculates the total tokens that would have been required (dividing current tokens by the remaining 35%) and subtracts the actual tokens used.
Converting Tokens to USD Costs
Model Pricing Lookup
To convert savings into dollars, the script uses the MODEL_OUTPUT_PRICE_PER_M table (lines 26-39), which maps model prefixes to USD per million output tokens. The priceForModel() function selects the first matching prefix, prioritizing the most specific match.
Currency Calculation and Formatting
Lines 48-50 multiply the estimated saved tokens by the model's price:
// From src/hooks/caveman-stats.js#L48-L50
const price = priceForModel(model);
const estSavedUsd = price !== null ? (estSavedTokens / 1_000_000) * price : 0;
The formatUsd() function (lines 49-53) then formats this value for display.
Passive Memory Compression Detection
Beyond active session savings, the command detects passive memory savings from compressed markdown files.
Finding Compressed File Pairs
findCompressedPairs() (lines 113-144) scans project folders for *.original.md / *.md pairs created by the caveman-compress skill. It calculates the byte difference between original and compressed versions.
Token Estimation from Bytes
The byte savings are converted to approximate token counts using a divisor of 4 (bytes per token), displayed under "Memory compressed" in the final statistics.
Lifetime Aggregation and Status Line Updates
Historical Data Storage
Every successful run appends session totals to .caveman-history.jsonl. The aggregateHistory() function (lines 63-84) reads this file, keeps only the latest entry per session, and sums output_tokens, est_saved_tokens, and est_saved_usd.
Generating the Status Line Suffix
The aggregated savings are converted to a human-readable suffix (e.g., ⛏ 12.4k) via humanizeTokens() (lines 302-307) and written to .caveman-statusline-suffix. This allows Claude's UI to display lifetime savings without shell parsing.
Rendering the Output
Detailed Statistics Report
The formatStats() function (lines 447-462) generates the multi-line report shown when running /caveman-stats, including the attribution breakdown, compression estimates, and USD savings.
Shareable Summary
When invoked with --share, formatShare() (lines 330-340) creates a concise, tweet-ready line suitable for social media sharing.
# Generate detailed report
/caveman-stats
# Create shareable summary
/caveman-stats --share
# Show lifetime totals for last 7 days
/caveman-stats --since 7d
Programmatic Usage
You can also use the calculation functions directly in Node.js:
const { formatStats, parseSession, attributeByMode } = require('./src/hooks/caveman-stats');
const sessionPath = '/home/you/.claude/projects/foo/session.jsonl';
const parsed = parseSession(sessionPath);
const attribution = attributeByMode({
messages: parsed.messages,
modeLog: [],
mode: 'full',
flagMtimeMs: null,
outputTokens: parsed.outputTokens,
});
const stats = formatStats({
...parsed,
mode: 'full',
sessionPath,
compressed: null,
attribution,
});
console.log(stats);
Summary
- Session Parsing: The command reads Claude's JSONL session logs using
parseSession()to extract raw token counts and model information fromsrc/hooks/caveman-stats.js. - Mode Attribution:
attributeByMode()maps each message to the active Caveman mode at the time of generation using timestamp matching against.caveman-mode-log.jsonl. - Savings Estimation:
deriveSavings()applies benchmark compression ratios (65% forfullmode) to calculate estimated tokens saved. - Cost Conversion:
priceForModel()looks up model-specific pricing inMODEL_OUTPUT_PRICE_PER_Mto convert saved tokens to USD. - Memory Compression:
findCompressedPairs()detects compressed markdown files and estimates passive savings. - Lifetime Tracking:
aggregateHistory()maintains running totals in.caveman-history.jsonland updates.caveman-statusline-suffixfor UI display.
Frequently Asked Questions
How does caveman-stats handle sessions where the mode changed mid-conversation?
The attributeByMode() function matches each message's timestamp against the mode transition log (.caveman-mode-log.jsonl). Tokens generated before a switch are attributed to the previous mode, while those after belong to the new mode. Unmatched tokens are categorized as unknownTokens.
Why does the savings calculation only consider output tokens?
According to the source code and repository documentation (docs/HONEST-NUMBERS.md), Caveman specifically targets output token reduction through compression and context management. Input tokens remain largely unaffected by the mode's optimizations, so the calculation focuses solely on output_tokens to provide an honest metric of the tool's impact.
Where does the 65% compression benchmark come from?
The 65% savings ratio for full mode is hard-coded in the COMPRESSION map at lines 19-20 of src/hooks/caveman-stats.js. This benchmark represents the estimated reduction in output tokens achieved when Caveman's compression and context optimization features are fully active.
Can I use caveman-stats without Claude Code?
While designed as a Claude Code slash command, the core functions are accessible programmatically. You can import parseSession, attributeByMode, and deriveSavings from src/hooks/caveman-stats.js and pass a session file path directly. However, the mode attribution logic expects the .caveman-mode-log.jsonl and flag files created by the Caveman mode tracker.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →