gsd-remix 1.0.1 → 1.0.2
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.
|
@@ -490,7 +490,7 @@ npx -y get-shit-done-cc@latest --claude --global
|
|
|
490
490
|
|
|
491
491
|
Capture output. If install fails, show error and exit.
|
|
492
492
|
|
|
493
|
-
Clear the update cache so
|
|
493
|
+
Clear the update cache so future update checks start fresh:
|
|
494
494
|
|
|
495
495
|
```bash
|
|
496
496
|
expand_home() {
|
|
@@ -30,8 +30,7 @@ function detectConfigDir(baseDir) {
|
|
|
30
30
|
const globalConfigDir = detectConfigDir(homeDir);
|
|
31
31
|
const projectConfigDir = detectConfigDir(cwd);
|
|
32
32
|
// Use a shared, tool-agnostic cache directory to avoid multi-runtime
|
|
33
|
-
// resolution mismatches
|
|
34
|
-
// but statusline reads from another (#1421).
|
|
33
|
+
// resolution mismatches when different runtimes trigger update checks (#1421).
|
|
35
34
|
const cacheDir = path.join(homeDir, '.cache', 'gsd');
|
|
36
35
|
const cacheFile = path.join(cacheDir, 'gsd-update-check.json');
|
|
37
36
|
|
|
@@ -1,107 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// gsd-hook-version: {{GSD_VERSION}}
|
|
3
3
|
// Claude Code Statusline - GSD Edition
|
|
4
|
-
// Shows: model |
|
|
4
|
+
// Shows: model | directory | context usage | rate limits
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const os = require('os');
|
|
9
9
|
|
|
10
|
-
// --- GSD state reader -------------------------------------------------------
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Walk up from dir looking for .planning/STATE.md.
|
|
14
|
-
* Returns parsed state object or null.
|
|
15
|
-
*/
|
|
16
|
-
function readGsdState(dir) {
|
|
17
|
-
const home = os.homedir();
|
|
18
|
-
let current = dir;
|
|
19
|
-
for (let i = 0; i < 10; i++) {
|
|
20
|
-
const candidate = path.join(current, '.planning', 'STATE.md');
|
|
21
|
-
if (fs.existsSync(candidate)) {
|
|
22
|
-
try {
|
|
23
|
-
return parseStateMd(fs.readFileSync(candidate, 'utf8'));
|
|
24
|
-
} catch (e) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
const parent = path.dirname(current);
|
|
29
|
-
if (parent === current || current === home) break;
|
|
30
|
-
current = parent;
|
|
31
|
-
}
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Parse STATE.md frontmatter + Phase line from body.
|
|
37
|
-
* Returns { status, milestone, milestoneName, phaseNum, phaseTotal, phaseName }
|
|
38
|
-
*/
|
|
39
|
-
function parseStateMd(content) {
|
|
40
|
-
const state = {};
|
|
41
|
-
|
|
42
|
-
// YAML frontmatter between --- markers
|
|
43
|
-
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
-
if (fmMatch) {
|
|
45
|
-
for (const line of fmMatch[1].split('\n')) {
|
|
46
|
-
const m = line.match(/^(\w+):\s*(.+)/);
|
|
47
|
-
if (!m) continue;
|
|
48
|
-
const [, key, val] = m;
|
|
49
|
-
const v = val.trim().replace(/^["']|["']$/g, '');
|
|
50
|
-
if (key === 'status') state.status = v === 'null' ? null : v;
|
|
51
|
-
if (key === 'milestone') state.milestone = v === 'null' ? null : v;
|
|
52
|
-
if (key === 'milestone_name') state.milestoneName = v === 'null' ? null : v;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Phase: N of M (name) or Phase: none active (...)
|
|
57
|
-
const phaseMatch = content.match(/^Phase:\s*(\d+)\s+of\s+(\d+)(?:\s+\(([^)]+)\))?/m);
|
|
58
|
-
if (phaseMatch) {
|
|
59
|
-
state.phaseNum = phaseMatch[1];
|
|
60
|
-
state.phaseTotal = phaseMatch[2];
|
|
61
|
-
state.phaseName = phaseMatch[3] || null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Fallback: parse Status: from body when frontmatter is absent
|
|
65
|
-
if (!state.status) {
|
|
66
|
-
const bodyStatus = content.match(/^Status:\s*(.+)/m);
|
|
67
|
-
if (bodyStatus) {
|
|
68
|
-
const raw = bodyStatus[1].trim().toLowerCase();
|
|
69
|
-
if (raw.includes('ready to plan') || raw.includes('planning')) state.status = 'planning';
|
|
70
|
-
else if (raw.includes('execut')) state.status = 'executing';
|
|
71
|
-
else if (raw.includes('complet') || raw.includes('archived')) state.status = 'complete';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return state;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Format GSD state into display string.
|
|
80
|
-
* Format: "v1.9 Code Quality · executing · fix-graphiti-deployment (1/5)"
|
|
81
|
-
* Gracefully degrades when parts are missing.
|
|
82
|
-
*/
|
|
83
|
-
function formatGsdState(s) {
|
|
84
|
-
const parts = [];
|
|
85
|
-
|
|
86
|
-
// Milestone: version + name (skip placeholder "milestone")
|
|
87
|
-
if (s.milestone || s.milestoneName) {
|
|
88
|
-
const ver = s.milestone || '';
|
|
89
|
-
const name = (s.milestoneName && s.milestoneName !== 'milestone') ? s.milestoneName : '';
|
|
90
|
-
const ms = [ver, name].filter(Boolean).join(' ');
|
|
91
|
-
if (ms) parts.push(ms);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Status
|
|
95
|
-
if (s.status) parts.push(s.status);
|
|
96
|
-
|
|
97
|
-
// Phase (name intentionally omitted to keep the middle slot compact)
|
|
98
|
-
if (s.phaseNum && s.phaseTotal) {
|
|
99
|
-
parts.push(`ph ${s.phaseNum}/${s.phaseTotal}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return parts.join(' · ');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
10
|
// --- stdin ------------------------------------------------------------------
|
|
106
11
|
|
|
107
12
|
function runStatusline() {
|
|
@@ -177,66 +82,6 @@ function runStatusline() {
|
|
|
177
82
|
}
|
|
178
83
|
}
|
|
179
84
|
|
|
180
|
-
// Current task from todos
|
|
181
|
-
let task = '';
|
|
182
|
-
const homeDir = os.homedir();
|
|
183
|
-
// Respect CLAUDE_CONFIG_DIR for custom config directory setups (#870)
|
|
184
|
-
const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, '.claude');
|
|
185
|
-
const todosDir = path.join(claudeDir, 'todos');
|
|
186
|
-
if (session && fs.existsSync(todosDir)) {
|
|
187
|
-
try {
|
|
188
|
-
const files = fs.readdirSync(todosDir)
|
|
189
|
-
.filter(f => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))
|
|
190
|
-
.map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
|
|
191
|
-
.sort((a, b) => b.mtime - a.mtime);
|
|
192
|
-
|
|
193
|
-
if (files.length > 0) {
|
|
194
|
-
try {
|
|
195
|
-
const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
|
|
196
|
-
const inProgress = todos.find(t => t.status === 'in_progress');
|
|
197
|
-
if (inProgress) task = inProgress.activeForm || '';
|
|
198
|
-
} catch (e) {}
|
|
199
|
-
}
|
|
200
|
-
} catch (e) {
|
|
201
|
-
// Silently fail on file system errors - don't break statusline
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// GSD state (milestone · status · phase) — shown when no todo task
|
|
206
|
-
const gsdStateStr = task ? '' : formatGsdState(readGsdState(dir) || {});
|
|
207
|
-
|
|
208
|
-
// GSD update available?
|
|
209
|
-
// Check shared cache first (#1421), fall back to runtime-specific cache for
|
|
210
|
-
// backward compatibility with older gsd-check-update.js versions.
|
|
211
|
-
let gsdUpdate = '';
|
|
212
|
-
const sharedCacheFile = path.join(homeDir, '.cache', 'gsd', 'gsd-update-check.json');
|
|
213
|
-
const legacyCacheFile = path.join(claudeDir, 'cache', 'gsd-update-check.json');
|
|
214
|
-
const cacheFile = fs.existsSync(sharedCacheFile) ? sharedCacheFile : legacyCacheFile;
|
|
215
|
-
if (fs.existsSync(cacheFile)) {
|
|
216
|
-
try {
|
|
217
|
-
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
218
|
-
if (cache.update_available) {
|
|
219
|
-
gsdUpdate = '\x1b[33m⬆ /gsd-update\x1b[0m │ ';
|
|
220
|
-
}
|
|
221
|
-
if (cache.stale_hooks && cache.stale_hooks.length > 0) {
|
|
222
|
-
// If installed version is ahead of npm latest, this is a dev install.
|
|
223
|
-
// Running /gsd-update would downgrade — show a contextual warning instead.
|
|
224
|
-
const isDevInstall = (() => {
|
|
225
|
-
if (!cache.installed || !cache.latest || cache.latest === 'unknown') return false;
|
|
226
|
-
const parseV = v => v.replace(/^v/, '').split('.').map(Number);
|
|
227
|
-
const [ai, bi, ci] = parseV(cache.installed);
|
|
228
|
-
const [an, bn, cn] = parseV(cache.latest);
|
|
229
|
-
return ai > an || (ai === an && bi > bn) || (ai === an && bi === bn && ci > cn);
|
|
230
|
-
})();
|
|
231
|
-
if (isDevInstall) {
|
|
232
|
-
gsdUpdate += '\x1b[33m⚠ dev install — re-run installer to sync hooks\x1b[0m │ ';
|
|
233
|
-
} else {
|
|
234
|
-
gsdUpdate += '\x1b[31m⚠ stale hooks — run /gsd-update\x1b[0m │ ';
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
} catch (e) {}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
85
|
// Rate limits (5h / 7d remaining)
|
|
241
86
|
function rlColor(remain) {
|
|
242
87
|
if (remain > 50) return '\x1b[32m';
|
|
@@ -270,24 +115,11 @@ function runStatusline() {
|
|
|
270
115
|
|
|
271
116
|
// Output
|
|
272
117
|
const dirname = path.basename(dir);
|
|
273
|
-
|
|
274
|
-
? `\x1b[1m${task}\x1b[0m`
|
|
275
|
-
: gsdStateStr
|
|
276
|
-
? `\x1b[2m${gsdStateStr}\x1b[0m`
|
|
277
|
-
: null;
|
|
278
|
-
|
|
279
|
-
if (middle) {
|
|
280
|
-
process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ ${middle} │ \x1b[2m${dirname}\x1b[0m${ctx}${rateLimits}`);
|
|
281
|
-
} else {
|
|
282
|
-
process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}${rateLimits}`);
|
|
283
|
-
}
|
|
118
|
+
process.stdout.write(`\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}${rateLimits}`);
|
|
284
119
|
} catch (e) {
|
|
285
120
|
// Silent fail - don't break statusline on parse errors
|
|
286
121
|
}
|
|
287
122
|
});
|
|
288
123
|
}
|
|
289
124
|
|
|
290
|
-
// Export helpers for unit tests. Harmless when run as a script.
|
|
291
|
-
module.exports = { readGsdState, parseStateMd, formatGsdState };
|
|
292
|
-
|
|
293
125
|
if (require.main === module) runStatusline();
|
|
@@ -30,8 +30,7 @@ function detectConfigDir(baseDir) {
|
|
|
30
30
|
const globalConfigDir = detectConfigDir(homeDir);
|
|
31
31
|
const projectConfigDir = detectConfigDir(cwd);
|
|
32
32
|
// Use a shared, tool-agnostic cache directory to avoid multi-runtime
|
|
33
|
-
// resolution mismatches
|
|
34
|
-
// but statusline reads from another (#1421).
|
|
33
|
+
// resolution mismatches when different runtimes trigger update checks (#1421).
|
|
35
34
|
const cacheDir = path.join(homeDir, '.cache', 'gsd');
|
|
36
35
|
const cacheFile = path.join(cacheDir, 'gsd-update-check.json');
|
|
37
36
|
|
package/hooks/gsd-statusline.js
CHANGED
|
@@ -1,107 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// gsd-hook-version: {{GSD_VERSION}}
|
|
3
3
|
// Claude Code Statusline - GSD Edition
|
|
4
|
-
// Shows: model |
|
|
4
|
+
// Shows: model | directory | context usage | rate limits
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const os = require('os');
|
|
9
9
|
|
|
10
|
-
// --- GSD state reader -------------------------------------------------------
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Walk up from dir looking for .planning/STATE.md.
|
|
14
|
-
* Returns parsed state object or null.
|
|
15
|
-
*/
|
|
16
|
-
function readGsdState(dir) {
|
|
17
|
-
const home = os.homedir();
|
|
18
|
-
let current = dir;
|
|
19
|
-
for (let i = 0; i < 10; i++) {
|
|
20
|
-
const candidate = path.join(current, '.planning', 'STATE.md');
|
|
21
|
-
if (fs.existsSync(candidate)) {
|
|
22
|
-
try {
|
|
23
|
-
return parseStateMd(fs.readFileSync(candidate, 'utf8'));
|
|
24
|
-
} catch (e) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
const parent = path.dirname(current);
|
|
29
|
-
if (parent === current || current === home) break;
|
|
30
|
-
current = parent;
|
|
31
|
-
}
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Parse STATE.md frontmatter + Phase line from body.
|
|
37
|
-
* Returns { status, milestone, milestoneName, phaseNum, phaseTotal, phaseName }
|
|
38
|
-
*/
|
|
39
|
-
function parseStateMd(content) {
|
|
40
|
-
const state = {};
|
|
41
|
-
|
|
42
|
-
// YAML frontmatter between --- markers
|
|
43
|
-
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
44
|
-
if (fmMatch) {
|
|
45
|
-
for (const line of fmMatch[1].split('\n')) {
|
|
46
|
-
const m = line.match(/^(\w+):\s*(.+)/);
|
|
47
|
-
if (!m) continue;
|
|
48
|
-
const [, key, val] = m;
|
|
49
|
-
const v = val.trim().replace(/^["']|["']$/g, '');
|
|
50
|
-
if (key === 'status') state.status = v === 'null' ? null : v;
|
|
51
|
-
if (key === 'milestone') state.milestone = v === 'null' ? null : v;
|
|
52
|
-
if (key === 'milestone_name') state.milestoneName = v === 'null' ? null : v;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Phase: N of M (name) or Phase: none active (...)
|
|
57
|
-
const phaseMatch = content.match(/^Phase:\s*(\d+)\s+of\s+(\d+)(?:\s+\(([^)]+)\))?/m);
|
|
58
|
-
if (phaseMatch) {
|
|
59
|
-
state.phaseNum = phaseMatch[1];
|
|
60
|
-
state.phaseTotal = phaseMatch[2];
|
|
61
|
-
state.phaseName = phaseMatch[3] || null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Fallback: parse Status: from body when frontmatter is absent
|
|
65
|
-
if (!state.status) {
|
|
66
|
-
const bodyStatus = content.match(/^Status:\s*(.+)/m);
|
|
67
|
-
if (bodyStatus) {
|
|
68
|
-
const raw = bodyStatus[1].trim().toLowerCase();
|
|
69
|
-
if (raw.includes('ready to plan') || raw.includes('planning')) state.status = 'planning';
|
|
70
|
-
else if (raw.includes('execut')) state.status = 'executing';
|
|
71
|
-
else if (raw.includes('complet') || raw.includes('archived')) state.status = 'complete';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return state;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Format GSD state into display string.
|
|
80
|
-
* Format: "v1.9 Code Quality · executing · fix-graphiti-deployment (1/5)"
|
|
81
|
-
* Gracefully degrades when parts are missing.
|
|
82
|
-
*/
|
|
83
|
-
function formatGsdState(s) {
|
|
84
|
-
const parts = [];
|
|
85
|
-
|
|
86
|
-
// Milestone: version + name (skip placeholder "milestone")
|
|
87
|
-
if (s.milestone || s.milestoneName) {
|
|
88
|
-
const ver = s.milestone || '';
|
|
89
|
-
const name = (s.milestoneName && s.milestoneName !== 'milestone') ? s.milestoneName : '';
|
|
90
|
-
const ms = [ver, name].filter(Boolean).join(' ');
|
|
91
|
-
if (ms) parts.push(ms);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Status
|
|
95
|
-
if (s.status) parts.push(s.status);
|
|
96
|
-
|
|
97
|
-
// Phase (name intentionally omitted to keep the middle slot compact)
|
|
98
|
-
if (s.phaseNum && s.phaseTotal) {
|
|
99
|
-
parts.push(`ph ${s.phaseNum}/${s.phaseTotal}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return parts.join(' · ');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
10
|
// --- stdin ------------------------------------------------------------------
|
|
106
11
|
|
|
107
12
|
function runStatusline() {
|
|
@@ -177,66 +82,6 @@ function runStatusline() {
|
|
|
177
82
|
}
|
|
178
83
|
}
|
|
179
84
|
|
|
180
|
-
// Current task from todos
|
|
181
|
-
let task = '';
|
|
182
|
-
const homeDir = os.homedir();
|
|
183
|
-
// Respect CLAUDE_CONFIG_DIR for custom config directory setups (#870)
|
|
184
|
-
const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, '.claude');
|
|
185
|
-
const todosDir = path.join(claudeDir, 'todos');
|
|
186
|
-
if (session && fs.existsSync(todosDir)) {
|
|
187
|
-
try {
|
|
188
|
-
const files = fs.readdirSync(todosDir)
|
|
189
|
-
.filter(f => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))
|
|
190
|
-
.map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
|
|
191
|
-
.sort((a, b) => b.mtime - a.mtime);
|
|
192
|
-
|
|
193
|
-
if (files.length > 0) {
|
|
194
|
-
try {
|
|
195
|
-
const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
|
|
196
|
-
const inProgress = todos.find(t => t.status === 'in_progress');
|
|
197
|
-
if (inProgress) task = inProgress.activeForm || '';
|
|
198
|
-
} catch (e) {}
|
|
199
|
-
}
|
|
200
|
-
} catch (e) {
|
|
201
|
-
// Silently fail on file system errors - don't break statusline
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// GSD state (milestone · status · phase) — shown when no todo task
|
|
206
|
-
const gsdStateStr = task ? '' : formatGsdState(readGsdState(dir) || {});
|
|
207
|
-
|
|
208
|
-
// GSD update available?
|
|
209
|
-
// Check shared cache first (#1421), fall back to runtime-specific cache for
|
|
210
|
-
// backward compatibility with older gsd-check-update.js versions.
|
|
211
|
-
let gsdUpdate = '';
|
|
212
|
-
const sharedCacheFile = path.join(homeDir, '.cache', 'gsd', 'gsd-update-check.json');
|
|
213
|
-
const legacyCacheFile = path.join(claudeDir, 'cache', 'gsd-update-check.json');
|
|
214
|
-
const cacheFile = fs.existsSync(sharedCacheFile) ? sharedCacheFile : legacyCacheFile;
|
|
215
|
-
if (fs.existsSync(cacheFile)) {
|
|
216
|
-
try {
|
|
217
|
-
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
218
|
-
if (cache.update_available) {
|
|
219
|
-
gsdUpdate = '\x1b[33m⬆ /gsd-update\x1b[0m │ ';
|
|
220
|
-
}
|
|
221
|
-
if (cache.stale_hooks && cache.stale_hooks.length > 0) {
|
|
222
|
-
// If installed version is ahead of npm latest, this is a dev install.
|
|
223
|
-
// Running /gsd-update would downgrade — show a contextual warning instead.
|
|
224
|
-
const isDevInstall = (() => {
|
|
225
|
-
if (!cache.installed || !cache.latest || cache.latest === 'unknown') return false;
|
|
226
|
-
const parseV = v => v.replace(/^v/, '').split('.').map(Number);
|
|
227
|
-
const [ai, bi, ci] = parseV(cache.installed);
|
|
228
|
-
const [an, bn, cn] = parseV(cache.latest);
|
|
229
|
-
return ai > an || (ai === an && bi > bn) || (ai === an && bi === bn && ci > cn);
|
|
230
|
-
})();
|
|
231
|
-
if (isDevInstall) {
|
|
232
|
-
gsdUpdate += '\x1b[33m⚠ dev install — re-run installer to sync hooks\x1b[0m │ ';
|
|
233
|
-
} else {
|
|
234
|
-
gsdUpdate += '\x1b[31m⚠ stale hooks — run /gsd-update\x1b[0m │ ';
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
} catch (e) {}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
85
|
// Rate limits (5h / 7d remaining)
|
|
241
86
|
function rlColor(remain) {
|
|
242
87
|
if (remain > 50) return '\x1b[32m';
|
|
@@ -270,24 +115,11 @@ function runStatusline() {
|
|
|
270
115
|
|
|
271
116
|
// Output
|
|
272
117
|
const dirname = path.basename(dir);
|
|
273
|
-
|
|
274
|
-
? `\x1b[1m${task}\x1b[0m`
|
|
275
|
-
: gsdStateStr
|
|
276
|
-
? `\x1b[2m${gsdStateStr}\x1b[0m`
|
|
277
|
-
: null;
|
|
278
|
-
|
|
279
|
-
if (middle) {
|
|
280
|
-
process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ ${middle} │ \x1b[2m${dirname}\x1b[0m${ctx}${rateLimits}`);
|
|
281
|
-
} else {
|
|
282
|
-
process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}${rateLimits}`);
|
|
283
|
-
}
|
|
118
|
+
process.stdout.write(`\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}${rateLimits}`);
|
|
284
119
|
} catch (e) {
|
|
285
120
|
// Silent fail - don't break statusline on parse errors
|
|
286
121
|
}
|
|
287
122
|
});
|
|
288
123
|
}
|
|
289
124
|
|
|
290
|
-
// Export helpers for unit tests. Harmless when run as a script.
|
|
291
|
-
module.exports = { readGsdState, parseStateMd, formatGsdState };
|
|
292
|
-
|
|
293
125
|
if (require.main === module) runStatusline();
|