@opengsd/gsd-core 1.3.1 → 1.4.0-rc.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.
Files changed (136) hide show
  1. package/.claude-plugin/plugin.json +23 -0
  2. package/GEMINI.md +53 -0
  3. package/agents/gsd-advisor-researcher.md +1 -20
  4. package/agents/gsd-ai-researcher.md +2 -21
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-code-reviewer.md +1 -1
  7. package/agents/gsd-domain-researcher.md +2 -21
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-eval-planner.md +1 -1
  10. package/agents/gsd-executor.md +1 -1
  11. package/agents/gsd-framework-selector.md +1 -1
  12. package/agents/gsd-nyquist-auditor.md +1 -1
  13. package/agents/gsd-pattern-mapper.md +1 -1
  14. package/agents/gsd-phase-researcher.md +92 -166
  15. package/agents/gsd-planner.md +9 -36
  16. package/agents/gsd-project-researcher.md +62 -141
  17. package/agents/gsd-security-auditor.md +1 -1
  18. package/agents/gsd-ui-auditor.md +1 -1
  19. package/agents/gsd-ui-checker.md +1 -1
  20. package/agents/gsd-ui-researcher.md +3 -22
  21. package/agents/gsd-user-profiler.md +1 -1
  22. package/agents/gsd-verifier.md +8 -2
  23. package/bin/install.js +1977 -339
  24. package/commands/gsd/autonomous.md +2 -0
  25. package/commands/gsd/execute-phase.md +2 -0
  26. package/commands/gsd/graphify.md +11 -6
  27. package/commands/gsd/import.md +6 -2
  28. package/commands/gsd/plan-phase.md +4 -2
  29. package/commands/gsd/progress.md +1 -0
  30. package/commands/gsd/stats.md +1 -0
  31. package/commands/gsd/update.md +3 -2
  32. package/gemini-extension.json +6 -0
  33. package/gsd-core/bin/check-latest-version.cjs +61 -6
  34. package/gsd-core/bin/gsd-tools.cjs +238 -32
  35. package/gsd-core/bin/lib/check-command-router.cjs +1 -0
  36. package/gsd-core/bin/lib/cli-exit.cjs +42 -0
  37. package/gsd-core/bin/lib/command-routing-hub.cjs +1 -1
  38. package/gsd-core/bin/lib/commands.cjs +5 -4
  39. package/gsd-core/bin/lib/config.cjs +28 -4
  40. package/gsd-core/bin/lib/core.cjs +72 -28
  41. package/gsd-core/bin/lib/graphify.cjs +2 -2
  42. package/gsd-core/bin/lib/init-command-router.cjs +2 -2
  43. package/gsd-core/bin/lib/init.cjs +19 -3
  44. package/gsd-core/bin/lib/install-profiles.cjs +58 -0
  45. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  46. package/gsd-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +1 -1
  47. package/gsd-core/bin/lib/intel.cjs +3 -20
  48. package/gsd-core/bin/lib/package-legitimacy.cjs +368 -0
  49. package/gsd-core/bin/lib/phase.cjs +3 -3
  50. package/gsd-core/bin/lib/research-provider.cjs +137 -0
  51. package/gsd-core/bin/lib/research-store.cjs +167 -0
  52. package/gsd-core/bin/lib/roadmap-upgrade.cjs +4 -19
  53. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +67 -9
  54. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +56 -0
  55. package/gsd-core/bin/lib/runtime-homes.cjs +32 -11
  56. package/gsd-core/bin/lib/security.cjs +73 -0
  57. package/gsd-core/bin/lib/shell-command-projection.cjs +9 -0
  58. package/gsd-core/bin/lib/surface.cjs +54 -11
  59. package/gsd-core/bin/lib/validate.cjs +2 -2
  60. package/gsd-core/bin/lib/verification-command-router.cjs +31 -0
  61. package/gsd-core/bin/lib/verification.cjs +193 -0
  62. package/gsd-core/bin/lib/verify.cjs +2 -2
  63. package/gsd-core/bin/lib/workstream-inventory.cjs +1 -1
  64. package/gsd-core/bin/lib/worktree-base-ref.cjs +325 -0
  65. package/gsd-core/bin/lib/worktree-safety.cjs +31 -0
  66. package/gsd-core/bin/shared/config-schema.manifest.json +2 -1
  67. package/gsd-core/bin/verify-reapply-patches.cjs +8 -11
  68. package/gsd-core/references/planner-load-graph-context.md +36 -0
  69. package/gsd-core/references/planning-config.md +3 -1
  70. package/gsd-core/references/research-documentation-lookup.md +29 -0
  71. package/gsd-core/references/research-philosophy.md +29 -0
  72. package/gsd-core/references/research-verification-protocol.md +27 -0
  73. package/gsd-core/workflows/execute-phase.md +19 -8
  74. package/gsd-core/workflows/help/modes/full.md +4 -3
  75. package/gsd-core/workflows/ingest-docs.md +3 -2
  76. package/gsd-core/workflows/plan-phase.md +14 -10
  77. package/gsd-core/workflows/plan-review-convergence.md +3 -3
  78. package/gsd-core/workflows/review.md +24 -7
  79. package/gsd-core/workflows/ship.md +5 -8
  80. package/gsd-core/workflows/spec-phase.md +2 -1
  81. package/gsd-core/workflows/update.md +34 -6
  82. package/hooks/dist/gsd-config-reload.js +133 -0
  83. package/hooks/dist/gsd-context-monitor.js +1 -1
  84. package/hooks/dist/gsd-cursor-post-tool.js +75 -0
  85. package/hooks/dist/gsd-cursor-session-start.js +52 -0
  86. package/hooks/dist/gsd-workflow-guard.js +1 -0
  87. package/hooks/dist/gsd-worktree-path-guard.js +1 -1
  88. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  89. package/hooks/gsd-config-reload.js +133 -0
  90. package/hooks/gsd-context-monitor.js +1 -1
  91. package/hooks/gsd-cursor-post-tool.js +75 -0
  92. package/hooks/gsd-cursor-session-start.js +52 -0
  93. package/hooks/gsd-workflow-guard.js +1 -0
  94. package/hooks/gsd-worktree-path-guard.js +1 -1
  95. package/hooks/hooks.json +69 -0
  96. package/hooks/managed-hooks-registry.cjs +3 -0
  97. package/package.json +8 -1
  98. package/scripts/affected-tests-lib.cjs +3 -2
  99. package/scripts/build-hooks.js +7 -0
  100. package/scripts/changeset/cli.cjs +226 -28
  101. package/scripts/changeset/lint.cjs +5 -4
  102. package/scripts/changeset/new.cjs +4 -4
  103. package/scripts/check-alias-drift.cjs +77 -71
  104. package/scripts/check-env.cjs +185 -179
  105. package/scripts/check-npm-integrity.cjs +115 -109
  106. package/scripts/ci-guard-runner.cjs +11 -5
  107. package/scripts/ci-prepare-test-scope.cjs +27 -22
  108. package/scripts/ci-rebase-check.cjs +46 -45
  109. package/scripts/ci-test-scope.cjs +126 -22
  110. package/scripts/diff-touches-shipped-paths.cjs +52 -44
  111. package/scripts/gen-inventory-manifest.cjs +38 -32
  112. package/scripts/gen-research-agents.cjs +276 -0
  113. package/scripts/issue-dedupe.cjs +278 -0
  114. package/scripts/lib/cli-exit.cjs +56 -0
  115. package/scripts/lint-command-contract.cjs +28 -22
  116. package/scripts/lint-descriptions.cjs +32 -28
  117. package/scripts/lint-docs-required.cjs +4 -4
  118. package/scripts/lint-legacy-dir-name.cjs +56 -52
  119. package/scripts/lint-pr-check-project-dir.cjs +3 -1
  120. package/scripts/lint-shell-command-projection-drift.cjs +27 -22
  121. package/scripts/lint-skill-deps.cjs +31 -26
  122. package/scripts/lint-test-file-count.allowlist.json +2 -0
  123. package/scripts/lint-test-file-count.cjs +5 -4
  124. package/scripts/mutation-matrix.cjs +6 -3
  125. package/scripts/prompt-injection-scan.sh +1 -1
  126. package/scripts/release-notes/discord-release-summary.cjs +373 -0
  127. package/scripts/release-notes/format-github-release-notes.cjs +8 -3
  128. package/scripts/release-tarball-smoke.cjs +6 -4
  129. package/scripts/research-profiles.cjs +149 -0
  130. package/scripts/run-affected-tests.cjs +2 -1
  131. package/scripts/run-cross-platform-tests.cjs +11 -7
  132. package/scripts/run-tests.cjs +8 -7
  133. package/scripts/strip-prose-atrefs.cjs +1 -1
  134. package/scripts/sync-manifest-versions.cjs +119 -0
  135. package/scripts/sync-runtime-launcher.cjs +0 -3
  136. package/scripts/verify-npm-publish.cjs +14 -26
