Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

426 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AI Battery icon

AI Battery

Get the most out of your Claude subscription.

aibattery.dev

Monitor rate limits, context health, and token usage — always visible in your macOS menu bar.

Swift macOS License CI GitHub stars Downloads


AI Battery open in the menu bar showing 5-hour and 7-day rate limits, throttling status, context health, projects, and insights

Live rate limits, context health, and usage trends without leaving the menu bar.

📦 Install

Homebrew (recommended)
brew tap KyleNesium/tap
brew install --cask aibattery
Quick install — paste in Terminal
curl -sL https://github.com/KyleNesium/AIBattery/releases/latest/download/AIBattery.zip -o /tmp/AIBattery.zip && ditto -x -k /tmp/AIBattery.zip /Applications && xattr -cr /Applications/AIBattery.app && open /Applications/AIBattery.app
💿 DMG download

Download from Releases:

  1. Open AIBattery.dmg and drag AI Battery to Applications
  2. Launch from Applications — macOS will block it on first run
  3. Open System Settings → Privacy & Security → scroll down → click Open Anyway

[!TIP] If macOS says the app is damaged, run xattr -cr /Applications/AIBattery.app then relaunch.

🛠 Build from source
git clone https://github.com/KyleNesium/AIBattery.git && cd AIBattery
./scripts/build-app.sh
open .build/AIBattery.app

Requires macOS 13+ and Claude Code. See aibattery.dev for more info.


🔄 Update

AI Battery checks for updates once per day. When available, the header arrow turns yellow and a banner appears.

Method How
In-app (recommended) Click Install UpdateSparkle downloads, verifies, replaces, and relaunches
Homebrew brew upgrade --cask aibattery
DMG Download from Releases, drag to Applications

Settings and OAuth sessions carry over automatically. Updates are user-initiated only — nothing downloads in the background.


🔐 Authentication

OAuth 2.0 with PKCE — same protocol as Claude Code. Supports up to 3 accounts (separate Claude orgs).

Step Action
1 Launch AI Battery — the auth screen appears on first run
2 Click Sign In → browser opens to Anthropic's sign-in
3 Sign in → copy the authorization code
4 Paste into AI Battery → done

Multiple accounts: Use the header dropdown to switch accounts or add new ones (up to 3). Each account has its own rate limits, tokens, and identity.

🔑 Session details
  • Sessions auto-refresh with a 5-minute buffer to avoid clock-skew issues
  • Temporary server errors retry automatically
  • Refresh token stored in macOS Keychain per account (separate from Claude Code credentials); access token held in memory only
  • Error messages are specific — expired codes, invalid codes, server errors, and network errors each get a clear description
🛡 Why does macOS block the app or ask about Keychain access?

AI Battery isn't notarized — there's no Apple Developer license behind this project, so macOS treats it as unidentified. Two prompts may appear on first launch:

  • Gatekeeper block — macOS prevents the app from opening. Fix: System Settings → Privacy & Security → Open Anyway (see Install)
  • Keychain access — the app stores its OAuth refresh token in macOS Keychain (one item per account), Apple's encrypted credential store. Click Always Allow. The prompt may reappear once after an in-app update because the new binary has a different ad-hoc signature.

The Gatekeeper prompt is one-time. The Keychain prompt appears once on first launch and once after each in-app update.


📐 Metrics

A minimal API fetch reads Claude Code account metadata for 5-hour and 7-day usage, with legacy header fallback when available. Local JSONL session logs provide token counts and context health — never your message content. Click the ✦ icon to open the dashboard.

The tab picker selects which metric drives the ✦ icon color:

Mode Tracks Best for
5-Hour Burst rate limit Knowing when you'll get throttled
📅 7-Day Sustained rate limit Pacing usage across the week
🧠 Context Session context fullness Knowing when to start fresh
(A) Auto Highest urgency metric Always seeing the most critical metric

Selected metric moves to the top. The other two stay visible below.

Note

The 5-hour and 7-day bars come from Anthropic API sliding-window headers. They may differ from Claude Code /usage, which uses different plan-usage semantics and reset timings.

