claude-usage-limits 1.5.0 → 1.5.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.5.0",
4
+ "version": "1.5.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",
package/README.md CHANGED
@@ -466,6 +466,12 @@ Good enough to plan with, not a bill. The honest caveats:
466
466
  - A rebuilt figure only counts what this machine did. If you also worked on
467
467
  another device it reads low, which is the dangerous direction, so treat it as
468
468
  a floor until you refresh.
469
+ - If a rebuild comes out above a full window, it is refused rather than capped.
470
+ Local transcripts only see this machine, so a window mostly spent elsewhere
471
+ makes a point look far too cheap and any live spend divides to hundreds of
472
+ percent. Capping that at 100 would tell someone sitting at half their budget
473
+ that it was gone. The window is reported as unknown instead, with a nudge to
474
+ run `/usage`.
469
475
 
470
476
  [how-it-works.md](skills/usage-limits/references/how-it-works.md) has the field
471
477
  names, the formulas, and the rest of it.
@@ -491,7 +497,7 @@ test/ node --test, no dependencies
491
497
  node --test
492
498
  ```
493
499
 
494
- 157 tests over the pricing, the window arithmetic, plan and credit detection,
500
+ 164 tests over the pricing, the window arithmetic, plan and credit detection,
495
501
  the status line, the before-prompt line, job forecasting, per-project
496
502
  attribution, the CLI, packaging, and the settings save/restore.
497
503
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-usage-limits",
3
- "version": "1.5.0",
3
+ "version": "1.5.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",
@@ -237,6 +237,14 @@ function briefText(parts) {
237
237
  'That figure was rebuilt from local history because the ' +
238
238
  'snapshot is ' + parts.snapshotAge + ' old; run /usage to refresh it.'
239
239
  );
240
+ } else if (parts.staleWindows) {
241
+ // Do not quietly carry on with a window we know is wrong and could not
242
+ // rebuild. Say it is unknown and point at the one command that fixes it.
243
+ sentences.push(
244
+ 'A window is past its reset and could not be rebuilt from local history, ' +
245
+ 'so its reading is unknown rather than current; the snapshot is ' +
246
+ parts.snapshotAge + ' old, so run /usage before trusting the rest.'
247
+ );
240
248
  }
241
249
  if (parts.othersSummary) sentences.push('Other windows: ' + parts.othersSummary + '.');
242
250
  if (parts.session) {
@@ -290,6 +298,7 @@ async function run(now, hookInput) {
290
298
  session: sessionSpend(events, sessionId),
291
299
  othersSummary: summariseOthers(windows, binding && binding.key),
292
300
  sessions: usage.activeSessions(events, now, usage.CONCURRENT_WINDOW_MS),
301
+ staleWindows: windows.filter((w) => w.stale).length,
293
302
  binding: binding
294
303
  ? {
295
304
  key: binding.key,
@@ -324,6 +333,7 @@ async function run(now, hookInput) {
324
333
  : null,
325
334
  session: view.session,
326
335
  rebuilt: Boolean(binding && binding.estimated),
336
+ staleWindows: view.staleWindows || 0,
327
337
  snapshotAge: usage.formatDuration(base.snapshotAgeMs),
328
338
  pressure: pressure(binding, now, config, view.turnsLeft),
329
339
  });
@@ -772,6 +772,9 @@ function collect(now) {
772
772
  // was spent inside the window it describes equalled its percentage. That
773
773
  // dollars-per-point figure is a property of the plan, not of the moment, so it
774
774
  // still prices the window running now.
775
+ // Anything above this and the calibration, not the budget, is what is full.
776
+ const SATURATION_LIMIT = 105;
777
+
775
778
  function reconstructWindow(spec, snapshot, events, now) {
776
779
  if (!snapshot || typeof snapshot.utilization !== 'number') return null;
777
780
  if (snapshot.utilization <= 0) return null;
@@ -786,9 +789,18 @@ function reconstructWindow(spec, snapshot, events, now) {
786
789
  const usdPerPercent = past.cost / snapshot.utilization;
787
790
  const liveStart = now - spec.span;
788
791
  const live = totals(events.filter((e) => e.at >= liveStart && e.at <= now));
792
+ const raw = live.cost / usdPerPercent;
793
+
794
+ // A rebuild that overflows the window is not a full window, it is a broken
795
+ // calibration. Local transcripts only see this machine, so if the closed
796
+ // window was mostly spent elsewhere its price per point comes out far too
797
+ // small and any live spend divides to hundreds of percent. Capping that at
798
+ // 100 would report a full budget to someone sitting at half, which is worse
799
+ // than admitting the reading cannot be rebuilt.
800
+ if (raw > SATURATION_LIMIT) return null;
789
801
 
790
802
  return {
791
- percentUsed: Math.min(100, Math.round(live.cost / usdPerPercent)),
803
+ percentUsed: Math.min(100, Math.round(raw)),
792
804
  usdPerPercent,
793
805
  spentUSD: live.cost,
794
806
  turns: live.turns,
@@ -1236,6 +1248,7 @@ module.exports = {
1236
1248
  readEvents,
1237
1249
  buildWindow,
1238
1250
  reconstructWindow,
1251
+ SATURATION_LIMIT,
1239
1252
  buildWindows,
1240
1253
  bindingWindow,
1241
1254
  dominantEffort,