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:

  • outputTokens from usage.output_tokens
  • cacheReadTokens from usage.cache_read_input_tokens
  • turns incremented per assistant message
  • model from the first message.model encountered
  • messages array 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 from src/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% for full mode) to calculate estimated tokens saved.
  • Cost Conversion: priceForModel() looks up model-specific pricing in MODEL_OUTPUT_PRICE_PER_M to 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.jsonl and updates .caveman-statusline-suffix for 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →