Release notes

FEATURE

Read-only commands stop mutating settings, and offline costs are accurate

We've patched several quiet hazards in how the system operates under the hood. Previously, when you ran terminal commands like usage report that were supposed to simply read data, the system would aggressively overwrite your Claude or Codex config files behind your back, and if the app was killed mid-write, it could completely ruin your configuration file. The app now behaves itself: read-only commands absolutely will not touch your settings, and the actual installation flows now use "atomic writes" — guaranteeing the entire file is safely written before swapping it in, leaving no broken files even if it crashes mid-way. Additionally, for "offline cold starts" (when you have no internet and no cached price table), we fixed the built-in fallback prices where the cost of the newest Opus 4.6 model was drastically underestimated by 3×. Now your cost estimates are accurate even completely offline. It's like using a library catalog computer: it used to secretly reset your PIN just because you searched for a book, and a power outage could erase your card; now it sticks to reading only, and tells you exactly the right late fees even when the library's internet is down.

Original changelog

Original changelogRead-only commands stop mutating settings, and offline costs are accurate
**Read-only CLI commands silently mutated user settings**: `usage daily` / `report` / `sessions` / `dashboard` and other read commands unconditionally called `setup()` or `update_hook()`, potentially writing to `~/.claude/settings.json` or `~/.codex/config.toml` on every invocation. Fix: only `setup` / `unsetup` mutate user settings; other commands now show a one-line "Hook not installed. Run: usage setup" hint when the hook isn't installed.
**Opus 4.6 / 4.7 cost was underestimated 3× on offline cold start**: `pricing.py`'s fallback table listed Opus as `5e-6 / 25e-6` (input / output per token), but the published Anthropic rate is `15e-6 / 75e-6`. Affected scenario: no pricing cache *and* LiteLLM live fetch fails. Users with network access or a cached price table are unaffected.
**`adapters/codex.py` sqlite connection leak**: `_load_thread_models()` wrapped the work in `try / except`, but `conn.close()` ran *after* `execute().fetchall()` — any exception in between left the connection dangling. Now uses `contextlib.closing()` to guarantee release.
**Mid-write crash could leave `~/.codex/config.toml` truncated**: `setup_hook.py`'s `_setup_codex` / `_unsetup_codex` used plain `write_text()`, so a crash or kill during setup could corrupt Codex config. Now uses `mkstemp + os.replace` atomic write, sharing a single module-private helper with Claude settings.
**`analyzer/cost.py` removed**: it was a weakened duplicate of `pricing.py` — bidirectional substring model matching (prone to misclassification), no cache TTL, and an SSL-cert-verification-disabled fallback when fetching the price table (a security concern for cost data). `analyzer/{aggregator,blocks,reporter}` now import `pricing.calculate_cost` directly; the latter accepts a `typing.Protocol` so both `history_loader.UsageEntry` and `adapters.types.UsageEntry` work. Net 76 lines of duplicate cost-calc code removed.