claude-limit-statusline 0.2.0 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +60 -11
  2. package/bin/cli.js +85 -40
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,13 +1,24 @@
1
1
  # claude-limit-statusline
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/claude-limit-statusline.svg)](https://www.npmjs.com/package/claude-limit-statusline)
4
+ [![npm downloads](https://img.shields.io/npm/dm/claude-limit-statusline.svg)](https://www.npmjs.com/package/claude-limit-statusline)
5
+ [![license](https://img.shields.io/npm/l/claude-limit-statusline.svg)](./LICENSE)
6
+ [![node](https://img.shields.io/node/v/claude-limit-statusline.svg)](https://nodejs.org)
7
+
3
8
  A [Claude Code](https://code.claude.com/docs) status line that shows your **real
4
9
  subscription limits** — the 5‑hour session window and the 7‑day weekly window —
5
10
  with a **live reset countdown**.
6
11
 
12
+ 📦 [npm](https://www.npmjs.com/package/claude-limit-statusline) ·
13
+ 🔗 [GitHub](https://github.com/ann0nip/claude-limit-statusline)
14
+
7
15
  ```
8
- 🤖 Opus 4.8 (1M context) | 🧠 42k (4%) | Session 17% · resets in 0h47m (23:12) | 📅 Week 10% · resets in 2d 21h (Jun 03 19:54)
16
+ 🤖 Opus 4.8 | 🧠 42k (4%) | 🕔 Session 17% · resets in 0h47m | 📅 Week 10%
9
17
  ```
10
18
 
19
+ That's the default (`medium`). Pick the [size](#sizes) that fits your terminal —
20
+ from `full` (both reset countdowns) down to `bare` (just the two percentages).
21
+
11
22
  Unlike tools that estimate the 5‑hour block from local logs, this reads the
12
23
  **official `rate_limits` payload** that Claude Code provides on stdin — the same
13
24
  numbers you see when you run `/usage`. No guessing, no rounding to the hour.
@@ -36,10 +47,10 @@ for you (merging, never clobbering your other settings). Then open a **new**
36
47
  Claude Code session and send one message — `rate_limits` populates after the
37
48
  first API response.
38
49
 
39
- Want only the two limits? Pass your display options straight through:
50
+ Want a different size? Pass your display options straight through:
40
51
 
41
52
  ```bash
42
- cc-limits --install --segments=session,week
53
+ cc-limits --install --size=compact
43
54
  ```
44
55
 
45
56
  To remove it again:
@@ -53,6 +64,12 @@ cc-limits --uninstall
53
64
  > installed command isn't on the `PATH` of the non‑login shell Claude Code uses
54
65
  > for the status line.
55
66
 
67
+ > **Upgrading from 0.3.x / 0.2.x?** The old adaptive‑width behavior is gone —
68
+ > replaced by fixed [sizes](#sizes) (default `medium`). Re‑run `cc-limits
69
+ > --install` (optionally with `--size=…`) to refresh the baked‑in command; any
70
+ > leftover `--width` / `--no-adapt` flags are now ignored. The session icon also
71
+ > changed from ⏳ to 🕔.
72
+
56
73
  <details>
57
74
  <summary>Manual setup (if you prefer)</summary>
58
75
 
@@ -79,15 +96,44 @@ If the bar stays blank (nvm/Volta `PATH` issue), use absolute paths instead —
79
96
  | --- | --- | --- |
80
97
  | `🤖 model` | Active model | local |
81
98
  | `🧠 42k (4%)` | Tokens in the current context window | local |
82
- | `⏳ Session 17% · resets in 0h47m (23:12)` | **Real** 5‑hour limit used + reset | server |
83
- | `📅 Week 10% · resets in 2d 21h (Jun 03 19:54)` | **Real** 7‑day limit used + reset | server |
99
+ | `🕔 Session 17% · resets in 0h47m (23:12)` | **Real** 5‑hour limit used (+ reset) | server |
100
+ | `📅 Week 10% · resets in 2d 21h (Jun 03 19:54)` | **Real** 7‑day limit used (+ reset) | server |
84
101
 
85
102
  The percentage **is** your "how close am I to the limit" gauge. Subscription
86
103
  limits are dynamic, so Anthropic does not expose a fixed token cap — only a
87
104
  percentage, which is exactly what this surfaces.
88
105
 
89
106
  Before the first API response (and right after `/compact`) the session segment
90
- shows `⏳ Session --` until fresh data arrives.
107
+ shows `🕔 Session --` until fresh data arrives.
108
+
109
+ ## Sizes
110
+
111
+ Claude Code shows the status line on a single line and truncates anything past
112
+ the terminal width. So instead of guessing, you **pick the size that fits** —
113
+ set it once with `--size` (or `CC_LIMITS_SIZE`). The default is `medium`.
114
+
115
+ ```text
116
+ full 🤖 Opus 4.8 (1M context) | 🧠 42k (4%) | 🕔 Session 17% · resets in 0h47m (23:12) | 📅 Week 10% · resets in 2d 21h (Jun 03 19:54)
117
+ medium 🤖 Opus 4.8 | 🧠 42k (4%) | 🕔 Session 17% · resets in 0h47m | 📅 Week 10%
118
+ compact 🤖 Opus 4.8 | 🧠 42k (4%) | 🕔 Session 17% | 📅 Week 10%
119
+ mini 🤖 Opus 4.8 | 🧠 42k | 🕔 S 17% | 📅 W 10%
120
+ bare 🕔 S 17% | 📅 W 10%
121
+ ```
122
+
123
+ | Size | Shows |
124
+ | --- | --- |
125
+ | `full` | Model (with context length), tokens + %, both limits with reset countdown & clock |
126
+ | `medium` | Model, tokens + %, session countdown, both percentages *(default)* |
127
+ | `compact` | Model, tokens + %, both percentages — no countdowns |
128
+ | `mini` | Model, tokens, short labels |
129
+ | `bare` | Just the two limits |
130
+
131
+ ```bash
132
+ cc-limits --install --size=compact
133
+ ```
134
+
135
+ You can still fine‑tune any size with [`--segments`](#flags) and
136
+ [`--reset`](#flags) below.
91
137
 
92
138
  ## Configuration
93
139
 
@@ -102,20 +148,22 @@ Pick **which segments** to show (and their order). The four segments are
102
148
  "command": "cc-limits --no-context"
103
149
  ```
104
150
 
105
- Pick **which reset countdowns** to show:
151
+ Cap **which reset countdowns** a size may show (this can hide a countdown,
152
+ never add one):
106
153
 
107
154
  ```jsonc
108
- "command": "cc-limits --reset=session" // session reset only
109
- "command": "cc-limits --no-reset" // just percentages, no countdowns
155
+ "command": "cc-limits --size=full --reset=session" // full, but no week countdown
156
+ "command": "cc-limits --no-reset" // just percentages, no countdowns
110
157
  ```
111
158
 
112
159
  ### Flags
113
160
 
114
161
  | Flag | Description |
115
162
  | --- | --- |
163
+ | `--size=full\|medium\|compact\|mini\|bare` | How much detail to show (default `medium`) |
116
164
  | `--segments=a,b,c` | Allowlist + order. Subset of `model,context,session,week` |
117
165
  | `--no-<segment>` | Hide one segment (e.g. `--no-context`). Repeatable |
118
- | `--reset=both\|session\|week\|none` | Which reset countdowns to show (default `both`) |
166
+ | `--reset=both\|session\|week\|none` | Cap which reset countdowns may show (default `both`) |
119
167
  | `--no-reset` | Shorthand for `--reset=none` |
120
168
  | `--no-color` | Disable ANSI colors |
121
169
  | `--demo` | Print a sample line (no stdin needed) |
@@ -127,6 +175,7 @@ Equivalent to the flags, handy if you don't want to edit the command string:
127
175
 
128
176
  | Env var | Default | Description |
129
177
  | --- | --- | --- |
178
+ | `CC_LIMITS_SIZE` | `medium` | `full` / `medium` / `compact` / `mini` / `bare` |
130
179
  | `CC_LIMITS_SEGMENTS` | `model,context,session,week` | Segments + order |
131
180
  | `CC_LIMITS_RESET` | `both` | `both` / `session` / `week` / `none` |
132
181
  | `CC_LIMITS_WARN` | `70` | % at/above which a limit turns yellow |
@@ -136,7 +185,7 @@ Equivalent to the flags, handy if you don't want to edit the command string:
136
185
 
137
186
  ```bash
138
187
  cc-limits --demo
139
- cc-limits --segments=session,week --reset=session --demo
188
+ cc-limits --size=compact --demo
140
189
  ```
141
190
 
142
191
  ## How it works
package/bin/cli.js CHANGED
@@ -57,14 +57,26 @@ if (segSel != null && segSel !== "") {
57
57
  SEGMENTS = ALL_SEGMENTS.filter((s) => !argv.includes(`--no-${s}`));
58
58
  }
59
59
 
60
- // Which reset countdowns to show: both | session | week | none.
60
+ // Size preset: how much detail to show. The user picks the one that fits their
61
+ // terminal — predictable, no width guessing. Default "medium".
62
+ const SIZES = ["full", "medium", "compact", "mini", "bare"];
63
+ let SIZE = (
64
+ getFlagValue("--size") ||
65
+ process.env.CC_LIMITS_SIZE ||
66
+ "medium"
67
+ ).toLowerCase();
68
+ if (!SIZES.includes(SIZE)) SIZE = "medium";
69
+
70
+ // Which reset countdowns are *allowed*: both | session | week | none. This only
71
+ // caps what a preset would show (it can hide a countdown, never add one).
72
+ // Default both (no extra cap on top of the chosen preset).
61
73
  let RESET_MODE = (
62
74
  getFlagValue("--reset") ||
63
75
  process.env.CC_LIMITS_RESET ||
64
76
  "both"
65
77
  ).toLowerCase();
66
78
  if (argv.includes("--no-reset")) RESET_MODE = "none";
67
- function showReset(which) {
79
+ function resetAllowed(which) {
68
80
  return RESET_MODE === "both" || RESET_MODE === which;
69
81
  }
70
82
 
@@ -133,65 +145,87 @@ function fmtClock(epochSec, withDate) {
133
145
  }
134
146
 
135
147
  // ---------- segment renderers ----------
136
- function renderModel(data) {
137
- const model = clean(data?.model?.display_name || "Claude");
148
+ // A `v` (variant) describes how compact to be:
149
+ // v.model "full" = name as-is | "trim" = drop "(…)" suffix | "off" = hide
150
+ // v.ctx "pct" = "🧠 172k (17%)" | "tokens" = "🧠 172k" | "off" = hide
151
+ // v.short true => short limit labels (Session→S, Week→W)
152
+ // v.rs/v.rw reset detail for session/week: 2 = "…Xd Yh (clock)", 1 = "…Xd Yh", 0 = none
153
+ function renderModel(data, v) {
154
+ let model = clean(data?.model?.display_name || "Claude");
155
+ // Drop trailing parenthetical (e.g. "Opus 4.8 (1M context)" → "Opus 4.8").
156
+ if (v.model === "trim") model = model.replace(/\s*\([^)]*\)\s*$/, "").trim();
138
157
  return paint("🤖 " + model, C.cyan);
139
158
  }
140
- function renderContext(data) {
159
+ function renderContext(data, v) {
141
160
  const cw = data?.context_window || {};
142
161
  const tokens =
143
162
  (Number(cw.total_input_tokens) || 0) +
144
163
  (Number(cw.total_output_tokens) || 0);
145
164
  let s = "🧠 " + humanTokens(tokens);
146
- if (cw.used_percentage != null) s += ` (${round(cw.used_percentage)}%)`;
165
+ if (v.ctx === "pct" && cw.used_percentage != null) {
166
+ s += ` (${round(cw.used_percentage)}%)`;
167
+ }
147
168
  return paint(s, C.gray);
148
169
  }
149
- function renderLimit(limit, { icon, label, which, withDate }) {
170
+ function renderLimit(limit, { icon, label, shortLabel, withDate }, { short, reset }) {
171
+ const lbl = short ? shortLabel : label;
150
172
  if (!limit || limit.used_percentage == null) {
151
- return paint(`${icon} ${label} --`, C.dim);
173
+ return paint(`${icon} ${lbl} --`, C.dim);
152
174
  }
153
175
  const p = round(limit.used_percentage);
154
- let s = `${icon} ${label} ${paint(p + "%", pctColor(p))}`;
155
- if (limit.resets_at > 0 && showReset(which)) {
156
- s += paint(
157
- ` · resets in ${fmtCountdown(limit.resets_at)} (${fmtClock(
158
- limit.resets_at,
159
- withDate
160
- )})`,
161
- C.dim
162
- );
176
+ let s = `${icon} ${lbl} ${paint(p + "%", pctColor(p))}`;
177
+ if (limit.resets_at > 0 && reset > 0) {
178
+ const clock = reset >= 2 ? ` (${fmtClock(limit.resets_at, withDate)})` : "";
179
+ s += paint(` · resets in ${fmtCountdown(limit.resets_at)}${clock}`, C.dim);
163
180
  }
164
181
  return s;
165
182
  }
166
183
 
167
- function render(data) {
184
+ // Build one status line at a given variant. Reset levels (rs/rw) are also
185
+ // capped by the user's --reset choice (it can hide a countdown, never add one).
186
+ function buildLine(data, v) {
168
187
  const rl = data?.rate_limits || {};
188
+ const cap = (which, level) => (resetAllowed(which) ? level : 0);
169
189
  const out = [];
170
190
  for (const seg of SEGMENTS) {
171
- if (seg === "model") out.push(renderModel(data));
172
- else if (seg === "context") out.push(renderContext(data));
173
- else if (seg === "session")
191
+ if (seg === "model") {
192
+ if (v.model !== "off") out.push(renderModel(data, v));
193
+ } else if (seg === "context") {
194
+ if (v.ctx !== "off") out.push(renderContext(data, v));
195
+ } else if (seg === "session") {
174
196
  out.push(
175
- renderLimit(rl.five_hour, {
176
- icon: "⏳",
177
- label: "Session",
178
- which: "session",
179
- withDate: false,
180
- })
197
+ renderLimit(
198
+ rl.five_hour,
199
+ { icon: "🕔", label: "Session", shortLabel: "S", withDate: false },
200
+ { short: v.short, reset: cap("session", v.rs) }
201
+ )
181
202
  );
182
- else if (seg === "week")
203
+ } else if (seg === "week") {
183
204
  out.push(
184
- renderLimit(rl.seven_day, {
185
- icon: "📅",
186
- label: "Week",
187
- which: "week",
188
- withDate: true,
189
- })
205
+ renderLimit(
206
+ rl.seven_day,
207
+ { icon: "📅", label: "Week", shortLabel: "W", withDate: true },
208
+ { short: v.short, reset: cap("week", v.rw) }
209
+ )
190
210
  );
211
+ }
191
212
  }
192
213
  return out.join(SEP);
193
214
  }
194
215
 
216
+ // Size presets, richest → smallest. The user picks one with --size / CC_LIMITS_SIZE.
217
+ const PRESETS = {
218
+ full: { model: "full", ctx: "pct", short: false, rs: 2, rw: 2 },
219
+ medium: { model: "trim", ctx: "pct", short: false, rs: 1, rw: 0 },
220
+ compact: { model: "trim", ctx: "pct", short: false, rs: 0, rw: 0 },
221
+ mini: { model: "trim", ctx: "tokens", short: true, rs: 0, rw: 0 },
222
+ bare: { model: "off", ctx: "off", short: true, rs: 0, rw: 0 },
223
+ };
224
+
225
+ function render(data) {
226
+ return buildLine(data, PRESETS[SIZE] || PRESETS.medium);
227
+ }
228
+
195
229
  // ---------- demo payload ----------
196
230
  function demoPayload() {
197
231
  const now = Math.floor(Date.now() / 1000);
@@ -223,25 +257,33 @@ if (argv.includes("--help") || argv.includes("-h")) {
223
257
  "real Pro/Max rate limits (5h session + 7d week) with reset countdowns.",
224
258
  "",
225
259
  "Setup (writes ~/.claude/settings.json for you):",
226
- " cc-limits --install configure the status line",
227
- " cc-limits --install --segments=session,week ...with display options",
228
- " cc-limits --uninstall remove it again",
260
+ " cc-limits --install configure the status line",
261
+ " cc-limits --install --size=compact ...at a chosen size",
262
+ " cc-limits --uninstall remove it again",
229
263
  "",
230
264
  "Or set it manually in ~/.claude/settings.json:",
231
265
  ' "statusLine": { "type": "command", "command": "cc-limits" }',
232
266
  "",
267
+ "Size (pick the one that fits your terminal — default medium):",
268
+ " --size=full 🤖 Opus 4.8 (1M context) | 🧠 172k (17%) | 🕔 Session 14% · resets in 2h55m (04:00) | 📅 Week 12% · resets…",
269
+ " --size=medium 🤖 Opus 4.8 | 🧠 172k (17%) | 🕔 Session 14% · resets in 2h55m | 📅 Week 12%",
270
+ " --size=compact 🤖 Opus 4.8 | 🧠 172k (17%) | 🕔 Session 14% | 📅 Week 12%",
271
+ " --size=mini 🤖 Opus 4.8 | 🧠 172k | 🕔 S 14% | 📅 W 12%",
272
+ " --size=bare 🕔 S 14% | 📅 W 12%",
273
+ "",
233
274
  "Segments (default: all, in this order): model, context, session, week",
234
275
  " --segments=session,week Show only these, in this order",
235
276
  " --no-context Hide a single segment (repeatable)",
236
277
  " --no-model --no-week ...",
237
278
  "",
238
- "Reset countdowns:",
239
- " --reset=both|session|week|none Which resets to show (default both)",
279
+ "Reset countdowns (a preset's countdowns can be hidden, never added):",
280
+ " --reset=both|session|week|none Which resets MAY show (default both)",
240
281
  " --no-reset Same as --reset=none",
241
282
  "",
242
283
  "Other flags: --demo, --no-color, -h/--help",
243
284
  "",
244
285
  "Env vars:",
286
+ " CC_LIMITS_SIZE=full|medium|compact|mini|bare",
245
287
  " CC_LIMITS_SEGMENTS=model,context,session,week",
246
288
  " CC_LIMITS_RESET=both|session|week|none",
247
289
  " CC_LIMITS_WARN=70 yellow threshold (% of a limit)",
@@ -307,7 +349,10 @@ function doUninstall() {
307
349
  }
308
350
 
309
351
  if (argv.includes("--install")) {
310
- const passthrough = argv.filter((a) => a !== "--install" && a !== "--uninstall");
352
+ // Bake only persistent display flags into the command never the one-shot
353
+ // ones (--install/--demo/--help) or the status line would break.
354
+ const TRANSIENT = new Set(["--install", "--uninstall", "--demo", "--help", "-h"]);
355
+ const passthrough = argv.filter((a) => !TRANSIENT.has(a));
311
356
  doInstall(passthrough);
312
357
  process.exit(0);
313
358
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-limit-statusline",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Claude Code status line that shows your REAL Pro/Max subscription limits — 5-hour session and 7-day weekly usage with a live reset countdown. Uses the official rate_limits payload, not a local estimate.",
5
5
  "type": "commonjs",
6
6
  "bin": {