feat: quota tracking via /api/oauth/usage#37
Merged
Conversation
Adds a Usage tab and threshold-crossing notifications backed by the unofficial /api/oauth/usage endpoint that Claude Code's own statusline already consumes. No SwiftTerm or TUI parsing — the OAuth access token is read from the macOS Keychain (one-time system prompt to grant access), and the JSON response is decoded into a typed QuotaSnapshot. Surfaces: - QuotaProbe: reads Keychain entry "Claude Code-credentials", extracts claudeAiOauth.accessToken, calls api.anthropic.com/api/oauth/usage with the documented headers (Authorization Bearer + anthropic-beta oauth-2025-04-20). Failures (missing token, denied access, 401, 429) return nil and log to stderr — soft signal, never fatal. - Poller in PanelController: 60s cadence while panel is visible, 5 min by default when hidden. Configurable via STACKNUDGE_USAGE_POLL_MIN. - Usage tab (Cmd+3): renders bars for "Current session" (5h), "Current week (all)", "Current week (Opus)" / "(Sonnet)" if the user's plan has those tiers. Color-coded green / yellow / red. Reset times shown per tier. - Threshold notifications: when any tier crosses the configured threshold, a UNNotification banner fires — once per period per tier (state resets when resets_at advances). Master switch + threshold selection (50/70/80/90/95) live in the new Settings → Usage section. Cmd+1/2/3/4 now jumps Events / Sessions / Usage / Settings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop ExtraUsage type + parser — it was rendered nowhere after the earlier UI removal, just dangling code. - Cache RelativeDateTimeFormatter statics in UsageView + Panel so SwiftUI re-renders don't allocate one per call. - Render fiveHour/sevenDay only when present (matching the Opus/Sonnet pattern); fall back to the empty state when the whole snapshot is blank. The previous "Not available on this plan" was misleading since those tiers ARE always available — a nil indicates a probe issue, not a plan tier. - Invalidate quotaTimer in applicationWillTerminate. - Fix copy-paste comment in QuotaProbe header. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an in-panel Usage tab + threshold-crossing notifications for Claude Code subscription quotas. Mirrors what
claude /usageshows in the TUI, but always-visible and proactive — alerts you when you're about to burn through your weekly cap.How it gets the data
Claude Code's
/usageslash command querieshttps://api.anthropic.com/api/oauth/usageand renders the bars itself. We do the same call directly:Claude Code-credentials). One-time system prompt grants access.claudeAiOauth.accessTokenfrom the JSON blob.GET /api/oauth/usagewith the documented headers (Authorization: Bearer …,anthropic-beta: oauth-2025-04-20).QuotaSnapshot { fiveHour, sevenDay, sevenDayOpus, sevenDaySonnet }.No SwiftTerm, no TUI parsing, no
claudesubprocess. The endpoint is what Claude Code's own statusline depends on, so the data is the same Anthropic shows you.What lands
STACKNUDGE_USAGE_POLL_MIN).resets_atadvances (new period, fresh budget).Quota alerts(toggle, default On) +Alert threshold(cycle: 50% / 70% / 80% / 90% / 95%, default 80%).Files
panel/SessionUsage.swift(new) —QuotaSnapshottypes,QuotaProbe(Keychain + HTTP),UsageView(SwiftUI tab content).panel/Panel.swift— Adds.usagecase inPanelContentView,Cmd+3hotkey, the poller + threshold-evaluation logic.panel/PanelNav.swift—.usagePanelMode, snapshot + lastUpdated fields, new Settings rows for the alert toggle / threshold cycle, row-layout shifts.panel/Settings.swift— New "Usage" section between Voice and Actions.build.sh— Registers the new source.Trade-offs / known gotchas
claude /usage, ClaudeBar, and every statusline tool. Multiple maintained projects rely on it today.Test plan
Claude Code-credentials— click Always Allowclaude /usageshows in your terminal~/.stack-nudge/config🤖 Generated with Claude Code