3A macOS menu bar application that displays your [Hyper (Charm)](https://hyper.charm.land) credit balance — and, if you use the Claude Code CLI, your Claude subscription limits alongside it.
7- ⚡ **Menu bar display** — shows `⚡{balance}` (e.g. `⚡42`) right in your menu bar
8- 🤖 **Claude Code limits** — your Pro/Max 5-hour and 7-day usage windows in the popover, with the percentage used and a countdown to the next reset. No setup: if you have signed in with the Claude Code CLI, it just appears. If you haven't, the section doesn't
9- 🔄 **Auto-refresh** — fetches your balance on a configurable interval (1m / 5m / 15m / 30m), and again on wake from sleep
10- 📊 **Balance sparkline** — a subtle mini-chart in the popover shows your balance trend over recent fetches
11- 📋 **Click-to-copy** — click the balance number in the popover to copy it to the clipboard
12- 🖱️ **Right-click menu** — right-click the menu bar icon for quick access to Refresh, Open hyper.charm.land, and Quit
13- 🔔 **Low-balance notifications** — alerts you when your balance drops below 10 credits, including on the very first fetch if you are already below the threshold
14- 🔒 **Keychain storage** — your API key is stored securely in macOS Keychain
15- 🎨 **Color-coded balance** — green (≥100), yellow (10–99), red (<10)
16- 🔐 **Secure input** — API key field is masked
17- 🚀 **Launch at login** — optional, using `SMAppService`
18- 🔑 **Get key link** — quick link to [hyper.charm.land](https://hyper.charm.land) right next to the API key field
19- 🫥 **Agent app** — no dock icon, no main window; lives entirely in the menu bar
23### Homebrew (recommended)
27brew install --cask usage-menubar
33brew install --cask ilyakooo0/tap/usage-menubar
36### Download the pre-built release
381. Go to the [Releases page](https://github.com/ilyakooo0/usage-menubar/releases)
392. Download the latest `UsageMenubar-*.zip`
403. Unzip and move `UsageMenubar.app` to `/Applications`
414. Launch the app — it will appear in your menu bar as ⚡
43> **Note:** Releases are **ad-hoc signed** — the CI build applies `codesign -s -` to the app, which gives it a stable code identity but is *not* an Apple Developer ID signature. macOS will still warn you on first launch: right-click the app → **Open**, or allow it in **System Settings → Privacy & Security**. Removing that prompt entirely requires a paid Developer ID certificate and notarization.
471. Click the ⚡ icon in your menu bar
482. Enter your Hyper API key (starts with `sk-...`) in the **API Key** field
504. Your balance will appear immediately
52You can get your API key from [hyper.charm.land](https://hyper.charm.land).
54### Claude Code limits (optional, no setup)
56If you use the [Claude Code CLI](https://claude.com/claude-code), the popover also shows how much of your Pro/Max plan you have used: the limit currently in effect as a headline percentage, the 5-hour and 7-day windows underneath, and how long until the next reset.
58There is nothing to configure. The app reads the OAuth credentials Claude Code already stores when you run `claude` and sign in — first from its login-Keychain item, then from `~/.claude/.credentials.json` if the Keychain has nothing. If neither exists, the Claude section simply doesn't appear; it is not treated as an error, and the Hyper balance is unaffected.
60Two things worth knowing:
62- **macOS will ask once.** Those credentials belong to Claude Code, not to this app, so the first read prompts you to allow access to the `Claude Code-credentials` Keychain item. Choose **Always Allow** and you won't be asked again.
63- **The credentials are read-only, with one exception.** When the access token has expired, the app refreshes it against Anthropic's token endpoint and writes the result back to wherever it read it from — exactly as Claude Code itself would. Nothing else about them is ever modified, and the fields the app doesn't model are preserved verbatim, so your CLI session keeps working.
67Everything is configured from the popover — click the ⚡ icon in the menu bar.
69| Setting | Where | Notes |
70| ------------------- | ------------------------------ | ------------------------------------------------------------------------------------ |
71| **API key** | **API Key** field → **Save** | Stored in the macOS Keychain. Clearing the field and saving deletes the stored key. |
72| **Launch at login** | **Launch at Login** toggle | Registers the app with `SMAppService`. |
73| **Notifications** | System permission prompt | macOS asks on first launch (only if not already decided). Required for low-balance alerts. |
74| **Refresh now** | **Refresh** button | Forces an immediate fetch, on top of the automatic refresh. |
75| **Refresh interval**| **Refresh every** picker | Choose 1m, 5m, 15m, or 30m. Defaults to 5m. Stored in `UserDefaults` and persisted across launches. |
77The app also refreshes automatically when your Mac wakes from sleep.
85- [xcodegen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`)
91git clone https://github.com/ilyakooo0/usage-menubar.git
94# Generate the Xcode project
98open UsageMenubar.xcodeproj
100# Or build from the command line
102 -scheme UsageMenubar \
103 -configuration Release \
104 -archivePath build/UsageMenubar.xcarchive \
106 CODE_SIGNING_ALLOWED=NO
113 -scheme UsageMenubar \
114 -configuration Debug \
115 -destination "platform=macOS"
120Versions follow the format `YYYY.MM.DD.HHHH` where `HHHH` is the UTC hour and minute (24-hour).
122| Commit time (UTC) | Version |
123| ------------------------ | ----------------- |
124| 2026-07-09 14:23 | `2026.07.09.1423` |
125| 2026-01-01 00:00 | `2026.01.01.0000` |
126| 2026-12-31 23:59 | `2026.12.31.2359` |
128The version is derived automatically from the commit timestamp in the GitHub Actions release workflow.
134├── project.yml # xcodegen project definition
136│ ├── UsageMenubarApp.swift # @main App, NSStatusBar, timer, sleep/wake
137│ ├── ViewModel.swift # Observable state: balance, Claude usage, refresh, notifications
138│ ├── MenuView.swift # SwiftUI popover view
139│ ├── CreditsChecker.swift # API client (GET /v1/credits)
140│ ├── ClaudeUsageClient.swift # Claude OAuth client (GET /api/oauth/usage) + models
141│ ├── KeychainHelper.swift # Keychain wrapper for API key
142│ ├── VersionFormatter.swift # YYYY.MM.DD.HHHH formatting
143│ ├── Info.plist # LSUIElement=true (agent app)
144│ ├── UsageMenubar.entitlements # Network client; deliberately not sandboxed
145│ └── Assets.xcassets/ # App icon, accent color
146├── UsageMenubarTests/
147│ ├── CreditsCheckerTests.swift # API, retry, fractional balance, keychain tests
148│ ├── ClaudeUsageClientTests.swift # Usage decoding, token refresh, retry, credential store
149│ └── VersionFormatterTests.swift # Version formatting tests
150├── .github/workflows/
151│ └── release.yml # Build + release on push to master
1591. **Menu bar item**: The app creates an `NSStatusItem` with a variable-length button showing `⚡{balance}` or `⚡?`. Left-click toggles the popover; right-click opens a context menu (Refresh, Open hyper.charm.land, Quit).
1602. **Popover**: Clicking the menu bar item shows an `NSPopover` containing a SwiftUI `MenuView` with the balance, sparkline, API key field, and settings.
1613. **API client**: `CreditsChecker` performs `GET https://hyper.charm.land/v1/credits` with `Authorization: Bearer <key>`, retries transient failures (5xx, timeouts) with exponential backoff, and decodes `{"balance": <int>}` (tolerating fractional values by rounding).
1624. **Keychain**: The API key is stored/retrieved via `KeychainHelper` using the macOS Security framework. `save()` returns a `Bool` indicating success.
1635. **Timer**: A `Timer` fires at the user-selected interval (1m / 5m / 15m / 30m, default 5m) to refresh the balance; the app also refreshes on wake from sleep. Changing the interval in the popover restarts the timer immediately.
1646. **State**: `ViewModel` owns the balance, loading/error state, balance history (for the sparkline), refresh task (cancellable to prevent races), and the refresh interval, and is shared between the menu bar item and the popover.
1657. **Notifications**: When the balance crosses below 10 credits — or is already below 10 on the first successful fetch — `ViewModel` posts a local `UNUserNotification`. Permission is requested only once.
1668. **Agent app**: `LSUIElement=true` in `Info.plist` makes the app a background agent — no dock icon, no main window.
1679. **Claude usage**: `ClaudeUsageClient` reads Claude Code's OAuth credentials (its `Claude Code-credentials` Keychain item via `/usr/bin/security`, falling back to `~/.claude/.credentials.json`), refreshes the access token when it has expired, and fetches `GET https://api.anthropic.com/api/oauth/usage` with `Authorization: Bearer …` and `anthropic-beta: oauth-2025-04-20`. It is an actor so that overlapping refreshes (timer, manual click, wake) join a single in-flight token exchange — the endpoint rotates the refresh token, and rotating it twice would sign Claude Code out. Missing credentials are reported as "not configured" rather than as an error, and the popover hides the section.
16810. **Independent fetches**: `ViewModel.refresh()` runs the Hyper and Claude requests concurrently with `async let`. Neither can fail, stall, or be unconfigured in a way that affects the other.
16911. **No sandbox**: Reading credentials that belong to another app means spawning `/usr/bin/security` and reading a file in the user's home directory, neither of which the App Sandbox permits. The app is distributed ad-hoc signed via Homebrew rather than through the App Store, where the sandbox would be mandatory.
173- **Author**: Ilya Koo ([@ilyakooo0](https://github.com/ilyakooo0))
174- **API**: [Hyper by Charm](https://hyper.charm.land)
175- **Built with**: Swift 5.9, SwiftUI, macOS 14+
179MIT — see [LICENSE](LICENSE).
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list