Skip to content

feat: add weekly Sonnet and Opus usage widgets#361

Open
shopkinspoint wants to merge 1 commit intosirmalloc:mainfrom
shopkinspoint:feat/weekly-per-model-usage
Open

feat: add weekly Sonnet and Opus usage widgets#361
shopkinspoint wants to merge 1 commit intosirmalloc:mainfrom
shopkinspoint:feat/weekly-per-model-usage

Conversation

@shopkinspoint
Copy link
Copy Markdown

Closes #359.

What

Adds two new widgets — weekly-sonnet-usage and weekly-opus-usage — that surface the per-model weekly buckets that the /api/oauth/usage endpoint already returns alongside five_hour and seven_day. These are the same buckets that Claude Code's own /usage panel 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_day bar 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.com today:

{
  "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_opus for accounts that haven't used Opus this week) — the widgets render null in that case, mirroring the existing pattern in WeeklyUsageWidget.

Changes

  • src/utils/usage-fetch.ts — extend UsageApiResponseSchema with seven_day_sonnet / seven_day_opus (PerModelWeeklyBucketSchema); thread weeklySonnetUsage / weeklySonnetResetAt / weeklyOpusUsage / weeklyOpusResetAt through parseUsageApiResponse, the cached schema, and parseCachedUsageData so cache files round-trip.
  • src/utils/usage-types.ts — add the four new optional fields to UsageData.
  • src/types/RenderContext.ts — add the four fields to RenderUsageData.
  • src/types/StatusJSON.ts — accept seven_day_sonnet / seven_day_opus in the rate_limits block (the same field names Claude Code passes through).
  • src/utils/usage-prefetch.ts — extract the per-model buckets from rate_limits and add weekly-sonnet-usage / weekly-opus-usage to USAGE_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 — add resolveWeeklySonnetUsageWindow / resolveWeeklyOpusUsageWindow. They use the per-model resets_at if present, falling back to seven_day.resets_at (the per-model timestamps are within ~1s of the all-models one, so this is safe).
  • src/widgets/WeeklySonnetUsage.ts and src/widgets/WeeklyOpusUsage.ts — new widgets, structurally identical to WeeklyUsage (same display modes, same color, same keybinds), only differing in label and which usageData field they read.
  • src/widgets/index.ts and src/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_at matches seven_day.resets_at to within ~1 second in practice (they're computed from the same window boundary), so a separate weekly-sonnet-reset-timer would just duplicate weekly-reset-timer. The new resolvers fall back to weeklyResetAt if 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

$ bun run lint   # tsc + eslint, clean
$ bun test
 1179 pass
 0 fail
 2234 expect() calls
Ran 1179 tests across 105 files.
  • src/widgets/__tests__/WeeklySonnetUsage.test.ts and WeeklyOpusUsage.test.ts — mirror WeeklyUsage.test.ts, including the existing runUsagePercentWidgetSuite (which I extended to accept the new field names) and one new case asserting the widget returns null when the per-model bucket is absent.
  • src/utils/__tests__/usage-prefetch.test.ts — three new prefetch cases (per-model widgets pull from rate_limits when 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 new extractUsageDataFromRateLimits cases (extract per-model buckets, leave fields undefined when buckets absent, treat null per-model buckets as absent).

Smoke test

Custom config exercising all three widgets, fed via bun run src/ccstatusline.ts --config …:

Weekly: 29.0% | Weekly Sonnet: 8.0% | Weekly Opus: 12.0%

When seven_day_opus is null in the payload, the Opus widget renders nothing (existing renderer collapses the surrounding separator).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add per-model weekly usage widgets (Sonnet / Opus)

1 participant