@opengsd/gsd-core 1.3.1 → 1.4.0-rc.1
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/agents/gsd-advisor-researcher.md +1 -20
- package/agents/gsd-ai-researcher.md +1 -20
- package/agents/gsd-domain-researcher.md +1 -20
- package/agents/gsd-executor.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-ui-researcher.md +2 -21
- package/agents/gsd-verifier.md +8 -2
- package/bin/install.js +85 -4
- package/commands/gsd/graphify.md +11 -6
- package/commands/gsd/import.md +6 -2
- package/commands/gsd/plan-phase.md +2 -2
- package/gsd-core/bin/check-latest-version.cjs +3 -2
- 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/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/security.cjs +73 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +3 -0
- 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 +2 -2
- 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 +22 -5
- package/gsd-core/workflows/ship.md +5 -8
- package/gsd-core/workflows/spec-phase.md +2 -1
- package/gsd-core/workflows/update.md +2 -1
- package/hooks/dist/gsd-context-monitor.js +1 -1
- package/hooks/dist/gsd-workflow-guard.js +1 -0
- package/hooks/dist/gsd-worktree-path-guard.js +1 -1
- package/hooks/gsd-context-monitor.js +1 -1
- package/hooks/gsd-workflow-guard.js +1 -0
- package/hooks/gsd-worktree-path-guard.js +1 -1
- package/package.json +4 -1
- package/scripts/affected-tests-lib.cjs +3 -2
- package/scripts/changeset/cli.cjs +183 -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 +6 -4
- 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/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 +1 -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/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-runtime-launcher.cjs +0 -3
- package/scripts/verify-npm-publish.cjs +14 -26
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
const { execFileSync } = require('child_process');
|
|
34
34
|
const fs = require('fs');
|
|
35
35
|
const path = require('path');
|
|
36
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
36
37
|
|
|
37
38
|
// Constructed with split to avoid self-match when this script is scanned.
|
|
38
39
|
const FORBIDDEN = 'get-shit' + '-done';
|
|
@@ -90,67 +91,70 @@ function isBinary(fullPath) {
|
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
94
|
+
function main() {
|
|
95
|
+
// Enumerate tracked files via git ls-files so only committed/staged source is checked.
|
|
96
|
+
let trackedFiles;
|
|
97
|
+
try {
|
|
98
|
+
trackedFiles = execFileSync('git', ['ls-files'], { cwd: REPO_ROOT, encoding: 'utf8' })
|
|
99
|
+
.split('\n')
|
|
100
|
+
.map((f) => f.trim())
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
throw new ExitError(1, 'ERROR lint-legacy-dir-name: git ls-files failed: ' + err.message);
|
|
104
|
+
}
|
|
104
105
|
|
|
105
|
-
const violations = [];
|
|
106
|
+
const violations = [];
|
|
106
107
|
|
|
107
|
-
for (const relPath of trackedFiles) {
|
|
108
|
-
|
|
108
|
+
for (const relPath of trackedFiles) {
|
|
109
|
+
if (isAllowlisted(relPath)) continue;
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
const fullPath = path.join(REPO_ROOT, relPath);
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
// Skip this guard script itself.
|
|
114
|
+
if (path.resolve(fullPath) === SELF_PATH) continue;
|
|
114
115
|
|
|
115
|
-
|
|
116
|
+
if (isBinary(fullPath)) continue;
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
let content;
|
|
119
|
+
try {
|
|
120
|
+
content = fs.readFileSync(fullPath, 'utf8');
|
|
121
|
+
} catch {
|
|
122
|
+
// Unreadable files (permissions, etc.) — skip silently.
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
124
125
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
126
|
+
const lines = content.split('\n');
|
|
127
|
+
for (let i = 0; i < lines.length; i++) {
|
|
128
|
+
const line = lines[i];
|
|
129
|
+
// Skip lines that carry the explicit allow marker.
|
|
130
|
+
if (line.includes(ALLOW_MARKER)) continue;
|
|
131
|
+
|
|
132
|
+
FORBIDDEN_RE.lastIndex = 0;
|
|
133
|
+
let match;
|
|
134
|
+
while ((match = FORBIDDEN_RE.exec(line)) !== null) {
|
|
135
|
+
violations.push({
|
|
136
|
+
file: relPath,
|
|
137
|
+
line: i + 1,
|
|
138
|
+
col: match.index + 1,
|
|
139
|
+
text: match[0],
|
|
140
|
+
});
|
|
141
|
+
}
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
|
-
}
|
|
143
144
|
|
|
144
|
-
if (violations.length === 0) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
145
|
+
if (violations.length === 0) {
|
|
146
|
+
process.stdout.write('ok lint-legacy-dir-name: ' + trackedFiles.length + ' tracked file(s) checked, 0 violations\n');
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
148
149
|
|
|
149
|
-
process.stderr.write('\nERROR lint-legacy-dir-name: ' + violations.length + ' violation(s) found\n\n');
|
|
150
|
-
for (const v of violations) {
|
|
151
|
-
|
|
150
|
+
process.stderr.write('\nERROR lint-legacy-dir-name: ' + violations.length + ' violation(s) found\n\n');
|
|
151
|
+
for (const v of violations) {
|
|
152
|
+
process.stderr.write(' ' + v.file + ':' + v.line + ':' + v.col + ' — ' + JSON.stringify(v.text) + '\n');
|
|
153
|
+
}
|
|
154
|
+
process.stderr.write('\n');
|
|
155
|
+
process.stderr.write('Fix: rename to gsd-core, or add `gsd-allow-legacy-name` marker on the line if the\n');
|
|
156
|
+
process.stderr.write(' use is intentional (migration modules, tests, guard, changeset).\n\n');
|
|
157
|
+
return 1;
|
|
152
158
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
process.stderr.write(' use is intentional (migration modules, tests, guard, changeset).\n\n');
|
|
156
|
-
process.exit(1);
|
|
159
|
+
|
|
160
|
+
runMain(main);
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
|
+
const { runMain } = require('./lib/cli-exit.cjs');
|
|
8
|
+
|
|
7
9
|
const ROOT = path.join(__dirname, '..');
|
|
8
10
|
|
|
9
11
|
const DEFAULT_RELATIVE_FILES = [
|
|
@@ -84,7 +86,7 @@ function main(argv = process.argv.slice(2)) {
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
if (require.main === module) {
|
|
87
|
-
|
|
89
|
+
runMain(main);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
module.exports = {
|
|
@@ -13,19 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
16
17
|
|
|
17
18
|
const ROOT = path.resolve(__dirname, '..');
|
|
18
|
-
const targetArg = process.argv[2] || path.join(ROOT, 'bin', 'install.js');
|
|
19
|
-
const target = path.resolve(targetArg);
|
|
20
|
-
const rel = path.relative(ROOT, target);
|
|
21
|
-
|
|
22
|
-
let content;
|
|
23
|
-
try {
|
|
24
|
-
content = fs.readFileSync(target, 'utf8');
|
|
25
|
-
} catch (error) {
|
|
26
|
-
process.stderr.write(`lint-shell-command-projection-drift: failed to read ${target}: ${error.message}\n`);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
19
|
|
|
30
20
|
const forbidden = [
|
|
31
21
|
{
|
|
@@ -42,16 +32,31 @@ const forbidden = [
|
|
|
42
32
|
},
|
|
43
33
|
];
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
35
|
+
function main() {
|
|
36
|
+
const targetArg = process.argv[2] || path.join(ROOT, 'bin', 'install.js');
|
|
37
|
+
const target = path.resolve(targetArg);
|
|
38
|
+
const rel = path.relative(ROOT, target);
|
|
39
|
+
|
|
40
|
+
let content;
|
|
41
|
+
try {
|
|
42
|
+
content = fs.readFileSync(target, 'utf8');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new ExitError(1, `lint-shell-command-projection-drift: failed to read ${target}: ${error.message}`);
|
|
45
|
+
}
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
const matches = forbidden.filter((rule) => rule.pattern.test(content));
|
|
48
|
+
if (matches.length === 0) {
|
|
49
|
+
process.stdout.write(`ok shell-projection-drift: ${rel}\n`);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
process.stderr.write(`ERROR shell-projection-drift: inline serialized shim builders found in ${rel}\n`);
|
|
54
|
+
for (const match of matches) {
|
|
55
|
+
process.stderr.write(` - ${match.label}\n`);
|
|
56
|
+
}
|
|
57
|
+
process.stderr.write('Route shim/wrapper rendering through gsd-core/bin/lib/shell-command-projection.cjs\n');
|
|
58
|
+
process.stderr.write('Safe subprocess execution via spawnSync/execFileSync is intentionally allowed.\n');
|
|
59
|
+
return 1;
|
|
54
60
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
process.exit(1);
|
|
61
|
+
|
|
62
|
+
runMain(main);
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
const fs = require('fs');
|
|
27
27
|
const path = require('path');
|
|
28
|
+
const { runMain } = require('./lib/cli-exit.cjs');
|
|
28
29
|
|
|
29
30
|
const PROFILES_MODULE = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'install-profiles.cjs');
|
|
30
31
|
const { PROFILES, loadSkillsManifest, resolveProfile } = require(PROFILES_MODULE);
|
|
@@ -142,39 +143,43 @@ function checkProfileClosure(manifest) {
|
|
|
142
143
|
// Main
|
|
143
144
|
// ---------------------------------------------------------------------------
|
|
144
145
|
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
)
|
|
146
|
+
function main() {
|
|
147
|
+
const manifest = loadSkillsManifest(commandsDir);
|
|
148
|
+
const allStems = new Set(
|
|
149
|
+
[...manifest.keys()].filter((k) => !k.startsWith('_calls_agents_'))
|
|
150
|
+
);
|
|
149
151
|
|
|
150
|
-
const consistencyViolations = checkFrontmatterBodyConsistency(manifest, allStems);
|
|
151
|
-
const closureViolations = checkProfileClosure(manifest);
|
|
152
|
+
const consistencyViolations = checkFrontmatterBodyConsistency(manifest, allStems);
|
|
153
|
+
const closureViolations = checkProfileClosure(manifest);
|
|
152
154
|
|
|
153
|
-
const totalViolations = consistencyViolations.length + closureViolations.length;
|
|
155
|
+
const totalViolations = consistencyViolations.length + closureViolations.length;
|
|
154
156
|
|
|
155
|
-
if (totalViolations === 0) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
157
|
+
if (totalViolations === 0) {
|
|
158
|
+
const checked = manifest.size;
|
|
159
|
+
process.stdout.write('ok lint-skill-deps: ' + checked + ' skill(s) checked, 0 violations\n');
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
160
162
|
|
|
161
|
-
process.stderr.write('\nERROR lint-skill-deps: ' + totalViolations + ' violation(s) found\n\n');
|
|
163
|
+
process.stderr.write('\nERROR lint-skill-deps: ' + totalViolations + ' violation(s) found\n\n');
|
|
162
164
|
|
|
163
|
-
if (consistencyViolations.length > 0) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
if (consistencyViolations.length > 0) {
|
|
166
|
+
process.stderr.write('Frontmatter to body consistency violations (' + consistencyViolations.length + '):\n\n');
|
|
167
|
+
for (const v of consistencyViolations) {
|
|
168
|
+
process.stderr.write(' ' + v.filePath + '\n');
|
|
169
|
+
process.stderr.write(' ' + v.message + '\n\n');
|
|
170
|
+
}
|
|
171
|
+
process.stderr.write('Fix: add missing deps to requires: in the skill frontmatter.\n\n');
|
|
168
172
|
}
|
|
169
|
-
process.stderr.write('Fix: add missing deps to requires: in the skill frontmatter.\n\n');
|
|
170
|
-
}
|
|
171
173
|
|
|
172
|
-
if (closureViolations.length > 0) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
if (closureViolations.length > 0) {
|
|
175
|
+
process.stderr.write('Profile closure violations (' + closureViolations.length + '):\n\n');
|
|
176
|
+
for (const v of closureViolations) {
|
|
177
|
+
process.stderr.write(' ' + v.message + '\n');
|
|
178
|
+
}
|
|
179
|
+
process.stderr.write('\nFix: add the missing skills to the profile base set in install-profiles.cjs.\n\n');
|
|
176
180
|
}
|
|
177
|
-
|
|
181
|
+
|
|
182
|
+
return 1;
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
|
|
185
|
+
runMain(main);
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const { assertWithinAllowlist } = require('./lib/allowlist-ratchet.cjs');
|
|
20
|
+
const { runMain } = require('./lib/cli-exit.cjs');
|
|
20
21
|
|
|
21
22
|
const ROOT = path.join(__dirname, '..');
|
|
22
23
|
const PROD_DIRS = [
|
|
@@ -201,14 +202,14 @@ function run() {
|
|
|
201
202
|
|
|
202
203
|
if (jsonMode) {
|
|
203
204
|
console.log(JSON.stringify({ ok: failures.length === 0, results, failures, hints: [] }, null, 2));
|
|
204
|
-
|
|
205
|
+
return failures.length > 0 ? 1 : 0;
|
|
205
206
|
}
|
|
206
207
|
|
|
207
208
|
if (failures.length === 0) {
|
|
208
209
|
const inAllowlist = results.filter(r => r.verdict === Verdict.OK_IN_ALLOWLIST).length;
|
|
209
210
|
console.log(`ok lint-test-file-count: ${results.length} module(s) checked, 0 failures` +
|
|
210
211
|
(inAllowlist > 0 ? `, ${inAllowlist} allowlisted` : ''));
|
|
211
|
-
|
|
212
|
+
return 0;
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
process.stderr.write(`\nERROR lint-test-file-count: ${failures.length} module(s) exceed the test-file limit\n\n`);
|
|
@@ -231,7 +232,7 @@ function run() {
|
|
|
231
232
|
}
|
|
232
233
|
process.stderr.write('\nFix: consolidate test files per module (one primary + one integration).\n');
|
|
233
234
|
process.stderr.write('Or update scripts/lint-test-file-count.allowlist.json with PR justification.\n\n');
|
|
234
|
-
|
|
235
|
+
return 1;
|
|
235
236
|
}
|
|
236
237
|
|
|
237
238
|
module.exports = {
|
|
@@ -242,4 +243,4 @@ module.exports = {
|
|
|
242
243
|
_loadAllowlist: loadAllowlist,
|
|
243
244
|
};
|
|
244
245
|
|
|
245
|
-
if (require.main === module) run
|
|
246
|
+
if (require.main === module) runMain(run);
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
const { execFileSync } = require('child_process');
|
|
33
33
|
const { readFileSync } = require('fs');
|
|
34
34
|
|
|
35
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
36
|
+
|
|
35
37
|
// ── Single source of truth: covered modules ───────────────────────────────────
|
|
36
38
|
// Each entry: { cjs: '<built artifact>', tests: ['tests/...', ...] }
|
|
37
39
|
// A module is "covered" iff its tests are wired into the Stryker command runner
|
|
@@ -121,7 +123,7 @@ function parseArgs(argv) {
|
|
|
121
123
|
' --base <ref> Git ref to diff against (default: origin/${GITHUB_BASE_REF:-next})',
|
|
122
124
|
' --print Human-readable output instead of JSON',
|
|
123
125
|
].join('\n'));
|
|
124
|
-
|
|
126
|
+
throw new ExitError(0);
|
|
125
127
|
} else {
|
|
126
128
|
throw new Error(`unknown argument: ${arg}`);
|
|
127
129
|
}
|
|
@@ -211,9 +213,10 @@ function main() {
|
|
|
211
213
|
console.log(JSON.stringify(result, null, 2));
|
|
212
214
|
}
|
|
213
215
|
} catch (err) {
|
|
216
|
+
if (err instanceof ExitError) throw err;
|
|
214
217
|
console.error(`mutation-matrix: ${err.message}`);
|
|
215
|
-
|
|
218
|
+
throw new ExitError(2);
|
|
216
219
|
}
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
main
|
|
222
|
+
runMain(main);
|
|
@@ -83,7 +83,7 @@ ALLOWLIST=(
|
|
|
83
83
|
# These files contain intentional injection examples / security-model prose
|
|
84
84
|
# and are not attack vectors — they explain/demonstrate injection patterns.
|
|
85
85
|
'TEST-EXAMPLES.md'
|
|
86
|
-
'
|
|
86
|
+
'explanation/security-model.md'
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
is_allowlisted() {
|
|
@@ -4,6 +4,7 @@ const path = require('path');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const { execFileSync } = require('child_process');
|
|
7
|
+
const { runMain, ExitError } = require('../lib/cli-exit.cjs');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Classify a What's-Changed bullet line into 'Feature', 'Fix', or 'Enhancement'.
|
|
@@ -155,7 +156,7 @@ function formatReleaseNotes({ generatedBody, version, prerelease, packageName })
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
// CLI entry point
|
|
158
|
-
|
|
159
|
+
function main() {
|
|
159
160
|
try {
|
|
160
161
|
const argv = process.argv.slice(2);
|
|
161
162
|
|
|
@@ -248,9 +249,13 @@ if (require.main === module) {
|
|
|
248
249
|
process.stdout.write(formatted + '\n');
|
|
249
250
|
}
|
|
250
251
|
} catch (err) {
|
|
251
|
-
|
|
252
|
-
|
|
252
|
+
if (err instanceof ExitError) throw err;
|
|
253
|
+
throw new ExitError(1, err && err.message ? err.message : String(err));
|
|
253
254
|
}
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
if (require.main === module) {
|
|
258
|
+
runMain(main);
|
|
259
|
+
}
|
|
260
|
+
|
|
256
261
|
module.exports = { formatReleaseNotes, classifyTitle };
|
|
@@ -46,6 +46,7 @@ const fs = require('fs');
|
|
|
46
46
|
const os = require('os');
|
|
47
47
|
const path = require('path');
|
|
48
48
|
const { PACKAGE_NAME } = require('../gsd-core/bin/lib/package-identity.cjs');
|
|
49
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
49
50
|
// 120 s proved too tight on Windows GitHub-hosted runners: cold-cache
|
|
50
51
|
// `npm install -g` with a 1499-file tarball took ~120 s exactly, causing
|
|
51
52
|
// spawnSync to fire SIGTERM and return { status: null, stdout: '', stderr: '' }
|
|
@@ -587,23 +588,24 @@ function cliMain() {
|
|
|
587
588
|
};
|
|
588
589
|
if (isJson) process.stdout.write(JSON.stringify(result) + '\n');
|
|
589
590
|
cleanup(packDir, installPrefix, fixtureDir);
|
|
590
|
-
|
|
591
|
+
throw new ExitError(1);
|
|
591
592
|
}
|
|
592
593
|
}
|
|
593
594
|
} catch (err) {
|
|
595
|
+
if (err instanceof ExitError) throw err;
|
|
594
596
|
const result = {
|
|
595
597
|
code: SMOKE.PACK_FAILED,
|
|
596
598
|
details: { error: err.message, stderr: err.stderr },
|
|
597
599
|
};
|
|
598
600
|
if (isJson) process.stdout.write(JSON.stringify(result) + '\n');
|
|
599
601
|
cleanup(packDir, installPrefix, fixtureDir);
|
|
600
|
-
|
|
602
|
+
throw new ExitError(1);
|
|
601
603
|
}
|
|
602
604
|
|
|
603
605
|
const result = runSmoke({ tarballPath, installPrefix, expectedVersion, fixtureDir });
|
|
604
606
|
if (isJson) process.stdout.write(JSON.stringify(result) + '\n');
|
|
605
607
|
cleanup(packDir, installPrefix, fixtureDir);
|
|
606
|
-
|
|
608
|
+
return result.code === SMOKE.OK ? 0 : 1;
|
|
607
609
|
}
|
|
608
610
|
|
|
609
611
|
function cleanup(...dirs) {
|
|
@@ -623,5 +625,5 @@ function cleanup(...dirs) {
|
|
|
623
625
|
module.exports = { SMOKE, runSmoke, binInvocation };
|
|
624
626
|
|
|
625
627
|
if (require.main === module) {
|
|
626
|
-
cliMain
|
|
628
|
+
runMain(cliMain);
|
|
627
629
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* research-profiles.cjs — hand-authored profile table for the 7 researcher agents.
|
|
6
|
+
*
|
|
7
|
+
* Each profile field is derived verbatim from the agent's current committed state so
|
|
8
|
+
* that the initial --check in gen-research-agents.cjs is green by construction.
|
|
9
|
+
*
|
|
10
|
+
* Fields:
|
|
11
|
+
* name — verbatim frontmatter `name:` value
|
|
12
|
+
* description — verbatim frontmatter `description:` value
|
|
13
|
+
* color — verbatim frontmatter `color:` value
|
|
14
|
+
* tools — verbatim frontmatter `tools:` value (single string, comma-separated)
|
|
15
|
+
* requiredIncludes — @~/.claude/gsd-core/references/<file>.md strings the body MUST contain
|
|
16
|
+
* requiredSeamCalls — `gsd-tools query <cmd>` strings the body MUST contain
|
|
17
|
+
* outputContract — strings the body MUST contain (output path, return marker, etc.)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const PROFILES = [
|
|
21
|
+
{
|
|
22
|
+
name: 'gsd-project-researcher',
|
|
23
|
+
description:
|
|
24
|
+
'Researches domain ecosystem before roadmap creation. Produces files in .planning/research/ consumed during roadmap creation. Spawned by /gsd:new-project or /gsd:new-milestone orchestrators.',
|
|
25
|
+
color: 'cyan',
|
|
26
|
+
tools:
|
|
27
|
+
'Read, Write, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*',
|
|
28
|
+
requiredIncludes: [
|
|
29
|
+
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
|
30
|
+
'@~/.claude/gsd-core/references/research-philosophy.md',
|
|
31
|
+
'@~/.claude/gsd-core/references/research-verification-protocol.md',
|
|
32
|
+
],
|
|
33
|
+
requiredSeamCalls: [
|
|
34
|
+
'gsd-tools query research-plan',
|
|
35
|
+
'gsd-tools query research-store put',
|
|
36
|
+
'gsd-tools query classify-confidence',
|
|
37
|
+
],
|
|
38
|
+
outputContract: [
|
|
39
|
+
'.planning/research/',
|
|
40
|
+
'## RESEARCH COMPLETE',
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'gsd-phase-researcher',
|
|
45
|
+
description:
|
|
46
|
+
'Researches how to implement a phase before planning. Produces RESEARCH.md consumed by gsd-planner. Spawned by /gsd:plan-phase orchestrator.',
|
|
47
|
+
color: 'cyan',
|
|
48
|
+
tools:
|
|
49
|
+
'Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*',
|
|
50
|
+
requiredIncludes: [
|
|
51
|
+
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
|
52
|
+
'@~/.claude/gsd-core/references/research-philosophy.md',
|
|
53
|
+
'@~/.claude/gsd-core/references/research-verification-protocol.md',
|
|
54
|
+
],
|
|
55
|
+
requiredSeamCalls: [
|
|
56
|
+
'gsd-tools query research-plan',
|
|
57
|
+
'gsd-tools query research-store put',
|
|
58
|
+
'gsd-tools query classify-confidence',
|
|
59
|
+
'gsd-tools query package-legitimacy check',
|
|
60
|
+
],
|
|
61
|
+
outputContract: [
|
|
62
|
+
'.planning/phases/XX-name/{phase_num}-RESEARCH.md',
|
|
63
|
+
'## RESEARCH COMPLETE',
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'gsd-advisor-researcher',
|
|
68
|
+
description:
|
|
69
|
+
'Researches a single gray area decision and returns a structured comparison table with rationale. Spawned by discuss-phase advisor mode.',
|
|
70
|
+
color: 'cyan',
|
|
71
|
+
tools: 'Read, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*',
|
|
72
|
+
requiredIncludes: [
|
|
73
|
+
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
|
74
|
+
],
|
|
75
|
+
requiredSeamCalls: [],
|
|
76
|
+
outputContract: [
|
|
77
|
+
'| Option | Pros | Cons | Complexity | Recommendation |',
|
|
78
|
+
'**Rationale:**',
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'gsd-ai-researcher',
|
|
83
|
+
description:
|
|
84
|
+
'Researches a chosen AI framework\'s official docs to produce implementation-ready guidance — best practices, syntax, core patterns, and pitfalls distilled for the specific use case. Writes the Framework Quick Reference and Implementation Guidance sections of AI-SPEC.md. Spawned by /gsd:ai-integration-phase orchestrator.',
|
|
85
|
+
color: '"#34D399"',
|
|
86
|
+
tools:
|
|
87
|
+
'Read, Write, Edit, Bash, Grep, Glob, WebFetch, WebSearch, mcp__context7__*',
|
|
88
|
+
requiredIncludes: [
|
|
89
|
+
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
|
90
|
+
],
|
|
91
|
+
requiredSeamCalls: [],
|
|
92
|
+
outputContract: [
|
|
93
|
+
'AI-SPEC.md',
|
|
94
|
+
'Section 3',
|
|
95
|
+
'Section 4',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'gsd-domain-researcher',
|
|
100
|
+
description:
|
|
101
|
+
'Researches the business domain and real-world application context of the AI system being built. Surfaces domain expert evaluation criteria, industry-specific failure modes, regulatory context, and what "good" looks like for practitioners in this field — before the eval-planner turns it into measurable rubrics. Spawned by /gsd:ai-integration-phase orchestrator.',
|
|
102
|
+
color: '"#A78BFA"',
|
|
103
|
+
tools:
|
|
104
|
+
'Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*',
|
|
105
|
+
requiredIncludes: [
|
|
106
|
+
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
|
107
|
+
],
|
|
108
|
+
requiredSeamCalls: [],
|
|
109
|
+
outputContract: [
|
|
110
|
+
'AI-SPEC.md',
|
|
111
|
+
'Section 1b',
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'gsd-ui-researcher',
|
|
116
|
+
description:
|
|
117
|
+
'Produces UI-SPEC.md design contract for frontend phases. Reads upstream artifacts, detects design system state, asks only unanswered questions. Spawned by /gsd:ui-phase orchestrator.',
|
|
118
|
+
color: '"#E879F9"',
|
|
119
|
+
tools:
|
|
120
|
+
'Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__firecrawl__*, mcp__exa__*, mcp__tavily__*, mcp__ref__*, mcp__jina__*',
|
|
121
|
+
requiredIncludes: [
|
|
122
|
+
'@~/.claude/gsd-core/references/research-documentation-lookup.md',
|
|
123
|
+
],
|
|
124
|
+
requiredSeamCalls: [
|
|
125
|
+
'gsd-tools query commit',
|
|
126
|
+
],
|
|
127
|
+
outputContract: [
|
|
128
|
+
'UI-SPEC.md',
|
|
129
|
+
'## UI-SPEC COMPLETE',
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'gsd-research-synthesizer',
|
|
134
|
+
description:
|
|
135
|
+
'Synthesizes research outputs from parallel researcher agents into SUMMARY.md. Spawned by /gsd:new-project after 4 researcher agents complete.',
|
|
136
|
+
color: 'purple',
|
|
137
|
+
tools: 'Read, Write, Bash',
|
|
138
|
+
requiredIncludes: [],
|
|
139
|
+
requiredSeamCalls: [
|
|
140
|
+
'gsd-tools query commit',
|
|
141
|
+
],
|
|
142
|
+
outputContract: [
|
|
143
|
+
'.planning/research/SUMMARY.md',
|
|
144
|
+
'## SYNTHESIS COMPLETE',
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
module.exports = { PROFILES };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { spawnSync } = require('child_process');
|
|
4
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
4
5
|
|
|
5
6
|
const CROSS_PLATFORM_TEST_REASON = Object.freeze({
|
|
6
7
|
PASS: 'pass',
|
|
@@ -50,14 +51,17 @@ function runCrossPlatformTests(options = {}, deps = {}) {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
if (require.main === module) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
function main() {
|
|
55
|
+
const result = runCrossPlatformTests();
|
|
56
|
+
const line = `[cross-platform-tests] reason=${result.reason} exit=${result.exitCode}`;
|
|
57
|
+
if (result.ok) {
|
|
58
|
+
process.stdout.write(`${line}\n`);
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
process.stderr.write(`${line}\n`);
|
|
62
|
+
throw new ExitError(result.exitCode);
|
|
58
63
|
}
|
|
59
|
-
|
|
60
|
-
process.exit(result.exitCode);
|
|
64
|
+
runMain(main);
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
module.exports = { CROSS_PLATFORM_TEST_REASON, runCrossPlatformTests };
|