claude-usage-limits 1.18.0 → 1.19.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.18.0",
4
+ "version": "1.19.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.18.0",
3
+ "version": "1.19.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-usage-limits",
3
- "version": "1.18.0",
3
+ "version": "1.19.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",
@@ -2160,6 +2160,85 @@ function criticalOthers(windows, bindingKey, threshold) {
2160
2160
  );
2161
2161
  }
2162
2162
 
2163
+ // What effort is ACTUALLY running, and how we know.
2164
+ //
2165
+ // This looked like a plugin bug and is not one. Ultracode is not a separate
2166
+ // effort level: Claude Code's own words are "ultracode: xhigh + dynamic
2167
+ // workflow orchestration (this session only)". So a session in ultracode has
2168
+ // effortLevel xhigh in settings.json and writes effort "xhigh" into every
2169
+ // transcript line - both correct, and both unable to say which MODE is on.
2170
+ // "This session only" means it is never written to disk at all.
2171
+ //
2172
+ // So the honest answer is a chain, and every reading says where it came from,
2173
+ // because "xhigh" from a stale settings file and "xhigh" from a live override
2174
+ // deserve different confidence:
2175
+ //
2176
+ // 1. CLAUDE_CODE_EFFORT_LEVEL - Claude Code's own session override. Hooks
2177
+ // inherit the environment, so this is live and authoritative.
2178
+ // 2. An override recorded here by hand, for the one case nothing can see:
2179
+ // /effort ultracode typed in the UI, which touches neither disk nor env.
2180
+ // 3. settings.effortLevel - the persisted default.
2181
+ // 4. The transcript - stamped at session start and never re-stamped.
2182
+ const EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'ultracode'];
2183
+
2184
+ function effortOverrideFile() {
2185
+ return path.join(configDir(), 'usage-limits-effort.json');
2186
+ }
2187
+
2188
+ function readEffortOverride() {
2189
+ try {
2190
+ const parsed = JSON.parse(fs.readFileSync(effortOverrideFile(), 'utf8'));
2191
+ if (!parsed || !EFFORT_LEVELS.includes(parsed.effort)) return null;
2192
+ // A hand-set override describes one session. Left lying around it would
2193
+ // outlive the session it was true for, so it expires.
2194
+ if (!Number.isFinite(parsed.at) || Date.now() - parsed.at > 12 * 60 * 60 * 1000) return null;
2195
+ return parsed;
2196
+ } catch (err) {
2197
+ return null;
2198
+ }
2199
+ }
2200
+
2201
+ function writeEffortOverride(level, now) {
2202
+ if (level === null) {
2203
+ try {
2204
+ fs.unlinkSync(effortOverrideFile());
2205
+ } catch (err) {
2206
+ // Already gone is the outcome asked for.
2207
+ }
2208
+ return { ok: true, cleared: true };
2209
+ }
2210
+ if (!EFFORT_LEVELS.includes(level)) return { ok: false, error: 'effort must be one of: ' + EFFORT_LEVELS.join(', ') };
2211
+ try {
2212
+ fs.mkdirSync(configDir(), { recursive: true });
2213
+ writeJsonAtomic(effortOverrideFile(), { effort: level, at: Number.isFinite(now) ? now : Date.now() });
2214
+ return { ok: true, effort: level };
2215
+ } catch (err) {
2216
+ return { ok: false, error: err.message };
2217
+ }
2218
+ }
2219
+
2220
+ function effortNow(sessionId, env) {
2221
+ const environment = env || process.env;
2222
+ const fromEnv = String(environment.CLAUDE_CODE_EFFORT_LEVEL || '').trim().toLowerCase();
2223
+ if (EFFORT_LEVELS.includes(fromEnv)) return { effort: fromEnv, source: 'environment', live: true };
2224
+
2225
+ const override = readEffortOverride();
2226
+ if (override) return { effort: override.effort, source: 'set by hand', live: true };
2227
+
2228
+ let configured = null;
2229
+ try {
2230
+ configured = collect(Date.now()).settings;
2231
+ } catch (err) {
2232
+ configured = null;
2233
+ }
2234
+ const level = configured && configured.effortLevel;
2235
+ if (EFFORT_LEVELS.includes(level)) return { effort: level, source: 'settings', live: false };
2236
+
2237
+ const seen = liveEffort(sessionId);
2238
+ if (seen && EFFORT_LEVELS.includes(seen.effort)) return { effort: seen.effort, source: 'transcript', live: false };
2239
+ return null;
2240
+ }
2241
+
2163
2242
  // Is the setting bigger than the work needs?
2164
2243
  //
2165
2244
  // Everything else here is budget-triggered: it speaks when a window is filling.
@@ -3794,6 +3873,42 @@ async function main(argv) {
3794
3873
  }
3795
3874
  return 0;
3796
3875
  }
3876
+ // `--effort ultracode` records what nothing on the machine can see: the
3877
+ // picker's choice is session-only and touches neither disk nor environment.
3878
+ // `--effort clear` forgets it again.
3879
+ const effortAt = argv.indexOf('--effort');
3880
+ if (effortAt !== -1) {
3881
+ const wanted = String(argv[effortAt + 1] || '').trim().toLowerCase();
3882
+ const say = (text) => {
3883
+ process.stdout.write(text + '\n');
3884
+ return 0;
3885
+ };
3886
+ if (!wanted) {
3887
+ const found = effortNow();
3888
+ if (!found) return say('No effort reading available.');
3889
+ const guess = found.source === 'settings' || found.source === 'transcript';
3890
+ return say(
3891
+ 'Effort reads as ' + found.effort + ' (' + found.source + ').' +
3892
+ (guess
3893
+ ? '\nIf you picked ultracode in the session, nothing on disk says so - Claude Code calls it' +
3894
+ ' "xhigh + dynamic workflow orchestration (this session only)".' +
3895
+ ' Tell this: usage.js --effort ultracode'
3896
+ : '')
3897
+ );
3898
+ }
3899
+ if (wanted === 'clear' || wanted === 'off') {
3900
+ writeEffortOverride(null);
3901
+ return say('Effort override cleared; back to the environment, then settings.');
3902
+ }
3903
+ const done = writeEffortOverride(wanted, Date.now());
3904
+ return say(
3905
+ done.ok
3906
+ ? 'Effort recorded as ' + done.effort +
3907
+ '. It expires after twelve hours, and CLAUDE_CODE_EFFORT_LEVEL still wins.'
3908
+ : done.error
3909
+ );
3910
+ }
3911
+
3797
3912
  const recommendAt = argv.indexOf('--recommend');
3798
3913
  if (recommendAt !== -1) {
3799
3914
  // The turn count is optional: with one the verdict is about that job,
@@ -3886,6 +4001,11 @@ module.exports = {
3886
4001
  criticalOthers,
3887
4002
  escapeRoute,
3888
4003
  settingFit,
4004
+ effortNow,
4005
+ readEffortOverride,
4006
+ writeEffortOverride,
4007
+ effortOverrideFile,
4008
+ EFFORT_LEVELS,
3889
4009
  levers,
3890
4010
  betterCalibration,
3891
4011
  calibrationForPlan,