Read ~/.kimi-code credentials directly and poll api.kimi.com
/coding/v1/usages for the weekly quota and rate-limit windows,
mirroring the existing Codex/Claude quota services.
- New KimiSubscriptionService with lenient decoding (string/number
values, enum-style time units, derived used from remaining) and
429 backoff
- Quota chip on the Kimi Code tab, Plan insight, and a Kimi tab in
Settings with connect/disconnect
- Independent refresh cadence anchor so Kimi-only setups don't poll
on every payload tick
- Expired tokens surface as terminal with automatic recovery once
the CLI refreshes the credential file
- Keep insights visible for quota-capable providers on empty days
- Widen Settings so six tabs don't collapse into the overflow menu
Compact one-row strip: correction rate with count, median time to first
edit, and the top reworked file, plus one coaching note derived locally
with the same thresholds and copy as the CLI's workflow-insights
buildCoachingNotes. Decodes two new optional payload blocks
(current.workflow and current.topReworkedFiles), so payloads from older
CLIs still parse. The section hides entirely when there is no signal and
individual stats never render as zero placeholders. It reads the store's
current payload, so it follows the selected agent tab automatically.
Adds unit tests for the duration formatter, note selection, model
derivation, and payload decoding.
Co-authored-by: reviewer <review@local>
The Update badge previously replaced only the menubar (via the CLI's
menubar --force installer), and the CLI banner only copied the update
command to the clipboard. One click now runs the whole sequence: update
the CLI in place through the package manager it was installed with
(brew upgrade, or npm install -g codeburn@latest --force; the npm next
to the codeburn launcher wins so nvm/volta/asdf installs update inside
their own toolchain), re-read the installed version, then run the app
replacement from the freshly updated CLI. No recognizable package
manager surfaces the manual command instead of guessing at a mutation;
a homebrew CLI without a findable brew never falls through to npm,
which would create a second conflicting install. The badge now also
appears for CLI-only updates, and the banner's primary action is Update
now with the copyable command kept as secondary. Six resolution tests,
mutation-verified.
Co-authored-by: reviewer <review@local>
Scopes the menubar to one Claude config for multi-config (CLAUDE_CONFIG_DIRS)
setups, with All as the default. Rebased onto main and fixed the review
findings from the original #635:
- Fix a TS2206 build break (a 'type' modifier inside an import type block).
- Reject --claude-config-source with a non-Claude --provider, and scan Claude
only in the scoped branch (a config is Claude-only): fixes provider data
leaking into a scoped query and avoids parsing every provider's corpus.
- Scope the macOS menu-bar figure to the selected config (badge matched the
popover), clear the selection when switching to a non-Claude provider tab,
and stop the on-disk badge fallback from showing an unscoped number while
scoped.
- Tag Claude Desktop / Cowork sessions as their own 'claude-desktop' source so
they are a selectable bucket instead of silently vanishing from per-config
views (sum of options now equals All).
- Skip the redundant Claude discovery walk for plain single-config users while
keeping idle configs and Claude Desktop selectable.
Reviewed by Codex 5.6; all findings addressed. Full suite: 1581 TS tests, 76
Swift tests, tsc clean.
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.
PR #393 accidentally deleted the RefreshBackoff integration from PR #388,
leaving RefreshBackoff.swift as dead code and tests calling nonexistent
methods. Delete the dead code and fix the broken test target.
Add exponential backoff to the loading watchdog (8s, 16s, 32s... up to
60s, max 6 attempts) so it stops hammering the CLI when it's unavailable.
After exhausting retries, show an error overlay with a Retry button.
Fix recoverFromStuckLoading to skip recovery when a fetch is already
in-flight (avoids killing healthy fetches via generation bump).
Fix selectedDay to return nil for multi-day selections, and pass days
through startInteractiveSelectionRefresh so the cache key matches
currentKey.
Commit 5800179 (multi-day calendar) accidentally removed stuck-loading
recovery. Re-add an 8-second watchdog loop in MenuBarContent that calls
recoverFromStuckLoading(). Change inFlightKeys from Set to timestamped
Dict so the watchdog can detect orphaned quiet-refresh entries. Stop
nuking payloadRefreshGeneration in the watchdog (it was killing healthy
in-flight fetches). Bump Package.swift platform to macOS 15 to match
NSAttributedString(attachment:) usage.
* 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.
* Fix Antigravity provider detection, Codex fork double-counting, and tab ordering
Antigravity:
- Handle ephemeral port (--https_server_port 0) via lsof fallback
- Force reparse when cached turns are 0 (server may have been unavailable)
- Persist precomputed costUSD in session cache for correct pricing
- Map gemini-pro-agent to gemini-3.1-pro pricing
- Add display names for gemini-pro-agent and gemini-3.5-flash-low
Codex:
- Detect forked sessions via forked_from_id in session_meta
- Skip replayed parent events within 5s of fork creation time
- Use parent session ID in dedup key so parent+fork don't double-count
Menubar:
- Sort provider tabs by cost descending instead of enum declaration order
* Add tooling breakdowns to CLI dashboard and menubar
Surface skills, subagents, tools, and MCP server usage in both the
text dashboard and menubar app. Parse subagent types from Agent/Task
tool calls, aggregate with skills into a merged "Skills & Agents"
panel. Remove Top Sessions panel from CLI dashboard.
* Fix per-provider data loss, division-by-zero, and decode fragility
- Per-provider multi-day queries only merged cost/calls from cache,
dropping categories/models/sessions/tokens. Remove broken cache
shortcut and always do full parse for per-provider periods.
- Remove per-provider daily history double-counting from overlapping
cache + live data.
- Guard maxCost against zero in ActivitySection and ModelsSection to
prevent NaN in bar width calculations.
- Use offset-based ForEach ID in BarTooltipCard to avoid duplicate
model name collisions.
- Make cacheHitPercent, topActivities, topModels, providers use
decodeIfPresent for backward compat with older CLI versions.
- Skip currency switch when FX rate fetch fails with no cache,
preventing rate/symbol desync.
- Use readSessionFile in Gemini parser for 128MB size cap.
- Truncate Codex userMessage to 500 chars like other providers.
* Restore cache-backed trend history for provider-filtered views
The previous commit removed the broken per-provider cache shortcut but
also dropped cache-backed daily history, causing provider-filtered views
to lose trend data outside the selected period range.
Use allCacheDays for historical days (cost/calls per provider is accurate
in cache) and today's entry from the full parse. No overlap since cache
ends at yesterday.
* Quiet routine pricing warnings + menubar recovery from stuck-loading
CLI:
- Default `codeburn` invocation no longer prints "no pricing data for model"
warnings on every run. Greeting a fresh user with three lines of stderr
before the dashboard even draws looked like the tool was broken on first
launch. The warning now requires --verbose, and the suppressed pricing
miss still results in $0 cost (correct for unmapped models).
- Local-model heuristic skips the warning entirely for Ollama tags
(`qwen3.6:35b-a3b-bf16`), GGUF/quantized fingerprints, and similar names
that will never have public pricing. The "update codeburn" hint was
actively misleading there.
- When the warning does fire (with --verbose), it points users at
`codeburn model-alias <model> <known-model>` as the actual escape hatch
alongside the package update suggestion.
Menubar:
- Replace perpetual "Loading…" spinner with a FetchErrorOverlay when the
per-key fetch fails and the cache is empty. User sees the error and a
Retry button instead of an infinite hang.
- Add diagnostic breadcrumbs (NSLog, invisible to normal users — Console.app
/ `log stream --process CodeBurnMenubar` only) for the four states that
produce a stuck loading overlay:
- subprocess timeout after 45s
- fetch result dropped due to Task cancellation (rapid tab switch)
- fetch result dropped due to mid-fetch calendar rollover
- retry attempt where the last successful fetch is >2 min stale
- Track lastSuccessByKey separately from cache freshness so the staleness
diagnostic survives day-rollover cache wipes.
* Stop flashing the compare-view loading screen on background refresh
When the 30s CLI tick updated `projects` while the user was reading the
model comparison results, the projects-watching effect always fired
setLoadTrigger, which flipped phase to 'loading' and re-ran the slow
scanSelfCorrections walk over every provider's session directory. The
user lost their scroll position and saw a loading flash mid-read.
Recompute the comparison rows in place when:
- the user is already on the results phase, AND
- both picked models still exist in the new aggregate.
Skip the corrections rescan on these in-place refreshes — corrections
drift slowly enough that holding the previous value until the user
re-enters compare is acceptable, and the rescan is the slow part of the
load. Initial selection and post-selection load still run the full
pipeline.
* Gate Claude OAuth refresh attempts on terminal failures
Anthropic returns invalid_grant (HTTP 400) when the user's refresh token has
been revoked or rotated, typically after they re-ran claude login on another
device. The previous code rethrew the raw error every refresh cycle, leaving
the Plan UI stuck on a Swift error string and pummeling Anthropic's token
endpoint forever.
The new SubscriptionRefreshGate captures a fingerprint of
~/.claude/.credentials.json on terminal failure and stops trying until that
fingerprint changes (the user re-logs-in). Transient 5xx/network failures
get exponential backoff capped at 6 hours.
Two new SubscriptionError cases let the UI distinguish "user must reconnect"
from "Anthropic is flaky right now" and show a clean reconnect CTA instead
of raw HTTP guts.
* Inline live-quota progress bar inside each AgentTab chip
When a provider exposes a live quota source, the AgentTab chip grows by ~3pt
to host a thin weekly-utilization bar directly under the label. Hovering the
chip reveals a popover with all four Anthropic windows (5-hour, weekly, weekly
Opus, weekly Sonnet) plus reset countdowns. Click still switches the tab as
before.
Today only Claude has a quota source (the existing /api/oauth/usage path);
other providers' chips render unchanged. The QuotaSummary abstraction lets
us bolt on Cursor/Copilot/Codex meters in follow-up commits.
Subscription is now refreshed eagerly on the periodic loop so the bar lights
up without forcing the user to open a deep view first. The previous
SubscriptionRefreshGate keeps a dead refresh token from spamming Anthropic.
Adds two new SubscriptionLoadState cases (terminalFailure, transientFailure)
so the deep Plan view shows a "reconnect" message instead of a raw Swift
error string when the user's claude login expired.
* Replace SubscriptionClient with credential-store + service architecture
The previous SubscriptionClient never persisted refreshed access tokens, so
every 30s tick read the expired token from Keychain, refreshed it (1 call),
fetched usage with the new token (2nd call), and threw the new token away —
3 API calls per cycle, which burned through Anthropic's per-account rate
budget and produced the 429s and `invalid_grant` loops users were seeing.
The replacement mirrors CodexBar's proven pattern:
- ClaudeCredentialStore owns the credential lifecycle. Bootstrap is strictly
user-initiated (Connect button in the Plan tab); the menubar does not touch
Claude's keychain at startup. After bootstrap, refreshed tokens — including
rotated refresh tokens — are persisted to a local cache file under
~/Library/Application Support/CodeBurn (mode 0600). Using a file instead of
our own keychain item means rebuild signature changes don't trigger a
startup keychain prompt; the only prompt the user ever sees is the one for
Claude Code-credentials on Connect.
- ClaudeUsageFetcher (folded into the service) is a pure /api/oauth/usage
call with one allowed 401-recovery roundtrip. 429s record an explicit
backoff window honouring Retry-After.
- ClaudeSubscriptionService orchestrates bootstrap / refresh / disconnect,
applies the 429 backoff, and surfaces terminal vs transient failures so
the UI can show the right CTA.
- Reading Claude's keychain now tries the entry keyed by NSUserName() first
and falls back to the unscoped query, so users who re-ran /login and ended
up with two Claude Code-credentials items pick up the fresh one. This was
the actual cause of "I logged in but the menubar still shows stale data".
User-facing additions:
- A proper Settings window (right-click → Settings…) with General / Claude /
About tabs. Provider quota cadence is configurable (Manual / 1m / 2m / 5m /
15m). New providers plug in as additional tabs.
- Plan tab: notBootstrapped → "Connect Claude subscription" CTA;
terminalFailure → "Reconnect Claude" with the correct /login instruction
for Claude Code 2.1; transientFailure preserves the last loaded view with
a retrying badge.
- AgentTab quota bar slot is always reserved so chip height doesn't jitter
when the user connects for the first time. Hover popover has 250ms enter
/ 150ms exit debounce so swiping across chips doesn't pop a popover for
every chip touched.
- Disconnect requires confirmation, clears capacityEstimates and the
subscription snapshot store so a reconnect under a different account
doesn't surface "Based on last cycle" projections from the old account.
Validator findings applied: cadence anchor only updates on successful
refresh (not every attempt), refresh-token rotation persists in memory
before keychain write so a write failure doesn't lock the user out, server
error bodies are sanitized (token redaction + 240-char cap) before they
reach the UI or NSLog, and Refresh Now refreshes both the menubar payload
and quota.
* Add Codex live quota + multi-provider warning, with validator fixes
CodexCredentialStore reads ~/.codex/auth.json (ChatGPT-mode only) on
user-initiated Connect, caches under Application Support like Claude.
CodexSubscriptionService hits chatgpt.com/backend-api/wham/usage with
the bearer token + ChatGPT-Account-Id header, parses primary/secondary
windows, additional per-model rate limits (e.g. GPT-5.3-Codex-Spark),
and credits balance with a Double-or-String fallback.
Plan-tier enum captures the full ChatGPT plan list including prolite,
free_workspace, education, quorum, k12, plus an unknown(String) case
that preserves the raw plan name when OpenAI ships a tier we haven't
mapped yet.
Multi-provider warning system:
- Menubar flame tints from neutral to yellow (70%) → orange (90%) →
red (100%) based on the worst-affected connected provider's worst
window. Uses NSImage.SymbolConfiguration palette colors.
- Popover header gains a warning row when any provider is at 70%+.
"Claude 79% of quota used", "Claude 79% · Codex 92%", or
"Claude over limit (105%)" when severity hits .danger.
- Hover popover gains a plan-name badge in the top-right corner so
users know which subscription is feeding the bar.
- Codex chip surfaces the credits balance and any non-zero per-model
additional rate limits as footer rows.
Validator fixes applied in the same commit:
- Provider-specific reconnect / disconnected copy in QuotaDetailPopover
(was hardcoded to Claude).
- Generation-token guard on refreshSubscriptionReportingSuccess and
refreshCodexReportingSuccess so a Disconnect during an in-flight
fetch can't resume after the await and re-populate the cleared state.
- Codex codexQuotaSummary promotes secondary to primary when only one
window is returned, so free / guest tiers don't render an empty bar.
- Memory-cache TTL is now actually consulted in currentRecord (the
isFresh check was dead code, leaving cached records valid forever).
- sanitizeForUI now redacts OpenAI sk-* keys, JWT tokens, and Bearer
headers in addition to Claude sk-ant-*.
- Removed diagnostic NSLog that wrote raw chatgpt.com response bodies
to the unified log.
- Codex Connect / Reconnect copy in Settings explains the auth.json
prerequisite and the API-key vs ChatGPT-mode distinction.
- Disconnect dialogs now state explicitly that the auth.json /
credentials keychain entry is left untouched.
- Plan badge in the popover gets line-limit + truncation + max-width
so a long unknown plan name can't overflow the row.
- Renamed shadowing `let max` to `let worst` in aggregateQuotaStatus.
* Add Codex Plan tab + size plan badge to content
The Plan tab is now visible when the Codex chip is selected, mirroring
the Claude tab's deep view. CodexPlanInsight renders the user's plan
tier ("Pro Lite", "Plus", etc.), the primary and secondary rate-limit
windows with reset countdowns, and any non-zero per-model additional
limits (e.g. GPT-5.3-Codex-Spark) so power users see them.
The "On pace at reset" projection that Claude's Plan view shows is not
included here — that math feeds from local Claude per-message spend
extrapolated against API quota windows, and our local Codex spend is
not a 1:1 signal for the ChatGPT-subscription rate windows reported by
wham/usage. Wiring a Codex extrapolator is a follow-up.
Drop the maxWidth=90 frame on the plan badge in the hover popover. It
was stretching short labels like "Pro Lite" to fill the full 90pt slot;
fixedSize makes the badge hug the text. Plan names are bounded short
strings, so truncation is a non-issue in practice.
Five interleaving menubar regressions traced back to the cache-wipe and
showLoading additions in 18c3c8f, surfaced by adversarial multi-agent
review against the v0.9.6 baseline.
- forceRefresh no longer calls store.invalidateCache(). Wiping the
whole cache on every wake or manual refresh emptied todayPayload,
flipped showAgentTabs to false, and made cache[key] == nil for all
keys, which forced the full-popover loading overlay over already
rendered data. The day-rollover guard inside refresh() still wipes
the cache when the calendar date changes, so the legitimate part of
18c3c8f is preserved.
- Overlay condition is now !store.hasCachedData. Without this, the
popover briefly rendered $0.00 placeholders before the overlay slid
in on a cold key, and reflashed the overlay on every manual refresh
even when fresh data was on screen.
- refreshStatusButton skips while popover is anchored. Rewriting the
button's attributedTitle changes its intrinsic width, which makes
macOS reflow the status item and detaches the anchored popover to
the screen's top-left default position. popoverDidClose runs the
refresh once so the menubar title catches up immediately on
dismiss.
- showAgentTabs is sticky via hasAnyProvidersInCache. Prevents the
one-frame flicker where the tab strip vanished while the new key's
payload had not yet arrived.
- observeStore tracks store.currency. Without this, switching
currency did not propagate to refreshStatusButton until the next
30s payload tick, leaving the menubar showing the old currency
symbol and rate.
- Day-rollover race in refresh and refreshQuietly: capture cacheDate
at fetch start, drop the write if the calendar date changed during
the await. Prevents an in-flight fetch from yesterday polluting
today's freshly cleared cache.
- Manual refresh button passes showLoading: true again. Safe now that
the overlay is gated on cache state instead of isLoading; the
refresh button icon swaps to the spinner glyph for visible feedback,
while the popover body keeps the existing data and updates when the
fetch lands.
Cache now tracks the calendar date and clears on day rollover so
overnight sleep no longer shows yesterday's numbers. Wake-from-sleep
invalidates the entire cache before fetching. Manual refresh and wake
explicitly request loading feedback so the spinner is visible even
when stale data exists.
The menubar ran --optimize on every 30-second CLI invocation. As
sessions accumulated throughout the day, optimize got heavier until
it exceeded the 45-second timeout. When the fetch failed with no
cached data, the loading overlay had no escape hatch and stayed
forever.
- Never pass includeOptimize from the menubar (background loop,
forceRefresh, tab/period switches, manual refresh button)
- On fetch failure with empty cache, retry without optimize as
fallback so the spinner always clears
- refreshQuietly also skips optimize
PR #221 unified the period logic but missed the TUI hotkey bar,
GNOME indicator popup, and macOS menubar app. All surfaces now
consistently show '6 Months' instead of 'All' or 'all time'.
CLI timeout increased from 20s to 45s to handle cold file-cache latency on
provider-specific queries. Loading overlay now appears when the all-provider
payload confirms a provider has spend but its dedicated data hasn't loaded yet.
Manual refresh (force: true) bypasses the in-flight guard so users can always
re-fetch. Tab strip prefers the provider-specific payload cost when available
so it stays in sync with the hero section.
- Fix refresh loop: proper while loop with 30s sleep and force:true
instead of single-fire Task that never repeated
- Fix loading overlay: counter-based isLoading so concurrent fetches
don't flicker the overlay on/off
- Fix rapid tab switching: cancel previous switchTask, check
Task.isCancelled after CLI returns to discard stale results
- Fix tab strip vs hero desync: fetch provider-specific and all-provider
data in parallel so costs arrive from same data snapshot
- Fix stale menubar icon after wake: forceRefresh now fetches today/all
in parallel alongside the current selection
- Fix accent color: ThemeState is now @Observable so color changes
propagate via observation, removing .id() view hierarchy teardown
- Fix currency flash: defer store.currency and symbol update until a
rate is available so symbol and rate apply atomically
- Fix export: terminationHandler instead of waitUntilExit (no UI freeze),
HHmmss in filename to prevent overwrite on double-export
- Fix CurrencyState: @MainActor isolation with proper Sendable
conformance, nonisolated on pure static functions
- Fix streak count: iterate calendar days instead of sparse history
entries so gaps are counted as streak-breakers
- Fix TrendBar identity: stable date-based id instead of UUID
- Add GPT-5.3 and DeepSeek model display names
forceRefresh() was missing force:true, so the cache TTL guard
silently skipped every LaunchAgent and wake-triggered refresh.
Also adds right-click context menu and version label in footer.
- OpenClaw: JSONL parser with multi-path discovery, tool extraction
(toolCall + tool_use block types), model tracking via model_change
and custom model-snapshot events
- Roo Code + KiloCode: shared Cline-family parser extracts model from
<model> tags in api_conversation_history.json, strips provider
prefixes from model names
- Add cline-auto and openclaw-auto aliases and display names
- Add menubar provider filters and tab colors for all three
- Show cached data instantly instead of blocking on CLI refresh
* 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
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.
Add package.json repository/bugs/homepage fields. Swap hardcoded
AgentSeal/codeburn URLs to getagentseal/codeburn across README,
mac README, macOS menubar star banner, and the menubar installer's
release-API endpoint. 301 redirects keep old URLs working, but
canonical links now point at the current org.
Co-authored-by: AgentSeal <hello@agentseal.org>
Tabs were filtering on `value > 0` (today's spend), which hid the row
whenever only one provider had activity today. The CLI's providers map
already contains only providers detected on the system, so showing the
map as-is matches user intent: a tab for each installed tool,
regardless of today's spend. Tab strip only hides when nothing is
detected.
This also makes the Plan pill reachable again: it gates on
`selectedProvider == .claude`, which required clicking the Claude tab
to select.
The tab strip was visible for everyone regardless of which tools they
actually run, which produced a row of All + one provider for Claude-only
users and a row of All + zeros for users on exotic stacks. Hide the
whole row until a second provider has real spend, matching the behavior
the GNOME extension ships with.
Also expand ProviderFilter to include every provider the CLI supports
(OpenCode and Pi were missing) so their tabs appear when those tools
produce sessions. The CLI already emits pi and opencode in the payload's
providers map; the Mac app just wasn't offering a tab for them.
visibleFilters now filters on value > 0 instead of key presence, because
the CLI includes zero-cost entries for discovered-but-unused providers
and we don't want those rendering as blank tabs.