claude-usage-limits 1.13.2 → 1.14.1

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.13.2",
4
+ "version": "1.14.1",
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.13.2",
3
+ "version": "1.14.1",
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
@@ -1010,3 +1010,33 @@ licence is there for. Do not wait on me to add something for you.
1010
1010
  ## License
1011
1011
 
1012
1012
  MIT. See [LICENSE](LICENSE).
1013
+
1014
+ ## The wall is not the end of the budget
1015
+
1016
+ A full window is not always a reason to stop, and the plugin now says which.
1017
+
1018
+ A **per-model weekly** counts turns by that model only. It is not your budget,
1019
+ it is that model's: lowering effort frees nothing, and switching model retires
1020
+ the window outright while every other window carries on. A **shared window**
1021
+ follows the account wherever the model goes, so a switch buys nothing and
1022
+ effort is the lever instead.
1023
+
1024
+ Once the binding window is half gone the budget line names the lever that
1025
+ applies, which window would bind after the switch and how full it is, and the
1026
+ command:
1027
+
1028
+ ```
1029
+ This window is scoped to one model, so it is not the account's budget:
1030
+ switching model retires it. After a switch the binding window would be weekly
1031
+ at 77 per cent. Use /model opus.
1032
+ ```
1033
+
1034
+ At the wall it goes further and says outright that you are not out of budget
1035
+ and must not stop as though you were. It refuses to claim an escape it cannot
1036
+ evidence: trading an 89 for an 87 is a lateral move, not a way out, and with no
1037
+ other readable window there is nothing behind the claim, so it says nothing.
1038
+
1039
+ This exists because of a real session. The Fable weekly hit 89 per cent, the
1040
+ line said the budget was nearly gone, and the work stopped - with the 5-hour
1041
+ window at 46 and every other model untouched. One command would have carried it
1042
+ on.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-usage-limits",
3
- "version": "1.13.2",
3
+ "version": "1.14.1",
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",
@@ -370,7 +370,39 @@ effort setting does not control any of it:
370
370
  `references/tactics.md` has the full list and the billing reasons behind each
371
371
  one.
372
372
 
373
- ## 5. Checkpoint before the wall
373
+ ## 5. The wall is not the end of the budget: switch, do not stop
374
+
375
+ Before treating a full window as a reason to stop, check what kind of window it
376
+ is. There are two, and only one of them is the account's budget.
377
+
378
+ - **A per-model weekly** (`weekly (Fable)`, `weekly (Opus)`, `weekly (Sonnet)`)
379
+ counts turns by *that model only*. It is not your budget, it is that model's.
380
+ Lowering effort does not free a single point of it. **Switching model retires
381
+ it outright** - `/model opus`, `/model sonnet`, or
382
+ `node scripts/lowpower.js on --model <id>`. Every other window carries on
383
+ exactly as before.
384
+ - **A shared window** (the 5-hour, the all-models weekly) follows the account
385
+ wherever the model goes, so a switch buys nothing. Here the lever is
386
+ **effort**: `/effort medium` measured six times cheaper a turn than ultra on
387
+ this account. `node scripts/usage.js --recommend` names both levers with the
388
+ commands.
389
+
390
+ The budget line does this arithmetic for you. Once the binding window is half
391
+ gone it says which lever applies, and at the wall it says outright that you are
392
+ not out of budget and must not stop as though you were. When it does, the
393
+ sequence is: switch, say in one line that you switched and why, carry on with
394
+ the whole request at full quality.
395
+
396
+ This is written down because it was got wrong. A session ended with the Fable
397
+ weekly at 89 per cent and the line saying the budget was nearly gone - while the
398
+ 5-hour sat at 46 and every other model on the account was untouched. One command
399
+ would have carried it on. Stopping there was not caution; it was quitting with a
400
+ reason that sounded like one.
401
+
402
+ Only when the switch is genuinely unavailable - no other window has room, or the
403
+ user has ruled it out - does the checkpoint below apply.
404
+
405
+ ## 6. Checkpoint before the wall
374
406
 
375
407
  When the binding window is under roughly 10 percent, or under about ten turns
376
408
  of headroom, keep working and start keeping the work landable:
@@ -389,7 +421,7 @@ afternoon, so there is no longer anything to buy by winding down.
389
421
  The one thing not to economise on is the handoff itself. Everything else can be
390
422
  picked up next session; a session that ends without one cannot.
391
423
 
392
- ## 6. The relay: when the handoff picks itself up
424
+ ## 7. The relay: when the handoff picks itself up
393
425
 