@@ -61,6 +61,7 @@ function currentBranch(cwd) {
61
61
  cwd,
62
62
  encoding: 'utf8',
63
63
  stdio: ['ignore', 'pipe', 'ignore'],
64
+ windowsHide: true,
64
65
  });
65
66
  if (result.status !== 0) return '';
66
67
  return result.stdout.trim();
@@ -18,7 +18,7 @@ const fs = require('fs');
18
18
  const path = require('path');
19
19
  const { spawnSync } = require('child_process');
20
20
 
21
- const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 };
21
+ const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000, windowsHide: true };
22
22
 
23
23
  function git(args, cwd) {
24
24
  return spawnSync('git', args, { ...SPAWNOPT, cwd });
@@ -18,7 +18,10 @@
18
18
  const MANAGED_HOOKS = [
19
19
  'gsd-check-update-worker.js',
20
20
  'gsd-check-update.js',
21
+ 'gsd-config-reload.js',
21
22
  'gsd-context-monitor.js',
23
+ 'gsd-cursor-post-tool.js',
24
+ 'gsd-cursor-session-start.js',
22
25
  'gsd-graphify-update.sh',
23
26
  'gsd-phase-boundary.sh',
24
27
  'gsd-prompt-guard.js',
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ // gsd-hook-version: {{GSD_VERSION}}
3
+ // gsd-config-reload.js — FileChanged hook: hot-reload GSD config context
4
+ // Fires when .planning/config.json is modified, created, or deleted.
5
+ //
6
+ // When the user edits .planning/config.json mid-session, this hook reads the
7
+ // updated config and injects a summary as additionalContext so the agent knows
8
+ // the new configuration without requiring a session restart.
9
+ //
10
+ // Input (from Claude Code):
11
+ // { session_id, cwd, hook_event_name: "FileChanged",
12
+ // file_path: "/abs/path/.planning/config.json", event: "change"|"add"|"unlink" }
13
+ //
14
+ // Output:
15
+ // { hookSpecificOutput: { hookEventName: "FileChanged", additionalContext: "..." } }
16
+ // or exits 0 silently (if config absent, unreadable, or event is "unlink").
17
+ //
18
+ // Enabled for all Claude Code installs. This hook is always-on — it is a
19
+ // no-op when .planning/config.json is absent (ENOENT → exit 0).
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ let input = '';
25
+ // Timeout guard: if stdin does not close within 8s exit silently rather than
26
+ // hanging until Claude Code kills the process and reports "hook error".
27
+ const stdinTimeout = setTimeout(() => process.exit(0), 8000);
28
+ process.stdin.setEncoding('utf8');
29
+ process.stdin.on('data', chunk => (input += chunk));
30
+ process.stdin.on('end', () => {
31
+ clearTimeout(stdinTimeout);
32
+ try {
33
+ const data = JSON.parse(input);
34
+ const event = data.event; // "change" | "add" | "unlink"
35
+ const filePath = data.file_path || '';
36
+ const cwd = data.cwd || process.cwd();
37
+
38
+ // Only handle the GSD planning config — verify both basename and that the
39
+ // resolved path is .planning/config.json relative to cwd. The hook
40
+ // matcher ('config.json') fires on any watched config.json; this guard
41
+ // ensures an unrelated config.json in node_modules/ or elsewhere does not
42
+ // inject spurious additionalContext.
43
+ const basename = path.basename(filePath);
44
+ if (basename !== 'config.json') {
45
+ process.exit(0);
46
+ }
47
+ const expectedPath = path.resolve(cwd, '.planning', 'config.json');
48
+ if (path.resolve(filePath) !== expectedPath) {
49
+ process.exit(0);
50
+ }
51
+
52
+ // On unlink (deletion) emit a brief notice and exit
53
+ if (event === 'unlink') {
54
+ process.stdout.write(JSON.stringify({
55
+ hookSpecificOutput: {
56
+ hookEventName: 'FileChanged',
57
+ additionalContext:
58
+ 'GSD config (.planning/config.json) was deleted. ' +
59
+ 'Falling back to built-in defaults for this session.',
60
+ },
61
+ }));
62
+ process.exit(0);
63
+ }
64
+
65
+ // Read the updated config file
66
+ let config;
67
+ try {
68
+ const raw = fs.readFileSync(filePath, 'utf8');
69
+ config = JSON.parse(raw);
70
+ } catch (e) {
71
+ if (e && e.code === 'ENOENT') process.exit(0);
72
+ // Malformed JSON — inform the agent without crashing
73
+ process.stdout.write(JSON.stringify({
74
+ hookSpecificOutput: {
75
+ hookEventName: 'FileChanged',
76
+ additionalContext:
77
+ 'GSD config (.planning/config.json) was modified but could not be parsed. ' +
78
+ 'Check the file for JSON syntax errors.',
79
+ },
80
+ }));
81
+ process.exit(0);
82
+ }
83
+
84
+ // Build a concise summary of key config fields the agent cares about
85
+ const lines = ['GSD config reloaded (.planning/config.json updated):'];
86
+
87
+ if (config.runtime) lines.push(` runtime: ${config.runtime}`);
88
+ if (config.mode) lines.push(` mode: ${config.mode}`);
89
+
90
+ // hooks section (opt-in toggles agents act on)
91
+ if (config.hooks && typeof config.hooks === 'object') {
92
+ const hookKeys = Object.entries(config.hooks)
93
+ .filter(([, v]) => v !== undefined)
94
+ .map(([k, v]) => `${k}=${v}`)
95
+ .join(', ');
96
+ if (hookKeys) lines.push(` hooks: { ${hookKeys} }`);
97
+ }
98
+
99
+ // workflow section (key toggles)
100
+ if (config.workflow && typeof config.workflow === 'object') {
101
+ const wfKeys = Object.entries(config.workflow)
102
+ .filter(([, v]) => v !== undefined)
103
+ .map(([k, v]) => `${k}=${v}`)
104
+ .join(', ');
105
+ if (wfKeys) lines.push(` workflow: { ${wfKeys} }`);
106
+ }
107
+
108
+ // model overrides (agents use these)
109
+ if (config.models && typeof config.models === 'object') {
110
+ const modelKeys = Object.entries(config.models)
111
+ .filter(([, v]) => v !== undefined)
112
+ .map(([k, v]) => `${k}=${v}`)
113
+ .join(', ');
114
+ if (modelKeys) lines.push(` models: { ${modelKeys} }`);
115
+ }
116
+
117
+ if (lines.length === 1) {
118
+ // No notable fields — still confirm the reload happened
119
+ lines.push(' (no notable keys changed)');
120
+ }
121
+
122
+ const additionalContext = lines.join('\n');
123
+ process.stdout.write(JSON.stringify({
124
+ hookSpecificOutput: {
125
+ hookEventName: 'FileChanged',
126
+ additionalContext,
127
+ },
128
+ }));
129
+ } catch (e) {
130
+ // Silent fail — never block the session on a config reload error
131
+ process.exit(0);
132
+ }
133
+ });
@@ -150,7 +150,7 @@ process.stdin.on('end', () => {
150
150
  spawn(
151
151
  process.execPath,
152
152
  [gsdTools, 'state', 'record-session', '--stopped-at', stoppedAt],
153
- { cwd, detached: true, stdio: 'ignore' }
153
+ { cwd, detached: true, stdio: 'ignore', windowsHide: true }
154
154
  ).unref();
155
155
  warnData.criticalRecorded = true;
156
156
  // Persist the sentinel so subsequent debounce cycles don't re-fire
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ // gsd-hook-version: {{GSD_VERSION}}
3
+ // gsd-cursor-post-tool.js — Cursor postToolUse hook (issue #777)
4
+ //
5
+ // Cursor invokes this script after each tool call completes.
6
+ // Protocol: JSON from Cursor on stdin; JSON response on stdout.
7
+ //
8
+ // Input schema (cursor postToolUse):
9
+ // { tool_name, tool_input, tool_output, duration,
10
+ // conversation_id, generation_id, model, hook_event_name,
11
+ // cursor_version, workspace_roots, user_email, transcript_path }
12
+ //
13
+ // Output schema (cursor postToolUse):
14
+ // { additional_context?: string } ← injected as context after the tool use
15
+ //
16
+ // Behaviour:
17
+ // - After a write-class tool that targets .planning/, reminds the agent
18
+ // to keep STATE.md current.
19
+ // - Fails open: any error silently exits 0.
20
+ //
21
+ // Cursor docs: https://cursor.com/docs/hooks
22
+
23
+ 'use strict';
24
+
25
+ const WRITE_TOOL_RE = /write|edit|replace|create|delete|remove|append|apply|patch|insert|mkdir/i;
26
+ const PATH_KEY_RE = /^(path|file|file_?path|filepath|target_?path|target|dir|directory|uri|filename)$/i;
27
+ const PLANNING_PATH_RE = /(^|[\\/])\.planning([\\/]|$)/;
28
+
29
+ let raw = '';
30
+ const stdinTimeout = setTimeout(() => {
31
+ // Timeout guard: exit silently rather than hanging.
32
+ process.exit(0);
33
+ }, 10000);
34
+
35
+ process.stdin.setEncoding('utf8');
36
+ process.stdin.on('data', (chunk) => { raw += chunk; });
37
+ process.stdin.on('end', () => {
38
+ clearTimeout(stdinTimeout);
39
+ try {
40
+ let input;
41
+ try { input = JSON.parse(raw || '{}'); } catch { process.stdout.write(JSON.stringify({})); return; }
42
+
43
+ const toolName = String(
44
+ input.tool_name || input.toolName || ''
45
+ ).toLowerCase();
46
+
47
+ const isWrite = WRITE_TOOL_RE.test(toolName);
48
+ if (!isWrite) { process.stdout.write(JSON.stringify({})); return; }
49
+
50
+ // Collect only PATH-bearing field values (not free-form content).
51
+ const paths = [];
52
+ const walk = (v, depth) => {
53
+ if (depth > 5 || paths.length > 64) return;
54
+ if (Array.isArray(v)) { for (const x of v) walk(x, depth + 1); return; }
55
+ if (v && typeof v === 'object') {
56
+ for (const k of Object.keys(v)) {
57
+ const val = v[k];
58
+ if (typeof val === 'string' && PATH_KEY_RE.test(k)) paths.push(val);
59
+ else walk(val, depth + 1);
60
+ }
61
+ }
62
+ };
63
+ walk(input.tool_input || input.toolInput || {}, 0);
64
+
65
+ if (paths.some((p) => PLANNING_PATH_RE.test(p))) {
66
+ process.stdout.write(JSON.stringify({
67
+ additional_context:
68
+ 'GSD: .planning/ artifact updated — ensure STATE.md reflects the latest phase and progress.',
69
+ }));
70
+ return;
71
+ }
72
+ } catch { /* fall through to empty response */ }
73
+
74
+ process.stdout.write(JSON.stringify({}));
75
+ });
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ // gsd-hook-version: {{GSD_VERSION}}
3
+ // gsd-cursor-session-start.js — Cursor sessionStart hook (issue #777)
4
+ //
5
+ // Cursor invokes this script at the start of each agent session.
6
+ // Protocol: JSON from Cursor on stdin; JSON response on stdout.
7
+ //
8
+ // Input schema (cursor sessionStart):
9
+ // { session_id, is_background_agent, composer_mode, conversation_id,
10
+ // generation_id, model, hook_event_name, cursor_version,
11
+ // workspace_roots, user_email, transcript_path }
12
+ //
13
+ // Output schema (cursor sessionStart):
14
+ // { additional_context?: string } ← injected into the session as context
15
+ //
16
+ // Behaviour:
17
+ // - If .planning/STATE.md is present, injects a brief state reminder.
18
+ // - If absent, nudges the user toward /gsd:new-project.
19
+ // - Fails open: any error silently exits 0 so a hook bug never wedges Cursor.
20
+ //
21
+ // Cursor docs: https://cursor.com/docs/hooks
22
+
23
+ 'use strict';
24
+
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+
28
+ const MSG_PRESENT =
29
+ 'GSD: .planning/STATE.md is present — review the current phase and any blockers before acting.';
30
+ const MSG_ABSENT =
31
+ 'GSD: no .planning/ workflow found — run /gsd:new-project to start a tracked workflow.';
32
+
33
+ let raw = '';
34
+ const stdinTimeout = setTimeout(() => {
35
+ // Timeout guard: exit silently rather than hanging.
36
+ process.exit(0);
37
+ }, 10000);
38
+
39
+ process.stdin.setEncoding('utf8');
40
+ process.stdin.on('data', (chunk) => { raw += chunk; });
41
+ process.stdin.on('end', () => {
42
+ clearTimeout(stdinTimeout);
43
+ try {
44
+ const statePath = path.join(process.cwd(), '.planning', 'STATE.md');
45
+ const statePresent = fs.existsSync(statePath);
46
+ const msg = statePresent ? MSG_PRESENT : MSG_ABSENT;
47
+ process.stdout.write(JSON.stringify({ additional_context: msg }));
48
+ } catch {
49
+ // Fail open — never block a Cursor session because of a GSD hook error.
50
+ process.stdout.write(JSON.stringify({}));
51
+ }
52
+ });
@@ -61,6 +61,7 @@ function currentBranch(cwd) {
61
61
  cwd,
62
62
  encoding: 'utf8',
63
63
  stdio: ['ignore', 'pipe', 'ignore'],
64
+ windowsHide: true,
64
65
  });
65
66
  if (result.status !== 0) return '';
66
67
  return result.stdout.trim();
@@ -18,7 +18,7 @@ const fs = require('fs');
18
18
  const path = require('path');
19
19
  const { spawnSync } = require('child_process');
20
20
 
21
- const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 };
21
+ const SPAWNOPT = { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000, windowsHide: true };
22
22
 
23
23
  function git(args, cwd) {
24
24
  return spawnSync('git', args, { ...SPAWNOPT, cwd });
@@ -0,0 +1,69 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-check-update.js\"" }
7
+ ]
8
+ }
9
+ ],
10
+ "PreToolUse": [
11
+ {
12
+ "matcher": "Write|Edit",
13
+ "hooks": [
14
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-prompt-guard.js\"", "timeout": 5 },
15
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-read-guard.js\"", "timeout": 5 }
16
+ ]
17
+ },
18
+ {
19
+ "matcher": "Write|Edit|MultiEdit",
20
+ "hooks": [
21
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-worktree-path-guard.js\"", "timeout": 5 }
22
+ ]
23
+ }
24
+ ],
25
+ "PostToolUse": [
26
+ {
27
+ "matcher": "Bash|Edit|Write|MultiEdit|Agent|Task",
28
+ "hooks": [
29
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-context-monitor.js\"", "timeout": 10 }
30
+ ]
31
+ },
32
+ {
33
+ "matcher": "Read",
34
+ "hooks": [
35
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-read-injection-scanner.js\"", "timeout": 5 }
36
+ ]
37
+ }
38
+ ],
39
+ "SubagentStop": [
40
+ {
41
+ "hooks": [
42
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-context-monitor.js\"", "timeout": 10 }
43
+ ]
44
+ }
45
+ ],
46
+ "Stop": [
47
+ {
48
+ "hooks": [
49
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-context-monitor.js\"", "timeout": 10 }
50
+ ]
51
+ }
52
+ ],
53
+ "PreCompact": [
54
+ {
55
+ "hooks": [
56
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-context-monitor.js\"", "timeout": 10 }
57
+ ]
58
+ }
59
+ ],
60
+ "FileChanged": [
61
+ {
62
+ "matcher": "config.json",
63
+ "hooks": [
64
+ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gsd-config-reload.js\"", "timeout": 8 }
65
+ ]
66
+ }
67
+ ]
68
+ }
69
+ }
@@ -18,7 +18,10 @@
18
18
  const MANAGED_HOOKS = [
19
19
  'gsd-check-update-worker.js',
20
20
  'gsd-check-update.js',
21
+ 'gsd-config-reload.js',
21
22
  'gsd-context-monitor.js',
23
+ 'gsd-cursor-post-tool.js',
24
+ 'gsd-cursor-session-start.js',
22
25
  'gsd-graphify-update.sh',
23
26
  'gsd-phase-boundary.sh',
24
27
  'gsd-prompt-guard.js',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengsd/gsd-core",
3
- "version": "1.3.1",
3
+ "version": "1.4.0-rc.2",
4
4
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
5
5
  "bin": {
6
6
  "gsd-core": "bin/install.js",
@@ -12,6 +12,9 @@
12
12
  "gsd-core",
13
13
  "assets",
14
14
  "agents",
15
+ ".claude-plugin",
16
+ "gemini-extension.json",
17
+ "GEMINI.md",
15
18
  "hooks",
16
19
  "scripts"
17
20
  ],
@@ -62,6 +65,9 @@
62
65
  "typescript": "^6.0.3",
63
66
  "typescript-eslint": "^8.60.0"
64
67
  },
