gsd-lite 0.5.14 → 0.5.15
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +0 -0
- package/README.md +0 -0
- package/agents/debugger.md +0 -0
- package/agents/executor.md +0 -0
- package/agents/researcher.md +0 -0
- package/agents/reviewer.md +0 -0
- package/commands/doctor.md +0 -0
- package/commands/prd.md +0 -0
- package/commands/resume.md +0 -0
- package/commands/start.md +0 -0
- package/commands/status.md +0 -0
- package/commands/stop.md +0 -0
- package/hooks/context-monitor.js +0 -0
- package/hooks/gsd-auto-update.cjs +57 -2
- package/hooks/gsd-context-monitor.cjs +0 -0
- package/hooks/gsd-session-init.cjs +0 -0
- package/hooks/gsd-session-stop.cjs +0 -0
- package/hooks/gsd-statusline.cjs +0 -0
- package/hooks/hooks.json +0 -0
- package/hooks/lib/gsd-finder.cjs +0 -0
- package/install.js +63 -7
- package/launcher.js +0 -0
- package/package.json +1 -1
- package/references/anti-rationalization-full.md +0 -0
- package/references/evidence-spec.md +0 -0
- package/references/execution-loop.md +0 -0
- package/references/git-worktrees.md +0 -0
- package/references/questioning.md +0 -0
- package/references/review-classification.md +0 -0
- package/references/state-diagram.md +0 -0
- package/references/testing-patterns.md +0 -0
- package/src/schema.js +0 -0
- package/src/server.js +0 -0
- package/src/tools/orchestrator/debugger.js +0 -0
- package/src/tools/orchestrator/executor.js +10 -0
- package/src/tools/orchestrator/helpers.js +0 -0
- package/src/tools/orchestrator/index.js +0 -0
- package/src/tools/orchestrator/researcher.js +0 -0
- package/src/tools/orchestrator/resume.js +0 -0
- package/src/tools/orchestrator/reviewer.js +0 -0
- package/src/tools/state/constants.js +0 -0
- package/src/tools/state/crud.js +0 -0
- package/src/tools/state/index.js +0 -0
- package/src/tools/state/logic.js +0 -0
- package/src/tools/verify.js +0 -0
- package/src/utils.js +0 -0
- package/uninstall.js +12 -1
- package/workflows/debugging.md +0 -0
- package/workflows/deviation-rules.md +0 -0
- package/workflows/execution-flow.md +0 -0
- package/workflows/research.md +0 -0
- package/workflows/review-cycle.md +0 -0
- package/workflows/tdd-cycle.md +0 -0
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"name": "gsd",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "AI orchestration tool — GSD management shell + Superpowers quality core. 5 commands, 4 agents, 5 workflows, MCP server, context monitoring.",
|
|
16
|
-
"version": "0.5.
|
|
16
|
+
"version": "0.5.15",
|
|
17
17
|
"keywords": [
|
|
18
18
|
"orchestration",
|
|
19
19
|
"mcp",
|
package/.mcp.json
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
package/agents/debugger.md
CHANGED
|
File without changes
|
package/agents/executor.md
CHANGED
|
File without changes
|
package/agents/researcher.md
CHANGED
|
File without changes
|
package/agents/reviewer.md
CHANGED
|
File without changes
|
package/commands/doctor.md
CHANGED
|
File without changes
|
package/commands/prd.md
CHANGED
|
File without changes
|
package/commands/resume.md
CHANGED
|
File without changes
|
package/commands/start.md
CHANGED
|
File without changes
|
package/commands/status.md
CHANGED
|
File without changes
|
package/commands/stop.md
CHANGED
|
File without changes
|
package/hooks/context-monitor.js
CHANGED
|
File without changes
|
|
@@ -337,7 +337,7 @@ function validateExtractedPackage(extractDir) {
|
|
|
337
337
|
// ── Download & Install ─────────────────────────────────────
|
|
338
338
|
async function downloadAndInstall(tarballUrl, verbose = false, token = null) {
|
|
339
339
|
const tmpDir = path.join(os.tmpdir(), `gsd-update-${Date.now()}`);
|
|
340
|
-
const backupPath = path.join(
|
|
340
|
+
const backupPath = path.join(runtimeDir, 'package.json.bak');
|
|
341
341
|
let backedUp = false;
|
|
342
342
|
try {
|
|
343
343
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
@@ -450,6 +450,35 @@ function writeNotification(notification) {
|
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
+
// ── Cache Cleanup ─────────────────────────────────────────
|
|
454
|
+
// Remove old plugin cache versions, keeping the N most recent.
|
|
455
|
+
function pruneOldCacheVersions(cacheBase, keepCount = 3, verbose = false) {
|
|
456
|
+
try {
|
|
457
|
+
if (!fs.existsSync(cacheBase)) return;
|
|
458
|
+
const entries = fs.readdirSync(cacheBase, { withFileTypes: true })
|
|
459
|
+
.filter(e => e.isDirectory())
|
|
460
|
+
.map(e => e.name);
|
|
461
|
+
if (entries.length <= keepCount) return;
|
|
462
|
+
|
|
463
|
+
// Sort by semver: split into [major, minor, patch] and compare numerically
|
|
464
|
+
const sorted = entries.slice().sort((a, b) => {
|
|
465
|
+
const pa = a.split('.').map(Number);
|
|
466
|
+
const pb = b.split('.').map(Number);
|
|
467
|
+
for (let i = 0; i < 3; i++) {
|
|
468
|
+
if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0);
|
|
469
|
+
}
|
|
470
|
+
return 0;
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const toRemove = sorted.slice(0, sorted.length - keepCount);
|
|
474
|
+
for (const ver of toRemove) {
|
|
475
|
+
const verPath = path.join(cacheBase, ver);
|
|
476
|
+
fs.rmSync(verPath, { recursive: true, force: true });
|
|
477
|
+
if (verbose) console.log(` Pruned old cache: ${ver}`);
|
|
478
|
+
}
|
|
479
|
+
} catch { /* best effort */ }
|
|
480
|
+
}
|
|
481
|
+
|
|
453
482
|
// ── Plugin Cache Sync ─────────────────────────────────────
|
|
454
483
|
// When installed as a plugin, the MCP server runs from plugins/cache/gsd/gsd/<version>/
|
|
455
484
|
// The auto-update installs to ~/.claude/gsd/ (runtime dir) via install.js,
|
|
@@ -492,11 +521,17 @@ function syncPluginCache(extractedDir, verbose = false) {
|
|
|
492
521
|
|
|
493
522
|
// Install dependencies in cache dir
|
|
494
523
|
if (!fs.existsSync(path.join(newCachePath, 'node_modules', '@modelcontextprotocol'))) {
|
|
495
|
-
spawnSync('npm', ['install', '--omit=dev', '--ignore-scripts'], {
|
|
524
|
+
const npmResult = spawnSync('npm', ['install', '--omit=dev', '--ignore-scripts'], {
|
|
496
525
|
cwd: newCachePath,
|
|
497
526
|
stdio: 'pipe',
|
|
498
527
|
timeout: 60000,
|
|
499
528
|
});
|
|
529
|
+
if (npmResult.status !== 0) {
|
|
530
|
+
// npm install failed — don't update registry to point to broken cache
|
|
531
|
+
if (verbose) console.error(' npm install failed in cache dir, aborting cache sync');
|
|
532
|
+
fs.rmSync(newCachePath, { recursive: true, force: true });
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
500
535
|
}
|
|
501
536
|
|
|
502
537
|
// Update installed_plugins.json to point to new cache path
|
|
@@ -507,6 +542,26 @@ function syncPluginCache(extractedDir, verbose = false) {
|
|
|
507
542
|
fs.writeFileSync(tmpPlugins, JSON.stringify(plugins, null, 2) + '\n');
|
|
508
543
|
fs.renameSync(tmpPlugins, pluginsFile);
|
|
509
544
|
|
|
545
|
+
// Update settings.json statusLine if it points to the old cache path
|
|
546
|
+
try {
|
|
547
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
548
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
549
|
+
if (settings.statusLine?.command?.includes('/plugins/cache/gsd/gsd/')) {
|
|
550
|
+
const oldCmd = settings.statusLine.command;
|
|
551
|
+
const updated = oldCmd.replace(/\/plugins\/cache\/gsd\/gsd\/[^/]+\//g, `/plugins/cache/gsd/gsd/${newVersion}/`);
|
|
552
|
+
if (updated !== oldCmd) {
|
|
553
|
+
settings.statusLine.command = updated;
|
|
554
|
+
const tmpSettings = settingsPath + `.${process.pid}.tmp`;
|
|
555
|
+
fs.writeFileSync(tmpSettings, JSON.stringify(settings, null, 2) + '\n');
|
|
556
|
+
fs.renameSync(tmpSettings, settingsPath);
|
|
557
|
+
if (verbose) console.log(' StatusLine path updated to new version');
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
} catch {}
|
|
561
|
+
|
|
562
|
+
// Prune old cache versions — keep only the 3 most recent
|
|
563
|
+
pruneOldCacheVersions(cacheBase, 3, verbose);
|
|
564
|
+
|
|
510
565
|
if (verbose) console.log(` Plugin cache synced to v${newVersion}`);
|
|
511
566
|
} catch (err) {
|
|
512
567
|
// Best effort — don't fail the update if cache sync fails
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/hooks/gsd-statusline.cjs
CHANGED
|
File without changes
|
package/hooks/hooks.json
CHANGED
|
File without changes
|
package/hooks/lib/gsd-finder.cjs
CHANGED
|
File without changes
|
package/install.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Plugin installer for GSD-Lite
|
|
3
3
|
|
|
4
|
-
import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync, renameSync, rmSync } from 'node:fs';
|
|
4
|
+
import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync, renameSync, rmSync, readdirSync } from 'node:fs';
|
|
5
5
|
import { join, dirname } from 'node:path';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -13,12 +13,13 @@ const RUNTIME_DIR = join(CLAUDE_DIR, 'gsd');
|
|
|
13
13
|
const DRY_RUN = process.argv.includes('--dry-run');
|
|
14
14
|
|
|
15
15
|
// Single source of truth for hook files (used by copy loop and registration)
|
|
16
|
-
const HOOK_FILES = ['gsd-session-init.cjs', 'gsd-auto-update.cjs', 'gsd-context-monitor.cjs', 'gsd-statusline.cjs'];
|
|
16
|
+
const HOOK_FILES = ['gsd-session-init.cjs', 'gsd-auto-update.cjs', 'gsd-context-monitor.cjs', 'gsd-statusline.cjs', 'gsd-session-stop.cjs'];
|
|
17
17
|
|
|
18
18
|
// Hook registration config: hookType → { file identifier, matcher, timeout? }
|
|
19
19
|
const HOOK_REGISTRY = [
|
|
20
20
|
{ hookType: 'SessionStart', identifier: 'gsd-session-init', matcher: 'startup', timeout: 5 },
|
|
21
21
|
{ hookType: 'PostToolUse', identifier: 'gsd-context-monitor', matcher: '*' },
|
|
22
|
+
{ hookType: 'Stop', identifier: 'gsd-session-stop', matcher: '*', timeout: 3 },
|
|
22
23
|
];
|
|
23
24
|
|
|
24
25
|
function log(msg) { console.log(msg); }
|
|
@@ -135,6 +136,11 @@ export function main() {
|
|
|
135
136
|
for (const hookFile of HOOK_FILES) {
|
|
136
137
|
copyFile(join(__dirname, 'hooks', hookFile), join(CLAUDE_DIR, 'hooks', hookFile), `hooks/${hookFile}`);
|
|
137
138
|
}
|
|
139
|
+
// 5b. Hook library dependencies (e.g. gsd-finder.cjs used by statusline + session-init)
|
|
140
|
+
const hookLibDir = join(__dirname, 'hooks', 'lib');
|
|
141
|
+
if (existsSync(hookLibDir)) {
|
|
142
|
+
copyDir(hookLibDir, join(CLAUDE_DIR, 'hooks', 'lib'), 'hooks/lib → ~/.claude/hooks/lib/');
|
|
143
|
+
}
|
|
138
144
|
|
|
139
145
|
// 6. Stable runtime for MCP server
|
|
140
146
|
copyDir(join(__dirname, 'src'), join(RUNTIME_DIR, 'src'), 'runtime/src → ~/.claude/gsd/src/');
|
|
@@ -186,12 +192,36 @@ export function main() {
|
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
// Register statusLine (top-level setting) and hooks
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
// When installed as a plugin, hooks are managed by hooks.json via the plugin system.
|
|
196
|
+
// Only register in settings.json for manual installs to avoid double execution.
|
|
197
|
+
let statusLineRegistered = false;
|
|
192
198
|
let hooksRegistered = false;
|
|
193
|
-
|
|
194
|
-
if (
|
|
199
|
+
if (!isPluginInstall) {
|
|
200
|
+
if (!settings.hooks) settings.hooks = {};
|
|
201
|
+
const statuslinePath = join(CLAUDE_DIR, 'hooks', 'gsd-statusline.cjs');
|
|
202
|
+
statusLineRegistered = registerStatusLine(settings, statuslinePath);
|
|
203
|
+
for (const config of HOOK_REGISTRY) {
|
|
204
|
+
if (registerHookEntry(settings.hooks, config)) hooksRegistered = true;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
// Clean up stale manual hook entries left from previous install.js runs
|
|
208
|
+
if (settings.hooks) {
|
|
209
|
+
let cleaned = false;
|
|
210
|
+
for (const [hookType, identifier] of [
|
|
211
|
+
['PostToolUse', 'gsd-context-monitor'],
|
|
212
|
+
['SessionStart', 'gsd-session-init'],
|
|
213
|
+
['Stop', 'gsd-session-stop'],
|
|
214
|
+
]) {
|
|
215
|
+
if (Array.isArray(settings.hooks[hookType])) {
|
|
216
|
+
const before = settings.hooks[hookType].length;
|
|
217
|
+
settings.hooks[hookType] = settings.hooks[hookType].filter(e =>
|
|
218
|
+
!e.hooks?.some(h => h.command?.includes(identifier)));
|
|
219
|
+
if (settings.hooks[hookType].length < before) cleaned = true;
|
|
220
|
+
if (settings.hooks[hookType].length === 0) delete settings.hooks[hookType];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (cleaned) log(' ✓ Removed stale manual hook entries (plugin hooks.json handles registration)');
|
|
224
|
+
}
|
|
195
225
|
}
|
|
196
226
|
|
|
197
227
|
const tmpSettings = settingsPath + `.${process.pid}-${Date.now()}.tmp`;
|
|
@@ -204,6 +234,32 @@ export function main() {
|
|
|
204
234
|
log(' [dry-run] Would register MCP server in settings.json');
|
|
205
235
|
}
|
|
206
236
|
|
|
237
|
+
// 9. Prune old plugin cache versions (keep latest 3)
|
|
238
|
+
if (!DRY_RUN && isPluginInstall) {
|
|
239
|
+
const cacheBase = join(CLAUDE_DIR, 'plugins', 'cache', 'gsd', 'gsd');
|
|
240
|
+
if (existsSync(cacheBase)) {
|
|
241
|
+
try {
|
|
242
|
+
const entries = readdirSync(cacheBase, { withFileTypes: true })
|
|
243
|
+
.filter(e => e.isDirectory()).map(e => e.name);
|
|
244
|
+
if (entries.length > 3) {
|
|
245
|
+
const sorted = entries.slice().sort((a, b) => {
|
|
246
|
+
const pa = a.split('.').map(Number);
|
|
247
|
+
const pb = b.split('.').map(Number);
|
|
248
|
+
for (let i = 0; i < 3; i++) {
|
|
249
|
+
if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0);
|
|
250
|
+
}
|
|
251
|
+
return 0;
|
|
252
|
+
});
|
|
253
|
+
const toRemove = sorted.slice(0, sorted.length - 3);
|
|
254
|
+
for (const ver of toRemove) {
|
|
255
|
+
rmSync(join(cacheBase, ver), { recursive: true, force: true });
|
|
256
|
+
}
|
|
257
|
+
log(` ✓ Pruned ${toRemove.length} old cache version(s), kept latest 3`);
|
|
258
|
+
}
|
|
259
|
+
} catch { /* best effort */ }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
207
263
|
log('\n✓ GSD-Lite installed successfully!');
|
|
208
264
|
log(' Use /gsd:start to begin a new project');
|
|
209
265
|
log(' Use /gsd:resume to continue an existing project');
|
package/launcher.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/schema.js
CHANGED
|
File without changes
|
package/src/server.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -30,6 +30,16 @@ export async function handleExecutorResult({ result, basePath = process.cwd() }
|
|
|
30
30
|
return { error: true, message: `Task ${result.task_id} not found` };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// Auto-start parallel tasks: if a task is still pending (dispatched via parallel_available
|
|
34
|
+
// but not explicitly started by orchestrator-resume), transition it to running first.
|
|
35
|
+
if (task.lifecycle === 'pending') {
|
|
36
|
+
const startError = await persist(basePath, {
|
|
37
|
+
phases: [{ id: phase.id, todo: [{ id: task.id, lifecycle: 'running' }] }],
|
|
38
|
+
});
|
|
39
|
+
if (startError) return startError;
|
|
40
|
+
task.lifecycle = 'running';
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
// Build new decision entries — actual append happens atomically inside update()'s lock
|
|
34
44
|
const newDecisions = buildDecisionEntries(result.decisions, phase.id, task.id, (state.decisions || []).length);
|
|
35
45
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tools/state/crud.js
CHANGED
|
File without changes
|
package/src/tools/state/index.js
CHANGED
|
File without changes
|
package/src/tools/state/logic.js
CHANGED
|
File without changes
|
package/src/tools/verify.js
CHANGED
|
File without changes
|
package/src/utils.js
CHANGED
|
File without changes
|
package/uninstall.js
CHANGED
|
@@ -38,13 +38,23 @@ export function main() {
|
|
|
38
38
|
removeDir(join(CLAUDE_DIR, 'gsd-lite'), 'legacy gsd-lite runtime/');
|
|
39
39
|
|
|
40
40
|
// Remove hook files (both legacy and current names)
|
|
41
|
-
for (const name of ['context-monitor.js', 'gsd-statusline.cjs', 'gsd-context-monitor.cjs', 'gsd-session-init.cjs', 'gsd-auto-update.cjs']) {
|
|
41
|
+
for (const name of ['context-monitor.js', 'gsd-statusline.cjs', 'gsd-context-monitor.cjs', 'gsd-session-init.cjs', 'gsd-auto-update.cjs', 'gsd-session-stop.cjs']) {
|
|
42
42
|
const hookFile = join(CLAUDE_DIR, 'hooks', name);
|
|
43
43
|
if (existsSync(hookFile)) {
|
|
44
44
|
rmSync(hookFile);
|
|
45
45
|
log(` ✓ Removed hooks/${name}`);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
+
// Remove hook library dependencies
|
|
49
|
+
const hookLibDir = join(CLAUDE_DIR, 'hooks', 'lib');
|
|
50
|
+
if (existsSync(hookLibDir)) {
|
|
51
|
+
// Only remove GSD-owned files, not other plugins' libs
|
|
52
|
+
const gsdLibFile = join(hookLibDir, 'gsd-finder.cjs');
|
|
53
|
+
if (existsSync(gsdLibFile)) {
|
|
54
|
+
rmSync(gsdLibFile);
|
|
55
|
+
log(' ✓ Removed hooks/lib/gsd-finder.cjs');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
48
58
|
|
|
49
59
|
// Clean up plugin system directories (from /plugin install)
|
|
50
60
|
removeDir(join(CLAUDE_DIR, 'plugins', 'marketplaces', 'gsd'), 'plugins/marketplaces/gsd/');
|
|
@@ -123,6 +133,7 @@ export function main() {
|
|
|
123
133
|
['PostToolUse', 'gsd-context-monitor'],
|
|
124
134
|
['PostToolUse', 'context-monitor.js'],
|
|
125
135
|
['SessionStart', 'gsd-session-init'],
|
|
136
|
+
['Stop', 'gsd-session-stop'],
|
|
126
137
|
]) {
|
|
127
138
|
if (Array.isArray(settings.hooks[hookType])) {
|
|
128
139
|
const len = settings.hooks[hookType].length;
|
package/workflows/debugging.md
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/workflows/research.md
CHANGED
|
File without changes
|
|
File without changes
|
package/workflows/tdd-cycle.md
CHANGED
|
File without changes
|