@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.
- package/.claude-plugin/plugin.json +23 -0
- package/GEMINI.md +53 -0
- package/agents/gsd-advisor-researcher.md +1 -20
- package/agents/gsd-ai-researcher.md +2 -21
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.md +1 -1
- package/agents/gsd-domain-researcher.md +2 -21
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-eval-planner.md +1 -1
- package/agents/gsd-executor.md +1 -1
- package/agents/gsd-framework-selector.md +1 -1
- package/agents/gsd-nyquist-auditor.md +1 -1
- package/agents/gsd-pattern-mapper.md +1 -1
- package/agents/gsd-phase-researcher.md +92 -166
- package/agents/gsd-planner.md +9 -36
- package/agents/gsd-project-researcher.md +62 -141
- package/agents/gsd-security-auditor.md +1 -1
- package/agents/gsd-ui-auditor.md +1 -1
- package/agents/gsd-ui-checker.md +1 -1
- package/agents/gsd-ui-researcher.md +3 -22
- package/agents/gsd-user-profiler.md +1 -1
- package/agents/gsd-verifier.md +8 -2
- package/bin/install.js +1977 -339
- package/commands/gsd/autonomous.md +2 -0
- package/commands/gsd/execute-phase.md +2 -0
- package/commands/gsd/graphify.md +11 -6
- package/commands/gsd/import.md +6 -2
- package/commands/gsd/plan-phase.md +4 -2
- package/commands/gsd/progress.md +1 -0
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/update.md +3 -2
- package/gemini-extension.json +6 -0
- package/gsd-core/bin/check-latest-version.cjs +61 -6
- package/gsd-core/bin/gsd-tools.cjs +238 -32
- package/gsd-core/bin/lib/check-command-router.cjs +1 -0
- package/gsd-core/bin/lib/cli-exit.cjs +42 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +1 -1
- package/gsd-core/bin/lib/commands.cjs +5 -4
- package/gsd-core/bin/lib/config.cjs +28 -4
- package/gsd-core/bin/lib/core.cjs +72 -28
- package/gsd-core/bin/lib/graphify.cjs +2 -2
- package/gsd-core/bin/lib/init-command-router.cjs +2 -2
- package/gsd-core/bin/lib/init.cjs +19 -3
- package/gsd-core/bin/lib/install-profiles.cjs +58 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +1 -1
- package/gsd-core/bin/lib/intel.cjs +3 -20
- package/gsd-core/bin/lib/package-legitimacy.cjs +368 -0
- package/gsd-core/bin/lib/phase.cjs +3 -3
- package/gsd-core/bin/lib/research-provider.cjs +137 -0
- package/gsd-core/bin/lib/research-store.cjs +167 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +4 -19
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +67 -9
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +56 -0
- package/gsd-core/bin/lib/runtime-homes.cjs +32 -11
- package/gsd-core/bin/lib/security.cjs +73 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +9 -0
- package/gsd-core/bin/lib/surface.cjs +54 -11
- package/gsd-core/bin/lib/validate.cjs +2 -2
- package/gsd-core/bin/lib/verification-command-router.cjs +31 -0
- package/gsd-core/bin/lib/verification.cjs +193 -0
- package/gsd-core/bin/lib/verify.cjs +2 -2
- package/gsd-core/bin/lib/workstream-inventory.cjs +1 -1
- package/gsd-core/bin/lib/worktree-base-ref.cjs +325 -0
- package/gsd-core/bin/lib/worktree-safety.cjs +31 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +2 -1
- package/gsd-core/bin/verify-reapply-patches.cjs +8 -11
- package/gsd-core/references/planner-load-graph-context.md +36 -0
- package/gsd-core/references/planning-config.md +3 -1
- package/gsd-core/references/research-documentation-lookup.md +29 -0
- package/gsd-core/references/research-philosophy.md +29 -0
- package/gsd-core/references/research-verification-protocol.md +27 -0
- package/gsd-core/workflows/execute-phase.md +19 -8
- package/gsd-core/workflows/help/modes/full.md +4 -3
- package/gsd-core/workflows/ingest-docs.md +3 -2
- package/gsd-core/workflows/plan-phase.md +14 -10
- package/gsd-core/workflows/plan-review-convergence.md +3 -3
- package/gsd-core/workflows/review.md +24 -7
- package/gsd-core/workflows/ship.md +5 -8
- package/gsd-core/workflows/spec-phase.md +2 -1
- package/gsd-core/workflows/update.md +34 -6
- package/hooks/dist/gsd-config-reload.js +133 -0
- package/hooks/dist/gsd-context-monitor.js +1 -1
- package/hooks/dist/gsd-cursor-post-tool.js +75 -0
- package/hooks/dist/gsd-cursor-session-start.js +52 -0
- package/hooks/dist/gsd-workflow-guard.js +1 -0
- package/hooks/dist/gsd-worktree-path-guard.js +1 -1
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-config-reload.js +133 -0
- package/hooks/gsd-context-monitor.js +1 -1
- package/hooks/gsd-cursor-post-tool.js +75 -0
- package/hooks/gsd-cursor-session-start.js +52 -0
- package/hooks/gsd-workflow-guard.js +1 -0
- package/hooks/gsd-worktree-path-guard.js +1 -1
- package/hooks/hooks.json +69 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +8 -1
- package/scripts/affected-tests-lib.cjs +3 -2
- package/scripts/build-hooks.js +7 -0
- package/scripts/changeset/cli.cjs +226 -28
- package/scripts/changeset/lint.cjs +5 -4
- package/scripts/changeset/new.cjs +4 -4
- package/scripts/check-alias-drift.cjs +77 -71
- package/scripts/check-env.cjs +185 -179
- package/scripts/check-npm-integrity.cjs +115 -109
- package/scripts/ci-guard-runner.cjs +11 -5
- package/scripts/ci-prepare-test-scope.cjs +27 -22
- package/scripts/ci-rebase-check.cjs +46 -45
- package/scripts/ci-test-scope.cjs +126 -22
- package/scripts/diff-touches-shipped-paths.cjs +52 -44
- package/scripts/gen-inventory-manifest.cjs +38 -32
- package/scripts/gen-research-agents.cjs +276 -0
- package/scripts/issue-dedupe.cjs +278 -0
- package/scripts/lib/cli-exit.cjs +56 -0
- package/scripts/lint-command-contract.cjs +28 -22
- package/scripts/lint-descriptions.cjs +32 -28
- package/scripts/lint-docs-required.cjs +4 -4
- package/scripts/lint-legacy-dir-name.cjs +56 -52
- package/scripts/lint-pr-check-project-dir.cjs +3 -1
- package/scripts/lint-shell-command-projection-drift.cjs +27 -22
- package/scripts/lint-skill-deps.cjs +31 -26
- package/scripts/lint-test-file-count.allowlist.json +2 -0
- package/scripts/lint-test-file-count.cjs +5 -4
- package/scripts/mutation-matrix.cjs +6 -3
- package/scripts/prompt-injection-scan.sh +1 -1
- package/scripts/release-notes/discord-release-summary.cjs +373 -0
- package/scripts/release-notes/format-github-release-notes.cjs +8 -3
- package/scripts/release-tarball-smoke.cjs +6 -4
- package/scripts/research-profiles.cjs +149 -0
- package/scripts/run-affected-tests.cjs +2 -1
- package/scripts/run-cross-platform-tests.cjs +11 -7
- package/scripts/run-tests.cjs +8 -7
- package/scripts/strip-prose-atrefs.cjs +1 -1
- package/scripts/sync-manifest-versions.cjs +119 -0
- package/scripts/sync-runtime-launcher.cjs +0 -3
- package/scripts/verify-npm-publish.cjs +14 -26
|
@@ -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
|
+
});
|
|
@@ -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 });
|
package/hooks/hooks.json
ADDED
|
@@ -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
|
+
"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)
|
|
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
|
}
|
package/scripts/build-hooks.js
CHANGED
|
@@ -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',
|