394
426
  If a relay is armed, the budget line says so, and the shape of the last hour
395
427
  changes. The handoff stops being a note for a person to find and becomes the
@@ -556,7 +556,12 @@ function briefText(parts) {
556
556
  // do more cheaply on this model frees it; only running a different model
557
557
  // does, and that has to be said, because the obvious move at 90 percent is
558
558
  // to drop the effort and keep going, which spends the same window slower.
559
- if (parts.family) {
559
+ // Suppressed when the escape line below fires: that one says the same thing
560
+ // and names the model to switch to, where this one only ever managed
561
+ // "Use /model (or ...". Two sentences making one point is a cost paid on
562
+ // every prompt.
563
+ const namesTheSwitch = parts.escape && parts.escape.kind === 'model';
564
+ if (parts.family && !namesTheSwitch) {
560
565
  sentences.push(
561
566
  'That window counts ' + parts.family + ' turns only, so lowering effort does not free it: ' +
562
567
  'switching model does. Use /model (or scripts/lowpower.js on --model <other>) for work ' +
@@ -565,6 +570,26 @@ function briefText(parts) {
565
570
  }
566
571
  if (parts.othersSummary) sentences.push('Other windows: ' + parts.othersSummary + '.');
567
572
 
573
+ // Being at the wall and being out of budget are different things, and the
574
+ // difference is a command. Said as soon as the window is half gone, so it is
575
+ // already known by the time it matters.
576
+ const escape = parts.escape;
577
+ const escapeText =
578
+ escape && escape.kind === 'model'
579
+ ? 'This window is scoped to one model, so it is not the account\'s budget: switching model retires it. ' +
580
+ (escape.nextLabel
581
+ ? 'After a switch the binding window would be ' + escape.nextLabel + ' at ' + escape.nextPercent + ' per cent. '
582
+ : '') +
583
+ 'Use /model ' + (escape.suggest || '<another model>') + ' (or scripts/lowpower.js on --model <id>).'
584
+ : escape && escape.kind === 'effort'
585
+ ? 'A model switch does not free this window - it follows the account - but effort does: ' +
586
+ escape.to + ' measured ' + (escape.multiple ? escape.multiple + 'x ' : '') + 'cheaper a turn than ' +
587
+ (escape.from || 'the current effort') + '. Use /effort ' + escape.to + '.'
588
+ : null;
589
+ if (escapeText && parts.binding && Number.isFinite(parts.binding.percentUsed) && parts.binding.percentUsed >= HALF_SPENT) {
590
+ sentences.push(escapeText);
591
+ }
592
+
568
593
  // A window that is not binding can still be the expensive one to exhaust.
569
594
  if (parts.critical && parts.critical.length) {
570
595
  for (const other of parts.critical) {
@@ -668,7 +693,18 @@ function briefText(parts) {
668
693
  // go, keep a note of where things stand - not to shrink the work until it is
669
694
  // guaranteed to fit.
670
695
  const instruction =
671
- parts.pressure === 'gone'
696
+ // The escape outranks everything below it. A session that stops while a
697
+ // command would have carried it on has not been careful, it has quit - and
698
+ // that is a real session: the Fable weekly hit 89 per cent, the line said
699
+ // the budget was nearly gone, and the work ended with the five-hour window
700
+ // at 46 and every other model untouched.
701
+ escapeText && (parts.pressure === 'tight' || parts.pressure === 'gone')
702
+ ? 'This window is nearly gone, but you are not out of budget and you must ' +
703
+ 'not stop as though you were. ' + escapeText + ' Do that, say in one ' +
704
+ 'line that you switched and why, and carry on with the whole request at ' +
705
+ 'full quality. Only if the switch is impossible or the user has ruled it ' +
706
+ 'out: save the work, write the handoff, and say what is left.'
707
+ : parts.pressure === 'gone'
672
708
  ? 'The budget is gone, so nothing further will run. Save what exists and ' +
673
709
  'write the handoff: what is finished, what is next and in what order, ' +
674
710
  'which files are mid-change, and when the window resets.' +
@@ -917,6 +953,9 @@ async function run(now, hookInput) {
917
953
  turnsLeft: binding && Number.isFinite(binding.turnsLeft) ? binding.turnsLeft : null,
918
954
  session: data.session,
919
955
  othersSummary: summariseOthers(data.windows, binding && binding.key),
956
+ // The way out that is not stopping. Cached with the rest of the view
957
+ // because it is derived from the same one pass over the windows.
958
+ escape: usage.escapeRoute(data.windows, binding, data.effortWarning || null),
920
959
  // Every window, trimmed to the cacheable fields, so the corrected reading
921
960
  // can be recorded for all three columns of the status line on a cache
922
961
  // hit too. Recording the binding window alone left the other two at
@@ -996,6 +1035,7 @@ async function run(now, hookInput) {
996
1035
  ? familyLabel(binding.family)
997
1036
  : null,
998
1037
  othersSummary: view.othersSummary,
1038
+ escape: view.escape || null,
999
1039
  turnsLeft: view.turnsLeft,
1000
1040
  effortWarning: view.effortWarning || null,
1001
1041
  // Outside the cache: it is cheap, and it belongs to the other agent's
@@ -2091,6 +2091,64 @@ function criticalOthers(windows, bindingKey, threshold) {
2091
2091
  );
2092
2092
  }
2093
2093
 
2094
+ // The way out that is not stopping.
2095
+ //
2096
+ // This exists because of a session that ended for no reason. The Fable weekly
2097
+ // was at 89 per cent and binding, the line said the budget was nearly gone, and
2098
+ // the work stopped - while the five-hour window sat at 46 and every other model
2099
+ // on the account was untouched. A per-model weekly is not the account's budget.
2100
+ // It is one model's, and switching model retires it outright.
2101
+ //
2102
+ // So: when the window that binds is scoped to a model, say which window would
2103
+ // bind after a switch and how full it is. When it is shared, a model switch
2104
+ // changes nothing and the lever is effort instead. Either way the point is the
2105
+ // same - being at the wall is not the same as being out of budget, and an agent
2106
+ // that cannot tell the difference stops early and calls it caution.
2107
+ function escapeRoute(windows, binding, effortNote) {
2108
+ if (!binding || binding.percentUsed === null || binding.percentUsed === undefined) return null;
2109
+ const live = (windows || []).filter(
2110
+ (w) => w && w.percentUsed !== null && w.percentUsed !== undefined && !w.stale && w.key !== binding.key
2111
+ );
2112
+
2113
+ if (binding.family) {
2114
+ // Everything that would still apply once this model is no longer the one
2115
+ // running: the shared windows, plus any other model's weekly.
2116
+ const after = live.filter((w) => w.family !== binding.family);
2117
+ const next = after.length
2118
+ ? after.reduce((worst, w) => (w.percentUsed > worst.percentUsed ? w : worst))
2119
+ : null;
2120
+ // Only worth calling an escape if what replaces it is meaningfully emptier.
2121
+ // Trading an 89 for an 87 is not a way out, it is a lateral move. And with
2122
+ // no other readable window there is no evidence a switch helps at all: it
2123
+ // is true that the scoped window would retire, but "you have room" is a
2124
+ // claim, and a claim with nothing behind it is the thing not to make.
2125
+ if (!next || next.percentUsed >= binding.percentUsed - 10) return null;
2126
+ const roomier = after
2127
+ .filter((w) => w.family && w.family !== binding.family)
2128
+ .sort((a, b) => a.percentUsed - b.percentUsed)[0] || null;
2129
+ return {
2130
+ kind: 'model',
2131
+ frees: binding.label || binding.key,
2132
+ family: binding.family,
2133
+ nextLabel: next ? next.label || next.key : null,
2134
+ nextPercent: next ? Math.round(next.percentUsed) : null,
2135
+ suggest: roomier && roomier.family ? roomier.family : null,
2136
+ };
2137
+ }
2138
+
2139
+ // A shared window follows the account wherever the model goes, so the only
2140
+ // lever left is how dear a turn is.
2141
+ if (effortNote && effortNote.cheaper && effortNote.cheaper.effort) {
2142
+ return {
2143
+ kind: 'effort',
2144
+ from: effortNote.effort || null,
2145
+ to: effortNote.cheaper.effort,
2146
+ multiple: Number.isFinite(effortNote.cheaper.multiple) ? effortNote.cheaper.multiple : null,
2147
+ };
2148
+ }
2149
+ return null;
2150
+ }
2151
+
2094
2152
  // The window that will stop the work first.
2095
2153
  function bindingWindow(windows) {
2096
2154
  // A per-model weekly for a model that is not running cannot be the window
@@ -3681,6 +3739,7 @@ module.exports = {
3681
3739
  lastRejections,
3682
3740
  bindingWindow,
3683
3741
  criticalOthers,
3742
+ escapeRoute,
3684
3743
  betterCalibration,
3685
3744
  calibrationForPlan,
3686
3745
  stampPlan,