Auto mode: click the (A) button next to the tabs. It glows blue when active and automatically selects whichever metric has the highest percentage — so the menu bar always shows your most critical limit. To switch back to manual, click any tab — auto mode disables and switches in one action.

AI Battery dashboard showing 5-hour and 7-day rate limits with reset countdowns

🧠 Context Health

Shows your 5 most recent sessions with context health. Browse with < 1/5 > chevrons or swipe left/right. Stale sessions (idle > 30 min) show an amber "Idle" badge.

Each session displays: project name · git branch · duration · last active time.

Percentages are relative to the usable window — 80% of the model's raw context window. At 100%, Claude Code auto-compacts and quality drops. Keep at least 20-40% free for best results.

Color Range Meaning
🟢 Green < 60% Plenty of room
🟠 Orange 60–80% Quality may degrade
🔴 Red > 80% Start a fresh session
Context health view showing session context window fullness, model, and turn count
⚠️ Understanding context

Think of context as Claude's short-term memory. Every message, file read, tool call, and response accumulates in a 200K-token window. Nothing is discarded between turns. When it fills up, Claude Code auto-compacts — summarizing the session into a few paragraphs and clearing the rest. That summary is lossy: file contents, specific instructions, and nuanced decisions get compressed. Claude keeps working, but from a recap instead of the real conversation.

Long conversation (15+ turns) — Nothing is discarded between turns. Your messages, Claude's responses, tool calls, and results all accumulate. After ~15 turns the window is full of old history that Claude still reads every turn — slowing responses, reducing quality, and burning through your token budget on stale context.

High input:output ratio (20:1+) — More tokens are going in (file reads, error logs, tool results) than coming out. For example, reading 5 large files dumps thousands of tokens into context that Claude may only reference once. That data stays in the window for the rest of the session, consuming tokens on every subsequent turn and leaving less room for useful work.

Zero-output session — Session has multiple turns but no output tokens. May indicate an error loop or stalled conversation.

Rapid token consumption — Very short session with high token usage. Large files or long pastes may be filling the context window quickly.

Tip

When you hit orange or red:

  1. Run /compact to save a summary to project memory
  2. Keep key decisions in CLAUDE.md — loaded automatically every session
  3. Start a new terminal in the same directory and pick up where you left off

⚙️ Settings

Click ⚙️ in the header to configure:

Setting What it does
Add Account Connect another Claude account (up to 3)
✏️ Account names Custom label per account (shown in picker + menu bar)
🔁 Auto mode Always show the highest metric (pulsing blue button on metric toggle)
🔄 Refresh Poll interval: 10–60s · ~3 tokens per refresh
Idle Hide sessions idle longer than cutoff from context health: 30m–8h or Never
🎨 Colorblind Blue/cyan/amber/purple palette
🪟 All accounts in menu bar Show every connected account's percent in the menu bar (e.g. 42% | 23%). Star color and countdown reflect the worst account.
💲 Cost Always visible — API-equivalent cost in Insights and Projects
🔔 Alerts Notify on status page outages (all components)
Rate Limit Notify when usage crosses threshold (50–95%)
🚀 Launch at Login Start automatically when you log in
Settings panel with refresh interval, idle session cutoff, colorblind mode, alerts, and launch at login

Tip

Click any stat value (percentages, token counts, costs) to copy it to the clipboard.


📉 Activity & Insights

Interactive charts across three time windows:

Mode Window Shows
24H Trailing 24 hours Hourly activity, vs-yesterday trend, peak hour
7D Rolling 7 days Daily activity, weekly trend, busiest day
12M Rolling 12 months Monthly activity, month-over-month trend, busiest month

Below the chart: API-equivalent cost per model, throttle count, and cumulative stats (All Time messages/sessions, Longest session, Period date range).

24-hour activity chart with trend arrows, throttle count, and cost breakdown by model

💰 API Cost Equivalent

Dollar amounts show what your usage would have cost on Anthropic's pay-per-token API — not your actual bill. Pro, Max, and Teams subscribers pay a flat monthly fee. When the API-equivalent exceeds your monthly fee, your subscription is saving you money. The bigger the gap, the better the deal. Pricing uses Anthropic's published per-million-token rates.

