Mirror the combined multi-device dashboard in the macOS menubar via a
Local/Combined scope toggle (Settings -> Display). Builds on the menubar-json
`--scope combined` contract.
- MenubarScope enum (.local default) persisted in UserDefaults; PayloadCacheKey
gains a scope dimension so local and combined payloads cache separately.
- DataClient.fetch(scope:): a pure statusSubcommand builder appends
`--scope combined` only for combined, forces `--provider all` (the CLI rejects
combined with a provider/project/exclude filter), and coerces multi-day
selections to local (the CLI rejects combined + --days).
- MenubarPayload decodes the optional `combined` block (per-device + totals);
nil when absent (back-compat).
- The badge always stays on the local payload; the combined network pull lives
on a separate cache key and falls back to local with a "Combined unavailable"
indicator, so a slow/offline peer never blocks the badge.
- Hero shows combined totals + a per-device breakdown in Combined scope; the
token metric stays input+output (cache excluded) to match local; the
per-device daily-budget warning is suppressed for a multi-device total;
selecting Combined resets the provider tab to All.
Tests run via `swift test` (CommandLineTools Testing.framework on the rpath, no
Xcode): scope persistence, argv (local/combined/provider-forcing/multi-day),
cache-key partition, badge-survives-combined-failure, combined decode, token
consistency, provider reset, budget suppression.
The daily budget was always measured in dollars, so users tracking the
Tokens or Total Tokens metric had no token-based budget alert. Add a
separate token-denominated daily budget and route the flame tint and the
hero "budget exceeded" banner through one metric-aware check.
- New dailyTokenBudget (stored separately from the cost budget so switching
metric never reinterprets a threshold).
- Centralize the logic on AppStore: isTokenMetric, activeDailyBudget,
todayMetricTotal, isOverDailyBudget, dailyBudgetLabel.
- Settings shows a token-threshold picker (1M..100M) for token metrics and
the existing dollar picker for the cost metric, with metric-aware help text.
- Flame tint and the banner now use isOverDailyBudget / dailyBudgetLabel.
Add a new `localModelSavings` config and `codeburn model-savings` CLI
that maps a local-model name (e.g. llama3.1:8b) to a paid baseline
(e.g. gpt-4o). The local call still costs $0; the new `savingsUSD`
field tracks the counterfactual spend avoided by running locally and
is reported separately from `costUSD` everywhere a number is shown.
* Parser normalization (`applyLocalModelSavings`) runs on Claude
parse, direct provider calls, and the cached-call path. It forces
`costUSD` to 0 and attaches `savingsUSD` + `savingsBaselineModel`
+ `isLocalSavings` on the `ParsedApiCall`. Local-savings wins for
actual cost even when the same model is also in `modelAliases`.
* Session, project, day, model, category, activity, skill, and
subagent rollups all carry `savingsUSD` alongside `costUSD`.
* `status --format json` adds `today.savings` and `month.savings`.
* `status --format menubar-json` adds a `current.localModelSavings`
block (totalUSD, calls, byModel, byProvider) plus savings on
topModels, topProjects, topSessions, topActivities, and history
daily entries. Schema fields default-decode for backward compat.
* `report --format json` adds savings across overview/daily/
projects/models/activities/skills/subagents/topSessions, with
the active paid baseline name on each model row.
* `models` command gains a `Saved` column on table/markdown/CSV
and a `savingsUSD`/`savingsBaselineModel` pair in JSON. Default
`--min-cost 0.01` filter now ORs in `savingsUSD >= minCost` so
local models with $0 actual cost but >0 savings still surface.
* CSV/JSON exports add a `Saved (CODE)` column on summary/daily/
models/projects/sessions.
* Dashboard TUI shows a green 'saved $X by local models' footer
line in the overview when any savings are present.
* macOS Swift payload gains a `LocalModelSavings` Codable block
and savings fields on every model/activity/session/daily
struct. Hero shows a green leaf 'Saved $X' caption, models
section gets a green `Saved` column. `swift build` clean.
* GNOME indicator adds 'saved $X' to the hero meta line and a
`codeburn-model-saved` column to the model row.
* Daily cache schema bumped to v8 (`savingsUSD` on day/model/
category/provider). `savingsConfigHash` invalidates the cache
when the user changes their baseline mapping so historical
saved-spend numbers never lie about a stale baseline.
* Defensive `Object.hasOwn` lookup in `getLocalSavingsBaseline`
blocks the prototype-pollution test that previously surfaced via
the savings path with a hostile `__proto__` model name.
* New tests (5 files, 25 tests, 549 lines) cover pricing helpers,
end-to-end parser normalization, day aggregator savings,
menubar payload savings, CLI set/list/remove, and
daily-cache hash invalidation. Existing tests for daily-cache
/ day-aggregator / models-report updated for the new fields.
Full vitest suite: 1028/1028 passing across 73 test files.
`tsc --noEmit` clean. `npm run build` clean.
(Note: `mac/Tests` has a pre-existing `no such module 'Testing'`
environment error on the installed Swift toolchain, confirmed
on `main` before this PR; not caused by these changes.)
* Add multi-day calendar selection with custom popover UI
Replace native DatePicker with a custom-themed CalendarPopover supporting
non-contiguous multi-day selection. Add --days CLI flag for comma-separated
date filtering that post-filters from the bounding range scan.
CLI: parseDaysFlag() parses and validates dates, filterProjectsByDays()
filters turns to only selected days. Verified that sum of individual --day
calls matches --days output across cost, calls, activities, models, and
providers.
Swift: DataClient passes --days for multi-day, with single-day fallback.
AppStore tracks selectedDays as Set<String> with PayloadCacheKey support.
CalendarPopover uses pending state committed only on Done tap.
* Clear + Done in calendar resets to current period
When user clears all selected days and taps Done, switch back to the
active period instead of silently keeping the old day selection.
* Add CodeBurn Pro Mac App Store app
SwiftUI MenuBarExtra with litellm-snapshot pricing, Claude/Codex/Copilot
parsers, session discovery, auto-refresh timer, and dashboard UI matching
the real menubar design.
* Add appstore/ to .gitignore
Private Mac App Store build, not for the public repo.
* Add Optimize tab, token display modes, daily budget alerts, and project drill-down
- New Optimize insight tab with Retry Tax and Routing Waste computations
(pure math from session data, no LLM required)
- Retry tax: shows money wasted on failed edit retries, per-model breakdown
- Routing waste: counterfactual savings vs cheapest reliable model, per-model
- Token display modes: Cost ($), Tokens (up/down split), Total Tokens
- Daily budget alert: configurable threshold, flame turns yellow when exceeded
- Project drill-down: click project rows to see per-session cost, tokens, models
- Period-aware top sessions: 30-day view now shows 30-day costliest sessions
- Friendly project names: show directory name instead of full path, Home for ~
- Session cache TTL bumped to 180s to prevent re-parsing on non-today periods
- Audit fixes: array mutation, percentage rounding, ForEach ID collision,
baseline minimum threshold, editTurns scoping, first-of-month edge case
* Fix model badge ForEach ID collision and budget warning provider filter
- SessionDetailsList model badges used id: \.name which silently drops
duplicate model entries. Switched to enumerated offset-based ID.
- Hero budget warning compared against provider-filtered payload instead
of todayPayload (all providers). Now matches the menubar flame tint.
* Add Kiro provider and transparent auto-model naming
- Add Kiro IDE provider: parses .chat JSON files, estimates tokens,
normalizes dot-versioned model IDs for cost lookup
- Show "Cursor (auto)", "Copilot (auto)", "Kiro (auto)" in CLI
dashboard instead of pretending to know which model was used
- Route auto model names through BUILTIN_ALIASES for cost estimation
* Fix menubar tabs: add missing providers, show period-scoped costs
- Add Kiro, OMP to ProviderFilter enum so installed providers appear as tabs
- Merge Cursor + Cursor Agent into single Cursor tab
- Tab costs now reflect the selected period (7d/30d/month/all) instead
of always showing today
- Tab visibility still uses today's provider list so tabs don't
disappear when switching to periods with no data
* Add accent color picker to menubar with Apple system presets
- 9 presets using Apple's exact macOS dark-mode accent colors
(Ember, Blue, Purple, Pink, Red, Orange, Yellow, Green, Graphite)
- Color picker in header, persisted via UserDefaults
- "Burn" text stays fixed ember regardless of accent
- ThemeState is MainActor-isolated for thread safety
- Picker state lifted to AppStore so it survives .id() tree rebuild
- Accessibility labels on all color swatches
- Renamed brandAccentDark/brandEmberDeep/brandEmberGlow to match
their actual light/deep/glow semantics
* Fix review findings: case-sensitive cost lookup, Kiro timestamp guard, cache versioning
- Normalize provider dictionary keys to lowercase in tab cost lookup
so "Cursor Agent" (title-case from CLI) matches providerKeys
- Guard against missing/invalid/epoch startTime in Kiro parser to
prevent RangeError crash or 1970-01-01 ghost entries
- Bump DAILY_CACHE_VERSION to 4 so upgraded users get a clean
recompute with the new auto-model naming (cursor-auto vs default)
- Add version field to cursor-results.json cache to invalidate stale
entries that still use the old 'default' model name