68
+ "overrides": {
69
+ "qs": ">=6.15.2"
70
+ },
65
71
  "optionalDependencies": {
66
72
  "fallow": "^2.70.0"
67
73
  },
@@ -77,6 +83,7 @@
77
83
  "generate:identity": "node scripts/generate-package-identity.cjs",
78
84
  "prepack": "npm run build:lib",
79
85
  "prepare": "npm run build:lib",
86
+ "version": "node scripts/sync-manifest-versions.cjs --stage",
80
87
  "prepublishOnly": "npm run build:lib && npm run build:hooks",
81
88
  "pretest": "npm run build:lib && npm run lint:skill-deps",
82
89
  "pretest:coverage": "npm run build:lib && npm run lint:skill-deps",
@@ -4,6 +4,7 @@ const { execFileSync } = require('node:child_process');
4
4
  const { readdirSync, readFileSync, existsSync } = require('node:fs');
5
5
  const path = require('node:path');
6
6
 
7
+ const { ExitError } = require('./lib/cli-exit.cjs');
7
8
  const { suiteOf } = require('./run-tests.cjs');
8
9
 
9
10
  const CRITICAL_PATHS = [
@@ -409,7 +410,7 @@ function runNodeTestFiles(repoRoot, files) {
409
410
  if (firstFailure === 0) firstFailure = code;
410
411
  }
411
412
  }
