Commit graph

536 commits

Author SHA1 Message Date
iamtoruk
bd41fa3962 Add persistent disk cache for parsed session data
Some checks are pending
CI / semgrep (push) Waiting to run
Cache normalized turns/calls to ~/.cache/codeburn/session-cache.json so
the CLI skips re-parsing unchanged JSONL files on subsequent runs.
File reconciliation uses dev+ino+mtime+size fingerprinting; cost,
classification, and summaries are recomputed at query time. Atomic
writes via temp+fsync+rename, deep structural validation on load,
per-provider env fingerprinting, and best-effort save so cache failures
never break the CLI. ~6x speedup on warm cache.
2026-05-16 01:04:13 -07:00
iamtoruk
d568c8c103 Add src/summit.ts to gitignore 2026-05-15 23:17:20 -07:00
iamtoruk
2fb078bdfb Fix V8 OOM crash on 30-day period with Buffer-based line reader and large-line parser
Three-layer fix for V8 heap exhaustion when parsing heavy session data:

1. Buffer-based readSessionLines (fs-utils.ts): Replace readline with raw
   Buffer streaming using Buffer.indexOf(0x0a). Eliminates ConsString trees
   that caused OOM when regex-flattening 100MB+ lines. Two-state machine
   (ACCUMULATING/SCANNING) skips old lines at ~2KB cost instead of 200MB.

2. Large-line streaming parser (parser.ts): Hand-written JSON scanner for
   lines >32KB extracts only cost/token/tool fields without JSON.parse,
   avoiding full object graph allocation. Dual string/Buffer paths.

3. Dashboard memory management (dashboard.tsx): Disable auto-refresh for
   heavy periods (30d/month/all), clear old dataset before reload via
   nextTick to allow GC, prevent overlapping reloads with mutex, lazy
   optimize scanning on keypress instead of useEffect.

