A macOS menu bar app that shows live Claude Code and Codex CLI session usage
with Claude API-first sync plus local transcript fallback. No browser cookies, no manual API keys.
Claude 51% (2h30m) 6% (5d) | Codex 31% (1h10m) 45% (6d) ▾
━━ Claude Code ━━
5h: ▓▓▓▓▓░░░░░ 51.0% (in 2h30m)
weekly: ▓░░░░░░░░░ 6.0% (in 5d)
weekly · fable: ░░░░░░░░░░ 0.0% (in 5d)
plan: Max (5x) · local est.: 30.6M / 60.0M (29.4M left) · raw: 3.54M tok
source: API · last API update 14:48
━━ Codex CLI ━━
5h: ▓▓▓░░░░░░░ 31.0% (in 1h10m)
weekly: ▓▓▓▓░░░░░░ 45.0% (in 6d)
plan: plus · tokens: 3,036,637
source: local · last event 12s ago
────────────────────────────────
Refresh now
Calibrate Claude Code…
Re-authenticate ▸
────────────────────────────────
Claude Usage
Codex Analytics
────────────────────────────────
Settings ▸
Launch at login ✓
Open config
Copy diagnostic info
About v0.2.0
Quit
- Live 5h and weekly usage for Claude Code + Codex CLI in one place.
- Configurable menu bar title: pick your own label, show/hide 5h / weekly
percentages, optionally append remaining time (
"Claude 51% (2h30m)"). - Smart alerts — native macOS notifications when 5-hour or weekly usage
crosses per-provider, per-window thresholds (default
90%/95%, each fires once and re-arms when usage drops below the threshold; set to0to disable). - Source transparency — each provider's menu shows whether the current data
came from the API, local transcripts, or a mixed snapshot, and when it was
last refreshed (
source: API · last API update 14:48,source: mixed (API + local),source: local · last event 12s ago). - Settings in the menu — menu bar title, alert thresholds and usage
source are toggles and submenus; every change is written straight back to
config.toml, so a hand-edited file is never overwritten by stale state. - Re-authenticate without leaving the app — when a token expires the menu
runs the provider's own login (
claude auth login/codex login) in Terminal. The app never handles the OAuth flow or your credentials itself. - Menu bar icon and colour — an SF Symbol that follows light/dark mode, and the percentages turn orange as you approach an alert threshold, red once you cross it.
- Quick links to the Claude.ai and ChatGPT dashboards.
- Launch at login toggle from the menu (no need to touch
launchctl). - Copy diagnostic info button: version, config, provider state and recent logs onto your clipboard in one click — ideal for filing GitHub issues.
- Calibrate Claude Code against the real dashboard percent to correct plan-budget estimates.
- Claude API first:
source = "api"is the default and uses Claude Code's existing OAuth auth when available, with automatic local fallback if the usage endpoint is unavailable. - Codex CLI works offline too — the same percentages are inline in the rollout files, used automatically whenever the API call fails.
A .app bundle needs no Python, no uv and no checkout. Build it from a
clone:
packaging/build_app.sh --zipdist/cli-usage-bar.app is the app; the zip is what a GitHub release ships,
and packaging/homebrew/cli-usage-bar.rb is the cask that installs it.
Signing and notarising need an Apple Developer ID — see
packaging/README.md before publishing a build, since
an unsigned bundle will not open on someone else's Mac.
curl -fsSL https://raw.githubusercontent.com/svenedik/cli-usage-bar/main/install.sh | bashThis does four things:
- clones the repo into
~/.local/share/cli-usage-bar - installs Python deps with
uv - registers a
LaunchAgentso the app starts at login - installs a control command
usage-barat~/.local/bin/usage-bar
Make sure ~/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH" # add to ~/.zshrc or ~/.bashrcEverything is a single short command:
usage-bar start # start the app
usage-bar stop # stop the app
usage-bar restart # pick up code / config changes (starts it if stopped)
usage-bar status # running (pid 23979) | stopped
usage-bar logs # tail -f /tmp/cli-usage-bar.log
usage-bar config # open ~/.config/cli-usage-bar/config.toml
usage-bar update # git pull + deps + restart
usage-bar uninstall # remove LaunchAgent + install dir
usage-bar helpAfter editing config.toml, just click Refresh now in the menu (picks up
title/label changes) or run usage-bar restart (also picks up enabled,
plan, budget changes).
cli-usage-bar always reads local transcript files for Claude Code and Codex CLI. For Claude, the default mode is API-first:
- Claude Code (default
source = "api") — first tries Anthropic's internal OAuth usage endpoint for dashboard-exact percentages, including any third, model-scoped weekly pool your plan has (shown as its own row, e.g.weekly · fable). If that request fails because of auth, network, or rate limiting, the app automatically falls back to local transcript parsing so the menu bar keeps showing usage instead of an error. While API mode is healthy, the app also auto-calibrates the local fallback — budget and window reset times — from the exact API reading, and persists it to~/.config/cli-usage-bar/state.jsonso the calibration survives a restart. - Claude Code (
source = "local") —~/.claude/projects/<slug>/<session-id>.jsonl. Assistant messages are deduplicated by(message.id, requestId)— resumed and forked sessions replay earlier messages into new transcripts, which otherwise inflates the total several-fold — and then weighted into usage units before being divided by a plan budget. A usage unit is one Sonnet input token equivalent: plan limits are consumption-weighted, so an Opus token, a Haiku token and a cache read cannot count the same. Windows are anchored to the lastresets_atthe API reported (persisted in~/.config/cli-usage-bar/state.json); without one, the 5-hour window falls back to ccusage-style block detection and the weekly figure to a rolling 7 days — the real weekly window is a fixed week that resets on a set weekday, so that fallback over-reads until the first successful API call supplies the anchor. Local numbers are an estimate: usage from claude.ai, the mobile apps, or another machine draws on the same pool but never lands in these transcripts (see Calibrate below). Parsing is incremental — each file is read from where the previous refresh stopped — so a refresh costs milliseconds even with months of history on disk. - Codex CLI (default
source = "api") — askschatgpt.com/backend-api/codex/usagefor the current numbers, using the ChatGPT logincodexalready stored in~/.codex/auth.json. This matters because the local copy below only refreshes while a session runs: between sessions it keeps showing the last percentage the CLI saw, which can be a day stale. - Codex CLI (
source = "local") —~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl. Everytoken_countevent contains the same percentages inline underrate_limits— we just render them, fully offline. Which windows exist depends on the plan (a Plus account today reports a single weekly pool), so rows are labelled from each limit's own window length rather than assumed to be 5-hour and weekly. While a Codex CLI session is actively writing events (last event under 3 minutes old) the app drops its refresh interval to 15 seconds so new token counts surface quickly; it returns to the defaultrefresh_interval_secwhen the session goes idle.
Benefits:
- API-first mode gives exact dashboard numbers when Anthropic allows it, and local fallback when it doesn't.
- Local mode stays 100% offline. No cookie extraction, no API key management.
- Works whether you use Claude Code / Codex CLI via Pro, Max, Plus, etc.
- Trivial data schema — trivial to extend with a new provider.
Do I need an API key or token?
No manual token pasting is needed. In the default source = "api" mode the
app reuses the OAuth token that Claude Code already configured (from
CLAUDE_CODE_OAUTH_TOKEN or Claude Code's own macOS keychain entry). If you
switch to source = "local", the app stays fully offline.
Does it send my data anywhere? The app always reads local files:
~/.claude/projects/**/*.jsonl(Claude Code transcripts)~/.codex/sessions/**/rollout-*.jsonl(Codex CLI rollouts)~/.config/cli-usage-bar/config.toml(your settings)
and writes to ~/.config/cli-usage-bar/config.toml (only when you click
Calibrate) and /tmp/cli-usage-bar.log (app logs).
In the default source = "api" mode it additionally makes an HTTPS GET to
api.anthropic.com/api/oauth/usage every api_cache_seconds (default 600s),
authenticated with your existing Claude Code OAuth token. If the request
fails, the app falls back to local transcript data automatically. In
source = "local" mode there are no network requests.
Do I need to sign into Claude or ChatGPT in this app?
No extra sign-in happens inside this app. source = "api" reuses the Claude
Code auth you already set up on that Mac, and source = "local" works with no
network login at all.
What if I only use one of the two?
Set the other provider's enabled = false in the config and
usage-bar restart.
First run creates ~/.config/cli-usage-bar/config.toml. All fields are
optional; the generated defaults are shown below.
[general]
refresh_interval_sec = 60
show_title_percent = true # master switch for menu bar percentages
[claude_code]
enabled = true
plan = "max5" # pro | max5 | max20 | custom
custom_budget_units = 0 # only used when plan = "custom" (weighted units, not raw tokens)
weekly_budget_multiplier = 24.0 # weekly pool size relative to the 5-hour budget
title_label = "C" # prefix shown in the menu bar title
title_show_primary = true # include the 5-hour percent in the title
title_show_secondary = false # include the weekly percent in the title
title_show_reset = false # append remaining time next to each percent
alert_primary_percent = 90 # notify when 5h % crosses this (0 = disabled)
alert_secondary_percent = 95 # notify when weekly % crosses this (0 = disabled)
plan_label = "" # optional label, e.g. "Max (5x)"; empty = auto
source = "api" # default: exact OAuth usage with automatic local fallback
api_cache_seconds = 600 # cache for "api" mode (seconds)
[codex_cli]
enabled = true
title_label = "X" # prefix shown in the menu bar title
title_show_primary = true # include the 5-hour percent in the title
title_show_secondary = false # include the weekly percent in the title
title_show_reset = false # append remaining time next to each percent
alert_primary_percent = 90 # notify when 5h % crosses this (0 = disabled)
alert_secondary_percent = 95 # notify when weekly % crosses this (0 = disabled)Claude uses API-first mode by default:
[claude_code]
source = "api"
api_cache_seconds = 600This reuses your existing Claude Code login, tries the dashboard-exact usage
endpoint first, and falls back to local transcript parsing if the endpoint is
unavailable. Transient errors (network, 5xx) are retried every 15 seconds until
the API recovers. If Anthropic responds with 429 Too Many Requests, the app
backs off for 5 minutes before trying again so it doesn't extend the cooldown
window. Once the API succeeds it returns to the slower cached polling cadence.
Local transcript parsing keeps running in the background the whole time, and
its view is merged into the menu whenever the API returns a partial payload.
If you want fully offline behavior instead:
[claude_code]
source = "local"Each provider has two configurable thresholds — one for the 5-hour window
(alert_primary_percent, default 90) and one for the weekly window
(alert_secondary_percent, default 95). When usage crosses either threshold
you get a single native macOS notification for that window; it won't re-fire
until usage drops below the threshold again.
Example notification:
Claude Code · 5h
Usage at 91% (threshold 90%).
Set a threshold to 0 to disable that window entirely, e.g. to silence weekly
alerts while keeping the 5-hour alert:
[codex_cli]
alert_primary_percent = 80 # alert earlier for the 5h block
alert_secondary_percent = 0 # never alert on weeklySet the other provider's enabled = false, then usage-bar restart. Title
becomes just Claude 51% or Codex 31% with no separator.
source = "api" is the default. It uses dashboard-exact values when the
endpoint is available, with automatic local fallback when it is not.
If you want to tune its polling interval, set:
[claude_code]
source = "api"
api_cache_seconds = 600and usage-bar restart. The app will:
- Reuse your existing Claude Code OAuth token from
CLAUDE_CODE_OAUTH_TOKENor Claude Code's current macOS keychain entry. - Call Anthropic's usage endpoint every
api_cache_seconds(default 600s, cached between calls to stay well under rate limits). - If the API path fails with a transient error, retry every 15 seconds.
- If Anthropic returns
429, back off for 5 minutes before retrying so the cooldown window isn't extended. - Keep local transcript parsing running in the background the whole time.
- Use successful API reads to auto-calibrate the local fallback budget and window anchors, persisted across restarts.
- Fall back to local transcript-based numbers if the endpoint fails.
- Even during fallback, merge any partial API fields (e.g. weekly) over the
local view, and keep the source line up to date (
source: mixed (API + local)for partial merges,source: local (API offline)when the API path is down). Clicking Refresh now invalidates the API cache and forces a fresh fetch immediately.
Requirements:
claude auth statusshows you're signed in. If not, runclaude auth login --claudeaifirst.- Network access to
api.anthropic.com.
Notes:
- No manual token pasting. Calibration is disabled in this mode (percentages come from the source of truth whenever the API path succeeds).
- The endpoint is the same internal one the dashboard uses and isn't part
of Anthropic's public API contract, so in theory it could change. If it
ever does, flip back to
source = "local".
Anthropic doesn't publish 5-hour block budgets, so source = "local"
defaults are estimates. If your menu bar 5h % disagrees with the
Claude.ai dashboard and you'd rather stay offline:
- Click Calibrate Claude Code… in the menu
- Enter the percent shown in Claude.ai → Settings → Usage
- The app solves for your real budget and writes
plan = "custom"+custom_budget_unitsback to the config
(If you're already in source = "api" mode the menu shows "Calibration not
needed" — the API returns exact percentages.)
Codex CLI doesn't need calibration — percentages are inline in the rollout.
- macOS (Apple Silicon or Intel)
- Python 3.12+ (installed automatically by
uv) uv— https://github.com/astral-sh/uv
git clone https://github.com/svenedik/cli-usage-bar.git
cd cli-usage-bar
uv sync --no-dev
# LaunchAgent so it starts at login
sed -e "s|{{INSTALL_DIR}}|$PWD|g" -e "s|{{HOME}}|$HOME|g" \
packaging/com.svenedik.cli-usage-bar.plist \
> ~/Library/LaunchAgents/com.svenedik.cli-usage-bar.plist
launchctl load ~/Library/LaunchAgents/com.svenedik.cli-usage-bar.plist
# Control command on PATH
mkdir -p ~/.local/bin
ln -sf "$PWD/bin/usage-bar" ~/.local/bin/usage-bar- Icon doesn't appear —
usage-bar statusshould say "running". If it says "stopped", runusage-bar start. Logs:usage-bar logs. Claude Code: no recent usage messages— you haven't used Claude Code in the last 24 hours, or~/.claude/projectsisn't populated.Codex CLI: no rollout files found— you haven't runcodexyet.- Percentage looks wrong for Claude Code — use Calibrate Claude Code…
or edit
plan/custom_budget_unitsin the config andusage-bar restart. For byte-exact values switch tosource = "api"(see above). api: no usage data (missing token or network)— you're insource = "api"mode but no OAuth token was available from Claude Code's configured auth orapi.anthropic.comis unreachable. You can always fall back tosource = "local".api: Claude login required (runclaude auth login --claudeai)— Claude Code is not currently signed in on this Mac. Complete the login once, thenusage-bar restart.api: OAuth token expired (re-login to Claude Code)— the OAuth token in Claude Code's keychain entry is past its expiry. The app stops calling the endpoint (an expired token only earns 401s, and enough of those earn a 429) and re-checks the keychain periodically, so a fresh login is picked up without a restart. Runclaude auth login --claudeai. Aclaude setup-tokentoken does not work here: the usage endpoint needs the session scope that only the login flow grants, and a long-lived token gets a 403.api: OAuth token unreadable (re-login to Claude Code)— Claude Code reports a login exists, but the menu app could not read the OAuth token from the current env/keychain setup. Re-runclaude auth login --claudeai, then restart the app.api: auth failed (runclaude auth login --claudeai)— the upstream endpoint rejected the token. Sign into Claude Code again and the menu will recover on the next refresh.api: rate limited (try again shortly)— upstream429. The app keeps the local fallback visible and waits 5 minutes before retrying the API path so it doesn't extend the cooldown. Click Refresh now to force an early retry.
uv sync
uv run pytest
uv run ruff check
uv run python -m cli_usage_barThe app is a single rumps.App. Providers implement the Provider ABC and
produce a UsageSnapshot; adding a third provider is a matter of one file and
one test fixture.
MIT — see LICENSE.
- Inspiration: BOUSHABAMohammed/claude-bar (Claude.ai web usage) and steipete/CodexBar.
- 5-hour block algorithm inspired by ryoppippi/ccusage.
