feat: add weekly Sonnet and Opus usage widgets#361
Open
shopkinspoint wants to merge 1 commit intosirmalloc:mainfrom
Open
feat: add weekly Sonnet and Opus usage widgets#361shopkinspoint wants to merge 1 commit intosirmalloc:mainfrom
shopkinspoint wants to merge 1 commit intosirmalloc:mainfrom
Conversation
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.
Closes #359.
What
Adds two new widgets —
weekly-sonnet-usageandweekly-opus-usage— that surface the per-model weekly buckets that the/api/oauth/usageendpoint already returns alongsidefive_hourandseven_day. These are the same buckets that Claude Code's own/usagepanel renders as "Current week (Sonnet only)" / "Current week (Opus)".Why
For users near (or rate-limited on) Sonnet's weekly cap, the all-models
seven_daybar is a strict superset and undercounts Sonnet-specific pressure. The data is already in the response payload — ccstatusline just wasn't reading it.API response shape, captured against
api.anthropic.comtoday:{ "five_hour": { "utilization": ..., "resets_at": "..." }, "seven_day": { "utilization": ..., "resets_at": "..." }, "seven_day_sonnet": { "utilization": ..., "resets_at": "..." }, "seven_day_opus": { "utilization": ..., "resets_at": "..." } }Both per-model buckets can come back as
null(e.g.seven_day_opusfor accounts that haven't used Opus this week) — the widgets rendernullin that case, mirroring the existing pattern inWeeklyUsageWidget.Changes
src/utils/usage-fetch.ts— extendUsageApiResponseSchemawithseven_day_sonnet/seven_day_opus(PerModelWeeklyBucketSchema); threadweeklySonnetUsage/weeklySonnetResetAt/weeklyOpusUsage/weeklyOpusResetAtthroughparseUsageApiResponse, the cached schema, andparseCachedUsageDataso cache files round-trip.src/utils/usage-types.ts— add the four new optional fields toUsageData.src/types/RenderContext.ts— add the four fields toRenderUsageData.src/types/StatusJSON.ts— acceptseven_day_sonnet/seven_day_opusin therate_limitsblock (the same field names Claude Code passes through).src/utils/usage-prefetch.ts— extract the per-model buckets fromrate_limitsand addweekly-sonnet-usage/weekly-opus-usagetoUSAGE_WIDGET_TYPES. When a per-model widget is on screen but the prefetch payload lacks the per-model buckets (e.g. older Claude Code release), fall through to the API fetch — single call, then memo-cached like every other usage widget.src/utils/usage-windows.ts— addresolveWeeklySonnetUsageWindow/resolveWeeklyOpusUsageWindow. They use the per-modelresets_atif present, falling back toseven_day.resets_at(the per-model timestamps are within ~1s of the all-models one, so this is safe).src/widgets/WeeklySonnetUsage.tsandsrc/widgets/WeeklyOpusUsage.ts— new widgets, structurally identical toWeeklyUsage(same display modes, same color, same keybinds), only differing in label and whichusageDatafield they read.src/widgets/index.tsandsrc/utils/widget-manifest.ts— export and register.docs/USAGE.md— listed the new widgets in the Tokens/Usage/Context section and the keybind list.No new dedicated reset-timer widgets
Skipped on purpose. The per-model
resets_atmatchesseven_day.resets_atto within ~1 second in practice (they're computed from the same window boundary), so a separateweekly-sonnet-reset-timerwould just duplicateweekly-reset-timer. The new resolvers fall back toweeklyResetAtif the per-model one is absent so the time-cursor mode in the new widgets keeps working when only the legacy bucket is populated.Tests
src/widgets/__tests__/WeeklySonnetUsage.test.tsandWeeklyOpusUsage.test.ts— mirrorWeeklyUsage.test.ts, including the existingrunUsagePercentWidgetSuite(which I extended to accept the new field names) and one new case asserting the widget returnsnullwhen the per-model bucket is absent.src/utils/__tests__/usage-prefetch.test.ts— three new prefetch cases (per-model widgets pull fromrate_limitswhen present; fall through to API fetch when prefetch lacks per-model data; the all-models weekly widget still doesn't require per-model buckets) and three newextractUsageDataFromRateLimitscases (extract per-model buckets, leave fields undefined when buckets absent, treatnullper-model buckets as absent).Smoke test
Custom config exercising all three widgets, fed via
bun run src/ccstatusline.ts --config …:When
seven_day_opusisnullin the payload, the Opus widget renders nothing (existing renderer collapses the surrounding separator).