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.
- package/README.md +60 -11
- package/bin/cli.js +85 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
# claude-limit-statusline
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/claude-limit-statusline)
|
|
4
|
+
[](https://www.npmjs.com/package/claude-limit-statusline)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](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
|
|
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
|
|
50
|
+
Want a different size? Pass your display options straight through:
|
|
40
51
|
|
|
41
52
|
```bash
|
|
42
|
-
cc-limits --install --
|
|
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
|
-
|
|
|
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
|
|
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
|
-
|
|
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"
|
|
109
|
-
"command": "cc-limits --no-reset"
|
|
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` |
|
|
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 --
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
137
|
-
|
|
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)
|
|
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,
|
|
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} ${
|
|
173
|
+
return paint(`${icon} ${lbl} --`, C.dim);
|
|
152
174
|
}
|
|
153
175
|
const p = round(limit.used_percentage);
|
|
154
|
-
let s = `${icon} ${
|
|
155
|
-
if (limit.resets_at > 0 &&
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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")
|
|
172
|
-
|
|
173
|
-
else if (seg === "
|
|
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(
|
|
176
|
-
|
|
177
|
-
label: "Session",
|
|
178
|
-
|
|
179
|
-
|
|
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(
|
|
185
|
-
|
|
186
|
-
label: "Week",
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
227
|
-
" cc-limits --install --
|
|
228
|
-
" cc-limits --uninstall
|
|
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
|
|
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
|
-
|
|
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.
|
|
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": {
|