Also fixes three race conditions in dashboard reload deduplication:
- Early return after nextTick bypassing finally block (permanent mutex lock)
- A->B->A period switching dropping final reload (stale pending)
- Stale pendingReloadRef not cleared when in-flight matches request
2026-05-15 23:15:26 -07:00
iamtoruk
36e94169fb Fix changelog: command count, issue refs, Node guard, menubar section
- "Nine commands" → "Eight commands" (report has 2 call sites, 1 command)
- "Closes #196" → "Closes #320" (#196 was closed in 0.9.8)
- Add Node version guard entry (closes #319)
- Add Fixed (macOS menubar) section covering 8 commits since 0.9.8
2026-05-15 20:04:49 -07:00
iamtoruk
6a12d818d9 Sync package-lock.json to 0.9.9 and fix flaky menubar test
package-lock.json was stale at 0.9.7 with engines >=22; now matches
package.json 0.9.9 / >=22.13.0.

The menubar-json CLI test used hardcoded 10:00/11:00 UTC timestamps
which fall in the "future" when the test runs before those hours,
causing the menubar's todayRange (start..now) to exclude them.
Use timestamps relative to now instead.
2026-05-15 20:03:44 -07:00
iamtoruk
54e3123cc0 Fold Unreleased entries into 0.9.9 and add hydrateCache removal note
All Unreleased items (IBM Bob, cache write pricing, OpenCode MCP,
project names, Cursor bubbles) are already on main, so they belong
under 0.9.9. Replaced the narrow status-only hydrateCache bullet
with the broader 9-command removal.
2026-05-15 14:23:47 -07:00
iamtoruk
57e48e6e55 Stop eager daily-cache hydration on CLI commands that never consume it
Only `status --format menubar-json` uses getDaysInRange from the hydrated
cache. The other 9 call sites (report, today, month, export, optimize,
compare, models, yield) parse their own date ranges directly via
parseAllSessions. Removing hydrateCache from these paths avoids a 365-day
backfill parse that was the primary OOM multiplier on large session dirs.
2026-05-15 14:23:20 -07:00
Resham Joshi
041d9338c2
Merge pull request #335 from getagentseal/fix/oom-compact-entries
Some checks are pending
CI / semgrep (push) Waiting to run
Reduce Claude parser OOM risk (0.9.9)
2026-05-15 13:22:18 -07:00
iamtoruk
a7bb780618 Reduce Claude parser OOM risk via entry compaction (0.9.9)
Strip heavy fields from JournalEntry immediately after JSON.parse in the
JSONL hot loop. Keeps only what downstream consumers need: type, timestamp,
sessionId, cwd, compacted user text (2000 char total cap), assistant
model/usage/id, tool_use names with Skill and Bash inputs, and MCP
inventory attachments. Text, thinking, and tool_result blocks are dropped.

Also removes redundant hydrateCache() from status --format json and
terminal status paths, and clears the session cache between period
parses to avoid pinning both today and month result sets.

This is a mitigation, not a full fix. Very large month ranges still
materialize full ProjectSummary.turns arrays. The real fix is the
streaming single-pass parser refactor.
2026-05-15 13:20:50 -07:00
Resham Joshi
1f0aa7dce8
Merge pull request #328 from getagentseal/fix/menubar-tab-refresh-recovery
Some checks are pending
CI / semgrep (push) Waiting to run
Harden menubar refresh and release path
2026-05-15 10:10:05 -07:00
iamtoruk
d342f2205b Fix menubar all-provider refresh OOM 2026-05-14 21:57:51 -07:00
iamtoruk
909efcf989 Harden menubar refresh and installer 2026-05-14 18:32:15 -07:00
iamtoruk
478131d5b7 Harden menubar status refresh timer 2026-05-14 17:57:36 -07:00
iamtoruk
00c55873a4 Fix menubar tab refresh recovery 2026-05-14 11:26:15 -07:00
Resham Joshi
2ca92a97cf
Merge pull request #315 from getagentseal/fix/menubar-version-prefix
Some checks failed
CI / semgrep (push) Has been cancelled
Harden menubar refresh recovery
2026-05-13 20:33:42 -07:00
iamtoruk
403efd4727 Merge remote-tracking branch 'origin/main' into fix/menubar-version-prefix
# Conflicts:
#	package.json
#	src/parser.ts
2026-05-13 20:32:22 -07:00
iamtoruk
c626fc4a45 Fix menubar stale cache recovery 2026-05-13 20:22:15 -07:00
iamtoruk
aa946d0965 Keep CLI executable after build 2026-05-12 19:13:40 -07:00
Resham Joshi
151d24fb26
Drop Z suffix from day-aggregator test timestamps for timezone stability (#322)
Some checks failed
CI / semgrep (push) Has been cancelled
Timestamps with Z are interpreted as UTC, causing date bucketing tests
to fail in non-UTC timezones (e.g. UTC+12 shifts Apr 9 10:00Z to Apr 8).
Local timestamps without Z are interpreted in the runtime timezone,
matching how the aggregator actually buckets dates.

Based on #112 by @lfl1337, extended to cover all affected timestamps.
2026-05-11 22:25:32 -07:00
iamtoruk
f9a5d2c8e6 Add changelog entries for project path fix and Cursor undated bubbles 2026-05-11 22:19:15 -07:00
Resham Joshi
fe2e622038
Skip Cursor bubble rows that lack a createdAt timestamp (#321)
Bubble rows without createdAt were defaulting to new Date(), which
misattributed historical or undated usage to Today and inflated the
daily chart. Now filtered at the SQL level and skipped in application
code.

Based on the bubble-side fix from #262 by @darthrevanyunka.
2026-05-11 22:16:00 -07:00
Resham Joshi
3b71650f24
Fix mangled project paths in dashboard (#320)
* Fix mangled project paths in By Project and Top Sessions panels

shortProject() decoded Claude Code slugs by splitting on '-', which
broke directory names containing dashes ('foo-bar' became 'foo/bar').
Switch the dashboard to consume ProjectSummary.projectPath (the
canonical cwd already extracted by parser.ts) and rewrite shortProject
to operate on a real absolute path.

* shortProject: cache homedir, normalize Windows backslashes, fix stale test helper

---------

Co-authored-by: Abdallah Meghraoui <abdallah.meghraoui@outlook.com>
2026-05-11 22:02:38 -07:00
Resham Joshi
38e41e93c3
Add Node version guard for unsupported runtimes (#319)
Split CLI into a tiny launcher (src/cli.ts) that checks for Node >= 22.13.0
before dynamically importing the full CLI (src/main.ts). Users on Node 18
now get a clear upgrade message instead of a cryptic regex parse error from
string-width. Closes #232.
2026-05-11 21:50:17 -07:00
AgentSeal
a1b5e4bd00
Fix OpenCode MCP usage reporting (#318)
* Fix OpenCode MCP usage reporting

* Move OpenCode MCP changelog entry to Unreleased section

---------

Co-authored-by: ozymandiashh <234437643+ozymandiashh@users.noreply.github.com>
Co-authored-by: iamtoruk <hello@agentseal.org>
2026-05-11 21:30:27 -07:00
AgentSeal
c85beeaeae
Fix Claude 1-hour cache write pricing (#317)
Co-authored-by: ozymandiashh <234437643+ozymandiashh@users.noreply.github.com>
Co-authored-by: iamtoruk <hello@agentseal.org>
2026-05-11 21:23:04 -07:00
AgentSeal
03e22ecb80
Add IBM Bob provider with workspace extraction (#316)
Some checks are pending
CI / semgrep (push) Waiting to run
* Add IBM Bob provider

* Add workspace extraction for Cline-family providers

Extract project name from workspace directory in api_conversation_history.json
so sessions show actual folder names instead of the provider display name.
Thread projectPath through ParsedProviderCall to avoid unsanitizePath mangling
hyphenated folder names.

---------

Co-authored-by: ozymandiashh <234437643+ozymandiashh@users.noreply.github.com>
Co-authored-by: iamtoruk <hello@agentseal.org>
2026-05-11 20:54:13 -07:00
iamtoruk
b4b28becc8 Harden menubar refresh recovery 2026-05-11 20:44:06 -07:00
iamtoruk
4737bfb1fa Contribution rules: require real-data testing for new providers, one PR at a time 2026-05-11 20:04:18 -07:00
iamtoruk
f058f36dbd Normalize menubar version display 2026-05-11 11:21:39 -07:00
AgentSeal
cd8c646818
Merge pull request #311 from getagentseal/fix/menubar-manual-refresh-reset
Some checks are pending
CI / semgrep (push) Waiting to run
Make menubar Refresh Now reset stale state
2026-05-11 11:13:02 -07:00
iamtoruk
469d956312 Preserve menubar bundle seal during install 2026-05-11 11:11:37 -07:00
iamtoruk
33649e0a65 Refresh live quota progress from menubar 2026-05-11 11:09:20 -07:00
iamtoruk
ce0e1eb116 Make menubar refresh now reset stale state 2026-05-11 11:05:50 -07:00
AgentSeal
2301577e03
Merge pull request #310 from getagentseal/fix/menubar-wake-recovery
Fix menubar wake recovery and release asset selection
2026-05-11 10:58:33 -07:00
iamtoruk
1149ab6e43 Fix menubar wake recovery and release asset selection 2026-05-11 10:57:02 -07:00
iamtoruk
d9acd8c4cd Release 0.9.8
Some checks are pending
CI / semgrep (push) Waiting to run
2026-05-10 17:09:16 -07:00
Resham Joshi
02f4635cec
Fix node:sqlite V8 crash on invalid UTF-8 in text columns (#272)
node:sqlite calls v8::String::NewFromUtf8 with kAbort on TEXT columns.
Cursor chat blobs often contain truncated multi-byte chars from streaming
boundaries, which triggers a V8 CHECK abort (not a JS exception).

Select all text-content columns as CAST(col AS BLOB) so node:sqlite
returns Uint8Array instead. Decode in JS with TextDecoder fatal:false
which replaces bad bytes with U+FFFD. Covers all three SQLite providers
(Cursor, Goose, OpenCode).

Removes the version blocklist (MIN_NODE_22_PATCH) and lowers engines
requirement from >=22.20 to >=22 since the BLOB cast approach works
on all Node 22.x versions.

Closes #264
Closes #250
2026-05-10 17:05:08 -07:00
Resham Joshi
d142bd97ef
daily-cache: discard pre-v5 caches (fixes menubar providers regression) (#297)
PR #296 (Cursor per-project breakdown) bumped DAILY_CACHE_VERSION
from 4 to 5 but left MIN_SUPPORTED_VERSION at 2. The migration
path (isMigratableCache + migrateDays) only fills in missing
default fields; it does NOT recompute the providers / categories
/ models rollups from session data, because raw sessions are not
retained in the cache. So a v4 cache migrated to v5 carried
forward its old per-day provider totals (single 'cursor' bucket)
for the full retention window.

Effect on users post-#296: the macOS menubar's
`current.providers.cursor` would show the orphan-bucket subtotal
instead of the full Cursor cost for any historical day whose
daily entry was computed before #296 landed. Live-test on my
machine showed cursor=$3.78 against a migrated v4 cache vs
cursor=$4.08 (correct) after the daily cache was discarded — the
$0.30 gap was the workspace projects whose costs were no longer
aggregated under the 'cursor' label by the new code.

Fix: raise MIN_SUPPORTED_VERSION to 5 so any cache with
version < DAILY_CACHE_VERSION is renamed to `.bak` and the cache
is recomputed from scratch on next run. The recompute is the same
operation that backfills the cache for a new user, so the cost is
a one-time cold-path hit (~3s on the test machine).

Test for the migration case updated to assert the new
discard-and-bak behavior. Full suite: 46 files / 654 tests pass.
2026-05-10 16:05:59 -07:00
Resham Joshi
810b214476
Cursor: per-project breakdown by workspace (closes per-project half of #196) (#296)
Cursor's chat history showed as a single row labeled 'cursor' in
the dashboard because the global state.vscdb has no workspace
field on individual bubbles. The fix joins through Cursor's
per-workspace storage:

1. Walk ~/Library/Application Support/Cursor/User/workspaceStorage/*
2. For each hash dir, read workspace.json -> folder URI
3. Open that dir's state.vscdb, read
   ItemTable['composer.composerData'] -> allComposers list
4. Build Map<composerId, folder URI>
5. emit one SessionSource per workspace plus a catch-all 'cursor'
   source for composers that did not register against any
   workspace (multi-root workspaces, no-folder-open windows,
   deleted workspaces with surviving global rows)

The parser decodes source.path's #cursor-ws= tag, filters the
parsed bubbles to the composerIds that belong to this workspace,
and yields only those. The orphan-tag source negates the filter so
it captures every composer not in any workspace.

In passing, fix a real bug in the old code: parseBubbles set
`sessionId: row.conversation_id ?? 'unknown'`, but the JSON
`conversationId` field is empty in current Cursor builds, so every
call shipped with `sessionId: 'unknown'`. We now derive the
composer id from the row key (`bubbleId:<composerId>:<bubbleUuid>`)
which is what the workspace map joins on. The old behavior masked
the bug because every call went into a single 'cursor' project
anyway; with per-workspace bucketing the bug becomes load-bearing.
Cache version bumped 2 -> 3 to invalidate caches that still record
'unknown' as the session id.

Live-tested against my real 1.9 GB Cursor DB: the single 'cursor'
row with 1904 calls / $4.08 now breaks into 5 workspaces plus an
orphan bucket, totals reconcile exactly. 8 fixture-based tests
cover multi-workspace routing, orphan filtering, legacy bare DB
path backwards compat, multi-root workspace skip, vscode-remote
URI slugification, and total reconciliation across all sources.

Full suite: 46 files, 653 tests passing.
2026-05-10 15:35:57 -07:00
Resham Joshi
180e14da82
Merge pull request #293 from getagentseal/fix/menubar-loading-watchdog
Some checks are pending
CI / semgrep (push) Waiting to run
Fix menubar loading recovery deadlocks
2026-05-10 10:32:56 -07:00
Resham Joshi
3380517b24
README: drop Project Structure tree, add Sponsor section (#292)
The Project Structure tree was duplicating information that
docs/architecture.md already covers in better detail (and updates
faster). Removing it from the README keeps the marketing-facing
README scoped to "what is this and how do I install it" and
points contributors at the proper map.

In its place, a short Sponsor section pointing at
https://github.com/sponsors/iamtoruk so users who find the tool
useful know where to support development.
2026-05-10 03:35:16 -07:00
iamtoruk
d79deefaae Fix menubar refresh recovery deadlock 2026-05-10 03:30:56 -07:00
Resham Joshi
cdf7169a89
Cursor model aliases: cover every variant so non-Auto sessions price (#159) (#290)
Cursor emits model names in a `claude-<dot-version>-<tier>` shape
(`claude-4.6-sonnet`, `claude-4.5-opus`, `claude-4.5-opus-high-thinking`,
etc.) plus its own `composer-1` house model. None of these match
the canonical LiteLLM pricing keys (`claude-sonnet-4-6`,
`claude-opus-4-5`).

The alias map in `src/models.ts` filled some of these in v0.9.4
but missed:

- plain no-suffix forms: `claude-4.5-opus`, `claude-4.5-sonnet`,
  `claude-4.6-opus`
- haiku tier: `claude-4.5-haiku`, `claude-4.6-haiku`
- forward-looking: `claude-4.7-opus`
- Cursor's house model: `composer-1`

The dashboard rendered $0 for sessions that used any unaliased
model — visible in the screenshots posted in #159 even after the
v0.9.4 fix that added the `-thinking` variants.

This PR fills the gaps and adds 16 regression tests under
`Cursor model variants resolve to pricing` that assert every
model name in `src/providers/cursor.ts:modelDisplayNames` plus
the additional plain forms resolves to a non-null pricing entry
with `inputCostPerToken > 0` and `outputCostPerToken > 0`. So a
future LiteLLM snapshot bump or a typo in the alias map will fail
the test before users see $0.

Direct hits in the snapshot (no alias needed): `gpt-5`, `gpt-5.2`,
`grok-code-fast-1`, `gemini-3-pro` (already aliased). These are
covered in the test suite as well so a snapshot that drops them
would also be caught.

Tests: 45 files, 617 passing locally (16 new). Closes #159.
2026-05-10 03:27:44 -07:00
Resham Joshi
7a878f4d19
Classifier: feature verb wins over debug keyword (part of #196) (#289)
Some checks are pending
CI / semgrep (push) Waiting to run
Messages like "add error handling", "create an issue tracker", or
"implement the 404 page" were landing in the Debugging bucket
because the classifier checked DEBUG_KEYWORDS (which matches
`error`, `issue`, `404`) before FEATURE_KEYWORDS in both
`refineByKeywords` (tool-bearing turns) and `classifyConversation`
(chat-only turns). The position of the matched word in the
sentence is a much stronger intent signal than the order of the
checks in code, so we now pick whichever pattern matches earliest.

The new helper `firstMatchingCategory` runs each candidate regex
once with `RegExp.exec` and keeps the match with the lowest
`index`. Ties (rare in practice — same start position) break by
the order the candidates were listed, which is `refactoring >
feature > debugging` for coding turns. That ordering preserves
existing behavior for plain bug reports (e.g. "login is broken,
traceback below") while flipping mislabeled feature work to its
correct category.

8 regression tests in `tests/classifier.test.ts` cover the
mislabel cases from #196 plus tie-break / chat-only cases. Full
suite: 45 files / 609 tests, all green.

Closes the activity-misattribution half of #196. The Cursor
provider attribution half (single 'cursor' project for all
sessions) is addressed in a separate PR.
2026-05-09 22:48:11 -07:00
Resham Joshi
b72e51e538
Support CLAUDE_CONFIG_DIRS for scanning multiple Claude data dirs (#208) (#288)
Adds an OS-delimited list env var so a user with more than one
Claude account or profile can scan all of them in a single run.
Sessions across every configured dir merge into one ProjectSummary
per project, matching the option-1 design agreed on the issue
thread (no per-account splitting in the data model or the UI).

Format: `CLAUDE_CONFIG_DIRS=~/.claude-work:~/.claude-personal`
on POSIX, `;`-separated on Windows. Precedence is
CLAUDE_CONFIG_DIRS > CLAUDE_CONFIG_DIR > ~/.claude. Empty entries
in the list are skipped, duplicates are deduped on resolved path,
and a missing or unreadable dir does not abort the scan of the
others. If the user explicitly set CLAUDE_CONFIG_DIRS but every
listed entry is unreadable, a one-line stderr hint identifies the
attempted paths and the platform's expected delimiter, so a
Windows user typing the POSIX `:` does not get a silent zero-row
result. `~` is now also expanded in CLAUDE_CONFIG_DIR for
consistency.

Implementation is intentionally narrow: only `claude.ts` changes,
plus a small parser-cache key update so a stale cache from one
config does not bleed into a run with a different config (matters
for the macOS menubar and GNOME extension which run as long-lived
processes). The merge happens for free in
`src/parser.ts:scanProjectDirs`, which keys ProjectSummary entries
by canonical cwd (or the sanitized slug as a fallback). Two
SessionSource entries with the same `project` field land under the
same key and combine their sessions, regardless of which dir they
came from. No new fields on SessionSource / SessionSummary /
ProjectSummary, and no UI changes.

Tests: 12 fixture-based cases covering the unset path (default
~/.claude), single-dir override via CLAUDE_CONFIG_DIR, multi-dir
override via CLAUDE_CONFIG_DIRS, ~ expansion, dedup of repeated
entries, leading/trailing/doubled delimiters, missing dir
tolerated, file-not-directory entry tolerated, empty
CLAUDE_CONFIG_DIRS falls back to single-dir env, and two
parser-level integration tests asserting (a) two sessions from
two dirs sharing one cwd produce one ProjectSummary with combined
totals and no `account`/`accountPath` fields anywhere, and (b)
two sessions sharing a slug but with different canonical cwds
still merge by slug at the project-rollup layer (option 1
behavior pinned so a future refactor cannot quietly swap to
cwd-aware merging without an explicit opt-in).

Supersedes the alternative implementation in #227, which builds
per-account attribution (option 2) instead.
2026-05-09 22:04:45 -07:00
Resham Joshi
d1eb13fb91
Expose per-day one-shot data in daily JSON output (#279) (#280)
* Expose per-day one-shot data in daily JSON output

Closes #279.

Adds turns, editTurns, oneShotTurns, oneShotRate to each entry of the
`daily[]` array in `codeburn report --format json` output. The data was
already computed internally for activity-level rollups; this just buckets
it by date so consumers building daily-resolution efficiency dashboards
(streak tracking, heatmaps, rolling-window charts) don't have to re-derive
the rate from period-level activities.

Counting matches parser.ts categoryBreakdown semantics:
- every turn counts toward `turns`
- turns with hasEdits=true count toward `editTurns`
- edit turns with retries=0 count toward `oneShotTurns`
- oneShotRate is null (not 0) when editTurns=0 — a chat-only day's rate
  is undefined, and reading it as 0% would be misleading

Real consumer named in the issue: a 10-developer internal usage tracker
that scores days by cache hit + cost/call + (now) one-shot rate.

* Strengthen daily/activities reconciliation + CHANGELOG entry

- Fall back to turn.assistantCalls[0]?.timestamp when turn.timestamp is
  missing so daily aggregate doesn't drop turns that activities[] keeps.
  Previously sum(daily[].editTurns) could be < sum(activities[].editTurns)
  for sessions starting with assistant entries before any user line.
- Add Unreleased CHANGELOG entry for the daily one-shot fields.
2026-05-09 21:01:05 -07:00
Resham Joshi
4c29f6b880
Add Crush provider plus per-provider icon column in README (#286)
Closes #278.

Adds Charmbracelet Crush as a lazy-loaded provider:
- src/providers/crush.ts: walks ~/.local/share/crush/projects.json
  (XDG_DATA_HOME and CRUSH_GLOBAL_DATA aware), opens each project's
  crush.db read-only, queries root sessions where parent_session_id
  IS NULL. Emits one ParsedProviderCall per session with real
  prompt_tokens, completion_tokens, cost (dollars), and the
  dominant model resolved from messages.model.
- src/providers/index.ts: register crush alongside cursor, goose,
  opencode, antigravity, cursor-agent in the lazy import path.
- tests/providers/crush.test.ts: 10 fixture-based tests covering
  discovery, parsing, missing-registry, malformed JSON, missing db,
  child session exclusion, dominant model selection, dedup, and
  array-shaped legacy registry.

Schema source: charmbracelet/crush@v0.66.1
internal/db/migrations/20250424200609_initial.sql, verified by
spawning a research agent against upstream. The schema *comments*
in that migration claim millisecond timestamps but every actual
INSERT/UPDATE uses strftime('%s', 'now') which returns Unix
seconds; the parser treats values as seconds. Tokscale's
parser (junhoyeo/tokscale#346) gets this wrong and is off by
1000x, plus its parser misses the prompt_tokens/completion_tokens
columns that exist in Crush's schema. Our integration uses both,
so Crush sessions get real per-model attribution.

Menubar:
- mac/Sources/CodeBurnMenubar/AppStore.swift: add .crush case to
  ProviderFilter and its cliArg switch.
- mac/Sources/CodeBurnMenubar/Views/AgentTabStrip.swift: add
  Crush color to the per-tab color extension. The visibleFilters
  computed property already filters by detected providers, so the
  Crush tab appears automatically when a user has Crush data.

README:
- Replace the provider table with an icon-led layout. Icons live
  under assets/providers/<name>.<ext>. 14 icons sourced from
  junhoyeo/tokscale (MIT) under nominative fair use, 4 sourced
  separately: codex (OpenAI org avatar), cursor-agent (reuses the
  Cursor icon), kiro (kiro.dev favicon, ico->png via sips), omp
  (can1357/oh-my-pi icon.svg, MIT). Attribution line added.
- Add Crush row.

Docs:
- docs/providers/crush.md: full per-provider doc with verified
  schema excerpt, the seconds-vs-milliseconds quirk, and a
  "when fixing a bug here" checklist.
- docs/architecture.md: provider count 17 -> 18, test count
  41 -> 42, and crush in the lazy list.
- docs/providers/README.md: add Crush row to the lazy index.
- CONTRIBUTING.md: bump test count to 568 (was 558).

All 568 tests pass locally; swift build clean.
2026-05-09 20:47:56 -07:00
Resham Joshi
36c4684fba
Replace Data Location column with Doc link in README provider table (#285)
The provider table now points each row at its docs/providers/<name>.md
file (added in #284) instead of repeating a one-line path that the
per-provider doc covers in full. The Data Location column is dropped;
the new Doc column links to the markdown that owns the path, storage
format, dedup key, and known quirks.

The trailing sentence is updated to reference the per-provider docs as
the source of truth for data locations.
2026-05-09 20:46:15 -07:00
Resham Joshi
b4ed98cfa4
Add codeburn models per-model + per-task breakdown command (#287)
A single dense table of every (provider, model) you have used in the
selected period, sorted by cost. Inspired by tokscale's per-model
output and ccusage's responsive cli-table3 layout, ported to plain
Node with no new runtime dependency.

Default view: one row per (provider, model) with a Top Task cell
showing the dominant task category and its cost share, e.g.
`Coding (42%)`.

`--by-task` explodes each model into one row per task type, with
provider/model cells blanked on subsequent rows of the same group
and a horizontal divider between groups so the sections read as
distinct units.

Output formats: table (Unicode box-drawn, default), markdown
(GitHub-flavored, copy-paste friendly), json, csv.

Filters: --period (today/week/30days/month/all, default 30days),
--from/--to, --provider, --task, --top, --min-cost, --no-totals.

The table renderer auto-sizes every column to its content (no fixed
widths leaving trailing whitespace) and drops cache columns as a
pair when the terminal is narrow, then input/output, then top-task,
in that order. Provider, model, total, and cost stay regardless.
Visible-width math uses strip-ansi (already a dependency) so styled
cells pad correctly. Cyan headers, yellow totals, dim provider name.

The aggregator walks every parsed turn and attributes each
assistant call to its (provider, model, task) bucket, computing
real input / output / cache_write / cache_read tokens and cost.
Output tokens include reasoning. Cached input tokens are folded
into cache_read so the column matches what users intuitively expect.

19 fixture-based tests cover aggregation correctness, byTask
grouping, taskFilter, topN/minCost filters, reasoning-as-output,
all four renderers (table/markdown/json/csv), narrow-terminal
column dropping, CSV/markdown escaping, totals row toggle, and
visible-width math under styled cells.
2026-05-09 20:45:21 -07:00
Resham Joshi
6746ecc22f
Add CONTRIBUTING.md, docs/architecture.md, and per-provider docs (#284)
Document the contributor onboarding path:
- CONTRIBUTING.md: setup, npm scripts, coding conventions, PR process,
  the block-claude-coauthor enforcement, and the five providers without
  test coverage today (claude, gemini, goose, qwen, antigravity).
- docs/architecture.md: 12-command CLI surface, parser pipeline, three
  cache layers, 14 optimize detectors, and the mac / gnome / build
  layouts with cited line numbers.
- docs/providers/: one file per provider (17 providers plus the shared
  vscode-cline-parser helper). Each covers data path, storage format,
  caching, dedup key, quirks, and a "when fixing a bug here" checklist.

Also fix two pre-existing documentation issues surfaced while writing
the new docs:
- RELEASING.md claimed GitHub Actions auto-publishes the CLI when a
  v* tag is pushed. There is no such workflow; CLI publishing is
  manual via npm publish. Updated the CLI section to reflect reality
  and kept the menubar (mac-v* tag) automation accurate.
- .gitignore had CLAUDE.md unanchored, which on case-insensitive
  filesystems also matched docs/providers/claude.md. Anchored to
  /CLAUDE.md so the root-level memory file stays ignored without
  affecting subdirectory docs.

All cited file paths, line numbers, function names, and test counts
were verified against current code (41 test files, 558 tests passing).
2026-05-09 18:39:41 -07:00