claude-simple-status 1.0.2 → 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 +19 -1
- package/package.json +1 -1
- package/statusline.mjs +33 -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.
|
|
@@ -102,6 +103,11 @@ Quota data is cached to the system temp directory and refreshed every 2 minutes.
|
|
|
102
103
|
|
|
103
104
|
## Troubleshooting
|
|
104
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
|
+
|
|
105
111
|
If the statusline shows `ERR`, check the error log:
|
|
106
112
|
|
|
107
113
|
```bash
|
|
@@ -122,6 +128,18 @@ rm /tmp/claude-statusline-quota.json
|
|
|
122
128
|
Remove-Item $env:TEMP\claude-statusline-quota.json
|
|
123
129
|
```
|
|
124
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
|
+
|
|
125
143
|
## Contributing
|
|
126
144
|
|
|
127
145
|
Contributions are welcome! This project follows a few principles:
|
package/package.json
CHANGED
package/statusline.mjs
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
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
|
-
import { request } from 'https';
|
|
10
9
|
|
|
11
10
|
// Handle --uninstall flag (workaround: npm doesn't run preuninstall for global packages)
|
|
12
11
|
if (process.argv.includes('--uninstall')) {
|
|
@@ -31,6 +30,7 @@ const GREEN = '\x1b[0;32m';
|
|
|
31
30
|
const ORANGE = '\x1b[0;33m';
|
|
32
31
|
const RED = '\x1b[0;31m';
|
|
33
32
|
const CYAN = '\x1b[0;36m';
|
|
33
|
+
const MAGENTA_BOLD = '\x1b[1;35m';
|
|
34
34
|
const YELLOW_BOLD = '\x1b[1;33m';
|
|
35
35
|
const RESET = '\x1b[0m';
|
|
36
36
|
|
|
@@ -40,7 +40,10 @@ const CACHE_FILE = join(tmpdir(), 'claude-statusline-quota.json');
|
|
|
40
40
|
const LOCK_DIR = join(tmpdir(), 'claude-statusline-quota.lock');
|
|
41
41
|
const ERROR_FILE = join(tmpdir(), 'claude-statusline-error');
|
|
42
42
|
const LOG_FILE = join(tmpdir(), 'claude-statusline.log');
|
|
43
|
-
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
|
|
44
47
|
|
|
45
48
|
// Color a percentage value based on thresholds
|
|
46
49
|
function colorPct(val) {
|
|
@@ -89,30 +92,6 @@ function acquireLock() {
|
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
// Release lock
|
|
93
|
-
function releaseLock() {
|
|
94
|
-
try { rmdirSync(LOCK_DIR); } catch {}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Log error with timestamp
|
|
98
|
-
function logError(msg) {
|
|
99
|
-
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
100
|
-
try {
|
|
101
|
-
appendFileSync(LOG_FILE, `[${ts}] ${msg}\n`);
|
|
102
|
-
writeFileSync(ERROR_FILE, msg);
|
|
103
|
-
// Trim log to last 50 lines
|
|
104
|
-
const lines = readFileSync(LOG_FILE, 'utf8').split('\n').filter(Boolean);
|
|
105
|
-
if (lines.length > 50) {
|
|
106
|
-
writeFileSync(LOG_FILE, lines.slice(-50).join('\n') + '\n');
|
|
107
|
-
}
|
|
108
|
-
} catch {}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Clear error state
|
|
112
|
-
function clearError() {
|
|
113
|
-
try { writeFileSync(ERROR_FILE, ''); } catch {}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
95
|
// Spawn background refresh process
|
|
117
96
|
function refreshInBackground(token) {
|
|
118
97
|
const child = spawn(process.execPath, [
|
|
@@ -187,13 +166,22 @@ function toLocalTime(isoString) {
|
|
|
187
166
|
}
|
|
188
167
|
}
|
|
189
168
|
|
|
190
|
-
// Get current git branch name
|
|
169
|
+
// Get current git branch name (cached per cwd, 30s TTL)
|
|
191
170
|
function getGitBranch() {
|
|
171
|
+
const cwd = process.cwd();
|
|
192
172
|
try {
|
|
193
|
-
|
|
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', {
|
|
194
179
|
timeout: 1000,
|
|
195
180
|
stdio: ['ignore', 'pipe', 'ignore']
|
|
196
181
|
}).toString().trim();
|
|
182
|
+
cache[cwd] = { branch, ts: Date.now() };
|
|
183
|
+
try { writeFileSync(GIT_BRANCH_CACHE, JSON.stringify(cache)); } catch {}
|
|
184
|
+
return branch;
|
|
197
185
|
} catch {
|
|
198
186
|
return null;
|
|
199
187
|
}
|
|
@@ -237,12 +225,17 @@ async function main() {
|
|
|
237
225
|
}
|
|
238
226
|
}
|
|
239
227
|
|
|
240
|
-
// Parse quota data
|
|
228
|
+
// Parse quota data (show "--" if cache is too stale)
|
|
241
229
|
let fiveHourPct = '?';
|
|
242
230
|
let sevenDayPct = '?';
|
|
243
231
|
let resetLocal = '--:--';
|
|
232
|
+
const cacheIsStale = !quotaData || getFileAge(CACHE_FILE) > CACHE_STALE_AGE;
|
|
244
233
|
|
|
245
|
-
if (
|
|
234
|
+
if (cacheIsStale) {
|
|
235
|
+
fiveHourPct = '--';
|
|
236
|
+
sevenDayPct = '--';
|
|
237
|
+
resetLocal = '--:--';
|
|
238
|
+
} else if (quotaData) {
|
|
246
239
|
if (quotaData.five_hour === null || quotaData.seven_day === null) {
|
|
247
240
|
// Organization/team plan without individual quota
|
|
248
241
|
fiveHourPct = 'N/A';
|
|
@@ -262,11 +255,19 @@ async function main() {
|
|
|
262
255
|
hasError = errContent.length > 0;
|
|
263
256
|
} catch {}
|
|
264
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
|
+
|
|
265
266
|
// Get git branch
|
|
266
267
|
const branch = getGitBranch();
|
|
267
268
|
|
|
268
269
|
// Build output
|
|
269
|
-
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)}`;
|
|
270
271
|
if (token) {
|
|
271
272
|
output += ` | ${resetLocal} | 5h:${colorPct(fiveHourPct)} | 7d:${colorPct(sevenDayPct)}`;
|
|
272
273
|
if (hasError) {
|