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.
Self-correction scanner was only reading JSONL files inside session
subdirectories, missing the main session transcripts stored at the
project level. Also adds bordered box to results and widens winner
column for readability.
Adds scanSelfCorrections() which reads raw .jsonl session files (including subagent dirs) and counts per-model self-correction patterns for use in the model comparison metrics.
Side-by-side comparison of any two AI models using normalized metrics:
cost/call, one-shot rate, retry rate, self-correction rate, cache hit.
Accessible via codeburn compare and dashboard [c] shortcut.
The gapStart date was constructed with T00:00:00.000Z (UTC midnight),
causing it to land hours before local midnight. In PDT this meant
the gap fill re-parsed a partial slice of the previous day, and the
upsert replaced the full day with that partial data, losing cost.
Bump DAILY_CACHE_VERSION to 3 to force cache rebuild.
Remove the broken "Connect Claude" / "Reconnect Claude" buttons from
the Plan pane -- they opened a terminal session that did nothing useful
for already-logged-in users. Keep only the "Retry" button.
Add an auto-update checker that queries GitHub releases every 2 days in
the background. When a newer menubar build is available, an "Update"
pill appears in the header. Clicking it runs the existing installer
flow (download, replace, relaunch) with no manual steps.
Timestamps in session files are UTC ISO strings. Several code paths
extracted the date via .slice(0, 10) which gives the UTC date, while
date range filtering uses local-time boundaries. This caused turns
between UTC midnight and local midnight to be bucketed under the wrong
day -- the menubar showed lower today cost than the TUI because those
turns were attributed to tomorrow (UTC) but filtered as today (local).
format.ts already had a localDateString fix; this applies the same
pattern everywhere via dateKey() in day-aggregator.ts.
The daily cache never re-processed yesterday once cached, so a mid-day
run would freeze partial cost/call data permanently. The "All" provider
path in menubar-json relied on this cache, causing the menubar to show
wildly incorrect numbers while per-provider views (which parse fresh)
were correct. Now yesterday is evicted and recomputed on every run, and
addNewDays upserts instead of skipping duplicates as defense-in-depth.
Replace eval-based require with createRequire(import.meta.url) so the SQLite driver loads correctly when the CLI runs as ESM.
This restores OpenCode and Cursor session discovery instead of returning empty results when require is unavailable.
README gains a --from/--to example in the Usage block, a dedicated
'Date range filtering' subsection, and a note that JSON projects[]
now includes avgCostPerSession.
CHANGELOG opens an Unreleased section crediting @lfl1337 for PRs #78
and #80. Flags the projects.csv column-order shift (Avg/Session now
between Cost and Share) so consumers parsing by position read by
header instead.
Co-authored-by: AgentSeal <hello@agentseal.org>