412
- if (firstFailure !== 0) process.exit(firstFailure);
413
+ if (firstFailure !== 0) throw new ExitError(firstFailure);
413
414
  }
414
415
 
415
416
  function runSuite(repoRoot, suite) {
@@ -442,7 +443,7 @@ function resolveBaseRef() {
442
443
  * security), so every concrete match that pickAffectedTests put into `selected`
443
444
  * belongs to one of those suites and will be exercised by running all three.
444
445
  */
445
- function resolveRunPlan({ changedFiles, selected, widenRequired, criticalPath, noChanges }) {
446
+ function resolveRunPlan({ changedFiles: _changedFiles, selected, widenRequired, criticalPath, noChanges }) {
446
447
  if (noChanges) {
447
448
  return { mode: 'suite', suite: 'unit' };
448
449
  }
@@ -30,6 +30,13 @@ const HOOKS_TO_COPY = [
30
30
  // so require('./managed-hooks-registry.cjs') resolves in the installed hooks/ dir.
31
31
  'managed-hooks-registry.cjs',
32
32
  'gsd-context-monitor.js',
33
+ // Cursor lifecycle hooks (issue #777): sessionStart context injection + postToolUse monitor
34
+ 'gsd-cursor-session-start.js',
35
+ 'gsd-cursor-post-tool.js',
36
+ // Claude Code FileChanged hook (#770) — hot-reloads gsd config when
37
+ // .planning/config.json changes mid-session. Must ship to dist so the
38
+ // installer can copy it to the target hooks/ dir and register FileChanged.
39
+ 'gsd-config-reload.js',
33
40
  'gsd-prompt-guard.js',
34
41
  'gsd-read-guard.js',
35
42
  'gsd-read-injection-scanner.js',