anthropics / anthropics/claude-code
Status line: expose per-model weekly rate limit (Fable) in rate_limits, or keep cachedUsageUtilization fresh
- Lingua principale
- Python
- Stelle
- 145k
- Fork
- 23.1k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
## Problem
`/usage` shows a per-model weekly window ("Current week (Fable)"). A status line cannot show that row today: the stdin payload does not carry it, and the only on-disk copy is written solely when the user opens `/usage`, so it is usually stale or absent.
## What exists today
1. **Status line payload.** `rate_limits` carries only `five_hour`, `seven_day` and `spend_limit` (each with `used_percentage` and `resets_at`). There is no per-model window. Confirmed by capturing a live payload and against https://code.claude.com/docs/en/statusline.
2. **`~/.claude.json`.** The per-model row exists only in `cachedUsageUtilization.utilization.limits[]`:
```json
{"kind":"weekly_scoped","group":"weekly","percent":57,"resets_at":"…","scope":{"model":{"id":null,"display_name":"Fable"}}}
```
Claude Code writes this record only from its own `/usage` fetch, throttled to once per 5 minutes, and its own reader discards it after 1 hour. On my machine the record sat untouched from 2026-08-28 to 2026-09-09 while the file itself was rewritten constantly. A status line that reads it shows stale data or nothing unless the user opens `/usage`.
3. **Claude Code already produces the needed shape for another surface.** The stream-json control request
```json
{"type":"control_request","request":{"subtype":"get_usage","skip_behaviors":true}}
```
returns `rate_limits.model_scoped: [{display_name:"Fable", utilization:57, resets_at:"2026-09-10T05:59:59Z"}]`. The SDK types describe it as "Per-model weekly windows from the server limits[] array, filtered by the overage-included-models allowlist. Additive — present only when the server emits them." The TypeScript Agent SDK exposes it as `usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET()`. It needs no model call.
## Why the workarounds do not fit
A status line runs on every refresh (every 10 s here). Measured cost per refresh:
- `claude -p --safe-mode …` plus the `get_usage` control request: ≈1.4 s CPU, 340 MB peak RSS.
- Python Agent SDK: ≈2.5 s CPU, 430 MB peak RSS.
- Calling `/api/oauth/usage` directly: ≈50 ms, but it forces the status line to read the OAuth token from `.credentials.json` / Keychain. A status line script is the wrong place for a bearer token.
## Proposal A: add `model_scoped` to the status line payload (preferred)
Mirror the shape Claude Code already emits for `get_usage`, using the payload's existing conventions (`used_percentage`, epoch-second `resets_at`):
```json
"rate_limits": {
"five_hour": { "used_percentage": 75, "resets_at": 1788963000 },
"seven_day": { "used_percentage": 28, "resets_at": 1789020000 },
"model_scoped": [
{ "display_name": "Fable", "used_percentage": 57, "resets_at": 1789020000 }
]
}
```
- Additive and optional: absent when the server emits no per-model windows, so every existing script keeps working.
- Same source as `get_usage`: the server `limits[]` array filtered by the overage-included-models allowlist. No new fetch.
- Document it next to `spend_limit` in the statusline docs.
Concrete consumer: https://github.com/AbdallaM-Ibrahim/claude-code-statusline (Go, single binary, no network, no subprocesses; reads the stdin payload and local files only). The payload is parsed in `internal/limits/limits.go`, which already handles `five_hour` / `seven_day` and would pick up `model_scoped` from the same struct.
If you would take a PR for this, please say so here before I open one so I can match whatever naming or gating you prefer.
## Proposal B: keep `cachedUsageUtilization` fresh (fallback if A is rejected)
Refresh `cachedUsageUtilization` on the same 5-minute cadence Claude Code already uses for its own limit warnings, not only when `/usage` is opened, so a file-reading status line can trust `fetchedAtMs`.
A dedicated small file (for example `~/.claude/usage-cache.json`) is preferable to `~/.claude.json`: that file is ~100 KB and rewritten constantly, so polling it every refresh means re-reading and re-parsing the whole config to extract one array, and racing its frequent writes.
## Environment
- Claude Code v2.1.266
- Windows 11
- Team subscription
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Direzione di ricerca
Start by locating the status-line payload construction and the documentation section next to `spend_limit`; compare them with the `get_usage` control-request response described here. Confirm with maintainers whether Proposal A or B is in scope, then trace the chosen data path. Done means the per-model weekly window is exposed or its cache is refreshed on the stated cadence, existing consumers remain compatible, and the behavior is documented.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- go, typescript
- Ambito
- api, cli, developer-experience, documentation
- Tipo di issue
- Funzionalità
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Attiva
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 45/100