# Claude Anthropic Claude Code CLI and Claude Desktop's local agent mode. - **Source:** `src/providers/claude.ts` - **Loading:** eager (`src/providers/index.ts:1`) - **Test:** none directly. Coverage comes from `tests/parser-claude-cwd.test.ts`, `tests/parser-filter.test.ts`, and `tests/parser-mcp-inventory.test.ts`, which exercise `src/parser.ts` end-to-end against fixture session files. ## Where it reads from | Source | Path | |---|---| | Claude Code CLI | `$CLAUDE_CONFIG_DIR` if set, otherwise `~/.claude/projects/` | | Claude Desktop (macOS) | `~/Library/Application Support/Claude/local-agent-mode-sessions/` | | Claude Desktop (Windows, classic) | `%APPDATA%/Claude/local-agent-mode-sessions/` | | Claude Desktop (Windows, MSIX) | `%LOCALAPPDATA%/Packages//LocalCache/Roaming/Claude/local-agent-mode-sessions/` | | Claude Desktop (Linux) | `~/.config/Claude/local-agent-mode-sessions/` | For Desktop, `findDesktopProjectDirs` walks up to 8 levels deep looking for `projects/` subdirectories, skipping `node_modules` and `.git`. Desktop session roots are resolved in this order: 1. A non-empty `CODEBURN_DESKTOP_SESSIONS_DIR` overrides discovery and is the only returned root. 2. macOS uses the single path shown above. 3. Windows always includes the classic path first. It then scans `%LOCALAPPDATA%/Packages` for package directories whose names start with `Claude_` or contain `.Claude_`, sorted by package name, and includes only packages whose full MSIX sessions path exists as a directory. 4. Other platforms use the single Linux path shown above. All returned roots are absolute, resolved, and deduplicated. Missing or unreadable Windows package directories are ignored. ## Storage format JSONL, one event per line, per session file. Sessions live under `/.jsonl`. ## Parser `createSessionParser` returns an empty async generator (`claude.ts:101-105`). Claude is a special case: `src/parser.ts` reads Claude JSONL files directly with full turn grouping, dedup of streaming message IDs, and MCP tool inventory extraction. The provider object exists only so `discoverSessions` can return Claude session sources alongside the others. ## Pricing Claude Code reports total cache-write tokens in `usage.cache_creation_input_tokens`. When available, it also splits those writes by duration in `usage.cache_creation.ephemeral_5m_input_tokens` and `usage.cache_creation.ephemeral_1h_input_tokens`. CodeBurn keeps the existing aggregate cache-write token total for reports, but prices the 1-hour portion at 2x base input cost (1.6x the 5-minute cache-write rate exposed by LiteLLM). If the split fields are missing, the parser falls back to the legacy behavior and prices every cache write at the 5-minute rate. ## Bedrock sessions The JSONL has no provider field; the only trace of how a call was billed is the model id. With `CLAUDE_CODE_USE_BEDROCK=1` the assistant messages record Bedrock's id — `anthropic.claude-haiku-4-5-20251001-v1:0`, or with the cross-region profile prefix the user configured, `us.anthropic.…` — where a direct-API session records `claude-haiku-4-5-20251001`. `getModelRoute` reads the shape, so the rows are `Haiku 4.5`, `Haiku 4.5 (Bedrock)` and `Haiku 4.5 (Bedrock us)`: three SKUs at three prices, kept apart on every surface (see "Model rows and billing routes" in `../architecture.md`). Vertex ids (`claude-…@20251001`) are not a route yet; the `@` suffix is stripped and they merge with the direct row. ## Caching None at the provider level. The daily aggregation cache (`src/daily-cache.ts`) reuses prior computed days. ## Quirks - The parser is in `src/parser.ts`, not in `src/providers/claude.ts`. Anything that changes Claude parsing belongs in `parser.ts`. - Streaming responses produce duplicate message IDs across resumed sessions; `parser.ts` strips them via the global `seenMsgIds` Set. - Model display names are mapped in `claude.ts:7-20`; add new versions there when Anthropic releases them. ## Early quota resets Anthropic sometimes resets a usage window before its scheduled time. The menubar notices on the existing refresh lifecycle — no extra request — by comparing each fetch's windows against the previous fetch's readings, which are kept per window in `UserDefaults` alongside the record of what has already been announced. Everything is local; nothing is fetched to produce it. A detected reset posts one system notification and nothing else: there is no persistent UI for it. The detection is deliberately quiet. It needs a validated window length (the fixed 5-hour and 7-day limits), a stored reset that has not yet passed, and either a reset time anchored to a genuinely new cycle or a fall of at least 40 points that lands at or under 10%. A scheduled reset, a plan change, clock or timestamp skew, a window appearing or disappearing, a first observation and a reconnect after a terminal failure all produce nothing. ## When fixing a bug here 1. Confirm whether the bug is in **discovery** (sessions not picked up) or **parsing** (sessions found but data wrong). 2. Discovery bugs live in `claude.ts:78-99`. Verify the directory layout you expect actually matches what Claude writes today. 3. Parsing bugs live in `src/parser.ts`. Look for `parseSessionFile`, `groupIntoTurns`, and `dedupeStreamingMessageIds`. 4. Add a fixture under `tests/fixtures/` and a test under `tests/parser-claude-cwd.test.ts` (or a new file). Do not mock the filesystem.