claude-simple-status 1.0.1 → 1.1.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 +21 -2
- package/package.json +2 -2
- package/statusline.mjs +51 -32
package/README.md
CHANGED
|
@@ -19,7 +19,8 @@ A simple, no-frills statusline for [Claude Code](https://docs.anthropic.com/en/d
|
|
|
19
19
|
- **Cross-platform** — works on macOS, Linux, and Windows
|
|
20
20
|
- **Non-blocking** — returns cached data instantly, refreshes quota in the background
|
|
21
21
|
- **Color-coded** — green/orange/red percentages at a glance
|
|
22
|
-
- **
|
|
22
|
+
- **Stale-aware** — shows `--` for quota values when cache is outdated, real values appear after first refresh
|
|
23
|
+
- **Git-aware** — shows the current branch name in repos (cached 30s to reduce overhead)
|
|
23
24
|
- **Timezone-smart** — quota reset time converted to your local timezone
|
|
24
25
|
|
|
25
26
|
If the quota API is unreachable, a red `ERR` indicator appears at the end and clears automatically once the connection recovers.
|
|
@@ -32,9 +33,10 @@ npm install -g claude-simple-status
|
|
|
32
33
|
|
|
33
34
|
That's it — Claude Code is configured automatically. The statusline appears immediately.
|
|
34
35
|
|
|
35
|
-
To uninstall
|
|
36
|
+
To uninstall:
|
|
36
37
|
|
|
37
38
|
```bash
|
|
39
|
+
claude-simple-status --uninstall
|
|
38
40
|
npm uninstall -g claude-simple-status
|
|
39
41
|
```
|
|
40
42
|
|
|
@@ -101,6 +103,11 @@ Quota data is cached to the system temp directory and refreshed every 2 minutes.
|
|
|
101
103
|
|
|
102
104
|
## Troubleshooting
|
|
103
105
|
|
|
106
|
+
**Indicators:**
|
|
107
|
+
- `--` for quota values means the cache is stale (>5 minutes old) — values appear after the first background refresh
|
|
108
|
+
- `?` means quota data has never been fetched yet
|
|
109
|
+
- `ERR` (red) means the last quota fetch failed — clears automatically on recovery
|
|
110
|
+
|
|
104
111
|
If the statusline shows `ERR`, check the error log:
|
|
105
112
|
|
|
106
113
|
```bash
|
|
@@ -121,6 +128,18 @@ rm /tmp/claude-statusline-quota.json
|
|
|
121
128
|
Remove-Item $env:TEMP\claude-statusline-quota.json
|
|
122
129
|
```
|
|
123
130
|
|
|
131
|
+
## Related projects
|
|
132
|
+
|
|
133
|
+
### [claude-rig](https://github.com/edimuj/claude-rig)
|
|
134
|
+
|
|
135
|
+
Run multiple isolated Claude Code configurations simultaneously — each with its own plugins, skills, MCP servers, and settings. When a session is launched through claude-rig, the active profile name appears in the statusline in bold magenta as the first segment:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
minimal | main | Opus 4.6 | 12% | 14:30 | 5h:34% | 7d:12%
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
No configuration needed — claude-simple-status detects claude-rig automatically. Users not using claude-rig are unaffected.
|
|
142
|
+
|
|
124
143
|
## Contributing
|
|
125
144
|
|
|
126
145
|
Contributions are welcome! This project follows a few principles:
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-simple-status",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A simple statusline for Claude Code — git branch, model, context usage, and quota at a glance",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"claude-simple-status": "
|
|
7
|
+
"claude-simple-status": "statusline.mjs"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"postinstall": "node scripts/setup.mjs install",
|
package/statusline.mjs
CHANGED
|
@@ -2,17 +2,35 @@
|
|
|
2
2
|
// Claude Code Statusline - Shows Branch | Model | Context % | Next Reset | 5h Quota % | 7d Quota %
|
|
3
3
|
// Cross-platform Node.js version (no dependencies)
|
|
4
4
|
|
|
5
|
-
import { readFileSync, writeFileSync, mkdirSync, rmdirSync, statSync, existsSync
|
|
5
|
+
import { readFileSync, writeFileSync, mkdirSync, rmdirSync, statSync, existsSync } from 'fs';
|
|
6
6
|
import { homedir, tmpdir } from 'os';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { spawn, execSync } from 'child_process';
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
// Handle --uninstall flag (workaround: npm doesn't run preuninstall for global packages)
|
|
11
|
+
if (process.argv.includes('--uninstall')) {
|
|
12
|
+
const settingsFile = join(homedir(), '.claude', 'settings.json');
|
|
13
|
+
try {
|
|
14
|
+
const settings = JSON.parse(readFileSync(settingsFile, 'utf8'));
|
|
15
|
+
if (settings.statusLine?.command === 'claude-simple-status') {
|
|
16
|
+
delete settings.statusLine;
|
|
17
|
+
writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n');
|
|
18
|
+
console.log('claude-simple-status removed from Claude Code settings.');
|
|
19
|
+
} else {
|
|
20
|
+
console.log('Nothing to remove (statusLine not managed by claude-simple-status).');
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
console.log('Nothing to remove (~/.claude/settings.json not found).');
|
|
24
|
+
}
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
10
27
|
|
|
11
28
|
// ANSI color codes
|
|
12
29
|
const GREEN = '\x1b[0;32m';
|
|
13
30
|
const ORANGE = '\x1b[0;33m';
|
|
14
31
|
const RED = '\x1b[0;31m';
|
|
15
32
|
const CYAN = '\x1b[0;36m';
|
|
33
|
+
const MAGENTA_BOLD = '\x1b[1;35m';
|
|
16
34
|
const YELLOW_BOLD = '\x1b[1;33m';
|
|
17
35
|
const RESET = '\x1b[0m';
|
|
18
36
|
|
|
@@ -22,7 +40,10 @@ const CACHE_FILE = join(tmpdir(), 'claude-statusline-quota.json');
|
|
|
22
40
|
const LOCK_DIR = join(tmpdir(), 'claude-statusline-quota.lock');
|
|
23
41
|
const ERROR_FILE = join(tmpdir(), 'claude-statusline-error');
|
|
24
42
|
const LOG_FILE = join(tmpdir(), 'claude-statusline.log');
|
|
25
|
-
const CACHE_MAX_AGE = 120; // seconds
|
|
43
|
+
const CACHE_MAX_AGE = 120; // seconds - when to fetch
|
|
44
|
+
const CACHE_STALE_AGE = 300; // seconds - when to show "--" instead of old values
|
|
45
|
+
const GIT_BRANCH_CACHE = join(tmpdir(), 'claude-statusline-branches.json');
|
|
46
|
+
const GIT_BRANCH_MAX_AGE = 30; // seconds
|
|
26
47
|
|
|
27
48
|
// Color a percentage value based on thresholds
|
|
28
49
|
function colorPct(val) {
|
|
@@ -71,30 +92,6 @@ function acquireLock() {
|
|
|
71
92
|
}
|
|
72
93
|
}
|
|
73
94
|
|
|
74
|
-
// Release lock
|
|
75
|
-
function releaseLock() {
|
|
76
|
-
try { rmdirSync(LOCK_DIR); } catch {}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Log error with timestamp
|
|
80
|
-
function logError(msg) {
|
|
81
|
-
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
82
|
-
try {
|
|
83
|
-
appendFileSync(LOG_FILE, `[${ts}] ${msg}\n`);
|
|
84
|
-
writeFileSync(ERROR_FILE, msg);
|
|
85
|
-
// Trim log to last 50 lines
|
|
86
|
-
const lines = readFileSync(LOG_FILE, 'utf8').split('\n').filter(Boolean);
|
|
87
|
-
if (lines.length > 50) {
|
|
88
|
-
writeFileSync(LOG_FILE, lines.slice(-50).join('\n') + '\n');
|
|
89
|
-
}
|
|
90
|
-
} catch {}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Clear error state
|
|
94
|
-
function clearError() {
|
|
95
|
-
try { writeFileSync(ERROR_FILE, ''); } catch {}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
95
|
// Spawn background refresh process
|
|
99
96
|
function refreshInBackground(token) {
|
|
100
97
|
const child = spawn(process.execPath, [
|
|
@@ -169,13 +166,22 @@ function toLocalTime(isoString) {
|
|
|
169
166
|
}
|
|
170
167
|
}
|
|
171
168
|
|
|
172
|
-
// Get current git branch name
|
|
169
|
+
// Get current git branch name (cached per cwd, 30s TTL)
|
|
173
170
|
function getGitBranch() {
|
|
171
|
+
const cwd = process.cwd();
|
|
174
172
|
try {
|
|
175
|
-
|
|
173
|
+
const cache = readJsonFile(GIT_BRANCH_CACHE) || {};
|
|
174
|
+
const entry = cache[cwd];
|
|
175
|
+
if (entry && (Date.now() - entry.ts) < GIT_BRANCH_MAX_AGE * 1000) {
|
|
176
|
+
return entry.branch;
|
|
177
|
+
}
|
|
178
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
176
179
|
timeout: 1000,
|
|
177
180
|
stdio: ['ignore', 'pipe', 'ignore']
|
|
178
181
|
}).toString().trim();
|
|
182
|
+
cache[cwd] = { branch, ts: Date.now() };
|
|
183
|
+
try { writeFileSync(GIT_BRANCH_CACHE, JSON.stringify(cache)); } catch {}
|
|
184
|
+
return branch;
|
|
179
185
|
} catch {
|
|
180
186
|
return null;
|
|
181
187
|
}
|
|
@@ -219,12 +225,17 @@ async function main() {
|
|
|
219
225
|
}
|
|
220
226
|
}
|
|
221
227
|
|
|
222
|
-
// Parse quota data
|
|
228
|
+
// Parse quota data (show "--" if cache is too stale)
|
|
223
229
|
let fiveHourPct = '?';
|
|
224
230
|
let sevenDayPct = '?';
|
|
225
231
|
let resetLocal = '--:--';
|
|
232
|
+
const cacheIsStale = !quotaData || getFileAge(CACHE_FILE) > CACHE_STALE_AGE;
|
|
226
233
|
|
|
227
|
-
if (
|
|
234
|
+
if (cacheIsStale) {
|
|
235
|
+
fiveHourPct = '--';
|
|
236
|
+
sevenDayPct = '--';
|
|
237
|
+
resetLocal = '--:--';
|
|
238
|
+
} else if (quotaData) {
|
|
228
239
|
if (quotaData.five_hour === null || quotaData.seven_day === null) {
|
|
229
240
|
// Organization/team plan without individual quota
|
|
230
241
|
fiveHourPct = 'N/A';
|
|
@@ -244,11 +255,19 @@ async function main() {
|
|
|
244
255
|
hasError = errContent.length > 0;
|
|
245
256
|
} catch {}
|
|
246
257
|
|
|
258
|
+
// Get rig profile (claude-rig sets CLAUDE_CONFIG_DIR to ~/.claude-rig/profiles/<name>)
|
|
259
|
+
const rigProfile = (() => {
|
|
260
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
261
|
+
if (!configDir) return null;
|
|
262
|
+
const match = configDir.match(/\.claude-rig\/profiles\/([^/]+)\/?$/);
|
|
263
|
+
return match ? match[1] : null;
|
|
264
|
+
})();
|
|
265
|
+
|
|
247
266
|
// Get git branch
|
|
248
267
|
const branch = getGitBranch();
|
|
249
268
|
|
|
250
269
|
// Build output
|
|
251
|
-
let output = `${branch ? `${YELLOW_BOLD}${branch}${RESET} | ` : ''}${CYAN}${model}${RESET} | ${colorPct(contextUsed)}`;
|
|
270
|
+
let output = `${rigProfile ? `${MAGENTA_BOLD}${rigProfile}${RESET} | ` : ''}${branch ? `${YELLOW_BOLD}${branch}${RESET} | ` : ''}${CYAN}${model}${RESET} | ${colorPct(contextUsed)}`;
|
|
252
271
|
if (token) {
|
|
253
272
|
output += ` | ${resetLocal} | 5h:${colorPct(fiveHourPct)} | 7d:${colorPct(sevenDayPct)}`;
|
|
254
273
|
if (hasError) {
|