gsd-remix 1.0.0 → 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.
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/get-shit-done/workflows/update.md +2 -2
- package/hooks/dist/gsd-check-update.js +1 -2
- package/hooks/dist/gsd-statusline.js +2 -170
- package/hooks/gsd-check-update.js +1 -2
- package/hooks/gsd-statusline.js +2 -170
- package/package.json +4 -4
- package/sdk/package.json +3 -3
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/gsd-remix)
|
|
12
12
|
[](https://www.npmjs.com/package/gsd-remix)
|
|
13
|
-
[](https://github.com/Wynne-cwb/gsd-remix/actions/workflows/test.yml)
|
|
14
14
|
[](https://discord.gg/mYgfVNfA2r)
|
|
15
15
|
[](https://x.com/gsd_foundation)
|
|
16
16
|
[](https://dexscreener.com/solana/dwudwjvan7bzkw9zwlbyv6kspdlvhwzrqy6ebk8xzxkv)
|
|
@@ -226,8 +226,8 @@ To confirm that an existing `/gsd-*` command surface is coming from `gsd-remix`
|
|
|
226
226
|
Clone the repository, build hooks, and run the installer locally:
|
|
227
227
|
|
|
228
228
|
```bash
|
|
229
|
-
git clone https://github.com/
|
|
230
|
-
cd
|
|
229
|
+
git clone https://github.com/Wynne-cwb/gsd-remix.git
|
|
230
|
+
cd gsd-remix
|
|
231
231
|
npm run build:hooks
|
|
232
232
|
node bin/install.js --claude --local
|
|
233
233
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/gsd-remix)
|
|
12
12
|
[](https://www.npmjs.com/package/gsd-remix)
|
|
13
|
-
[](https://github.com/Wynne-cwb/gsd-remix/actions/workflows/test.yml)
|
|
14
14
|
[](https://discord.gg/mYgfVNfA2r)
|
|
15
15
|
[](https://x.com/gsd_foundation)
|
|
16
16
|
[](https://dexscreener.com/solana/dwudwjvan7bzkw9zwlbyv6kspdlvhwzrqy6ebk8xzxkv)
|
|
@@ -222,8 +222,8 @@ npx gsd-remix --all --global # 安装到所有目录
|
|
|
222
222
|
克隆仓库并在本地运行安装器:
|
|
223
223
|
|
|
224
224
|
```bash
|
|
225
|
-
git clone https://github.com/
|
|
226
|
-
cd
|
|
225
|
+
git clone https://github.com/Wynne-cwb/gsd-remix.git
|
|
226
|
+
cd gsd-remix
|
|
227
227
|
node bin/install.js --claude --local
|
|
228
228
|
```
|
|
229
229
|
|
|
@@ -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() {
|
|
@@ -554,7 +554,7 @@ Format completion message (changelog was already shown in confirmation step):
|
|
|
554
554
|
|
|
555
555
|
⚠️ Restart your runtime to pick up the new commands.
|
|
556
556
|
|
|
557
|
-
[View full changelog](https://github.com/
|
|
557
|
+
[View full changelog](https://github.com/Wynne-cwb/gsd-remix/blob/main/CHANGELOG.md)
|
|
558
558
|
```
|
|
559
559
|
</step>
|
|
560
560
|
|
|
@@ -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();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-remix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "An unofficial, opinionated remix of GSD for Claude Code, OpenCode, Gemini and Codex.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"gsd-remix": "bin/install.js"
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
39
|
-
"url": "git+https://github.com/
|
|
39
|
+
"url": "git+https://github.com/Wynne-cwb/gsd-remix.git"
|
|
40
40
|
},
|
|
41
|
-
"homepage": "https://github.com/
|
|
41
|
+
"homepage": "https://github.com/Wynne-cwb/gsd-remix",
|
|
42
42
|
"bugs": {
|
|
43
|
-
"url": "https://github.com/
|
|
43
|
+
"url": "https://github.com/Wynne-cwb/gsd-remix/issues"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=22.0.0"
|
package/sdk/package.json
CHANGED
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
],
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/
|
|
23
|
+
"url": "git+https://github.com/Wynne-cwb/gsd-remix.git",
|
|
24
24
|
"directory": "sdk"
|
|
25
25
|
},
|
|
26
|
-
"homepage": "https://github.com/
|
|
26
|
+
"homepage": "https://github.com/Wynne-cwb/gsd-remix/tree/main/sdk",
|
|
27
27
|
"bugs": {
|
|
28
|
-
"url": "https://github.com/
|
|
28
|
+
"url": "https://github.com/Wynne-cwb/gsd-remix/issues"
|
|
29
29
|
},
|
|
30
30
|
"author": "TÂCHES",
|
|
31
31
|
"license": "MIT",
|