Per-project token usage breakdown with costs
How token tracking works

AI Battery reads Claude Code's session logs (~/.claude/projects/) and stats cache (~/.claude/stats-cache.json) — it never writes to Claude Code's files or reads message content, only token counts.

To prevent totals from dropping when Claude Code rebuilds its cache, AI Battery maintains a persistent ledger (~/Library/Application Support/AIBattery/token-ledger.json) that keeps the high-water mark for each model. Token totals never decrease, even across cache rebuilds.

How project tracking works

The Projects section groups token usage by the directory you ran Claude Code in. AI Battery scans all .jsonl session logs under ~/.claude/projects/, deduplicates by message ID, groups by working directory, and computes API-equivalent cost per project.

Project data appears after you've run at least one Claude Code session.


🔧 Troubleshooting & FAQ

App appears in the menu bar then disappears

On first launch: macOS Gatekeeper may silently kill the app because it's not notarized.

xattr -cr /Applications/AIBattery.app

Then relaunch. This removes the quarantine flag that macOS adds to downloaded apps.

After working for a while: If the icon vanishes after the app has been running, update to the latest version — v2.0.3+ fixed concurrency issues that could cause intermittent crashes during background data refresh and sleep/wake cycles.

If it still happens:

  1. Open Console.app → filter for "AIBattery" → look for crash logs
  2. Check your macOS version — macOS 13.0–13.2 had MenuBarExtra bugs fixed in 13.3+
  3. Open an issue with the crash log
macOS says "AI Battery is damaged and can't be opened"

This is the quarantine flag. Run:

xattr -cr /Applications/AIBattery.app

Then relaunch.

Keychain access dialog keeps appearing

Click Always Allow when prompted. AI Battery stores its OAuth refresh token in macOS Keychain (one item per account). After a Sparkle in-app update, a single Keychain prompt may appear because the new binary has a different ad-hoc signature. This is a one-time prompt per update.

Only rate limits show — tokens, models, and activity are all empty?

Token usage, context health, and activity stats come from Claude Code's local session logs (~/.claude/). These populate after you've used Claude Code for a bit. To kickstart it:

  1. Run a few Claude Code sessions from the terminal
  2. Run /stats inside Claude Code — this generates the stats cache
  3. AI Battery refreshes automatically every polling cycle

Claude Code usage windows (5-hour / 7-day) are fetched from Anthropic account metadata. If Anthropic changes that response shape, AI Battery falls back to cached values and may show a warning until fresh usage data is available again.

Green ✦ at 0%?

Credits just reset, or no usage yet — this is normal.

What's "binding"?

Whichever rate limit window is currently the active constraint. The binding window determines the percentage shown in the menu bar.

What's ⚠️ "throttled"?

Anthropic is actively limiting your requests. Wait for the reset timer.

Why can AI Battery differ from Claude Code `/usage`?

AI Battery primarily shows Claude Code 5-hour and 7-day usage from Claude Code account metadata. It may fall back to legacy Anthropic headers or cached values when those windows are temporarily unavailable, so percentages and reset times can still differ from Claude Code /usage during API transitions or stale-data fallback.


🔒 Privacy & Security

📂 Local only Reads JSONL for token counts — never your message content
🔑 Keychain OAuth refresh token stored in macOS Keychain (kSecAttrAccessibleWhenUnlockedThisDeviceOnly) — access token is memory-only, never written to disk
🌐 Network api.anthropic.com (rate limits) · console.anthropic.com (OAuth) · status.claude.com (status) · api.github.com (update check, once/24h) · kylenesium.github.io (Sparkle appcast)
🚫 No tracking No analytics. No telemetry. No data collection. Period.
🔍 Open source Every line is auditable — review the code
Release checks Each release runs verify-release.sh — validates codesign, Sparkle signatures, version consistency, and bundle integrity before publish

🏗 Architecture

