claude-usage-limits 1.9.2 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "usage-limits",
3
3
  "displayName": "Usage Limits",
4
- "version": "1.9.2",
4
+ "version": "1.11.2",
5
5
  "description": "Puts your remaining Claude Code usage limit into Claude's context before every prompt, so it opens with what fits in the budget instead of starting work that gets cut off. Reports headroom as turns rather than percentages, prices a job before you start it, and detects your plan tier.",
6
6
  "author": {
7
7
  "name": "Ridelink",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usage-limits",
3
- "version": "1.9.2",
3
+ "version": "1.11.2",
4
4
  "description": "Reports how much of your Codex usage limit is left as turns of work rather than a percentage, prices a job before you start it, and counts the other agents sharing the same budget.",
5
5
  "author": {
6
6
  "name": "Ridelink",
package/README.md CHANGED
@@ -39,9 +39,10 @@ you just asked for is going to finish, and answering that needs the request and
39
39
  the budget in the same place. That place is the model's context, which is where
40
40
  this puts them.
41
41
 
42
- So: if you want to watch your usage, install a status line. This ships one too.
43
- If you want the thing spending the budget to know it is spending the budget,
44
- that is what this is for.
42
+ So: if you want to watch your usage, install a status line. This ships one
43
+ too, and a live panel that sits beside the chat, both drawn in Claude Code's
44
+ own colours from Claude Code's own numbers. If you want the thing spending the
45
+ budget to know it is spending the budget, that is what this is for.
45
46
 
46
47
  ## What it prints
47
48
 
@@ -275,30 +276,120 @@ trusted publishing over OIDC, so there is no publish token stored in the repo
275
276
  or in CI. `npm version` also syncs the version in the plugin manifest, so the
276
277
  marketplace and the npm package never disagree about which release is current.
277
278
 
278
- ## Status line
279
+ ## Right next to the chat
279
280
 
280
- For a permanent readout instead of asking, point Claude Code's status line at
281
- the same script. In `settings.json`:
281
+ ```
282
+ npx claude-usage-limits panel --open
283
+ ```
282
284
 
283
- ```json
284
- {
285
- "statusLine": {
286
- "type": "command",
287
- "command": "node ~/.claude/skills/usage-limits/scripts/usage.js --status"
288
- }
289
- }
285
+ That opens a narrow pane to the right of the one Claude Code is running in
286
+ (Windows Terminal, tmux, WezTerm, kitty, zellij and iTerm2 are all understood)
287
+ and draws the limits in it, live:
288
+
289
+ ```
290
+ ✻ Claude usage
291
+ Fable 5.1 · xhigh · working
292
+
293
+ Current session
294
+ ███████░░░░░░░░░░░░░░░░░░░░░ 24%
295
+ resets in 4h 12m at 6:40 PM
296
+
297
+ Current week (all models)
298
+ █░░░░░░░░░░░░░░░░░░░░░░░░░░░ 4%
299
+ resets in 1d 5h at Sun 7:00 PM
300
+
301
+ Current week (Fable)
302
+ █░░░░░░░░░░░░░░░░░░░░░░░░░░░ 3%
303
+ resets in 1d 5h at Sun 7:00 PM
304
+
305
+ Sessions · 1 working, 1 idle
306
+ ✳ Fable 5.1 usage-limits working
307
+ · Opus 5 ridelink idle 4m ago
308
+
309
+ live, updated 12s ago
310
+ q quit · r refresh
290
311
  ```
291
312
 
292
- It prints one line and prefixes `LOW` once a window passes 90 percent:
313
+ Four things about it are deliberate.
314
+
315
+ **It is drawn the way Claude Code draws things.** The colours are Claude
316
+ Code's own theme, read out of the CLI rather than approximated: the bar is the
317
+ one `/usage` paints, the title is the Claude orange, the spinner is Claude's
318
+ spinner with Claude's frames. A bar turns yellow at 80 percent and red at 90,
319
+ each window judged on its own. While Claude is working the title shimmers and
320
+ the spinner turns; while it waits, they stop. Under ultracode both go rainbow,
321
+ which is what Claude Code does with its max effort tag.
322
+
323
+ **The numbers are the ones Claude Code uses.** Every reading is the same GET
324
+ that `/usage` makes, with the login Claude Code already holds, plus the
325
+ rate-limit headers on Claude's own API responses whenever the status line
326
+ below is installed. Whichever is newer wins, and the footer says how old it
327
+ is. The week for one model (the Fable line above) only appears while that
328
+ model is the one running, because it cannot stop work on any other; when the
329
+ model cannot be told at all, nothing is hidden on a guess. The
330
+ reading is kept in `usage-limits-live.json`, and the report, the hooks and the
331
+ status line all prefer it whenever it is newer than Claude Code's own cache,
332
+ which can sit hours behind.
333
+
334
+ **It keeps drawing when things go wrong.** Offline, it shows the last reading
335
+ and says how old it is, and tries again at a widening interval. Signed out, it
336
+ says so in red and keeps checking, because Claude Code refreshes the login on
337
+ its own next call. Told to slow down by the endpoint, it waits exactly as long
338
+ as it was told. Too narrow a pane loses the breathing room, then the footer,
339
+ never the bars. Nothing in it refreshes or rotates the token, and the token is
340
+ never written anywhere by this plugin.
341
+
342
+ **It knows about the other Claudes.** Two windows share one limit, so the
343
+ panel lists every session this machine has heard from in the last quarter of
344
+ an hour: what it runs, where, and whether it is working right now, each with
345
+ its own spinner. The hooks are what say so (a prompt marks a session working,
346
+ every tool call keeps it so, the Stop hook marks it idle), so the list is live
347
+ without anyone polling anything. The status line adds `+1 working` when
348
+ another session is spending.
349
+
350
+ `panel` alone runs it in the current pane, `--once` prints one frame, `--json`
351
+ prints the fields, `--no-fetch` (or `USAGE_LIMITS_FETCH=off`) keeps it
352
+ entirely offline on the reading already on disk, and `--poll N` sets the
353
+ seconds between readings (30 while Claude works and 120 while it waits
354
+ otherwise). Installed as a plugin, `/usage-limits:panel` opens it from inside
355
+ the chat.
356
+
357
+ Not on the desktop app or claude.ai, which show the limits themselves. This is
358
+ for the terminal and the VS Code extension, where the only way to see them is
359
+ to ask with `/usage`.
360
+
361
+ ## Status line
362
+
363
+ The same bars, one line under the prompt:
364
+
365
+ ```
366
+ ✻ Fable 5.1 · xhigh session ████░░░░░░ 42% week █░░░░░░░░░ 7% fable █░░░░░░░░░ 3% +1 working
367
+ ```
293
368
 
294
369
  ```
295
- 5h 62% 1h 40m wk 75% 2d 4h
370
+ npx claude-usage-limits statusline on
371
+ npx claude-usage-limits statusline off
296
372
  ```
297
373
 
298
- `--status` reads only the cached percentages and never opens a transcript, so
299
- it runs in about a tenth of a second and is safe on every redraw. Use the full
300
- path rather than `~` if your shell does not expand it, and point it at the
301
- plugin copy instead if that is how you installed it.
374
+ `on` points Claude Code's `statusLine` setting at a small launcher in the
375
+ config directory that finds wherever the plugin is currently installed, so a
376
+ plugin update does not leave the status line pointing at a folder that has
377
+ gone. A status line that was already there is kept and printed above ours;
378
+ `--no-chain` replaces it instead, and `off` restores exactly what was there,
379
+ including nothing. `--refresh N` re-runs it every N seconds as well as on every
380
+ change, which keeps the spinner turning between responses at the cost of a
381
+ Node process every N seconds. The change applies to new sessions.
382
+
383
+ The line is also where the panel learns what Claude is doing. Claude Code
384
+ hands the status line the model in use, the effort level, and the rate limits
385
+ from its own response headers, and the line records them for the panel, which
386
+ never sees that JSON. It reads no transcripts and makes no network calls, so it
387
+ costs about a tenth of a second on every redraw. It shrinks to fit narrow
388
+ windows, honours `NO_COLOR` and Claude Code's `prefersReducedMotion`, and
389
+ prints nothing rather than an error if anything goes wrong.
390
+
391
+ The older one-line form is still there as `usage.js --status`, which prints
392
+ `5h 62% 1h 40m wk 75% 2d 4h` and prefixes `LOW` past 90 percent.
302
393
 
303
394
  ## It tells you where you stand, every time
304
395
 
@@ -331,6 +422,15 @@ explaining is pace: two days into a week you should be near 29 percent spent, so
331
422
  60 percent means you will not last the week, and that is worth hearing at 60
332
423
  rather than at 85.
333
424
 
425
+ The percentages behind the line are kept fresh too. If the reading on disk is
426
+ older than three minutes when a prompt goes in, the hook first takes the same
427
+ reading Claude Code takes for `/usage`, and the mid-turn pulse does the same
428
+ every two minutes through a long turn. That is what stops a burst of parallel
429
+ agents from emptying a window between two readings: eight of them once spent
430
+ half a window in five minutes while the line, seventeen minutes old, still
431
+ said 42 percent. Offline that is one quick failure and then a widening
432
+ backoff, never a wait on every prompt.
433
+
334
434
  One limit worth knowing: the hook fires when a prompt is submitted, so a
335
435
  message sent while Claude is already working does not refresh it. Claude Code
336
436
  delivers those into the running turn without re-running hooks, which no plugin
@@ -353,6 +453,14 @@ minute, so it costs about 400ms cold and 120ms warm.
353
453
  | `USAGE_LIMITS_PULSE` | on | `off` silences the mid-turn line; `always` prints it even when there is room. |
354
454
  | `USAGE_LIMITS_PULSE_SECONDS` | 120 | How often the mid-turn line can fire. |
355
455
  | `USAGE_LIMITS_TALLY` | on | Set to `off` to turn off the after-reply tally, the closing line and the session history. |
456
+ | `USAGE_LIMITS_FETCH` | on | `off` keeps the panel and the hooks off the network; they show the reading already on disk. |
457
+ | `USAGE_LIMITS_REFRESH` | 180 | Seconds a reading may age before the before-prompt hook takes a fresh one. The mid-turn pulse uses its own interval. |
458
+ | `USAGE_LIMITS_POLL` | 30 / 120 | Seconds between the panel's readings, working / idle. Never under 15. |
459
+ | `USAGE_LIMITS_MOTION` | on | `off` stops the spinner, the shimmer and the rainbow. Claude Code's `prefersReducedMotion` setting does the same. |
460
+ | `USAGE_LIMITS_STATUSLINE` | on | `off` blanks the status line while still recording the feed the panel reads. |
461
+ | `USAGE_LIMITS_CLOCK` | from settings | `12h` or `24h` for reset times; otherwise follows Claude Code's `timeFormat`. |
462
+ | `USAGE_LIMITS_COLOUR` | detected | `256` or `none` to override colour detection. `NO_COLOR` and `FORCE_COLOR` are honoured. |
463
+ | `USAGE_LIMITS_ASCII` | off | `1` draws the bars and the spinner with plain characters. |
356
464
 
357
465
  ## What a session cost
358
466
 
@@ -514,6 +622,18 @@ codex plugin marketplace add https://github.com/ridelink0/claude-code-usage-limi
514
622
  codex plugin add usage-limits@usage-limits
515
623
  ```
516
624
 
625
+ The panel works under Codex too, GPT-6 Astra included:
626
+
627
+ ```
628
+ npx claude-usage-limits panel --open --host codex
629
+ ```
630
+
631
+ It reads Codex's own meter the way `/status` does, through a short-lived
632
+ `codex app-server`, and draws the same bars under the title `Codex usage`,
633
+ with the model named the way Codex names it. Codex has no status line and no
634
+ hooks, so there is no spinner for a working session and no Sessions list; the
635
+ numbers are the point.
636
+
517
637
  ### One thing is different, and it is worth being straight about
518
638
 
519
639
  Under Claude Code the budget line arrives on its own, because a plugin can ship
@@ -542,6 +662,27 @@ Two smaller differences. There is no money column: Codex meters a share of an
542
662
  allowance and never quotes a price, so the percentages stand alone. And
543
663
  `lowpower` is Claude Code only, because it writes Claude's `settings.json`.
544
664
 
665
+ ## VS Code
666
+
667
+ The Claude Code extension for VS Code shows the limits only when you ask with
668
+ `/usage`, and it does not render a custom status line. So there is an
669
+ extension of its own in [vscode/](vscode/): the same bars as a view that sits
670
+ directly under the Claude Code chat in the secondary side bar (it contributes
671
+ into the Claude Code extension's own view container, so there is no gap and
672
+ nothing to arrange), a status bar item with the percentages that turns yellow
673
+ and red at the same thresholds, the Sessions list, and the same animations in
674
+ CSS. It carries the plugin's scripts inside it, so it has no dependencies and
675
+ reads the same files and takes the same reading as the terminal panel.
676
+
677
+ ```
678
+ cd vscode && npm run package
679
+ code --install-extension claude-usage-limits-<version>.vsix
680
+ ```
681
+
682
+ The `.vsix` is attached to each GitHub release. It is not on the Marketplace
683
+ yet; that needs a publisher account, and the steps are in
684
+ [vscode/README.md](vscode/README.md).
685
+
545
686
  ## Where it works
546
687
 
547
688
  Every surface of Claude Code on a machine shares one config directory, so
@@ -550,9 +691,9 @@ this reads all of them and does not care which one you are in:
550
691
  | Surface | Works | Notes |
551
692
  | --- | --- | --- |
552
693
  | Terminal (`claude`) | yes | |
553
- | VS Code extension | yes | |
694
+ | VS Code extension | yes | The VS Code extension above puts the bars under the chat and in the status bar. |
554
695
  | JetBrains extension | yes | |
555
- | Desktop app | yes | |
696
+ | Desktop app | yes | The budget line and the hooks. The panel, the status line and the VS Code extension are for the terminal and VS Code; the app shows the limits itself. |
556
697
  | Headless (`claude -p`) | yes | Scripts run fine, but there are no slash commands, so `lowpower.js` is the only way to change effort. |
557
698
  | Cloud and web sessions | partly | Those run on a remote machine with their own config directory. Percentages are per-account and stay correct; the pace is measured from whatever transcripts are local to wherever you run the script. |
558
699
 
@@ -600,8 +741,13 @@ Node 18 or newer, and a Claude Code recent enough to write
600
741
  `cachedUsageUtilization` into `~/.claude.json`. If the report says it found no
601
742
  snapshot, run `/usage` once inside Claude Code and it will be there.
602
743
 
603
- Nothing is uploaded. No API key, token, or credential is read. Everything comes
604
- from files already on the machine.
744
+ Nothing is uploaded. The report and the status line read only files already on
745
+ the machine. The panel, and the hooks when the reading on disk is older than a
746
+ few minutes, take the same reading Claude Code takes for `/usage`: they read
747
+ the login token Claude Code keeps and send it to Anthropic's usage endpoint,
748
+ nowhere else. The token is never written to disk by this plugin, never
749
+ printed, and never refreshed or rotated. `USAGE_LIMITS_FETCH=off` keeps all of
750
+ it offline, on the reading Claude Code itself last cached.
605
751
 
606
752
  ## How accurate is it
607
753
 
@@ -631,6 +777,24 @@ Good enough to plan with, not a bill. The honest caveats:
631
777
  - The account's own list of limits is read as well as the per-window buckets,
632
778
  so a per-model weekly such as `weekly (Fable)` shows up as a window of its
633
779
  own, priced from that model's calls alone.
780
+ - A per-model weekly only counts against you while you are running that model.
781
+ One at 88 per cent is not your wall if you are working on Opus: nothing you
782
+ do moves it. Those windows are still listed, marked `not in use`, but they
783
+ are never picked as the binding window, never raised as a warning, never a
784
+ reason a forecast says the job does not fit, and never what puts `LOW` on the
785
+ status line. Start using that model and they come straight back. Nothing is
786
+ suppressed unless both sides are recognised: an unknown model in the setting,
787
+ or a weekly scoped to a model this table has never heard of, is treated as
788
+ live, because hiding a limit that can stop the work is worse than showing one
789
+ that cannot.
790
+ - The report also says what the room left buys in turns of each model, priced
791
+ from that model's own measured cost per turn, against the window its spend
792
+ lands in. Rows sharing a window are alternatives rather than additions. A
793
+ model that has only ever run as a subagent gets no projected turn count at
794
+ all - its errands are not turns - though what delegating to it has cost is
795
+ still reported. What each model cost is remembered in
796
+ `usage-limits-models.json`, one entry per family, so a session that opens on
797
+ a model it has not run this week still knows its price.
634
798
  - The turn cost behind "turns of headroom" is a median over at least five
635
799
  turns, so one compaction cannot define your pace.
636
800
  - What a point of a window costs is learned once from the best sample seen and
@@ -670,15 +834,22 @@ skills/usage-limits/SKILL.md what the agent reads
670
834
  skills/usage-limits/agents/ how Codex lists the skill
671
835
  skills/usage-limits/scripts/ usage.js, brief.js, pulse.js, stop.js,
672
836
  sessionend.js, tally.js, codex.js, host.js,
673
- lowpower.js, install-codex-hook.js
837
+ lowpower.js, install-codex-hook.js,
838
+ recommend.js, panel.js, feed.js,
839
+ statusline.js, live.js, view.js, bars.js,
840
+ activity.js
674
841
  skills/usage-limits/references/ the longer notes
675
842
  hooks/hooks.json runs brief.js before each prompt, pulse.js
676
843
  during long turns, stop.js after each reply
677
844
  and sessionend.js when the session closes
678
845
  commands/check.md the /usage-limits:check command
679
846
  commands/session.md the /usage-limits:session command
847
+ commands/panel.md the /usage-limits:panel command
848
+ commands/statusline.md the /usage-limits:statusline command
680
849
  bin/cli.js the npx entry point
681
850
  tools/sync-version.js keeps the manifest version in step
851
+ vscode/ the VS Code extension; build.js copies the
852
+ scripts into vscode/lib and makes the vsix
682
853
  test/ node --test, no dependencies
683
854
  ```
684
855
 
@@ -688,7 +859,7 @@ test/ node --test, no dependencies
688
859
  node --test
689
860
  ```
690
861
 
691
- 308 tests over the pricing, the window arithmetic, plan and credit detection,
862
+ 469 tests over the pricing, the window arithmetic, plan and credit detection,
692
863
  the status line, the before-prompt line, the mid-turn pulse, the after-reply tally and the session history, job forecasting,
693
864
  per-project attribution, the Codex reader and its installer, the CLI,
694
865
  packaging, and the settings save/restore.
package/bin/cli.js CHANGED
@@ -18,6 +18,14 @@ const HELP = `claude-usage-limits - how much agent usage is left, and whether th
18
18
  claude-usage-limits --host codex read Codex's limits instead
19
19
  claude-usage-limits --host codex --refresh ask Codex for a live reading
20
20
 
21
+ claude-usage-limits panel live bars beside the chat; q quits
22
+ claude-usage-limits panel --open open it in a split pane to the right
23
+ claude-usage-limits panel --once one frame, for a pipe or a screenshot
24
+
25
+ claude-usage-limits statusline status is the status line installed
26
+ claude-usage-limits statusline on bars under the Claude Code prompt
27
+ claude-usage-limits statusline off put back what was there
28
+
21
29
  claude-usage-limits lowpower status show the current effort setting
22
30
  claude-usage-limits lowpower on lower effortLevel, remembering the old value
23
31
  claude-usage-limits lowpower on --effort medium --model sonnet
@@ -30,7 +38,9 @@ const HELP = `claude-usage-limits - how much agent usage is left, and whether th
30
38
  Reads the usage figures the agent already keeps on disk, plus its own session
31
39
  history, to report the remaining headroom as turns of work. Works with Claude
32
40
  Code and with Codex; the host is detected, or state it with --host. Nothing is
33
- uploaded and no credentials are read.
41
+ uploaded. The panel, and the hooks when the reading on disk has aged, take the
42
+ same reading Claude Code takes for /usage, with the login Claude Code already
43
+ holds, and send it nowhere else (USAGE_LIMITS_FETCH=off keeps it all offline).
34
44
 
35
45
  https://github.com/ridelink0/claude-code-usage-limits
36
46
  `;
@@ -58,6 +68,16 @@ function run(argv) {
58
68
  return Promise.resolve(installer.main(args.slice(1)));
59
69
  }
60
70
 
71
+ if (args[0] === 'panel') {
72
+ const panel = require('../skills/usage-limits/scripts/panel.js');
73
+ return Promise.resolve(panel.main(args.slice(1)));
74
+ }
75
+
76
+ if (args[0] === 'statusline') {
77
+ const statusline = require('../skills/usage-limits/scripts/statusline.js');
78
+ return Promise.resolve(statusline.main(args.slice(1)));
79
+ }
80
+
61
81
  const usage = require('../skills/usage-limits/scripts/usage.js');
62
82
  return Promise.resolve(usage.main(args));
63
83
  }
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Open the live usage panel in a pane beside this chat
3
+ ---
4
+
5
+ Run `node "${CLAUDE_PLUGIN_ROOT}/skills/usage-limits/scripts/panel.js" --open`.
6
+
7
+ It opens a narrow pane to the right of this one (Windows Terminal, tmux,
8
+ WezTerm, kitty, zellij or iTerm2) showing the current session, the current
9
+ week, and the week for the model in use when the account caps that model on
10
+ its own, as live bars in Claude's colours. If this terminal cannot be split it
11
+ prints the command to run in a second pane instead; pass that on to me.
12
+
13
+ Tell me in one line what happened and stop. Do not start other work as part
14
+ of this command.
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: Put the usage bars under the prompt (on), take them out (off), or say which it is (status)
3
+ ---
4
+
5
+ Run `node "${CLAUDE_PLUGIN_ROOT}/skills/usage-limits/scripts/statusline.js" $ARGUMENTS`.
6
+ With no argument it reports the current state.
7
+
8
+ It edits the `statusLine` entry in `settings.json` in the Claude config
9
+ directory (`~/.claude`, or `CLAUDE_CONFIG_DIR`) through a
10
+ temporary file, keeps a backup, and `off` restores exactly what was there. A
11
+ status line that was already set is kept and printed above ours. The change
12
+ applies to new Claude Code sessions, not this one.
13
+
14
+ Read the output back to me in one or two lines and stop. Do not start other
15
+ work as part of this command.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-usage-limits",
3
- "version": "1.9.2",
3
+ "version": "1.11.2",
4
4
  "description": "Puts your remaining Claude Code usage limit into Claude's context before every prompt, so it opens with what fits in the budget instead of starting work that gets cut off. Reports headroom as turns rather than percentages, prices a job before you start it, and detects your plan tier.",
5
5
  "keywords": [
6
6
  "claude",
@@ -110,6 +110,14 @@ from the start of the turn. A message sent while you are already working does
110
110
  not fire it again: it arrives without a budget line, and the numbers you are
111
111
  holding are now older than they look.
112
112
 
113
+ The reading itself is kept fresh: the hook takes the same reading Claude Code
114
+ takes for `/usage` when the one on disk is older than three minutes, and the
115
+ mid-turn pulse does the same every two minutes through a long turn. So the
116
+ figures are as good as `/usage` at those moments. What no hook can do is fire
117
+ between them, and a burst of parallel agents can spend a great deal in the
118
+ gap: eight workflow agents once emptied half a 5-hour window in five minutes.
119
+ Spawn agents knowing that each fresh context re-reads what you already hold.
120
+
113
121
  That matters most when it is the one thing you are about to assert. Re-check
114
122
  before saying a job fits, if any of these are true:
115
123
 
@@ -202,6 +210,22 @@ when a window that is not binding sits near its wall, say so and weigh it. The
202
210
  hook flags those. Running the weekly out to save a few turns of the 5-hour
203
211
  window is a bad trade even though the 5-hour is what runs out first.
204
212
 
213
+ The exception is a weekly that caps one model. `weekly (Fable)` at 88 per cent
214
+ is not your wall while you are working on Opus - nothing you do moves it, and
215
+ doing less work will not un-spend it. The report marks those `not in use` and
216
+ the hook leaves them out of what it asks you to weigh. Do not weigh them back
217
+ in. They matter again the moment you switch to that model, and that is when
218
+ they come back.
219
+
220
+ That cuts both ways, and the second half is the useful one: when the window
221
+ that binds is a per-model weekly, the same work on another model draws on a
222
+ different window. The report's **Model headroom** table says what the room left
223
+ buys in turns of each model, priced from what that model has really cost here.
224
+ Moving mechanical bulk onto a model with room is not doing less work - it is
225
+ the same work against a wall that is further away, which is the one economy
226
+ worth making. A model with no turn count in that table has only ever run as a
227
+ subagent; its errands are not turns, so there is nothing to project from.
228
+
205
229
  One thing to get right: **quote the binding window, not the roomiest one.**
206
230
  Two windows run at once and they are rarely in the same place. The turns of
207
231
  headroom and the reset time belong to whichever runs out first. Putting the
@@ -419,6 +443,13 @@ Do not run the report on every prompt. Once at the start of a long piece of
419
443
  work, and again if the job grows or something starts looping. The report costs
420
444
  a turn, which is the thing it is trying to save.
421
445
 
446
+ The panel (`scripts/panel.js --open`, or `/usage-limits:panel`) and the status
447
+ line (`scripts/statusline.js on`) are for the person watching, not for you.
448
+ Nothing in them changes what you should do, and they draw from the same
449
+ reading the budget line already gave you, so do not open or install them
450
+ unless asked. When asked, run the command, say in one line what it did, and
451
+ stop.
452
+
422
453
  ## Files
423
454
 
424
455
  | Path | What it is |
@@ -435,4 +466,9 @@ a turn, which is the thing it is trying to save.
435
466
  | `scripts/lowpower.js` | `status`, `on`, `off`. Restores what it replaced. Claude Code only. |
436
467
  | `scripts/recommend.js` | The chooser behind `usage.js --recommend`: posture, then the effort and model commands for each lever. Not meant to be called by hand. |
437
468
  | `references/tactics.md` | Every lever that lowers cost, and why it works. |
469
+ | `scripts/panel.js` | The live panel beside the chat: `--open` puts it in a split pane to the right, `--once` prints one frame, `--json` the fields. For the person, not for you; open it only when asked. |
470
+ | `scripts/statusline.js` | `status`, `on`, `off`. Puts the bars under the prompt and restores what was there. |
471
+ | `scripts/feed.js` | The status line command Claude Code runs. Not meant to be called by hand. |
472
+ | `scripts/live.js` | The usage reading itself, taken the way Claude Code takes it for `/usage`, kept in `usage-limits-live.json` where `collect()` prefers it when newer than the cache. |
473
+ | `scripts/view.js`, `scripts/bars.js`, `scripts/activity.js` | The display model, the drawing in Claude's colours, and the working/idle marks the hooks leave for the panel. Not meant to be called by hand. |
438
474
  | `references/how-it-works.md` | Where the numbers come from and where they are soft. |
@@ -1,7 +1,10 @@
1
1
  # Where the numbers come from
2
2
 
3
- Everything is read from local files. Nothing is sent anywhere, and no API key
4
- or token is read.
3
+ Everything is read from local files, with one exception: the panel, and the
4
+ hooks when the reading on disk is older than a few minutes, take the same
5
+ reading Claude Code takes for `/usage`, which sends the login token to
6
+ Anthropic's usage endpoint and nowhere else (the last section of this file).
7
+ Nothing else is sent anywhere, and `USAGE_LIMITS_FETCH=off` stops that too.
5
8
 
6
9
  ## Sources
7
10
 
@@ -36,6 +39,68 @@ window of its own, labelled `weekly (Fable)` and priced from that model's
36
39
  calls alone; `is_active` breaks ties in the binding choice; and a bucket that
37
40
  quotes `limit_dollars` is priced from that directly instead of being calibrated.
38
41
 
42
+ A per-model weekly caps one model family and nothing else, so it can only stop
43
+ work that uses that family. Which families are in use is taken from the model
44
+ in `settings.json` (and `ANTHROPIC_MODEL`), plus every model this session has
45
+ actually run, subagents included. Another session's models are not counted:
46
+ what another window is burning is not this one's constraint. A setting that
47
+ names a strategy rather than a model resolves to every model it runs, so
48
+ `opusplan` counts as both Opus and Sonnet.
49
+
50
+ A window for a family that is not in use is still listed everywhere it was
51
+ listed before - it is real, and switching to that model would make it bite -
52
+ but it is never chosen as the binding window, never raised as a warning, never
53
+ a reason a forecast says the job does not fit, and never what puts `LOW` on the
54
+ status line. It is marked `not in use` in the report and "(not this session's
55
+ model)" in the before-prompt line.
56
+
57
+ Nothing is suppressed unless both sides are recognised. If the model in use
58
+ cannot be worked out, or the weekly is scoped to a model this table has never
59
+ heard of, the window is treated as live. Hiding a limit that can stop the work
60
+ is the one failure worse than over-reporting one.
61
+
62
+ The one-line `--status` readout is the exception worth knowing about: it reads
63
+ no transcripts by design, so the only thing it can know about the running
64
+ model is the setting. It therefore shows every per-model weekly and simply
65
+ does not let an idle one raise `LOW`. The status line proper (`feed.js`) is
66
+ told the model by Claude Code itself and needs no such caution.
67
+
68
+ ## Model headroom
69
+
70
+ How much room is left is half the question; what that room buys is the other
71
+ half, and the answer differs by model. The snapshot cannot say: it has no model
72
+ dimension at all. Every transcript line carries its model, so each family is
73
+ joined to the window its spend lands in - its own weekly where the account
74
+ gives it one, the shared weekly otherwise - and priced from its own turns.
75
+
76
+ Rows that share a window are alternatives, not additions. They describe the
77
+ same remaining room spent on different models.
78
+
79
+ No turn count is projected for a model that has never taken a turn of its own.
80
+ A family that has only ever run as a subagent has errands to price, not turns:
81
+ 114 Sonnet calls on one machine averaged under two cents because they were
82
+ one-shot lookups, and dividing the remaining budget by that promised twenty-two
83
+ thousand Sonnet turns. The row still reports what delegating to that model has
84
+ cost, because that is measured; it does not project from it.
85
+
86
+ What a turn of each model cost is kept in `usage-limits-models.json` in the
87
+ config directory, one entry per family so it cannot grow, stamped with the plan
88
+ and read back through the same guard as the window calibration. It exists so a
89
+ session that opens on a model it has not run this week still knows what that
90
+ model costs. It is deliberately not written into `usage-limits-calibration.json`:
91
+ that file means one thing, the plan's blended price of a point, and three
92
+ callers read it on that basis.
93
+
94
+ There is no per-turn log. Every model figure comes from the transcripts, which
95
+ already hold it; an append-only ledger would grow without bound and record
96
+ nothing new.
97
+
98
+ Nothing here claims the meter weights a dollar of one model differently from a
99
+ dollar of another. No published or observed source shows that, and the two
100
+ prices per point this plugin learns - one for a shared weekly, one for a scoped
101
+ one - are percentages of two differently sized allowances, which says nothing
102
+ about weighting either way.
103
+
39
104
  The plan name comes from `oauthAccount.organizationType` in the same file.
40
105
  Current effort and model come from `settings.json` in the config directory.
41
106
 
@@ -206,3 +271,51 @@ be wrong about a budget.
206
271
 
207
272
  Rows priced this way are marked with an asterisk in the report, so an assumed
208
273
  rate never quietly passes for a published one.
274
+
275
+ ## The live reading, and which reading wins
276
+
277
+ Claude Code takes its own `/usage` figures with one GET to
278
+ `https://api.anthropic.com/api/oauth/usage`, sending the login token it holds
279
+ as a bearer token with the `anthropic-beta: oauth-2025-04-20` header and a
280
+ five second timeout. `scripts/live.js` makes exactly that call, reads the
281
+ token from `.credentials.json` inside the config directory (or the
282
+ `Claude Code-credentials` keychain entry on macOS), and writes the answer to
283
+ `usage-limits-live.json` with the account it belongs to. The token is used for
284
+ that one request and nothing else: never written, never printed, never
285
+ refreshed. If it has expired the endpoint says 401, the panel says "sign in to
286
+ Claude Code again", and Claude Code fixes it on its own next call.
287
+
288
+ `collect()` then has two snapshots of the same account, Claude Code's
289
+ `cachedUsageUtilization` and the live file, and takes whichever is newer. The
290
+ choice is `preferLive()`: the live file loses when it is older, when it names a
291
+ different account, or when its timestamp is more than a minute in the future.
292
+ `snapshotSource` in the report says which one was used. Everything downstream -
293
+ the budget line, the pulse, the status line, the forecast, the recommendation -
294
+ goes through `collect()`, so they all see the newer reading.
295
+
296
+ The status line has a third source that is fresher than either: Claude Code
297
+ hands it `rate_limits` built from the `anthropic-ratelimit-unified-*` headers
298
+ on its own API responses, for the session and the shared week. `feed.js`
299
+ records those per session in `usage-limits-feed.json`, along with the model
300
+ and effort in use, and `view.js` takes the newest of headers, live reading and
301
+ cache for each window. The per-model weeks are only in the endpoint's answer,
302
+ so they come from the live file or the cache.
303
+
304
+ Which per-model week to show is decided by the model in use, never by the
305
+ account's `is_active` flag: that flag marks the limit currently binding, not
306
+ whether the model is running. The status line's `model.id` is certain, the
307
+ `model` setting is the fallback, and with neither nothing is hidden.
308
+
309
+ Whether Claude is working comes from the hooks: the prompt hook marks the
310
+ session working (and whether the prompt said `ultracode`), every tool call
311
+ keeps it so, and the Stop and SessionEnd hooks mark it idle. Marks live in
312
+ `usage-limits-activity.json`, one per session, and a session silent for fifteen
313
+ minutes counts as idle whatever it last said, because a crash never sends
314
+ Stop.
315
+
316
+ The sessions list joins those marks with the status line feed (model, effort,
317
+ directory), the Stop hook's tally (project, cost, turns) and the prompt hook's
318
+ cache (when it last prompted), one row per session id, in
319
+ `activity.combine()`. A session is listed if any of them saw it in the last
320
+ fifteen minutes and is working only if its own mark says so and is fresh, so
321
+ the header, the list and the `+N working` on the status line always agree.