A native macOS menu bar app that shows your AI-tool quota remaining for the current billing cycle and its subcycles — with live countdown timers to each reset.
Supported providers:
The menu bar always shows the most constrained quota across all connected providers — the one you’d run out of first — with its reset countdown.
xcode-select --install) or full Xcodecd ai-usage
./scripts/build-app.sh
This produces dist/Vibe Check.app.
To install permanently:
cp -r "dist/Vibe Check.app" /Applications/
swift run
Note: On macOS 26 beta, swift run has a known CA rendering crash in MenuBarExtra. Use the built .app bundle instead.
— until connected.claude.ai/settings/usage.The app auto-refreshes every 60 seconds. Click the ↺ button to refresh manually.
To quit: open the panel and click Quit at the bottom right (the app has no dock icon by design).
Every provider maps its API into one canonical model: a ProviderSnapshot holding an ordered list of QuotaWindows (subcycles first, then billing-cycle windows), each with a used fraction, native quantities (requests / tokens / USD), and a reset timestamp. The UI, menu bar summary, refresh scheduling, and disk cache are written once against that model.
Claude and Cursor authenticate by embedding a WKWebView pointed at the vendor’s settings page. When you sign in, WebKit stores your session cookies; a WKHTTPCookieStoreObserver copies them into HTTPCookieStorage.shared in real time, so by the time you close the browser your session is available to URLSession. The app then calls the same internal APIs the vendor dashboards use:
GET https://claude.ai/api/organizations/{org_id}/usage
GET https://cursor.com/api/auth/me
GET https://cursor.com/api/usage?user={sub}
POST https://cursor.com/api/dashboard/get-monthly-invoice
POST https://cursor.com/api/dashboard/get-hard-limit
Copilot uses a GitHub PAT (classic, copilot scope) against the endpoint the editor plugins call:
GET https://api.github.com/copilot_internal/user
which returns quota_snapshots (premium_interactions, chat, completions) with entitlement, remaining, and the monthly quota_reset_date.
When a fetch fails, the app keeps showing the last good snapshot with a “stale” badge, backs off its refresh interval, and — on parse failures — writes the raw response body to ~/Library/Application Support/VibeCheck/captures/ so the parser can be fixed against real data.
Sources/
├── AIUsageApp.swift # NSStatusItem + NSPopover entry point (no MenuBarExtra)
├── AppModel.swift # @MainActor state; per-provider refresh loops with backoff; snapshot cache
├── Models.swift # Canonical model: ProviderSnapshot, QuotaWindow, ProviderStatus
├── UsageService.swift # ClaudeProvider — claude.ai internal usage API
├── GitHubCopilotService.swift # CopilotProvider — copilot_internal/user quota endpoint
├── CursorService.swift # CursorProvider — cursor.com dashboard API
├── CookieStore.swift # WKWebsiteDataStore → HTTPCookieStorage bridge
├── AuthWindowManager.swift # Claude auth window + real-time cookie observer
├── CursorAuthWindowManager.swift # Cursor auth window
├── KeychainStore.swift # PAT storage
├── Config.swift # Feature flags + RawCapture debug dumps
└── Views.swift # Generic provider card + quota gauges in NSPopover
Why NSStatusItem instead of MenuBarExtra?
SwiftUI’s MenuBarExtra has a rendering regression on macOS 26 beta (NSConcretePointerArray crash in CA::Context::commit_transaction). NSStatusItem + NSPopover is the pre-SwiftUI, stable alternative that bypasses this entirely.
Session cookies expire eventually. When stats stop updating or you see “Sign in required”, click Connect again to re-authenticate.
No data leaves your machine except to claude.ai (the same requests your browser makes). No telemetry, no external services, no API keys stored anywhere.