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.
Files changed (55) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.mcp.json +0 -0
  4. package/README.md +0 -0
  5. package/agents/debugger.md +0 -0
  6. package/agents/executor.md +0 -0
  7. package/agents/researcher.md +0 -0
  8. package/agents/reviewer.md +0 -0
  9. package/commands/doctor.md +0 -0
  10. package/commands/prd.md +0 -0
  11. package/commands/resume.md +0 -0
  12. package/commands/start.md +0 -0
  13. package/commands/status.md +0 -0
  14. package/commands/stop.md +0 -0
  15. package/hooks/context-monitor.js +0 -0
  16. package/hooks/gsd-auto-update.cjs +57 -2
  17. package/hooks/gsd-context-monitor.cjs +0 -0
  18. package/hooks/gsd-session-init.cjs +0 -0
  19. package/hooks/gsd-session-stop.cjs +0 -0
  20. package/hooks/gsd-statusline.cjs +0 -0
  21. package/hooks/hooks.json +0 -0
  22. package/hooks/lib/gsd-finder.cjs +0 -0
  23. package/install.js +63 -7
  24. package/launcher.js +0 -0
  25. package/package.json +1 -1
  26. package/references/anti-rationalization-full.md +0 -0
  27. package/references/evidence-spec.md +0 -0
  28. package/references/execution-loop.md +0 -0
  29. package/references/git-worktrees.md +0 -0
  30. package/references/questioning.md +0 -0
  31. package/references/review-classification.md +0 -0
  32. package/references/state-diagram.md +0 -0
  33. package/references/testing-patterns.md +0 -0
  34. package/src/schema.js +0 -0
  35. package/src/server.js +0 -0
  36. package/src/tools/orchestrator/debugger.js +0 -0
  37. package/src/tools/orchestrator/executor.js +10 -0
  38. package/src/tools/orchestrator/helpers.js +0 -0
  39. package/src/tools/orchestrator/index.js +0 -0
  40. package/src/tools/orchestrator/researcher.js +0 -0
  41. package/src/tools/orchestrator/resume.js +0 -0
  42. package/src/tools/orchestrator/reviewer.js +0 -0
  43. package/src/tools/state/constants.js +0 -0
  44. package/src/tools/state/crud.js +0 -0
  45. package/src/tools/state/index.js +0 -0
  46. package/src/tools/state/logic.js +0 -0
  47. package/src/tools/verify.js +0 -0
  48. package/src/utils.js +0 -0
  49. package/uninstall.js +12 -1
  50. package/workflows/debugging.md +0 -0
  51. package/workflows/deviation-rules.md +0 -0
  52. package/workflows/execution-flow.md +0 -0
  53. package/workflows/research.md +0 -0
  54. package/workflows/review-cycle.md +0 -0
  55. 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.14",
16
+ "version": "0.5.15",
17
17
  "keywords": [
18
18
  "orchestration",
19
19
  "mcp",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd",
3
- "version": "0.5.14",
3
+ "version": "0.5.15",
4
4
  "description": "AI orchestration tool for Claude Code — GSD management shell + Superpowers quality core",
5
5
  "author": {
6
6
  "name": "sdsrss",
package/.mcp.json CHANGED
File without changes
package/README.md CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/commands/prd.md CHANGED
File without changes
File without changes
package/commands/start.md CHANGED
File without changes
File without changes
package/commands/stop.md CHANGED
File without changes
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(pluginRoot, 'package.json.bak');
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
File without changes
package/hooks/hooks.json CHANGED
File without changes
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
- if (!settings.hooks) settings.hooks = {};
190
- const statuslinePath = join(CLAUDE_DIR, 'hooks', 'gsd-statusline.cjs');
191
- const statusLineRegistered = registerStatusLine(settings, statuslinePath);
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
- for (const config of HOOK_REGISTRY) {
194
- if (registerHookEntry(settings.hooks, config)) hooksRegistered = true;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-lite",
3
- "version": "0.5.14",
3
+ "version": "0.5.15",
4
4
  "description": "AI orchestration tool for Claude Code — GSD management shell + Superpowers quality core",
5
5
  "type": "module",
6
6
  "bin": {
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
File without changes
File without changes
File without changes
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;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes