Skip to content

Releases: jayson-jia-dev/cc-token-status

v1.5.18 — engineer ladder, achievement badges, subagents/ recovery

27 Apr 08:52

Choose a tag to compare

13 versions of polishing rolled into one release. Highlights since v1.4.5:

Recovers ~50% of hidden token history

Claude Code's Task-tool subagents store their sessions under ~/.claude/projects/.../subagents/. Most parsers (including the early version of this one) glob */*.jsonl and miss the entire subtree. We now recurse with **/*.jsonl — on a 2-machine fleet that surfaced 8,706 subagent-only msg.ids and entire active days that previously vanished from stats.

6-tier engineer ladder + 5-dimension radar

Starter → Planner → Engineer → Integrator → Architect → Orchestrator. Each level is computed from five dimensions — Usage, Context, Tools, Automation, Scale — with hover tooltips showing the per-dimension scoring breakdown so you know exactly what's measured and how to level up.

25+ achievement badges

Cost milestones from $100 Club through $1M Club; behavioral badges including Night Owl, Opus Devotee, Cache Master, Marathon Session, Ten-Project Master, Billion Tokens, Balanced Master, and more. Locked badges show a desaturated icon with a hover tooltip explaining the unlock condition.

Quality of life

  • Hostname shown on level card and submenu — clarity for multi-Mac setups
  • Per-version model colors (Opus 4.6 vs 4.7 now visually distinct in charts)
  • HTML report opens correctly even when `.html` association is hijacked by VS Code etc.
  • "Force Refresh Usage" menu item
  • Better hint when OAuth token is dormant ("open Claude Code to wake it up")
  • Pre-commit hook keeps VERSION in sync between .py and install.sh
  • Test coverage hardened against silent failures

Install / Update

```bash
curl -fsSL https://raw.githubusercontent.com/jayson-jia-dev/cc-token-status/main/install.sh | bash
```

Or use the "Manual Update" menu item in the plugin (SHA256-verified, atomic replace).

v1.4.5 — stop rate-limit notification spam

20 Apr 14:04

Choose a tag to compare

What you saw before

When util jumped to 100%, you got 4 simultaneous notifications: ⚠️ 80% / ⛔ 95% / 🛑 100% / 🔥 burn-rate. When the 5h window rolled over, the same 4 notifications fired again — a screenshot from the user showed 8 push notifications in 10 minutes.

What v1.4.5 does

  • One notification per escalation per limit. Jumping 0→100% now fires exactly one (🛑 100%).
  • Window rollover no longer re-fires. State lives in {limit_key: {tier, at}}, no reset timestamp in the key. User staying at 100% across a 5h boundary gets zero additional pushes.
  • Drop-and-recross re-fires correctly. If util falls below 80% and then climbs back, next crossing fires again (intentional — user actively escalated).
  • Burn-rate suppressed when already blocked. Once 100% notification fires, burn-rate is redundant.
  • Burn-rate math hardened. Clamps remaining_min to [0, 300] so malformed API responses can't produce false positives.

Tests

6 new regression cases covering:

  • Jump 0→100 fires once
  • Same-level repeat no-refire
  • Window rollover no-refire
  • Drop-and-recross re-fires
  • Escalation through tiers (each crossing once)
  • Burn suppressed at 100%

All 17 tests green.

Compatibility

Legacy state file format (pre-v1.4.5 keys like five_hour_80_<reset>) is discarded silently on first load. No migration needed. If you somehow miss a notification during the transition, util dropping and re-climbing will re-fire it.

v1.4.4 — silent cleanup protection

20 Apr 13:55

Choose a tag to compare

Drops the first-run macOS notification from v1.4.3. Raising a retention threshold can't lose data, so there's nothing for the user to review. Still logs to ~/.config/cc-token-stats/.update.log if you want an audit trail.

v1.4.3 — stop Claude Code deleting your cost history

20 Apr 13:52

Choose a tag to compare

⚠️ If you've used Claude Code for more than a month

Your oldest session JSONLs have likely been silently deleted. Claude Code's default cleanupPeriodDays is 30, and on every claude startup it quietly removes files in ~/.claude/projects/**/*.jsonl older than 30 days. No warning, no log, no undo.

For anyone using this plugin, that means cost and token history just evaporates after a month. For heavy users with multiple hundred-dollar months on their stats, 16 days of real data can disappear with no trace.

What v1.4.3 does

On every plugin refresh:

  1. Reads ~/.claude/settings.json
  2. If cleanupPeriodDays < 3650, writes back 99999 (≈274 years — pragmatic "never")
  3. First time it patches, sends a macOS notification so the change isn't invisible
  4. Preserves all other keys in settings.json (atomic write)

Runs before anything else in the plugin so protection activates even if later code crashes.

Why 99999 and not 0?

The docs say 0 means "disable cleanup," but issue #23710 shows it actually disables writing transcripts at all — a silent footgun. 99999 works reliably, matches community workaround recommendations, and is reversible (just edit settings.json).

This doesn't recover lost data

It only prevents future loss. If you've already lost old JSONLs:

  • Anthropic Console is the only place old spend is still visible
  • Future releases won't add recovery capability — the data is gone

For admin-managed Macs

If your team's settings.json is managed by MDM / dotfiles and needs to stay unchanged, you can disable this behavior by removing the plugin before first run — or upstream your own cleanupPeriodDays override so the plugin's patch is a no-op.

Sources

v1.4.2 — precision pass (full audit)

20 Apr 13:29

Choose a tag to compare

Full audit of every data source, calculation, and derived display

Triggered by a user request to "find every remaining issue at once". Audited 10 subsystems; 4 fixes + 1 cleanup + 1 comment rewrite. All backed by the existing 7-case regression suite plus 4 new cases (11/11 green).

What changed

U-1 — calc_user_level now dedups by msg.id

The user-level scoring counted every assistant row as a separate message, so session-resume rewrites were inflating "median session length" 40-80%. Same seen_ids set now used there as in scan(). Scoring is honest again.

R-1 + R-2 — recalc_remote_cost removed entirely

Only recalculated total_cost but not per-day daily.cost, creating a permanent ~$4 gap between the menu header and Daily Details 合计 line. Also used a token-ratio approximation for per-model allocation instead of home-mac's exact per-message sum. Trust what each machine wrote; auto_update guarantees fleet-wide version convergence within 24h anyway.

Verified on real data: home-mac cost now shows $426.16 (was recalc'd to $430.45); fleet total $2,058.67 = $1,632.51 local + $426.16 remote exactly; Daily 合计 agrees with header to $0.02 (was off by $4.27).

S-2 — save_sync preserves per-day sessions field

Previously dropped when writing, so remote per-day session counts were lost from fleet views. Now passes through the merge.

M-2 — Pro-rated months_active for ROI math

Was max(days/30, 1) — users in their first 30 days got charged a full month in the savings/multiplier calculation. Now pro-rated with only a 1/30-day floor to keep division safe.

D-1 — Dashboard drops get("inp") + get("input_tokens") OR-trick

load_remotes now normalizes every field to scan()'s shape (inp/out/cw/cr), so there's one getter per field. Previous code only worked because remotes happened to lack short aliases.

C-1 — Stale comment in _file_fingerprints rewritten

Old text cited 'avoid double-count' reasoning from pre-dedup era. Replaced with current 'cache invalidation parity' explanation.

Tests

+4 cases (total 11/11 green):

  • test_remote_total_cost_not_recalculated
  • test_remote_daily_sum_matches_total_cost
  • test_remote_fields_normalized_to_scan_shape
  • test_level_uses_deduped_session_length

What this release does NOT change

  • User-visible cost numbers stay the same as v1.4.1 (no new pricing or token-source changes)
  • Consistency between menu sections is now exact where before it had small gaps
  • No breaking config or sync-file format changes

Audit artifacts

The full 10-subsystem audit and the decision rationale for each finding lives in the commit message.

v1.4.1 — menu aggregation + TTL-aware cache pricing

20 Apr 11:09

Choose a tag to compare

What changed

1. Menu aggregation fix (P0-1 from community audit)

The menu was silently mixing scopes — top "Cumulative Cost" summed all machines, but Daily Details / Hourly Activity / Top Projects showed local only. Users with 2+ Macs saw numbers that didn't add up between sections.

Now one merge path (_merge_machines_data()) serves both menu and dashboard. Only the Machines panel still shows per-machine detail.

Impact on you: if you have remotes synced via iCloud, your Daily Details and date range will expand — more data, not less.

2. TTL-aware cache write pricing

Anthropic quotes two cache write rates:

  • 5-minute ephemeral: input × 1.25
  • 1-hour ephemeral: input × 2

Previously every cw token was priced at 1h rate. Now scan() reads usage.cache_creation.ephemeral_{5m,1h}_input_tokens when present and prices each bucket separately. Flat field falls back to 1h (verified as Claude Code's default).

Impact on you: numbers unchanged for 100% 1h traffic (the common case). If any session used 5m caching, cost drops a bit.

3. opus_old cache write rate fix

opus_old.cache_write was 18.75, which is the 5m rate at input=$15. The 1h rate is 30. Fixed while renaming keys to cache_write_{5m,1h}. opus_old is legacy (Opus 4.0/4.1 only) so impact is minimal — consistency across the pricing table matters more.

Tests

Added 2 regression tests:

  • test_cache_ttl_split_prices_5m_and_1h_differently
  • test_cache_ttl_missing_falls_back_to_1h

All 7 cases green.

Source citation

Added comment block on PRICING citing https://platform.claude.com/docs/en/about-claude/pricing.

Not in this release

  • P0-1 menu vs dashboard parity — fixed above
  • Pricing TTL split — done
  • Earliest-date puzzle — investigated, not a bug: no JSONL on this user's machines predates 2026-03-16. Earlier data would need to live on a machine that never ran the plugin.

Acknowledgments

Thanks to the home-office code review that caught the menu aggregation drift and prompted the TTL investigation.

v1.4.0 — msg.id dedup (cost correction)

20 Apr 07:50

Choose a tag to compare

⚠️ Heads-up for existing users

Your displayed cost number will drop 40–80% after upgrading. This is not a bug — the number finally matches reality.

What changed

Claude Code re-logs the same Anthropic API response multiple times into the same JSONL on session resume/continue. Every prior version of this plugin summed every duplicate row, inflating cost by 40–80% on real machines. Empirical measurement on one real machine:

Before After Δ
Raw sum $2,937.15
After msg.id dedup $1,597.31 −45.6%

Within-file duplication is the main offender (41% of rows in main-session JSONLs alone). Cross-file dups were 0 on the tested machine, but dedup is still global so it handles any pattern correctly.

Fixes

  • scan() now tracks a global seen_ids set; each msg.id counts exactly once (#P0-0)
  • calc_user_level was still using UTC date slicing for its active-days metric — now uses local-tz date to match scan() (#P0-2)
  • install.sh now verifies SHA256 before install, matching the guarantee auto_update() already provided (#P0-3)
  • New: tests/test_scan_dedup.py — 5-case regression suite covering unique/duplicate/cross-file/no-usage/session-totals paths

Cache invalidation

SCAN_CACHE_SCHEMA bumped to msgid-dedup-v1 — existing cache is discarded automatically on first run. No action needed.

Multi-machine note

Remote machines' synced data is cached with their old (inflated) numbers until each machine runs v1.4.0 at least once and writes back to iCloud. Trigger a manual scan on each Mac or wait up to 5 min for the next SwiftBar refresh.

Post-mortem

v1.3.11 tried to recursively scan subagents/*.jsonl without verifying that Claude Code writes the same msg.id into both parent and subagent files — cost jumped 2-3x. v1.3.12 reverted the glob but left the underlying within-file dedup bug untouched. v1.4.0 addresses the root cause with a regression test so future glob/filter changes can't repeat the class of failure.