The fallback path in modelDisplayName returned "Auto (Sonnet est.) (est.)"
for any model not listed in modelDisplayNames, double-tagging the est.
suffix and hiding the real model ID. New Cursor model IDs now surface as
their raw name with a single (est.) suffix until the display map is
updated. Adds a regression test.
Source cache fixes (empty-session guards, date range reordering) make
the cache safe for menubar use. Forcing noCache on every 15s poll was
re-parsing 5800+ files each time, causing the menubar to hang.
Source cache entries with zero sessions now treated as cache misses instead
of serving stale empty data. Date range skip moved after fingerprint check
so changed files are never incorrectly excluded. TUI refresh timer bypasses
in-memory CachedWindow cache. Menubar-json forces noCache. Swift menubar
adds explicit refreshStatusButton calls to avoid observation race.
Discovers transcripts at ~/.cursor/projects/*/agent-transcripts/*.txt
and joins against ~/.cursor/ai-tracking/ai-code-tracking.db for model
attribution. Token counts are estimated from transcript character
length since the attribution DB does not carry them; the model label
surfaces the estimation with an (est.) suffix on every row.
Deduplication keys prefix cursor-agent: to stay disjoint from the
existing cursor: prefix so the two providers do not cross-dedupe
on shared conversationId namespaces.
Tests cover: empty ~/.cursor/projects/, single transcript, multiple
projects, missing ai-code-tracking.db, unrecognized transcript format
skip, non-UUID filename fallback, and sqlite metadata join.
Closes#55
Two pre-existing type errors surfaced during the rebase against main:
1. JsonPlanSummary.id was hardcoded to four plan ids, but PlanId now
includes 'none' (PLAN_IDS was extended when 'codeburn plan clear'
was added). toJsonPlanSummary only runs for active plans at runtime,
but the static type still had to be widened. Use PlanId directly
instead of the hand-rolled union.
2. isActivePlan used Boolean(plan) as the nullish guard, which doesn't
narrow plan's type in TypeScript. Switch to an explicit
'plan !== undefined' so the subsequent .id and .monthlyUsd accesses
type-check.
npx tsc --noEmit is now clean; all 285 tests still pass.
Address review feedback on #74:
1. TUI plan row previously used the active tab's filtered projects as
plan spend, so 'Today' showed today's cost as plan spent. Switch
renderDashboard and reloadData to getPlanUsageOrNull(), which uses
the plan's own billing period regardless of tab.
2. Plan row rendered via a local formatUsd that hardcoded USD. Replace
every call with formatCost so 'codeburn currency EUR' flows through.
Removes the adjacent '$3,425.52' vs '$32.07' style mismatch.
3. renderPlanBar capped filled width at 100%, so 105% and 1700% looked
identical. Past 100%, render a full bar plus chevron tail sized by
order of magnitude (log10): 1.05x -> 1 chevron, 17x -> 2, 170x -> 3.
4. 'running on API overage pricing' is wrong for Claude Pro/Max (rate
limited, not charged overage). Drop that claim; keep the Nx-over
multiplier and match the under/near projection line structure.
5. Spell out 'equiv' as 'API-equivalent' in the plan label.
Dead code cleanup: getPlanUsageOrNullForProjects is now unused; remove
it. getPlanUsageFromProjects stays (unit tests still use it).
Adds `codeburn plan set <id>` to configure a subscription plan (Claude Pro,
Claude Max, Cursor Pro, or custom). When set, the Overview panel renders
an API-equivalent progress bar against subscription price with a
projected month-end cost.
Closes the loudest demand signal on the repo: issue #11 ("Subscription
vs API Use") from two independent voices, plus the routing-decision use
case raised in #12.
- src/config.ts: extends CodeburnConfig with Plan, adds readPlan/savePlan/clearPlan
- src/plans.ts: presets (claude-pro $20, claude-max $200, cursor-pro $20)
- src/plan-usage.ts: getPlanUsage, resetDay-aware period math (1-28),
median-of-7-day-trailing projection
- src/cli.ts: `codeburn plan [show|set|reset]` subcommand, plan wired
into JSON outputs for report/today/month/status (only when active)
- src/dashboard.tsx: Plan row in Overview, color-coded (green under 80%,
orange near, red over), with days-until-reset
- README.md: Plans section with honest framing (API-equivalent vs
subscription price, not token allowance)
- tests/plan-usage.test.ts, tests/plans.test.ts, tests/cli-plan.test.ts:
period math, presets, CLI round-trip
Resets respect resetDay across month boundaries. Uses median daily spend
(not mean) so one huge day doesn't distort the month-end projection.
Fixes#11
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove bidirectional fuzzy match in getModelCosts that could return
wrong pricing when a short canonical name prefix-matched a longer key
- Use explicit undefined check in parseLiteLLMEntry so free models with
zero cost are not silently dropped from the LiteLLM pricing database
- Destroy read stream in finally block of readSessionLines to prevent
file descriptor leaks when the generator is abandoned early
- Extend CSV injection escaping to cover tab and carriage-return prefixes
- Add optional chaining fallback for empty periods in exportCsv/exportJson
- Add regression tests for all fixes (models, export, fs-utils)
Period switching no longer resets optimize or compare views back to
the dashboard. Auto-refresh keeps the current screen. Arrow keys
now work in all views. Added period switch hints to compare status bar.
Closes#107
reloadData() was clearing optimizeResult before fetching, which
caused the optimize screen to flash back to the dashboard on every
30s refresh cycle. Let the projects useEffect re-scan naturally.
GitHub asset name includes a v prefix (v0.8.0) while
CFBundleShortVersionString does not (0.8.0). Strip the prefix
before comparing. Also capture stderr on update failure so the
button doesn't hang on "Updating..." forever.
Menubar: reduce cache TTL from 300s to 30s, background refresh from
60s to 15s, always fetch fresh data on tab switch instead of serving
stale cache. TUI: default auto-refresh to 30s (--refresh 0 to disable).
Closes#107
5-section compare view: Performance (one-shot, retry, self-correction),
Efficiency (cost/call, cost/edit, output/call, cache hit), Category
Head-to-Head bar charts, Working Style, and Context.
Planning rate now detects TaskCreate/TaskUpdate/TodoWrite instead of
only EnterPlanMode (which was never used, showing 0% for all models).
Validated against raw JSONL with zero false positives.
Responsive side-by-side layout at 90+ cols. Self-correction scanner
with compact file skipping and model+timestamp dedup. 274 tests.
Remove high-false-positive patterns (I'm sorry, I should have, sorry for).
Add precise patterns (you're right I, that was incorrect, let me correct).
Skip compact JSONL files that replay compressed context.
Deduplicate by model+timestamp to prevent double-counting.
Fix test timestamps to work with deduplication.
Period changes (arrows, 1-5) now update comparison results in place
instead of returning to dashboard. Status bar hidden in compare view
to reduce clutter.