
claude-usage-mac
Claude Code usage in the macOS menu bar and as a desktop widget
Install with your AI
Paste into Claude Code, Cursor, or any agent — it reads the repo and wires the tool into your project.
Install and set up claude-usage-mac (release-binary project) into my current project. Found on https://claudeers.com/claude-usage-mac Repo: https://github.com/saeedkolivand/claude-usage-mac Homepage/docs: https://claude-usage-mac.iamsaeed.dev/ Detected install method: release-binary → inspect the README Category: devtools. Platforms: cli, api, desktop. Read the repo's README for exact setup and env vars, then install it and wire it into my project. Claudeers Health Verdict: active; community-verified: false. Confirm the source before running anything.
Grab the latest release asset from GitHub.
# download a build from https://github.com/saeedkolivand/claude-usage-mac/releases
git clone https://github.com/saeedkolivand/claude-usage-mac
// compatibility
| Platforms | cli, api, desktop |
|---|---|
| Operating systems | — |
| AI compatibility | claude |
| License | — |
| Pricing | open-source |
| Language | Swift |
claude-usage-mac
Claude Code usage in the macOS menu bar and as a desktop widget — session and weekly limit percentages, token counts, and estimated cost.
A native port of claude-usage-streamdeck-plugin for people who don't own a Stream Deck.

Status: built, not yet run on real hardware. Everything compiles, tests pass, and the UI is reviewed through rendered snapshots — but the project is developed on Windows, so nobody has launched it on a Mac yet. See Unverified before trusting it.
Install
Requires macOS 14 or later.
brew tap saeedkolivand/tap
brew trust --cask saeedkolivand/tap/claude-usage
brew install --cask claude-usage
The trust step is required. Since Homebrew 6.0,
third-party taps must be explicitly trusted before Homebrew will evaluate their
Ruby — a response to a compromised tap being used to ship malware.
--cask <tap>/<cask> trusts only this one cask; brew trust saeedkolivand/tap
would trust everything in the tap, now and in future.
Or download the DMG from Releases, or build from source with
brew install xcodegen && ./build.sh.
The widget shows up in the widget gallery once the app has run at least once.
Upgrading from 0.2 or earlier? Remove any placed widget and drag out a fresh one. Widgets became configurable in 0.3 and macOS cannot migrate a widget across that change — it refuses the timeline request rather than rendering. Widgets configured under 0.3.0 or 0.3.1 need their profile picked again for the same reason.
Builds are ad-hoc signed, not notarized, so macOS quarantines them. The cask clears that for you; if you install the DMG by hand, run:
xattr -dr com.apple.quarantine "/Applications/Claude Usage.app"
The cask is in a personal tap rather than homebrew/cask because that repo
drops casks failing Gatekeeper checks from 2026-09-01,
and --no-quarantine is being removed.
Gallery
![]() | ![]() |
| Small — the 5-hour window, and when it resets. | Medium — both limits, plus today and this week. |
![]() | ![]() |
| Large — adds a 14-day chart. | Project-scoped — one directory's tokens and cost. No gauges: limits are account-level. |