AIBattery/
  Models/       — Data structs (UsageSnapshot, RateLimitUsage, TokenHealthStatus, ...)
  Services/     — OAuthManager, RateLimitFetcher, SessionLogReader, TokenHealthMonitor, ...
  ViewModels/   — Single UsageViewModel (@MainActor, ObservableObject)
  Views/        — SwiftUI views (popover sections, menu bar label, auth screen)
  Utilities/    — TokenFormatter, ModelNameMapper, ThemeColors, AppLogger

One dependencySparkle 2 for auto-update. Everything else is Apple frameworks (SwiftUI, Charts, Security, Foundation, AppKit).

📋 Detailed specs
File Covers
ARCHITECTURE.md Data flow, project tree, build config, network & file access
DATA_LAYER.md Every model, service, and algorithm
UI_SPEC.md View hierarchy, layout rules, section specs
CONSTANTS.md Every hardcoded value — thresholds, URLs, pricing, sizes

♿ Accessibility

  • VoiceOver — all interactive elements include accessibility labels and hints; collapsible sections announce state; copy actions announce confirmation
  • Keyboard navigation — fully navigable without a mouse
  • Colorblind mode — Settings → Display → Colorblind switches to a blue/cyan/amber/purple palette
  • First-launch tutorial — 3-step walkthrough on first use

🗑 Uninstall

Homebrew:

brew uninstall --cask aibattery
🧹 Manual uninstall
  1. Right-click AI Battery in the menu bar → Quit
  2. Open Applications in Finder → drag AI Battery to the Trash

To also remove stored settings:

security delete-generic-password -s "AIBattery" 2>/dev/null   # OAuth tokens (all accounts)
defaults delete com.KyleNesium.AIBattery 2>/dev/null           # Preferences

AI Battery doesn't write any other files. Your Claude Code data (~/.claude/) is untouched.


🤝 Contributing & Support

Contributions welcome — read the contributing guide first. See CHANGELOG.md for version history.

AI Battery is free and open source — always will be. If it helps you get more out of Claude, consider sponsoring the project.

Sponsor


🧪 Test Coverage

1114 tests across 72 test files.

