@specship/specship-win32-arm64 0.23.0 → 0.25.0
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/lib/dist/activation/steering.d.ts +4 -4
- package/lib/dist/activation/steering.d.ts.map +1 -1
- package/lib/dist/activation/steering.js +6 -6
- package/lib/dist/activation/steering.js.map +1 -1
- package/lib/dist/bin/specship.js +277 -40
- package/lib/dist/bin/specship.js.map +1 -1
- package/lib/dist/commands/specship/learn.md +5 -3
- package/lib/dist/commands/specship/spec.md +73 -15
- package/lib/dist/db/spec-queries.d.ts +73 -3
- package/lib/dist/db/spec-queries.d.ts.map +1 -1
- package/lib/dist/db/spec-queries.js +139 -5
- package/lib/dist/db/spec-queries.js.map +1 -1
- package/lib/dist/diagram/flow-diagram.d.ts +86 -0
- package/lib/dist/diagram/flow-diagram.d.ts.map +1 -0
- package/lib/dist/diagram/flow-diagram.js +0 -0
- package/lib/dist/diagram/flow-diagram.js.map +1 -0
- package/lib/dist/enforce/changed-files.d.ts +28 -0
- package/lib/dist/enforce/changed-files.d.ts.map +1 -0
- package/lib/dist/enforce/changed-files.js +72 -0
- package/lib/dist/enforce/changed-files.js.map +1 -0
- package/lib/dist/enforce/enforce.d.ts +28 -1
- package/lib/dist/enforce/enforce.d.ts.map +1 -1
- package/lib/dist/enforce/enforce.js +37 -5
- package/lib/dist/enforce/enforce.js.map +1 -1
- package/lib/dist/extraction/grammars.d.ts +10 -0
- package/lib/dist/extraction/grammars.d.ts.map +1 -1
- package/lib/dist/extraction/grammars.js +32 -3
- package/lib/dist/extraction/grammars.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +7 -0
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts +7 -0
- package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/specs/markdown-spec-extractor.js +81 -8
- package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm.gz +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm.gz +0 -0
- package/lib/dist/graph/spec-coverage.d.ts +86 -0
- package/lib/dist/graph/spec-coverage.d.ts.map +1 -0
- package/lib/dist/graph/spec-coverage.js +152 -0
- package/lib/dist/graph/spec-coverage.js.map +1 -0
- package/lib/dist/index.d.ts +81 -6
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +178 -21
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/installer/index.d.ts +33 -8
- package/lib/dist/installer/index.d.ts.map +1 -1
- package/lib/dist/installer/index.js +50 -50
- package/lib/dist/installer/index.js.map +1 -1
- package/lib/dist/installer/init-offer.d.ts +14 -9
- package/lib/dist/installer/init-offer.d.ts.map +1 -1
- package/lib/dist/installer/init-offer.js +13 -9
- package/lib/dist/installer/init-offer.js.map +1 -1
- package/lib/dist/installer/install-defaults.d.ts +62 -0
- package/lib/dist/installer/install-defaults.d.ts.map +1 -0
- package/lib/dist/installer/install-defaults.js +67 -0
- package/lib/dist/installer/install-defaults.js.map +1 -0
- package/lib/dist/installer/targets/claude.d.ts +14 -0
- package/lib/dist/installer/targets/claude.d.ts.map +1 -1
- package/lib/dist/installer/targets/claude.js +64 -0
- package/lib/dist/installer/targets/claude.js.map +1 -1
- package/lib/dist/installer/targets/shared.js +4 -4
- package/lib/dist/installer/targets/shared.js.map +1 -1
- package/lib/dist/mcp/fitness-tool.d.ts +5 -6
- package/lib/dist/mcp/fitness-tool.d.ts.map +1 -1
- package/lib/dist/mcp/fitness-tool.js +0 -16
- package/lib/dist/mcp/fitness-tool.js.map +1 -1
- package/lib/dist/mcp/health-tool.d.ts +26 -0
- package/lib/dist/mcp/health-tool.d.ts.map +1 -0
- package/lib/dist/mcp/health-tool.js +50 -0
- package/lib/dist/mcp/health-tool.js.map +1 -0
- package/lib/dist/mcp/maintainability-tool.d.ts +5 -7
- package/lib/dist/mcp/maintainability-tool.d.ts.map +1 -1
- package/lib/dist/mcp/maintainability-tool.js +0 -16
- package/lib/dist/mcp/maintainability-tool.js.map +1 -1
- package/lib/dist/mcp/model-context.d.ts +13 -2
- package/lib/dist/mcp/model-context.d.ts.map +1 -1
- package/lib/dist/mcp/model-context.js +39 -12
- package/lib/dist/mcp/model-context.js.map +1 -1
- package/lib/dist/mcp/proxy.d.ts.map +1 -1
- package/lib/dist/mcp/proxy.js +4 -1
- package/lib/dist/mcp/proxy.js.map +1 -1
- package/lib/dist/mcp/server-instructions.d.ts +1 -1
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
- package/lib/dist/mcp/server-instructions.js +5 -4
- package/lib/dist/mcp/server-instructions.js.map +1 -1
- package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
- package/lib/dist/mcp/spec-tools.js +213 -28
- package/lib/dist/mcp/spec-tools.js.map +1 -1
- package/lib/dist/mcp/tools.d.ts +12 -8
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +89 -48
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/resolution/spec-link-resolver.d.ts +44 -2
- package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/spec-link-resolver.js +132 -0
- package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
- package/lib/dist/resolution/test-title-links.d.ts +62 -0
- package/lib/dist/resolution/test-title-links.d.ts.map +1 -0
- package/lib/dist/resolution/test-title-links.js +108 -0
- package/lib/dist/resolution/test-title-links.js.map +1 -0
- package/lib/dist/search/query-parser.d.ts.map +1 -1
- package/lib/dist/search/query-parser.js +6 -1
- package/lib/dist/search/query-parser.js.map +1 -1
- package/lib/dist/server/project-registry.js +1 -1
- package/lib/dist/server/routes/claude.js +9 -36
- package/lib/dist/server/routes/graph.js +53 -7
- package/lib/dist/server/routes/mcp.js +4 -3
- package/lib/dist/server/routes/projects.js +4 -4
- package/lib/dist/server/routes/spec.js +49 -7
- package/lib/dist/server/server.js +29 -17
- package/lib/dist/skills/spec-author/SKILL.md +164 -0
- package/lib/dist/skills/spec-author/references/format.md +210 -0
- package/lib/dist/skills/spec-author/references/gap-questions.md +152 -0
- package/lib/dist/skills/spec-author/references/quality-rubric.md +125 -0
- package/lib/dist/skills/spec-author/references/review-checklist.md +175 -0
- package/lib/dist/spec-lint/lint.d.ts +82 -0
- package/lib/dist/spec-lint/lint.d.ts.map +1 -0
- package/lib/dist/spec-lint/lint.js +430 -0
- package/lib/dist/spec-lint/lint.js.map +1 -0
- package/lib/dist/statusline/index.js.map +1 -1
- package/lib/dist/statusline/render.d.ts +1 -1
- package/lib/dist/statusline/render.d.ts.map +1 -1
- package/lib/dist/statusline/render.js +3 -1
- package/lib/dist/statusline/render.js.map +1 -1
- package/lib/dist/ui/assets/index-BzuUWUAE.js +50 -0
- package/lib/dist/ui/index.html +1 -1
- package/lib/dist/verify/report-ingest.d.ts +88 -0
- package/lib/dist/verify/report-ingest.d.ts.map +1 -0
- package/lib/dist/verify/report-ingest.js +269 -0
- package/lib/dist/verify/report-ingest.js.map +1 -0
- package/lib/dist/workflows/defaults/spec-fix.yaml +103 -12
- package/lib/dist/workflows/defaults/spec-implement-mixed.yaml +264 -21
- package/lib/dist/workflows/defaults/spec-implement.yaml +260 -19
- package/lib/dist/workflows/defaults/spec-verify.yaml +60 -9
- package/lib/node_modules/.package-lock.json +1 -1
- package/lib/package.json +7 -3
- package/package.json +1 -1
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
- package/lib/dist/server/ingest/impact-query.js +0 -343
- package/lib/dist/server/routes/events.js +0 -152
- package/lib/dist/ui/assets/index-CjjaftDw.js +0 -50
|
@@ -17,19 +17,19 @@
|
|
|
17
17
|
*/
|
|
18
18
|
export declare const STEERING_TEXT: string;
|
|
19
19
|
/**
|
|
20
|
-
* The
|
|
20
|
+
* The lite-tier template (LOWMODEL-DOC, REQ-LOWMODEL-002): small models
|
|
21
21
|
* follow prescriptive templates far better than principles, and fan-out
|
|
22
22
|
* (subagents) multiplies their cost and confusion. Kept under ~80 tokens
|
|
23
23
|
* (REQ-LOWMODEL-002.A2).
|
|
24
24
|
*/
|
|
25
|
-
export declare const
|
|
25
|
+
export declare const STEERING_TEXT_LITE: string;
|
|
26
26
|
/**
|
|
27
27
|
* Decide whether to emit the steering line (REQ-STEER-002): silent unless the
|
|
28
28
|
* project is initialized (`.specship/` exists at cwd), and silent when the
|
|
29
29
|
* user opted out via `SPECSHIP_NO_STEERING=1`. Uninitialized projects and
|
|
30
30
|
* opted-out users get zero prompt noise. Tier-aware (REQ-LOWMODEL-002):
|
|
31
|
-
*
|
|
32
|
-
* standard line.
|
|
31
|
+
* lite-tier sessions get the prescriptive template; standard and frontier keep
|
|
32
|
+
* the standard line.
|
|
33
33
|
*/
|
|
34
34
|
export declare function buildSteeringNudge(cwd: string, env?: NodeJS.ProcessEnv, homedir?: string): string | null;
|
|
35
35
|
//# sourceMappingURL=steering.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steering.d.ts","sourceRoot":"","sources":["../../src/activation/steering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAKJ,CAAC;AAEvB;;;;;GAKG;AACH,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"steering.d.ts","sourceRoot":"","sources":["../../src/activation/steering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAKJ,CAAC;AAEvB;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAIsC,CAAC;AAEtE;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAUf"}
|
|
@@ -45,7 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
45
45
|
};
|
|
46
46
|
})();
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.
|
|
48
|
+
exports.STEERING_TEXT_LITE = exports.STEERING_TEXT = void 0;
|
|
49
49
|
exports.buildSteeringNudge = buildSteeringNudge;
|
|
50
50
|
const fs = __importStar(require("fs"));
|
|
51
51
|
const path = __importStar(require("path"));
|
|
@@ -62,12 +62,12 @@ exports.STEERING_TEXT = 'This project has a SpecShip code-graph index. Before re
|
|
|
62
62
|
'Treat the source it returns as already read; use Read/Grep only for content ' +
|
|
63
63
|
'it did not return.';
|
|
64
64
|
/**
|
|
65
|
-
* The
|
|
65
|
+
* The lite-tier template (LOWMODEL-DOC, REQ-LOWMODEL-002): small models
|
|
66
66
|
* follow prescriptive templates far better than principles, and fan-out
|
|
67
67
|
* (subagents) multiplies their cost and confusion. Kept under ~80 tokens
|
|
68
68
|
* (REQ-LOWMODEL-002.A2).
|
|
69
69
|
*/
|
|
70
|
-
exports.
|
|
70
|
+
exports.STEERING_TEXT_LITE = 'This project has a SpecShip index. For any code question, call exactly: ' +
|
|
71
71
|
'mcp__specship__specship_explore with the symbol/file names from the question. ' +
|
|
72
72
|
'ONE call, then answer from its output — the source it returns is already read. ' +
|
|
73
73
|
'Do not spawn subagents. Do not Read/Grep files the tool returned.';
|
|
@@ -76,8 +76,8 @@ exports.STEERING_TEXT_HAIKU = 'This project has a SpecShip index. For any code q
|
|
|
76
76
|
* project is initialized (`.specship/` exists at cwd), and silent when the
|
|
77
77
|
* user opted out via `SPECSHIP_NO_STEERING=1`. Uninitialized projects and
|
|
78
78
|
* opted-out users get zero prompt noise. Tier-aware (REQ-LOWMODEL-002):
|
|
79
|
-
*
|
|
80
|
-
* standard line.
|
|
79
|
+
* lite-tier sessions get the prescriptive template; standard and frontier keep
|
|
80
|
+
* the standard line.
|
|
81
81
|
*/
|
|
82
82
|
function buildSteeringNudge(cwd, env = process.env, homedir) {
|
|
83
83
|
// RUNSET-DOC: the opt-out resolves env > project .specship/settings.json >
|
|
@@ -91,6 +91,6 @@ function buildSteeringNudge(cwd, env = process.env, homedir) {
|
|
|
91
91
|
catch {
|
|
92
92
|
return null;
|
|
93
93
|
}
|
|
94
|
-
return (0, model_context_1.detectModelTier)(cwd, env, homedir) === '
|
|
94
|
+
return (0, model_context_1.detectModelTier)(cwd, env, homedir) === 'lite' ? exports.STEERING_TEXT_LITE : exports.STEERING_TEXT;
|
|
95
95
|
}
|
|
96
96
|
//# sourceMappingURL=steering.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steering.js","sourceRoot":"","sources":["../../src/activation/steering.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCH,gDAcC;AAnDD,uCAAyB;AACzB,2CAA6B;AAC7B,wDAAuD;AACvD,iEAA4D;AAE5D;;;;GAIG;AACU,QAAA,aAAa,GACxB,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,oBAAoB,CAAC;AAEvB;;;;;GAKG;AACU,QAAA,
|
|
1
|
+
{"version":3,"file":"steering.js","sourceRoot":"","sources":["../../src/activation/steering.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCH,gDAcC;AAnDD,uCAAyB;AACzB,2CAA6B;AAC7B,wDAAuD;AACvD,iEAA4D;AAE5D;;;;GAIG;AACU,QAAA,aAAa,GACxB,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,oBAAoB,CAAC;AAEvB;;;;;GAKG;AACU,QAAA,kBAAkB,GAC7B,0EAA0E;IAC1E,gFAAgF;IAChF,iFAAiF;IACjF,mEAAmE,CAAC;AAEtE;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAChC,GAAW,EACX,MAAyB,OAAO,CAAC,GAAG,EACpC,OAAgB;IAEhB,2EAA2E;IAC3E,qEAAqE;IACrE,IAAI,IAAA,iCAAc,EAAC,sBAAsB,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACnF,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,IAAI,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAA,+BAAe,EAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,0BAAkB,CAAC,CAAC,CAAC,qBAAa,CAAC;AAC5F,CAAC"}
|
package/lib/dist/bin/specship.js
CHANGED
|
@@ -925,6 +925,18 @@ function main() {
|
|
|
925
925
|
details.push(`Removed: ${result.filesRemoved}`);
|
|
926
926
|
clack.log.info(`${details.join(', ')} ${(0, glyphs_1.getGlyphs)().dash} ${formatNumber(result.nodesUpdated)} nodes in ${formatDuration(result.durationMs)}`);
|
|
927
927
|
}
|
|
928
|
+
// Spec parse problems from the sync's spec pass (REQ-REVINT-002.A2) —
|
|
929
|
+
// silent before, so a bare-path link bullet produced no edge and no word.
|
|
930
|
+
const specErrors = result.specErrors ?? 0;
|
|
931
|
+
const specWarnings = result.specWarnings ?? 0;
|
|
932
|
+
if (specErrors > 0 || specWarnings > 0) {
|
|
933
|
+
const parts = [];
|
|
934
|
+
if (specErrors > 0)
|
|
935
|
+
parts.push(`${specErrors} error${specErrors === 1 ? '' : 's'}`);
|
|
936
|
+
if (specWarnings > 0)
|
|
937
|
+
parts.push(`${specWarnings} warning${specWarnings === 1 ? '' : 's'}`);
|
|
938
|
+
clack.log.warn(`Spec parse: ${parts.join(', ')}`);
|
|
939
|
+
}
|
|
928
940
|
pushDriftNotices(result);
|
|
929
941
|
await pushJiraDriftComments(cg, result);
|
|
930
942
|
await pushJiraAutoPublish(result);
|
|
@@ -1384,7 +1396,7 @@ function main() {
|
|
|
1384
1396
|
}
|
|
1385
1397
|
else {
|
|
1386
1398
|
info(`Captured as proposal ${p.contentHash.slice(0, 12)} (${p.state}): ${p.title}`);
|
|
1387
|
-
info('Review
|
|
1399
|
+
info('Review the open proposals with `specship reflect` — nothing lands until you apply it.');
|
|
1388
1400
|
}
|
|
1389
1401
|
cg.destroy();
|
|
1390
1402
|
return;
|
|
@@ -1420,7 +1432,7 @@ function main() {
|
|
|
1420
1432
|
console.log(chalk.dim(` ${p.body}`));
|
|
1421
1433
|
console.log();
|
|
1422
1434
|
}
|
|
1423
|
-
info('
|
|
1435
|
+
info('These are proposals only — nothing is written until you apply one.');
|
|
1424
1436
|
cg.destroy();
|
|
1425
1437
|
}
|
|
1426
1438
|
catch (err) {
|
|
@@ -1472,7 +1484,7 @@ function main() {
|
|
|
1472
1484
|
else {
|
|
1473
1485
|
info(`Captured lesson as proposal ${p.contentHash.slice(0, 12)} (${p.state}): ${p.title}`);
|
|
1474
1486
|
info(`Target: ${p.targetKind === 'claude_md' ? 'project CLAUDE.md' : 'portable ~/.claude memory note'} — nothing written yet.`);
|
|
1475
|
-
info('Review
|
|
1487
|
+
info('Review with `specship reflect`; manage applied memory rules with `specship memory list`.');
|
|
1476
1488
|
}
|
|
1477
1489
|
cg.destroy();
|
|
1478
1490
|
}
|
|
@@ -1720,6 +1732,159 @@ function main() {
|
|
|
1720
1732
|
process.exit(1);
|
|
1721
1733
|
}
|
|
1722
1734
|
});
|
|
1735
|
+
/**
|
|
1736
|
+
* specship coverage [spec_id]
|
|
1737
|
+
*
|
|
1738
|
+
* The spec coverage rollup (REQ-VSTATE-006): per requirement and per acceptance
|
|
1739
|
+
* criterion, how much of the spec has TEST evidence — `implements` links never
|
|
1740
|
+
* count. Deterministic and cheap, which is what lets the implement workflow's
|
|
1741
|
+
* human gate quote a real number instead of an LLM's count (REQ-VSTATE-005.A2).
|
|
1742
|
+
*/
|
|
1743
|
+
program
|
|
1744
|
+
.command('coverage [spec]')
|
|
1745
|
+
.description('Show spec test-coverage: per requirement and criterion, tests-link state and verdict')
|
|
1746
|
+
.option('-p, --path <path>', 'Project path')
|
|
1747
|
+
.option('-j, --json', 'Output as JSON')
|
|
1748
|
+
.option('--line', 'Print only the one-line "N of M acceptance criteria …" summary')
|
|
1749
|
+
.action(async (specArg, options) => {
|
|
1750
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1751
|
+
try {
|
|
1752
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1753
|
+
error(`SpecShip not initialized in ${projectPath}`);
|
|
1754
|
+
process.exit(1);
|
|
1755
|
+
}
|
|
1756
|
+
const { default: SpecShip, formatCoverageLine } = await loadSpecShip();
|
|
1757
|
+
const cg = await SpecShip.open(projectPath);
|
|
1758
|
+
const report = cg.getSpecCoverage(specArg);
|
|
1759
|
+
if (options.json) {
|
|
1760
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1761
|
+
cg.destroy();
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
if (options.line) {
|
|
1765
|
+
console.log(formatCoverageLine(report));
|
|
1766
|
+
cg.destroy();
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
if (report.requirements.length === 0) {
|
|
1770
|
+
info(specArg
|
|
1771
|
+
? `No requirements under "${specArg}" — nothing to report.`
|
|
1772
|
+
: 'No requirements indexed yet.');
|
|
1773
|
+
cg.destroy();
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
const mark = {
|
|
1777
|
+
verified: chalk.green('✔'),
|
|
1778
|
+
tested: chalk.yellow('~'),
|
|
1779
|
+
untested: chalk.dim('·'),
|
|
1780
|
+
broken: chalk.red('✗'),
|
|
1781
|
+
};
|
|
1782
|
+
for (const req of report.requirements) {
|
|
1783
|
+
console.log(`${mark[req.verdict]} ${chalk.bold(req.specId)} ${chalk.dim(req.verdict)} — ${req.title}`);
|
|
1784
|
+
for (const c of req.criteria) {
|
|
1785
|
+
console.log(` ${mark[c.verdict]} ${c.specId} ${chalk.dim(`${c.verdict} · implements:${c.implementsLinks.count} tests:${c.testsLinks.count}`)}`);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
console.log();
|
|
1789
|
+
console.log(formatCoverageLine(report));
|
|
1790
|
+
cg.destroy();
|
|
1791
|
+
}
|
|
1792
|
+
catch (err) {
|
|
1793
|
+
error(`coverage failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1794
|
+
process.exit(1);
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
/**
|
|
1798
|
+
* specship lint [paths...]
|
|
1799
|
+
*
|
|
1800
|
+
* Spec-authoring lint (AUTHG-DOC). A thin registration: all rules, rendering
|
|
1801
|
+
* and the exit code live in `src/spec-lint/lint.ts` (`runLintCli` returns the
|
|
1802
|
+
* text and the code rather than printing or exiting, which is what makes it
|
|
1803
|
+
* testable). Needs no index — it reads the files directly, so it runs in a
|
|
1804
|
+
* fresh checkout and in CI before anything has been indexed.
|
|
1805
|
+
*/
|
|
1806
|
+
program
|
|
1807
|
+
.command('lint [paths...]')
|
|
1808
|
+
.description('Lint spec files for authoring problems (exits non-zero on an error finding)')
|
|
1809
|
+
.option('-p, --path <path>', 'Project path (paths are resolved against it; defaults to specs/)')
|
|
1810
|
+
.option('-j, --json', 'Output as JSON')
|
|
1811
|
+
.action(async (paths, options) => {
|
|
1812
|
+
const projectRoot = resolveProjectPath(options.path);
|
|
1813
|
+
try {
|
|
1814
|
+
const { runLintCli } = await Promise.resolve().then(() => __importStar(require('../spec-lint/lint')));
|
|
1815
|
+
const { output, exitCode } = runLintCli([...(paths ?? []), ...(options.json ? ['--json'] : [])], { projectRoot });
|
|
1816
|
+
console.log(output);
|
|
1817
|
+
if (exitCode !== 0)
|
|
1818
|
+
process.exit(exitCode);
|
|
1819
|
+
}
|
|
1820
|
+
catch (err) {
|
|
1821
|
+
error(`lint failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1822
|
+
process.exit(1);
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1825
|
+
/**
|
|
1826
|
+
* specship verify --report <file>
|
|
1827
|
+
*
|
|
1828
|
+
* Derive verification from a REAL test run (REQ-VSTATE-004): read a vitest JSON
|
|
1829
|
+
* report, match reported cases to `tests` links by file + test title, and
|
|
1830
|
+
* promote/demote those links. Replaces "an agent read the log and said it
|
|
1831
|
+
* passed" with something auditable back to a specific run.
|
|
1832
|
+
*
|
|
1833
|
+
* A malformed report exits non-zero having changed nothing (A4).
|
|
1834
|
+
*/
|
|
1835
|
+
program
|
|
1836
|
+
.command('verify')
|
|
1837
|
+
.description('Ingest a test report (vitest JSON) and promote/demote spec test links from it')
|
|
1838
|
+
.requiredOption('--report <file>', 'Path to the test report (vitest `--reporter=json` output)')
|
|
1839
|
+
.option('-p, --path <path>', 'Project path')
|
|
1840
|
+
.option('-j, --json', 'Output as JSON')
|
|
1841
|
+
.option('--run-id <id>', 'Identifier recorded as the run behind the verdict')
|
|
1842
|
+
.action(async (options) => {
|
|
1843
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1844
|
+
try {
|
|
1845
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1846
|
+
error(`SpecShip not initialized in ${projectPath}`);
|
|
1847
|
+
process.exit(1);
|
|
1848
|
+
}
|
|
1849
|
+
const { default: SpecShip } = await loadSpecShip();
|
|
1850
|
+
const cg = await SpecShip.open(projectPath);
|
|
1851
|
+
let result;
|
|
1852
|
+
try {
|
|
1853
|
+
result = cg.verifyFromReport(options.report, { runId: options.runId });
|
|
1854
|
+
}
|
|
1855
|
+
catch (err) {
|
|
1856
|
+
cg.destroy();
|
|
1857
|
+
error(`verify failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1858
|
+
process.exit(1);
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
if (options.json) {
|
|
1862
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1863
|
+
}
|
|
1864
|
+
else {
|
|
1865
|
+
success(`Read ${result.cases} test case(s) from ${result.reportPath} (run ${result.runId}).`);
|
|
1866
|
+
for (const p of result.promoted) {
|
|
1867
|
+
console.log(` ${chalk.green('✔')} ${p.specId} ${chalk.dim(`${p.from} → verified`)} — ${p.titles[0] ?? ''}`);
|
|
1868
|
+
}
|
|
1869
|
+
for (const d of result.demoted) {
|
|
1870
|
+
console.log(` ${chalk.red('✗')} ${d.specId} ${chalk.dim(`${d.from} → broken`)} — ${d.titles[0] ?? ''}`);
|
|
1871
|
+
}
|
|
1872
|
+
console.log();
|
|
1873
|
+
info(`${result.promoted.length} verified, ${result.demoted.length} broken, ${result.unchanged} unchanged, ${result.unmatched} unmatched.`);
|
|
1874
|
+
// Unmatched cases are reported, never silently dropped (A3).
|
|
1875
|
+
for (const u of result.unmatchedSample)
|
|
1876
|
+
console.log(chalk.dim(` unmatched: ${u}`));
|
|
1877
|
+
if (result.unmatched > result.unmatchedSample.length) {
|
|
1878
|
+
console.log(chalk.dim(` …and ${result.unmatched - result.unmatchedSample.length} more`));
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
cg.destroy();
|
|
1882
|
+
}
|
|
1883
|
+
catch (err) {
|
|
1884
|
+
error(`verify failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1885
|
+
process.exit(1);
|
|
1886
|
+
}
|
|
1887
|
+
});
|
|
1723
1888
|
/**
|
|
1724
1889
|
* specship fitness
|
|
1725
1890
|
*
|
|
@@ -1800,6 +1965,7 @@ function main() {
|
|
|
1800
1965
|
.option('-j, --json', 'Output as JSON')
|
|
1801
1966
|
.option('--strict', 'Treat every check as gating for this run only (nothing read from or written to config)')
|
|
1802
1967
|
.option('--enable-gate <checks...>', 'Persist gating for the named checks into specship.config.json, then run the gate')
|
|
1968
|
+
.option('--since <ref>', 'Evaluate only requirements whose links or spec files touch files changed since <ref> (drift + behaviour; fitness and maintainability stay repo-global)')
|
|
1803
1969
|
.action(async (pathArg, options) => {
|
|
1804
1970
|
const projectPath = resolveProjectPath(pathArg);
|
|
1805
1971
|
try {
|
|
@@ -1808,6 +1974,21 @@ function main() {
|
|
|
1808
1974
|
process.exit(1);
|
|
1809
1975
|
}
|
|
1810
1976
|
const enforce = await Promise.resolve().then(() => __importStar(require('../enforce/enforce')));
|
|
1977
|
+
// --since narrows the link-scoped checks (REQ-AUTHG-006.A1). An
|
|
1978
|
+
// unresolvable ref exits non-zero here: falling back to a full-repo run
|
|
1979
|
+
// would answer a question the caller did not ask and hide the typo
|
|
1980
|
+
// (A3).
|
|
1981
|
+
let scope;
|
|
1982
|
+
if (options.since) {
|
|
1983
|
+
const { changedFilesSince } = await Promise.resolve().then(() => __importStar(require('../enforce/changed-files')));
|
|
1984
|
+
try {
|
|
1985
|
+
scope = { changedFiles: changedFilesSince(projectPath, options.since), since: options.since };
|
|
1986
|
+
}
|
|
1987
|
+
catch (err) {
|
|
1988
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1989
|
+
process.exit(1);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1811
1992
|
// --enable-gate writes the opt-in config, then falls through to a
|
|
1812
1993
|
// normal run so the user immediately sees the gate with teeth
|
|
1813
1994
|
// (REQ-ENFORCE-004.A1).
|
|
@@ -1825,17 +2006,24 @@ function main() {
|
|
|
1825
2006
|
const { default: SpecShip } = await loadSpecShip();
|
|
1826
2007
|
const cg = await SpecShip.open(projectPath);
|
|
1827
2008
|
// --strict gates everything for this run only (REQ-ENFORCE-004.A2).
|
|
1828
|
-
const r = options.strict
|
|
2009
|
+
const r = options.strict
|
|
2010
|
+
? cg.getEnforce(enforce.strictEnforceConfig(), scope)
|
|
2011
|
+
: cg.getEnforce(undefined, scope);
|
|
1829
2012
|
if (options.json) {
|
|
1830
2013
|
console.log(JSON.stringify(r, null, 2));
|
|
1831
2014
|
cg.destroy();
|
|
1832
2015
|
process.exit(r.passed ? 0 : 1);
|
|
1833
2016
|
}
|
|
1834
2017
|
console.log(chalk.bold('\nEnforcement gate\n'));
|
|
2018
|
+
if (scope) {
|
|
2019
|
+
console.log(chalk.dim(` scoped to ${scope.changedFiles.length} file(s) changed since ${scope.since}` +
|
|
2020
|
+
' — drift and behaviour are link-scoped; fitness and maintainability are repo-global analyses and ran unscoped.\n'));
|
|
2021
|
+
}
|
|
1835
2022
|
for (const c of r.checks) {
|
|
1836
2023
|
const tag = c.gating ? chalk.dim('[gating]') : chalk.dim('[advisory]');
|
|
1837
2024
|
const mark = c.passed ? chalk.green('✓') : (c.gating ? chalk.red('✗') : chalk.yellow('•'));
|
|
1838
|
-
|
|
2025
|
+
const reach = scope ? chalk.dim(c.scoped ? ' [since]' : ' [repo-wide]') : '';
|
|
2026
|
+
console.log(` ${mark} ${c.check.padEnd(16)} ${tag}${reach} ${c.passed ? 'pass' : `${c.findings.length} finding(s)`}`);
|
|
1839
2027
|
if (!c.passed)
|
|
1840
2028
|
for (const f of c.findings.slice(0, 8))
|
|
1841
2029
|
console.log(chalk.dim(` ${f}`));
|
|
@@ -2458,16 +2646,16 @@ function main() {
|
|
|
2458
2646
|
.command('install')
|
|
2459
2647
|
.description('Install specship MCP server into Claude Code')
|
|
2460
2648
|
.option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt (local)')
|
|
2461
|
-
.option('-y, --yes', 'Non-interactive: defaults to --location=local
|
|
2462
|
-
.option('--no-permissions', 'Skip writing the auto-allow permissions list')
|
|
2649
|
+
.option('-y, --yes', 'Non-interactive: defaults to --location=local (the only question an interactive install asks)')
|
|
2650
|
+
.option('--no-permissions', 'Skip writing the auto-allow permissions list (written by default)')
|
|
2463
2651
|
.option('--sdd', '(default) Install the spec-driven-development governance tier (spec/authoring/review/design commands + the spec-author nudge hook)')
|
|
2464
2652
|
.option('--no-sdd', 'Skip the governance tier — retrieval-only install (the pre-0.18 default)')
|
|
2465
2653
|
.option('--path <repo>', 'Target repo to wire and initialize (default: current directory). Project-local files and the .specship index land there')
|
|
2466
2654
|
.option('--with-jira', 'Enable the optional JIRA integration (talks to your Atlassian instance; off by default — the core install is 100% local)')
|
|
2467
2655
|
.option('--with-designer', 'Enable the optional Designer integration (EXPERIMENTAL — drives claude.ai/design via a debug Chrome session and may break without notice; off by default)')
|
|
2468
|
-
.option('--statusline', 'Wire the SpecShip status-line segment into Claude
|
|
2469
|
-
.option('--skip-statusline', 'Do not add the status-line segment (
|
|
2470
|
-
.option('--skip-index', 'Do not
|
|
2656
|
+
.option('--statusline', '(default) Wire the SpecShip status-line segment into Claude; never overwrites an existing status line')
|
|
2657
|
+
.option('--skip-statusline', 'Do not add the status-line segment (it is added by default)')
|
|
2658
|
+
.option('--skip-index', 'Do not index the current project (it is indexed by default)')
|
|
2471
2659
|
.option('--print-config', 'Print the MCP config snippet and exit (no file writes). Claude Code by default; `--target gemini` prints the Gemini CLI settings snippet')
|
|
2472
2660
|
// -t/--target SELECTS the agents to wire (GEMINI-TARGET-DOC, REQ-GEMINI-002)
|
|
2473
2661
|
// and also picks the snippet --print-config prints. The legacy spellings
|
|
@@ -2509,30 +2697,23 @@ function main() {
|
|
|
2509
2697
|
}
|
|
2510
2698
|
try {
|
|
2511
2699
|
// Commander's `--no-permissions` makes `opts.permissions === false`;
|
|
2512
|
-
// omitting the flag leaves it `true` (the positive-form default)
|
|
2513
|
-
//
|
|
2514
|
-
//
|
|
2515
|
-
//
|
|
2516
|
-
// prompt is silently skipped on every interactive run.
|
|
2700
|
+
// omitting the flag leaves it `true` (the positive-form default), which
|
|
2701
|
+
// we must forward as `undefined` — "user did not override" — so the
|
|
2702
|
+
// installer can tell a default from an explicit choice and name the
|
|
2703
|
+
// defaults in its summary (REQ-SLIM-003.A2).
|
|
2517
2704
|
const explicitNoPermissions = opts.permissions === false;
|
|
2518
|
-
const autoAllow = explicitNoPermissions
|
|
2705
|
+
const autoAllow = explicitNoPermissions ? false : undefined;
|
|
2706
|
+
// Status line: default ON, never prompted (REQ-SLIM-003.A1).
|
|
2707
|
+
// `--skip-statusline` is the opt-out; `--statusline` is now a no-op
|
|
2708
|
+
// affirmation kept for compatibility (REQ-SLIM-003.A3). Distinct flag
|
|
2709
|
+
// names (rather than `--no-statusline`) dodge commander's `--no-*`
|
|
2710
|
+
// default-true coupling.
|
|
2711
|
+
const statusLine = opts.skipStatusline
|
|
2519
2712
|
? false
|
|
2520
|
-
: opts.
|
|
2713
|
+
: opts.statusline
|
|
2521
2714
|
? true
|
|
2522
2715
|
: undefined;
|
|
2523
|
-
|
|
2524
|
-
// forces off (both skip the prompt); neither ⇒ undefined ⇒ ask
|
|
2525
|
-
// interactively (default no). Distinct flag names dodge commander's
|
|
2526
|
-
// `--no-*` default-true coupling that would auto-install.
|
|
2527
|
-
const statusLine = opts.statusline
|
|
2528
|
-
? true
|
|
2529
|
-
: opts.skipStatusline
|
|
2530
|
-
? false
|
|
2531
|
-
: undefined;
|
|
2532
|
-
// The governance tier is opt-in (INSTALL-WEDGE-DOC): `--sdd` makes
|
|
2533
|
-
// `opts.sdd === true`; omitting it leaves it undefined → retrieval-only.
|
|
2534
|
-
// Forward `true` only when the user explicitly opted in.
|
|
2535
|
-
await runInstallerWithOptions({
|
|
2716
|
+
const outcome = await runInstallerWithOptions({
|
|
2536
2717
|
target: opts.target,
|
|
2537
2718
|
location: opts.location,
|
|
2538
2719
|
autoAllow,
|
|
@@ -2544,8 +2725,10 @@ function main() {
|
|
|
2544
2725
|
statusLine,
|
|
2545
2726
|
yes: opts.yes,
|
|
2546
2727
|
});
|
|
2547
|
-
//
|
|
2548
|
-
//
|
|
2728
|
+
// Index the current project (REQ-HANDSHAKE-004) before the smoke check,
|
|
2729
|
+
// so the new index is reflected by the index-queryable item. Not a
|
|
2730
|
+
// question any more (REQ-SLIM-003.A1) — `--skip-index` opts out, and the
|
|
2731
|
+
// summary below names the default.
|
|
2549
2732
|
const cwd = process.cwd();
|
|
2550
2733
|
const { decideInstallInit } = await Promise.resolve().then(() => __importStar(require('../installer/init-offer')));
|
|
2551
2734
|
const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../sync/git-hooks')));
|
|
@@ -2555,14 +2738,7 @@ function main() {
|
|
|
2555
2738
|
yes: opts.yes === true,
|
|
2556
2739
|
skipIndex: opts.skipIndex === true,
|
|
2557
2740
|
});
|
|
2558
|
-
|
|
2559
|
-
if (initDecision === 'offer') {
|
|
2560
|
-
const clack = await importESM('@clack/prompts');
|
|
2561
|
-
const ans = await clack.confirm({
|
|
2562
|
-
message: `Index this project (${cwd}) now, so Claude can explore it?`,
|
|
2563
|
-
});
|
|
2564
|
-
doIndex = ans === true; // a cancel (symbol) or "no" both decline (REQ-HANDSHAKE-004.A2)
|
|
2565
|
-
}
|
|
2741
|
+
const doIndex = initDecision === 'auto-index';
|
|
2566
2742
|
if (doIndex) {
|
|
2567
2743
|
console.log();
|
|
2568
2744
|
info(`Indexing ${cwd} …`);
|
|
@@ -2574,6 +2750,22 @@ function main() {
|
|
|
2574
2750
|
warn(`Indexing failed (run \`specship init -i\` later): ${e instanceof Error ? e.message : String(e)}`);
|
|
2575
2751
|
}
|
|
2576
2752
|
}
|
|
2753
|
+
// Name the defaulted indexing decision the same way the installer names
|
|
2754
|
+
// its own (REQ-SLIM-003.A2). The installer already printed this line when
|
|
2755
|
+
// a local install built the index itself, so it is never doubled.
|
|
2756
|
+
if (doIndex && !outcome?.indexed) {
|
|
2757
|
+
const { describeInstallDefaults } = await Promise.resolve().then(() => __importStar(require('../installer/install-defaults')));
|
|
2758
|
+
for (const line of describeInstallDefaults({
|
|
2759
|
+
statusLine: 'skipped',
|
|
2760
|
+
statusLineDefaulted: false,
|
|
2761
|
+
autoAllow: false,
|
|
2762
|
+
autoAllowDefaulted: false,
|
|
2763
|
+
index: 'indexed',
|
|
2764
|
+
indexDefaulted: true,
|
|
2765
|
+
})) {
|
|
2766
|
+
info(line);
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2577
2769
|
// Post-install smoke check (REQ-HANDSHAKE-002). Advisory: report failing
|
|
2578
2770
|
// items but never exit non-zero (REQ-HANDSHAKE-002.A4), so a provisioning
|
|
2579
2771
|
// script is never broken by it — the gating equivalent is `specship doctor`.
|
|
@@ -2918,6 +3110,51 @@ function main() {
|
|
|
2918
3110
|
// =============================================================================
|
|
2919
3111
|
// Spec layer / Workflow engine commands (v5)
|
|
2920
3112
|
// =============================================================================
|
|
3113
|
+
// @implements REQ-FLOWDIAG-001
|
|
3114
|
+
// Thin surface over SpecShip.generateFlowDiagram: render the indexed app's
|
|
3115
|
+
// entry points + call chains as a Mermaid flowchart under docs/. Once the
|
|
3116
|
+
// file exists, sync/index keep it fresh (REQ-FLOWDIAG-002).
|
|
3117
|
+
program
|
|
3118
|
+
.command('diagram [path]')
|
|
3119
|
+
.description('Generate a Mermaid flow diagram of the indexed application into docs/ (kept fresh by sync).')
|
|
3120
|
+
.option('-o, --output <path>', 'project-relative output path (default: docs/flow-diagram.md)')
|
|
3121
|
+
.option('--max-nodes <n>', 'node budget before modules collapse into summary nodes (default: 150)')
|
|
3122
|
+
.option('--depth <n>', 'max call-chain depth from each entry point (default: 6)')
|
|
3123
|
+
.option('--json', 'emit JSON')
|
|
3124
|
+
.action(async (pathArg, options) => {
|
|
3125
|
+
const projectRoot = path.resolve(pathArg ?? process.cwd());
|
|
3126
|
+
if (!(0, directory_1.isInitialized)(projectRoot)) {
|
|
3127
|
+
error(`SpecShip not initialized in ${projectRoot}. Run \`specship init -i\` first.`);
|
|
3128
|
+
process.exit(1);
|
|
3129
|
+
}
|
|
3130
|
+
const { SpecShip } = await loadSpecShip();
|
|
3131
|
+
const cg = await SpecShip.open(projectRoot);
|
|
3132
|
+
try {
|
|
3133
|
+
const override = {};
|
|
3134
|
+
if (options.output)
|
|
3135
|
+
override.output = options.output;
|
|
3136
|
+
if (options.maxNodes)
|
|
3137
|
+
override.maxNodes = Math.max(1, parseInt(options.maxNodes, 10) || 150);
|
|
3138
|
+
if (options.depth)
|
|
3139
|
+
override.depth = Math.max(1, parseInt(options.depth, 10) || 6);
|
|
3140
|
+
const result = cg.generateFlowDiagram(override);
|
|
3141
|
+
if (options.json) {
|
|
3142
|
+
// eslint-disable-next-line no-console
|
|
3143
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3144
|
+
}
|
|
3145
|
+
else {
|
|
3146
|
+
const verb = result.status === 'unchanged' ? 'is up to date' : result.status;
|
|
3147
|
+
success(`Flow diagram ${verb}: ${result.output}`);
|
|
3148
|
+
info(`${result.entryPoints} entry points · ${result.nodeCount} flow symbols · ${result.edgeCount} calls`);
|
|
3149
|
+
if (result.status === 'created') {
|
|
3150
|
+
info('Future `specship sync` / `specship index` runs will keep it fresh.');
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
finally {
|
|
3155
|
+
cg.close();
|
|
3156
|
+
}
|
|
3157
|
+
});
|
|
2921
3158
|
program
|
|
2922
3159
|
.command('drifted [path]')
|
|
2923
3160
|
.description('List spec links in concerning states (drifted, broken, orphaned).')
|