Every widget carries its own configuration, so you can place one per account or one per project, side by side.
What it reads
Two sources, both already on your machine. Nothing is sent anywhere.
| Source | Gives |
|---|---|
api.anthropic.com/api/oauth/usage, using the OAuth token Claude Code already stored | 5-hour and 7-day limit utilization, and when each resets |
~/.claude/projects/**/*.jsonl | today / this week / current session token counts and estimated cost |
The token is read from <config dir>/.credentials.json, falling back to the login
Keychain. We never log in, never write credentials, and never transmit anything
except the one authenticated GET above.
Claude Code names that Keychain item after the config dir it was authenticated
from: Claude Code-credentials for the default ~/.claude, and
Claude Code-credentials-<first 8 hex of sha256(config dir)> for anything
relocated with CLAUDE_CONFIG_DIR. Each service name is tried pinned to account
claude-code-user first and unpinned second, because the account name differs
between Claude Code versions.
An expired credential never hides a live one behind it: a leftover
.credentials.json, or a dead Keychain item sharing a service name with a
current one, is remembered but stepped over, and only reported if every other
candidate misses.
Claude Code only refreshes the config dir it is actually running in, so a profile you have stopped using parks on a dead token — and, until it is refreshed, on whatever numbers it last managed to fetch. Because the usage endpoint is account-scoped rather than config-dir-scoped, such a profile shows the rings of another profile signed in to the same account (same email and organization) if one is present. Failing that it says "Token expired — use Claude Code in this profile to refresh", which is the only thing that actually refreshes it.
A profile with no token at all keeps saying so rather than borrowing: that is also what a denied Keychain prompt looks like, and it is worth fixing rather than hiding.
macOS asks once per item for permission to read it. "Always Allow" stops it asking again.
Profiles
A profile is a Claude Code config folder — one logged-in account. Relocating it
with CLAUDE_CONFIG_DIR is the only way Claude Code supports more than one, so
that's what discovery looks for: ~/.claude, anything beside it whose name
starts with .claude, and folders you add in Settings. A folder counts when it
contains a projects directory.
Pick the menu bar's profile in Settings; each widget picks its own. Every profile keeps its own cache and its own history file, so one account can never show another's numbers.
macOS caveat. The Keychain item's name is hashed from the literal
CLAUDE_CONFIG_DIR string you exported — Claude Code does no path resolution, so
~/.claude-work, $HOME/.claude-work and /Users/you/.claude-work/ are three
different items. We try the plausible spellings; if yours is unusual the profile
reports no-token. Settings shows the name we look for, next to the folder, so
you can check it against:
security dump-keychain | grep -o '"svce"<blob>="Claude Code[^"]*"' | sort -u
Tokens and cost still work either way, since those come from transcripts on disk.
Architecture
Sources/
UsageCore/ data layer, no UI — shared by the app, the widget, and the CLI
SharedViews/ the ring gauge and palette, shared by the app and the widget
MenuBarApp/ MenuBarExtra, the 60s poller, settings
Widget/ AppIntent configuration and the widget families
UsageCLI/ prints the snapshot; the CI smoke test
swift test covers UsageCore with no Xcode involved. The app and widget
bundle is built by Xcode via XcodeGen, since SPM can't express an app that
embeds an extension. Both build systems compile Sources/UsageCore directly —
there's no framework target to embed and sign.
Getting data to the widget is the fiddly part, and worth writing down:
- The widget extension must be sandboxed. macOS never registers an
unsandboxed app extension, so it silently never appears in the widget gallery.
That rules out reading
~/.claudefrom the widget. - App Groups are the textbook answer and don't work here. They're a
provisioning-profile capability, so with ad-hoc signing and no team
containerURL(forSecurityApplicationGroupIdentifier:)returns nil. - The host app isn't sandboxed, so it writes directly into the widget's own
container at
~/Library/Containers/…widget/Data/Library/Application Support/. Inside the sandbox the widget reads exactly that as its Application Support — no entitlement, no App Group, no paid account.
The host only writes there once macOS has created the container; materializing one by hand leaves it without its container metadata, which can stop the extension launching at all. So a freshly added widget shows a placeholder until the next poll, at most 60 seconds.
Developing without a Mac
Swift doesn't build on Windows, so CI is the compiler and the display:
gh run download <run-id> -n snapshots -D snapshots
Tests/SnapshotTests renders every view — widget families, project scope, the
menu popover — across light and dark and every data state, and CI uploads the
PNGs. The gallery images above come from the same pipeline, so they can't drift
from the UI; they are the UI, with a backdrop and a shadow.
Unverified
What the snapshot loop cannot prove, and needs a pass on real hardware:
- The widget loads and appears in the widget gallery. It didn't at first — the
extension shipped unsandboxed and macOS never registered it.
build.shnow reports registration; if it says nothing is registered,killall chronodforces a rescan. - The Keychain consent prompt behaves, and the token actually reads.
- "Open at login" sticks —
SMAppServiceneeds a properly signed app. - The popover's Refresh / Settings / Quit buttons.
ImageRendererdraws interactive controls as unavailable, so they show as prohibition badges in every snapshot. Layout around them is real; the buttons themselves aren't. - The Settings window.
Formwith.formStyle(.grouped)is NSTableView-backed and renders empty detached, so there are deliberately no settings snapshots rather than blank ones posing as coverage.
Try the data layer
Runs on any Mac with a Swift toolchain, no Xcode project needed:
swift run usage-cli # human-readable summary
swift run usage-cli --profiles # list discovered profiles
swift run usage-cli --json # exactly what the widget will render
swift run usage-cli --write # write the snapshot files to disk
Cost accuracy
Costs are computed from token counts, because current Claude Code transcripts no
longer record a costUSD field. Cache writes are billed by TTL — 1.25x input
for the 5-minute cache and 2x for the 1-hour cache — and Claude Code writes
almost exclusively 1-hour entries. Collapsing both into the 5-minute rate (which
the Stream Deck plugin did before v1.7) understates the real figure
substantially.
On Pro/Max plans this is notional equivalent API spend, not money you were
charged. Rates live in Sources/UsageCore/Pricing.swift; edit them when
Anthropic changes pricing.
Scanning is incremental — per-file byte offsets, so a 60s poll re-reads only
what was appended rather than the multiple gigabytes an active
~/.claude/projects accumulates.
History and projects
Daily totals are kept in ~/Library/Application Support/ClaudeUsage/history.json.
They have to be recorded rather than recomputed: the scanner only reads the last
7 days of transcripts, and Claude Code prunes them after about a month. On first
launch a one-off backfill reads the whole archive so the chart starts populated
instead of filling in over a week.
Project names come from each entry's cwd. The directory name under
~/.claude/projects is a slug that flattens /, \ and _ all to -, so it
can't be reversed into a real name.
Releasing
git tag v0.3.0 && git push origin v0.3.0
That builds, stamps the version, makes the DMG, publishes a release, and updates
the cask in saeedkolivand/homebrew-tap.
The tap update needs a TAP_TOKEN repository secret — a fine-grained PAT with
Contents: read/write on homebrew-tap. Without it the release still publishes
and the step is skipped.
License
MIT
// faq
What is claude-usage-mac?
Claude Code usage in the macOS menu bar and as a desktop widget. It is open-source on GitHub.
Is claude-usage-mac free to use?
claude-usage-mac is open-source, so it is free to use.
What category does claude-usage-mac belong to?
claude-usage-mac is listed under devtools in the Claudeers registry of Claude-compatible tools.
// embed badge
[](https://claudeers.com/claude-usage-mac)
// retro hit counter
[](https://claudeers.com/claude-usage-mac)
// reviews
// guestbook
// related in Developer Tools
The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Curs…
Use Garry Tan's exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA
AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, and more). Turn any folder of code, SQL schemas, R scripts, shell scripts, docs,…
Makes your AI agent think like the laziest senior dev in the room. The best code is the code you never wrote.