Area Tests What's covered
Models 282 Token summaries, rate limit parsing (unified + standard + client data edge cases, timestamp rejection, 100%-without-status is not throttled), health status, metric modes, API profiles, usage snapshots (incl. escalation ladder, hysteresis, stored-property tripwire pinning the hand-written ==), model pricing, Claude system status indicators, standard rate limits, local usage estimate calibration policy (uncalibrated-only seeding, 20–80% calibration band, per-account isolation + legacy global-key migration + non-active-account 429 guard, billingType→tier mapping for mixed-tier accounts), expired-window + unbounded-throttle clearing on cache restore, rollover-artifact suppression (near-full reading on a just-started window), percent(for:) shows the real API utilization even when not fresh (never a local-token estimate substitution — the local estimate is used only when there is no API data at all; a fresh-but-wrong spike is corrected upstream), per-window rateLimitPercentConfirmed(for:) gating only the alarm so a throttle on one window can't confirm a stale near-full reading on the other
Services 369 Token ledger (incl. high-water merge, pending→resolved account migration, orphan pruning with empty-set guard), version checker, Sparkle updates, notifications, health monitor (incl. zero-window safety, idle-cutoff binary-search boundaries: strict-> at-cutoff exclusion, empty/all-before/all-after), status checker (incl. concurrency: detached-task callability + MainActor-non-blocking), session log reader (incl. NSLock/pendingInvalidation concurrency, incremental scanning, entry eviction), account store, stats cache, usage aggregator (incl. side-effects, integration tests, 7d rolling-window boundary), rate limit fetcher (incl. quota-vs-upstream 429 disambiguation, consecutive auth-failure counter, actor-isolation guarantees on nonisolated helpers, expired-window clearing on cachedOrEmpty, contract tests pinning both OAuth interpreters' status-code / payload / 429-throttle behavior incl. the typed authFailed/unavailable outcome split, per-account consecutive auth-failure bookkeeping shared by both fetch paths, launch-restore recovery: corrupt-blob self-heal + future-timestamp clock-skew clamp, pending→resolved rate-limit cache migration + launch orphan pruning so a dead pending account's stale blob can't surface a false limit, launch-restore rollover-artifact clearing so a just-rolled near-full reading isn't seeded as the stale fallback, spike-hold write-back via overrideCachedRateLimits so a held glitch value replaces the raw persisted reading and can't resurface on a later instant-paint), OAuth (incl. Sendable conformance on TokenResult/AuthError, transient-vs-auth-error classifier, concurrent-refresh deadlock-freedom, postToken retry contract via injectable transport: exactly-3-attempts on 5xx/timeout, no-retry on 401/invalid_grant, recovery on transient failure), Keychain + token storage (real-Keychain roundtrips with UUID accounts: set/get/delete, update-existing, per-account isolation, access-token-never-persisted), adaptive polling
Views 167 Activity chart data transforms (5H/7D/12M token-based), chart display state (loading vs empty vs data — no cold-start "No activity" flash), trend computation (token-based, incl. meaningful-previous threshold + display cap to prevent noise spikes like "+47999% vs yesterday"), session info formatting (incl. injectable-now for deterministic Today/Yesterday across midnight rollover), GaugeBar clamping, deferred rendering, status bar toggle/countdown, menu-bar render-skip key (percent bucketing, per-field re-render triggers), insights view formatting, metric toggle ordering, multi-account menu bar text rendering (formatting, ordering, missing slots, worst-percent, genuine-throttle-only exhaustion — 100% without status is at-capacity, not throttled — binding-window code prefix on throttled countdown, metric mode fallback), multi-account display resolver (end-to-end: gate + builder + countdown composition + single-account fallback, all v2.2.0-regression scenarios pinned), stale-throttle suppression in both the menu bar AND the popover bars (UsageBar.AlarmState: unconfirmed/cached data shows the last-known percent with no "Throttled"/"Limit reached" alarm and no 100% clamp; confirmed data still alarms)
ViewModels 83 Refresh interval clamping, error messages (incl. standard limits fallback), adaptive polling, throttle tracking, idle threshold constants, TTL-based stale rate limit expiry (incl. expired-window clearing on stale fallback), effective value generic guard, auth-error reconnect prompt, cross-account refresh-race guard (results discarded when the active account changes mid-fetch), stale-data notification gate (spike-confirmed rate limits only — a held/unconfirmed reading never fires alerts), rate-limit freshness gate (rateLimitsAreFresh — a successful but header-less fetch reusing held stale limits is NOT confirmed; alarmConfirmed arms the "Limit reached"/throttle alarm only on fresh data OR an authoritative throttle, fixing the false limit-reached on wake without flickering a genuine throttle), confirm-before-alarming spike filter (spikeConfirmedRateLimits — an isolated fresh near-full spike, e.g. a server eventual-consistency ~100% right after wake, is held at the previous displayed value until a second consecutive fresh poll confirms it; genuine throttles bypass but ARE near-full-tracked so a throttle→clear transition isn't held; a held window never inherits a previous "throttled" status; sustained limits and per-window independence preserved; the near-full memory is keyed by each window's reset instant so a memory from a previous window instance can't confirm a post-rollover glitch, reset-less readings match via sentinel; an overall-only throttle on the binding window bypasses the hold, and a hold never masks a genuine throttle on the other window; cold-start with no previous value shows the fresh reading — never a fabricated 0%), multi-account fan-out orchestration (end-to-end through the concurrent TaskGroup: toggle-off clearing, seed dedup so the active account isn't re-fetched, missing-token skip, nil-rate-limits exclusion, seed-only path) + shared eligible-account filter (non-pending AND authenticated, store order)
Utilities 213 Token/duration formatting (incl. billion-scale), model name mapping, theme colors (incl. isDarkMenuBar gold override), secure networking, menu bar icon rendering + quantized cache, throttle tracker (genuine-throttle-only event recording + on/off transition), typography, spacing, idle suspension policy, retry policy (exponential backoff + jitter + Retry-After parsing, parity tests pinning historical OAuth/StatusCheck/FileWatch formulas)

📄 License

MIT

About

A battery style usage meter for Claude Code. Rate limits, context health, and token usage in your macOS menu bar.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

22 stars

Watchers

3 watching

Forks

Releases

Packages

Contributors

Languages