You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: trust JSONL cache tiers, externalize all pricing to JSON
Claude Code now correctly reports cache write tiers per model:
- Main conversation (Opus/Sonnet) → ephemeral_1h_input_tokens
- Subagent requests (any model) → ephemeral_5m_input_tokens
Remove the cacheWriteAs1h override and -cache-5m flag — trust the
JSONL data as-is. Fallback for old logs without cache_creation
sub-object defaults to 1h.
Externalize all remaining hardcoded pricing to pricing.json:
- Per-model: cache_read, cache_write_5m, cache_write_1h (and
long_ctx variants) — with fillCacheDefaults() fallback that
derives from input price using standard multipliers when absent
- Global: long_context_threshold (200K), web_search_cost ($0.01)
Known regression: old JSONL logs (Dec 2025 – Feb 2026) where Claude
Code reported all cache writes as ephemeral_5m will now price
Opus/Sonnet cache writes at 1.25x instead of 2x. Accepted trade-off
— affects only historical data and the old override was wrong for
Haiku subagents (overpriced at 2x instead of correct 1.25x).
Copy file name to clipboardExpand all lines: CLAUDE.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -95,9 +95,9 @@ One API call produces multiple JSONL entries sharing the same `requestId`. `inpu
95
95
96
96
-**Flat package structure** — all code in `package main`, one concern per file
97
97
-**Dedup by requestId** — streaming duplicates collapsed by keeping the last entry per `requestId` in a map
98
-
-**Externalized pricing** — all model pricing, family prefixes, display names, and default model live in `pricing.json`. Embedded via `//go:embed`, with a remote-cached copy fetched from the repo every 24h. `initPricing()` prefers cached over embedded. Adding a new model requires only editing `pricing.json` — no code changes or binary release needed
98
+
-**Externalized pricing** — all model pricing (input, output, cache read/write tiers, long context), global settings (long context threshold, web search cost), family prefixes, display names, and default model live in `pricing.json`. Embedded via `//go:embed`, with a remote-cached copy fetched from the repo every 24h. `initPricing()` prefers cached over embedded. Adding a new model or adjusting pricing requires only editing `pricing.json` — no code changes or binary release needed. Cache fields in JSON are optional — `fillCacheDefaults()` derives them from input price using standard multipliers (0.1x read, 1.25x write-5m, 2x write-1h) when absent
99
99
-**Pricing resolution** — exact model ID → longest family prefix match → `defaultPricing`
100
-
-**Cache write pricing defaults to 1h** — Claude Code JSONL logs report all cache writes as `ephemeral_5m`, but Anthropic billing matches 1-hour tier pricing (2x input). Override with `-cache-5m`. `CacheWrite5m`/`CacheWrite1h` remain separate fields (different pricing multipliers)
100
+
-**Cache write tiers are trusted from JSONL** — Claude Code now correctly reports `ephemeral_5m_input_tokens` and `ephemeral_1h_input_tokens` per model (e.g. Haiku → 5m, Opus/Sonnet → 1h). Fallback for old logs without `cache_creation` sub-object defaults to 1h. `CacheWrite5m`/`CacheWrite1h` remain separate fields (different pricing multipliers)
101
101
-**Shared file parsing** — `parseFile()` in parser.go is used by both `parseLogs` (directory walk) and `parseSession` (statusline single-session)
102
102
-**Local timezone everywhere** — local midnight for cutoffs, `parsed.Local()` for date bucketing. Never use `UTC()` for user-facing date logic
103
103
-**MCP detection is best-effort** — all MCP detection functions return nil/empty on error; statusline never fails due to missing config
@@ -107,7 +107,7 @@ One API call produces multiple JSONL entries sharing the same `requestId`. `inpu
107
107
108
108
## Don't
109
109
110
-
- Don't add new model pricing by editing Go code — update `pricing.json` instead (models, families, and display_names sections)
110
+
- Don't add or change pricing by editing Go code — update `pricing.json` instead (models, families, display_names, long_context_threshold, web_search_cost)
111
111
- Don't use `log.Fatal` or `panic` — use `fmt.Fprintf(os.Stderr, ...)` + `os.Exit(1)`
112
112
- Don't use UTC for day boundaries — use `time.Date(...)` with `now.Location()` for local midnight
113
113
- Don't add JSON tags to `Bucket` — it's never directly marshalled; `printJSON` defines its own output structs
|`-base-dir`||`~/.claude`| Base directory for Claude Code data |
@@ -198,14 +195,6 @@ Thresholds are in USD (before currency conversion). They apply to the terminal o
198
195
|`-currency-rate`||`0`| Override exchange rate from USD (requires `-currency-symbol`) |
199
196
|`-version`|`-V`|| Print version and exit |
200
197
201
-
## How It Works
202
-
203
-
goccc parses Claude Code's JSONL conversation logs from `~/.claude/projects/`, deduplicates streaming responses (by `requestId`), and calculates costs using [Anthropic's published pricing](https://platform.claude.com/docs/en/about-claude/pricing) — including cache write tiers, long-context premiums (>200K input), and web search costs.
204
-
205
-
### Cache Write Pricing
206
-
207
-
Claude Code JSONL logs report all cache writes as `ephemeral_5m`, but Anthropic billing matches 1-hour tier pricing (2x input price). goccc defaults to 1-hour pricing to align with actual bills. Use `-cache-5m` to switch to 5-minute pricing (1.25x input) if your billing differs.
208
-
209
198
## Preserving Log History
210
199
211
200
Claude Code periodically deletes old log files. To keep more history for cost tracking, increase the cleanup period in `~/.claude/settings.json`:
0 commit comments