feat(claude): show the Fable weekly limit and fix /usage parsing for tall screens#217
Conversation
…tall screens The Claude CLI and OAuth usage API now report a Fable 5 weekly limit that ClaudeBar ignored. Parse it from both probes so it appears as a quota card in the window and as a selectable menu-bar metric (everything downstream of parsing is already generic over QuotaType.modelSpecific): - CLI probe: parse the "Current week (Fable)" section (paren-open anchor so a future "(Fable 5)" label still matches), with its own reset time falling back to the all-models weekly reset. - API probe: the endpoint reports model limits via a generic `limits` array (kind "weekly_scoped" + scope.model.display_name) instead of the legacy seven_day_<model> fields, which are now null. Parse it generically — any future model-scoped limit appears automatically — keyed on the first word of the display name to stay in sync with the CLI probe's key, deduped against legacy fields, and without clamping over-quota negative remaining. Also fix the CLI probe failing on every tick with recent CLI versions: the /usage screen grew past the probe's 50-row terminal (usage-contribution report), scrolling the quota sections into scrollback, which the terminal renderer never read. Render the whole buffer (visible rows + scrollback) instead of just the visible screen. Verified against a real /usage PTY capture and a live probe: session, weekly, and Fable quotas all parse (quotas=3, "Fable: 67% remaining"). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds parsing of a Claude Fable 5 weekly quota from both the CLI ChangesFable weekly quota parsing and scrollback support
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant API as OAuth Usage API
participant CLI as Claude CLI /usage
participant Renderer as TerminalRenderer
participant Probe as Claude Usage Probes
API->>Probe: usage JSON with limits array
Probe->>Probe: filter weekly_scoped, derive model quota
CLI->>Renderer: render output taller than visible rows
Renderer->>Renderer: capture rows + scrollback lines
Renderer-->>Probe: full text incl. off-screen quota sections
Probe->>Probe: extract fablePct, resolve reset time
Probe-->>Probe: append modelSpecific("fable") quota
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift (1)
474-505: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCross-file "fable" key derivation is duplicated and only comment-linked.
The model key here is derived by splitting/lowercasing
displayName, whileClaudeUsageProbe.swifthardcodes the literal"fable"string for the CLI path. The only thing keeping them in sync is a comment (// Key on the first word... must stay in sync with the key the CLI probe hardcodes). If Anthropic changes the API'sdisplay_nameformat (e.g., to "Claude Fable 5"), this loop would silently derive"claude"instead of"fable", breaking the menu-bar persistence contract without any compiler or test signal outside the two now-passing but independently-maintained fixtures.Consider extracting the key-derivation into a single shared helper (e.g. a
QuotaTypestatic function) that both probes call, so any future model-name format change is caught in one place.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift` around lines 474 - 505, The model key derivation for the Claude Fable quota is duplicated between this loop and ClaudeUsageProbe.swift, relying only on a comment to stay in sync. Extract the display-name-to-model-key logic into a shared helper or QuotaType utility and have both the weekly_scoped parsing path and the CLI probe use it, so the persisted model:<name> selection remains consistent if the API displayName format changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@Sources/Infrastructure/Claude/ClaudeAPIUsageProbe.swift`:
- Around line 474-505: The model key derivation for the Claude Fable quota is
duplicated between this loop and ClaudeUsageProbe.swift, relying only on a
comment to stay in sync. Extract the display-name-to-model-key logic into a
shared helper or QuotaType utility and have both the weekly_scoped parsing path
and the CLI probe use it, so the persisted model:<name> selection remains
consistent if the API displayName format changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 57a2ef99-7ca3-4947-9aff-446a944bb0a5
📒 Files selected for processing (7)
CHANGELOG.mdSources/Infrastructure/Claude/ClaudeAPIUsageProbe.swiftSources/Infrastructure/Claude/ClaudeUsageProbe.swiftSources/Infrastructure/Shared/TerminalRenderer.swiftTests/DomainTests/Provider/QuotaTypeTests.swiftTests/InfrastructureTests/Claude/ClaudeAPIUsageProbeTests.swiftTests/InfrastructureTests/Claude/ClaudeUsageProbeParsingTests.swift
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #217 +/- ##
==========================================
+ Coverage 81.65% 81.83% +0.17%
==========================================
Files 116 116
Lines 8713 8758 +45
==========================================
+ Hits 7115 7167 +52
+ Misses 1598 1591 -7
🚀 New features to boost your workflow:
|
Summary
The Claude CLI and OAuth usage API now report a Fable 5 weekly limit that ClaudeBar ignored. This PR parses it from both probes so it appears as a quota card in the window and as a selectable menu-bar metric. It also fixes a bug where the CLI probe failed on every tick with recent Claude CLI versions.
Fable weekly limit
Current week (Fable)section (paren-open anchor so a future(Fable 5)label still matches), with its own reset time and fallback to the all-models weekly reset.limitsarray (kind: "weekly_scoped"+scope.model.display_name) instead of the legacyseven_day_<model>fields, which are nownull(verified against the live endpoint). Parsing is generic — any future model-scoped limit appears automatically — keyed on the first word of the display name to stay in sync with the CLI probe's key, deduped against legacy fields, and preserving negativepercentRemainingas the over-quota signal per theUsageQuotacontract.QuotaType.modelSpecific, quota cards, the menu-bar quota picker, quota keys likemodel:fable) is already generic.Parse-failure fix (blocks Fable in CLI mode)
Recent Claude CLI versions render a
/usagescreen ~81 lines tall (new usage-contribution report). On the probe's 50-row PTY the quota sections scroll into scrollback, whichTerminalRenderer.extractScreenTextnever read — so parsing failed with "could not find 'Current session'" on every tick. The renderer now reads the whole buffer (visible rows + scrollback, capacity raised to 2000 lines).Verification
limitsarray incl. dedupe/malformed/over-quota cases, QuotaType round-trip); full Domain + Infrastructure suites pass./usagePTY capture (160×50 raw bytes) and a live app run: probe succeeds withquotas=3— Session 35% / Weekly 82% / Fable 67% remaining — matching the CLI exactly.Follow-up (not in this PR)
Summary by CodeRabbit
New Features
Bug Fixes