claude-usage-limits 1.5.4 → 1.6.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.
|
|
4
|
+
"version": "1.6.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",
|
package/README.md
CHANGED
|
@@ -207,6 +207,12 @@ A window with no recent spend to measure is ranked by how full it is rather
|
|
|
207
207
|
than being skipped, so a 5-hour window sitting at 95 percent is never passed
|
|
208
208
|
over just because nothing has gone through it in the last few minutes.
|
|
209
209
|
|
|
210
|
+
Binding is about what stops you soonest, though, not what stopping costs, and
|
|
211
|
+
those are different: a 5-hour window returns in hours, the weekly one in days.
|
|
212
|
+
So a window above 85 percent gets called out even when something shorter binds,
|
|
213
|
+
with its own reset time, because spending the weekly window to save a few turns
|
|
214
|
+
of the 5-hour one is a bad trade.
|
|
215
|
+
|
|
210
216
|
## When you keep typing
|
|
211
217
|
|
|
212
218
|
Every message sent while work is already running starts another turn, and each
|
|
@@ -509,7 +515,7 @@ test/ node --test, no dependencies
|
|
|
509
515
|
node --test
|
|
510
516
|
```
|
|
511
517
|
|
|
512
|
-
|
|
518
|
+
186 tests over the pricing, the window arithmetic, plan and credit detection,
|
|
513
519
|
the status line, the before-prompt line, job forecasting, per-project
|
|
514
520
|
attribution, the CLI, packaging, and the settings save/restore.
|
|
515
521
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-usage-limits",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|
|
@@ -171,6 +171,12 @@ Keep it to one line unless the work genuinely does not fit. The budget note is
|
|
|
171
171
|
a header, not a section, and it must never push the actual answer down the
|
|
172
172
|
page.
|
|
173
173
|
|
|
174
|
+
Which window binds is about what stops you soonest, not what stopping costs.
|
|
175
|
+
Those differ: a 5-hour window comes back in hours, the weekly one in days. So
|
|
176
|
+
when a window that is not binding sits near its wall, say so and weigh it. The
|
|
177
|
+
hook flags those. Running the weekly out to save a few turns of the 5-hour
|
|
178
|
+
window is a bad trade even though the 5-hour is what runs out first.
|
|
179
|
+
|
|
174
180
|
One thing to get right: **quote the binding window, not the roomiest one.**
|
|
175
181
|
Two windows run at once and they are rarely in the same place. The turns of
|
|
176
182
|
headroom and the reset time belong to whichever runs out first. Putting the
|
|
@@ -119,7 +119,6 @@ function aheadOfPace(window, now) {
|
|
|
119
119
|
return window.percentUsed - Math.min(100, Math.max(0, elapsed));
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
// Not whether to speak, which is always, but how hard to lean on it.
|
|
123
122
|
// Not whether to speak, which is always, but how hard to lean on it.
|
|
124
123
|
function pressure(window, now, config, turnsLeft) {
|
|
125
124
|
if (!window || window.percentUsed === null || window.stale) return 'unknown';
|
|
@@ -243,6 +242,17 @@ function briefText(parts) {
|
|
|
243
242
|
);
|
|
244
243
|
}
|
|
245
244
|
if (parts.othersSummary) sentences.push('Other windows: ' + parts.othersSummary + '.');
|
|
245
|
+
|
|
246
|
+
// A window that is not binding can still be the expensive one to exhaust.
|
|
247
|
+
if (parts.critical && parts.critical.length) {
|
|
248
|
+
for (const other of parts.critical) {
|
|
249
|
+
sentences.push(
|
|
250
|
+
'Note that ' + other.label + ' is at ' + other.percentUsed + '% and resets in ' +
|
|
251
|
+
other.resetsIn + ', so running that one out stops work for far longer than the ' +
|
|
252
|
+
'binding window would. Weigh it even though it is not what runs out first.'
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
246
256
|
if (parts.session) {
|
|
247
257
|
sentences.push(
|
|
248
258
|
'This session: ' + parts.session.turns + ' turns, ' +
|
|
@@ -296,6 +306,11 @@ async function run(now, hookInput) {
|
|
|
296
306
|
othersSummary: summariseOthers(data.windows, binding && binding.key),
|
|
297
307
|
sessions: data.sessions,
|
|
298
308
|
staleWindows: data.staleWindows,
|
|
309
|
+
critical: usage.criticalOthers(data.windows, binding && binding.key).map((w) => ({
|
|
310
|
+
label: w.label,
|
|
311
|
+
percentUsed: w.percentUsed,
|
|
312
|
+
resetsIn: Number.isFinite(w.msToReset) ? usage.formatDuration(w.msToReset) : 'an unknown time',
|
|
313
|
+
})),
|
|
299
314
|
snapshotAge: usage.formatDuration(data.snapshotAgeMs),
|
|
300
315
|
binding: binding
|
|
301
316
|
? {
|
|
@@ -335,6 +350,7 @@ async function run(now, hookInput) {
|
|
|
335
350
|
session: view.session,
|
|
336
351
|
rebuilt: Boolean(binding && binding.estimated),
|
|
337
352
|
staleWindows: view.staleWindows || 0,
|
|
353
|
+
critical: view.critical || [],
|
|
338
354
|
pointsSinceSnapshot: (binding && binding.pointsSinceSnapshot) || 0,
|
|
339
355
|
snapshotAge: view.snapshotAge,
|
|
340
356
|
pressure: pressure(binding, now, config, view.turnsLeft),
|
|
@@ -685,6 +685,24 @@ function buildWindow(spec, snapshot, events, now, options) {
|
|
|
685
685
|
return window;
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
+
// Which window binds is about what stops you soonest. It says nothing about
|
|
689
|
+
// what stopping costs. Running out of a 5-hour window waits hours; running out
|
|
690
|
+
// of the weekly one waits days. So a weekly window near the wall is worth
|
|
691
|
+
// hearing about even while a shorter window binds.
|
|
692
|
+
const CRITICAL_PERCENT = 85;
|
|
693
|
+
|
|
694
|
+
function criticalOthers(windows, bindingKey, threshold) {
|
|
695
|
+
const limit = Number.isFinite(threshold) ? threshold : CRITICAL_PERCENT;
|
|
696
|
+
return (windows || []).filter(
|
|
697
|
+
(w) =>
|
|
698
|
+
w &&
|
|
699
|
+
w.key !== bindingKey &&
|
|
700
|
+
!w.stale &&
|
|
701
|
+
w.percentUsed !== null &&
|
|
702
|
+
w.percentUsed >= limit
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
688
706
|
// The window that will stop the work first.
|
|
689
707
|
function bindingWindow(windows) {
|
|
690
708
|
const known = windows.filter((w) => w.percentUsed !== null);
|
|
@@ -1214,13 +1232,18 @@ function formatPercent(value) {
|
|
|
1214
1232
|
|
|
1215
1233
|
function renderForecast(data, turns) {
|
|
1216
1234
|
const lines = [];
|
|
1217
|
-
lines.push('Forecast for ' + turns + ' turns');
|
|
1218
|
-
lines.push('');
|
|
1219
1235
|
|
|
1236
|
+
// Checked before the heading is built, or a bad argument prints straight
|
|
1237
|
+
// into it: "Forecast for NaN turns".
|
|
1220
1238
|
if (!Number.isFinite(turns) || turns <= 0) {
|
|
1239
|
+
lines.push('Forecast');
|
|
1240
|
+
lines.push('');
|
|
1221
1241
|
lines.push(' Give a number of turns, for example --forecast 15.');
|
|
1222
1242
|
return lines.join('\n');
|
|
1223
1243
|
}
|
|
1244
|
+
|
|
1245
|
+
lines.push('Forecast for ' + turns + ' turns');
|
|
1246
|
+
lines.push('');
|
|
1224
1247
|
if (!data.rates) {
|
|
1225
1248
|
lines.push(' Nothing recent to price this against yet. Do some work in this');
|
|
1226
1249
|
lines.push(' session first, then ask again.');
|
|
@@ -1335,6 +1358,8 @@ module.exports = {
|
|
|
1335
1358
|
MIN_BASELINE_TURNS,
|
|
1336
1359
|
buildWindows,
|
|
1337
1360
|
bindingWindow,
|
|
1361
|
+
criticalOthers,
|
|
1362
|
+
CRITICAL_PERCENT,
|
|
1338
1363
|
dominantEffort,
|
|
1339
1364
|
typicalTurnCost,
|
|
1340
1365
|
activeSessions,
|