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.
Files changed (3) hide show
  1. package/README.md +19 -1
  2. package/package.json +1 -1
  3. 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
- - **Git-aware** — shows the current branch name in repos
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-simple-status",
3
- "version": "1.0.2",
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": {
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, appendFileSync } from 'fs';
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
- return execSync('git rev-parse --abbrev-ref HEAD', {
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 (quotaData) {
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) {