claude-usage-limits 1.13.2 → 1.14.0

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.0",
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.0",
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.0",
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
@@ -565,6 +565,26 @@ function briefText(parts) {
565
565
  }
566
566
  if (parts.othersSummary) sentences.push('Other windows: ' + parts.othersSummary + '.');
567
567
 
568
+ // Being at the wall and being out of budget are different things, and the
569
+ // difference is a command. Said as soon as the window is half gone, so it is
570
+ // already known by the time it matters.
571
+ const escape = parts.escape;
572
+ const escapeText =
573
+ escape && escape.kind === 'model'
574
+ ? 'This window is scoped to one model, so it is not the account\'s budget: switching model retires it. ' +
575
+ (escape.nextLabel
576
+ ? 'After a switch the binding window would be ' + escape.nextLabel + ' at ' + escape.nextPercent + ' per cent. '
577
+ : '') +
578
+ 'Use /model ' + (escape.suggest || '<another model>') + ' (or scripts/lowpower.js on --model <id>).'
579
+ : escape && escape.kind === 'effort'
580
+ ? 'A model switch does not free this window - it follows the account - but effort does: ' +
581
+ escape.to + ' measured ' + (escape.multiple ? escape.multiple + 'x ' : '') + 'cheaper a turn than ' +
582
+ (escape.from || 'the current effort') + '. Use /effort ' + escape.to + '.'
583
+ : null;
584
+ if (escapeText && parts.binding && Number.isFinite(parts.binding.percentUsed) && parts.binding.percentUsed >= HALF_SPENT) {
585
+ sentences.push(escapeText);
586
+ }
587
+
568
588
  // A window that is not binding can still be the expensive one to exhaust.
569
589
  if (parts.critical && parts.critical.length) {
570
590
  for (const other of parts.critical) {
@@ -668,7 +688,18 @@ function briefText(parts) {
668
688
  // go, keep a note of where things stand - not to shrink the work until it is
669
689
  // guaranteed to fit.
670
690
  const instruction =
671
- parts.pressure === 'gone'
691
+ // The escape outranks everything below it. A session that stops while a
692
+ // command would have carried it on has not been careful, it has quit - and
693
+ // that is a real session: the Fable weekly hit 89 per cent, the line said
694
+ // the budget was nearly gone, and the work ended with the five-hour window
695
+ // at 46 and every other model untouched.
696
+ escapeText && (parts.pressure === 'tight' || parts.pressure === 'gone')
697
+ ? 'This window is nearly gone, but you are not out of budget and you must ' +
698
+ 'not stop as though you were. ' + escapeText + ' Do that, say in one ' +
699
+ 'line that you switched and why, and carry on with the whole request at ' +
700
+ 'full quality. Only if the switch is impossible or the user has ruled it ' +
701
+ 'out: save the work, write the handoff, and say what is left.'
702
+ : parts.pressure === 'gone'
672
703
  ? 'The budget is gone, so nothing further will run. Save what exists and ' +
673
704
  'write the handoff: what is finished, what is next and in what order, ' +
674
705
  'which files are mid-change, and when the window resets.' +
@@ -917,6 +948,9 @@ async function run(now, hookInput) {
917
948
  turnsLeft: binding && Number.isFinite(binding.turnsLeft) ? binding.turnsLeft : null,
918
949
  session: data.session,
919
950
  othersSummary: summariseOthers(data.windows, binding && binding.key),
951
+ // The way out that is not stopping. Cached with the rest of the view
952
+ // because it is derived from the same one pass over the windows.
953
+ escape: usage.escapeRoute(data.windows, binding, data.effortWarning || null),
920
954
  // Every window, trimmed to the cacheable fields, so the corrected reading
921
955
  // can be recorded for all three columns of the status line on a cache
922
956
  // hit too. Recording the binding window alone left the other two at
@@ -996,6 +1030,7 @@ async function run(now, hookInput) {
996
1030
  ? familyLabel(binding.family)
997
1031
  : null,
998
1032
  othersSummary: view.othersSummary,
1033
+ escape: view.escape || null,
999
1034
  turnsLeft: view.turnsLeft,
1000
1035
  effortWarning: view.effortWarning || null,
1001
1036
  // 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,