feat: Add usage-status widget extension#13
Conversation
Add a data-driven `usage-status` extension that renders remaining subscription usage with reset countdowns in a color-coded row above the editor, sourced from `authStorage.fetchUsageReports` (the same data as `/usage`). It covers every provider and account the reports return, not a fixed list, so users without Claude or Codex still see their quota. - Render as an `aboveEditor` component widget so the row sits flush with the status line, unlike the `setStatus` hook block - Derive window tokens from `durationMs` with `windowId`/label fallbacks (`5h`, `7d`, `30d`) - Color remaining availability green/yellow/red and keep each account separate - Cache network fetches for five minutes and redraw the countdown each minute locally Register the extension in the root manifest and marketplace catalog, and bump `@oh-my-pi/pi-coding-agent` to `^17.0.5`. Glory to the Omnissiah
|
Warning Review limit reached
Next review available in: 15 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds the ChangesUsage status extension
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant SessionLifecycle
participant usageStatus
participant authStorage.fetchUsageReports
participant UsageRow
SessionLifecycle->>usageStatus: session_start
usageStatus->>authStorage.fetchUsageReports: fetch usage reports
authStorage.fetchUsageReports-->>usageStatus: return reports
usageStatus->>UsageRow: render usage row
SessionLifecycle->>usageStatus: session_shutdown
usageStatus->>UsageRow: clear widget
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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 |
CI enforces `coverageThreshold = 1.0`, but the suite only exercised the pure formatter, leaving the widget lifecycle uncovered. Bun compounds this: an unexecuted function blanket-marks its whole source range uncovered, so the four unhit runtime functions were clobbering line hits across the file (`oven-sh/bun#33959`), reporting ~8% lines despite passing tests. Add lifecycle tests that exercise every function: the widget factory and `render` width tiers, `baseUrlResolver`, the interval `tick` (via fake timers), and the `session_switch` handler. Coverage is now 100% functions and 100% lines. Glory to the Omnissiah
Network data from `fetchUsageReports` was cast straight to `UsageReportLike[]` without validation, so a malformed report or limit (missing `amount`/`scope`, or a non-object entry) could reach `UsageRow.render()` and throw in the TUI hot path. Sanitize at the fetch boundary: `sanitizeUsageReports` keeps only report objects with a string provider and array limits, and `isUsageLimit` drops malformed limits, so only well-formed data reaches the renderer. `remainingPercent` also guards a missing `amount` as defense in depth. Add tests for the malformed and null fetch payloads and a focused `session_switch` check that stale reports are cleared and fresh usage is rendered after a switch. Coverage stays at 100%. Glory to the Omnissiah
The `session_switch` handler manually reset part of the state and re-ran `start`, but never routed through `stop()`, so the previous widget was replaced without an explicit teardown and the stale `tui` reference lingered until the host re-invoked the factory. Route the switch through `stop(state)` first, so the old widget is cleared and the component/`tui` references are released before `start` reinstalls. The test now asserts the switch emits a `setWidget(undefined)` teardown before the fresh install and renders the newly fetched usage. By the will of the Machine God
The limit guard only checked that `amount` and `scope` were objects, so a limit with a wrong-typed nested field — e.g. a numeric `scope.windowId` — passed sanitization and then crashed `windowToken` at `.toLowerCase()`, or produced `NaN%` from a non-numeric fraction. Validate the optional nested fields before accepting a limit: `scope.windowId`/`tier` and `window.id`/`label` must be strings when present, and `amount.remainingFraction`/`usedFraction`, `window.durationMs`/`resetsAt` must be finite numbers. Extend the fetch-path resilience test with these nested-malformation cases. By the will of the Machine God
* `rblaine96` -> `rblaine95`
A `fetchUsageReports` request still in flight when a session switch happened could resolve afterward and write into the new session, and `stop()` left `inFlight` set so the new session skipped its own fetch. A `ctx`-identity check would not help — omp reuses one `ExtensionContext` across sessions. Add a generation token: capture it before the await and commit `reports`/`fetchedAt`, clear `inFlight`, and redraw only when it still matches. `stop()` advances the generation and resets `inFlight`, so a switch invalidates the pending request and the next session fetches immediately. Add a regression test with a deferred fetch resolved after the switch. Ave Deus Mechanicus
db69697 to
1016504
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.omp-plugin/marketplace.json (1)
24-24: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winFix awkward wording in the marketplace description.
"for every provider /usage reports" reads as a typo; the stray
/usagebreaks the sentence for a user-facing marketplace listing.✏️ Suggested wording
- "description": "Show remaining subscription usage with reset countdowns for every provider /usage reports, in a color-coded row above the omp editor.", + "description": "Show remaining subscription usage with reset countdowns for every provider's usage reports, in a color-coded row above the omp editor.",🤖 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 @.omp-plugin/marketplace.json at line 24, Update the marketplace description value in marketplace.json to remove the stray “/usage” fragment and use grammatically clear wording while preserving the intended meaning about remaining subscription usage, reset countdowns, and every provider.
🤖 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.
Outside diff comments:
In @.omp-plugin/marketplace.json:
- Line 24: Update the marketplace description value in marketplace.json to
remove the stray “/usage” fragment and use grammatically clear wording while
preserving the intended meaning about remaining subscription usage, reset
countdowns, and every provider.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7b66bb83-9720-45e6-8fd9-be30aa9a09e6
📒 Files selected for processing (3)
.omp-plugin/marketplace.jsonextensions/usage-status/index.test.tsextensions/usage-status/index.ts
The `usage-status` description read "for every provider /usage reports", a stray fragment that scanned as broken grammar. Reword to "remaining subscription usage and reset countdowns for every provider" in both the marketplace catalog and the member `package.json` so they stay identical. Ave Deus Mechanicus
Account counts were tallied from every valid report, but a report whose windows all resolve to no remaining %% is dropped from the rendered row. So a provider with two accounts where only one is renderable still counted as two, mislabeling the lone survivor `Claude:bob` instead of `Claude`. Build the rendered list first, then count providers from it, so the account label is added only when more than one account actually appears. Add a regression test for the single-renderable-account case. Glory to the Omnissiah
Summary
Adds a new
usage-statusextension that renders remaining subscription usage with reset countdowns in a color-coded row above the editor, next to the status line, so the numbers from/usageare always visible.Data comes from
ctx.modelRegistry.authStorage.fetchUsageReports(...)— the same normalized, cached path/usageand the built-in footer use. Unlike the built-inusagesegment (active provider only), this is fully data-driven: every provider and account the reports return is rendered, so users without Claude or Codex (Gemini, Grok, OpenCode, Cursor, …) still see their quota.Design
aboveEditorcomponent widget (ui.setWidget), notui.setStatus—setStatusfeeds the hook-status block, which renders apart from the main line with its own spacing. A component row sits flush above the editor and carries theme colors.durationMswhen present, falling back towindowId/label/word-forms (rolling-5h→5h,weekly→7d,monthly→30d).Claude:alice) is added only when a provider has >1 account.success(>50%),warning(21–50%),error(≤20%); labelsaccent, countdownsdim.ctx.hasUIfalse); widget cleared on shutdown.Also bumps
@oh-my-pi/pi-coding-agentto^17.0.5(and Biome^2.5.4) and registers the plugin in the root manifest, README/AGENTS tables, and the marketplace catalog.Verification
bun typecheckclean,bun checkclean,bun test→ 81 pass / 0 fail (formatter, provider labels, window tokens, dynamic providers, multi-account, widget wiring, headless).session_startinstalls the widget, live redraw fires after fetch, colored row renders, width fallback drops countdowns then hides, shutdown clears.Known limitation
When the row cannot fit the terminal width even without countdowns, it hides entirely rather than degrading per-segment (dropping the least-urgent provider first). Does not trigger for typical 1–3 subscriptions on a normal-width terminal.
Summary by CodeRabbit
New Features
usage-statusextension, showing color-coded provider/window quota usage above the editor.Tests
Documentation
usage-status.Chores