Verified on main at af59ee3. This doesn't overlap with #6786; that issue covers the 100-entry cap, subagent.calls and unchecked findings.
1. Findings are ranked by token share, but token types differ up to 100× in price
SKILL.md step 4 asks for figures "as a % of total tokens", where total is input_tokens.total + output_tokens. Every token counts the same. On Opus 5.5 a cache read is $0.20/M, a 1-hour cache write $8/M and an output token $20/M.
From 30 days of my transcripts (2,352 API calls, almost all Opus 5.5):
| share of tokens | share of cost at list prices | |
|---|---|---|
| cache reads | 97.7% | 45.6% |
| cache writes | 1.9% | 33.6% |
| output | 0.4% | 20.9% |
So "% of total tokens" mostly measures cache reads. Cache writes and output are over half of the cost and 2.3% of the denominator. It can move rows too: in the same report personal-website is 4th by tokens (12.2%) and 2nd by cost (15.4%).
Claude Code also writes its own cost meter into the transcript (cost-state entries with totalCostUSD). The analyzer skips them.
2. Cache breaks show size and context, not the cause
A break is any call with more than 100k uncached input (analyze-sessions.mjs#L387-L389). The 11 breaks in my 30-day report have three different causes:
- 6 came after the session sat idle longer than the cache TTL (78 minutes to 20 hours, 1-hour TTL).
- 4 came less than a minute after the previous call, so something early in the prompt changed.
- 1 was the first call of a resumed session.
The first group is fixed by compacting or starting fresh after a long break. The second is fixed by not changing MCP servers, tools, CLAUDE.md or the model mid-session. In the report they look the same, so the findings can't tell the user which one to do.
Suggested change
I implemented both on a fork: viniciusestevam/claude-plugins-official@55123ed (the plugin's three files, +237/−20).
analyze-sessions.mjs: estimate USD per call from a table of Claude prices, with 5-minute and 1-hour cache writes priced separately. Calls on models not in the table go intounpriced_callsand are not counted as $0. Addcost_usdto every scope,top_promptsandby_day. Readcost-stateand report the meter ascost_usd.measured, next tomeasured_sessions_estimated(the estimate for the same sessions). Give each break acause(idle_past_ttl,prefix_changed,compaction,session_start),gap_min,ttlandrewrite_usd, and addcache_break_causesto every scope.template.html: estimated cost and the meter in the hero, an "est. cost" stat, a "% cost" column (default sort) next to "% tokens", cost on top prompts, the cause on each break.SKILL.md: rank spend findings by cost, quote the meter only next to the estimate for the same sessions, and name the cause of each break.
What I checked:
- Every existing JSON field is identical to
mainfor--since 24h,7d,30dand all time. The change only adds fields. - A report built from
main's JSON renders as before. Both versions render with no console errors. - On my history, the estimate for the 28 sessions that log a meter is $200.58 against Claude Code's $210.53 (95%). The meter also counts calls that never reach the transcript.
- The break causes match an independent classification from the call timestamps and TTLs.
The price table will need updates when prices change. If that's unwanted, a smaller version is to drop the dollar figures and only weight token shares by type.
I haven't opened a PR because external PRs are closed automatically here, but the branch can be read or reused as is.
Environment
Claude Code 2.1.308, macOS, Node 26. Reproduce with node analyze-sessions.mjs --json --since 30d on any history with Opus calls: compare overall.input_tokens.cache_read with the total, and look at cache_breaks[*].ts against the timestamp of the call before each one in the same transcript.
Activity
0No activity yet.