@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
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
18
19
|
|
|
19
20
|
const MAX_LENGTH = 100;
|
|
20
21
|
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
|
@@ -46,38 +47,41 @@ function getFiles() {
|
|
|
46
47
|
.map(f => path.join(COMMANDS_DIR, f));
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
const
|
|
50
|
+
function main() {
|
|
51
|
+
const files = getFiles();
|
|
52
|
+
const violations = [];
|
|
51
53
|
|
|
52
|
-
for (const filePath of files) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
54
|
+
for (const filePath of files) {
|
|
55
|
+
let content;
|
|
56
|
+
try {
|
|
57
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
58
|
+
} catch (err) {
|
|
59
|
+
throw new ExitError(1, `ERROR: Cannot read file: ${filePath}\n ${err.message}`);
|
|
60
|
+
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
const description = parseDescription(content);
|
|
63
|
+
if (description === null) continue;
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
if (description.length > MAX_LENGTH) {
|
|
66
|
+
violations.push({ filePath, length: description.length, description });
|
|
67
|
+
}
|
|
66
68
|
}
|
|
67
|
-
}
|
|
68
69
|
|
|
69
|
-
if (violations.length === 0) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
70
|
+
if (violations.length === 0) {
|
|
71
|
+
const checked = files.length;
|
|
72
|
+
process.stdout.write(`ok lint-descriptions: ${checked} file(s) checked, 0 violations\n`);
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
74
75
|
|
|
75
|
-
process.stderr.write(`\nERROR lint-descriptions: ${violations.length} violation(s) found\n\n`);
|
|
76
|
-
for (const v of violations) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
process.stderr.write(`\nERROR lint-descriptions: ${violations.length} violation(s) found\n\n`);
|
|
77
|
+
for (const v of violations) {
|
|
78
|
+
const preview = v.description.length > 120 ? v.description.slice(0, 117) + '...' : v.description;
|
|
79
|
+
process.stderr.write(` ${v.filePath}\n`);
|
|
80
|
+
process.stderr.write(` Length : ${v.length} (max ${MAX_LENGTH})\n`);
|
|
81
|
+
process.stderr.write(` Desc : ${preview}\n\n`);
|
|
82
|
+
}
|
|
83
|
+
process.stderr.write(`Trim descriptions to <= ${MAX_LENGTH} chars. Flag docs belong in argument-hint:.\n\n`);
|
|
84
|
+
return 1;
|
|
81
85
|
}
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
|
|
87
|
+
runMain(main);
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
const { parseFragment, FRAGMENT_ERROR } = require('./changeset/parse.cjs');
|
|
17
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
17
18
|
|
|
18
19
|
const LINT_REASON = Object.freeze({
|
|
19
20
|
OK_NO_TRIGGERING_FRAGMENTS: 'ok_no_triggering_fragments',
|
|
@@ -161,8 +162,7 @@ function main() {
|
|
|
161
162
|
);
|
|
162
163
|
changedFiles = out.split('\n').filter(Boolean);
|
|
163
164
|
} catch (e) {
|
|
164
|
-
|
|
165
|
-
process.exit(2);
|
|
165
|
+
throw new ExitError(2, `could not compute diff: ${e.message}`);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
const { fragments, malformed } = readFragmentsFromDisk(changedFiles, rootDir);
|
|
@@ -204,10 +204,10 @@ function main() {
|
|
|
204
204
|
`exemption via \`<!-- docs-exempt: <reason> -->\` inside the fragment body also works.\n`,
|
|
205
205
|
);
|
|
206
206
|
}
|
|
207
|
-
|
|
207
|
+
return verdict.ok ? 0 : 1;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
if (require.main === module) main
|
|
210
|
+
if (require.main === module) runMain(main);
|
|
211
211
|
|
|
212
212
|
module.exports = {
|
|
213
213
|
evaluateLint,
|
|
@@ -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);
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"milestone": {
|
|
45
45
|
"files": [
|
|
46
|
+
"bug-730-milestone-phase-details-scope.test.cjs",
|
|
46
47
|
"milestone-archive.test.cjs",
|
|
47
48
|
"milestone-helper.test.cjs",
|
|
48
49
|
"milestone-prefixed-convention.test.cjs",
|
|
@@ -120,6 +121,7 @@
|
|
|
120
121
|
"install": {
|
|
121
122
|
"files": [
|
|
122
123
|
"bug-410-install-defaults-test-mode-guard.test.cjs",
|
|
124
|
+
"enh-776-install-gemini-hook-events.test.cjs",
|
|
123
125
|
"install-minimal-hooks.test.cjs",
|
|
124
126
|
"install-path-detection.test.cjs",
|
|
125
127
|
"install-regressions.test.cjs",
|
|
@@ -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() {
|