@specship/specship-win32-arm64 0.24.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.
Files changed (139) hide show
  1. package/lib/dist/activation/steering.d.ts +4 -4
  2. package/lib/dist/activation/steering.d.ts.map +1 -1
  3. package/lib/dist/activation/steering.js +6 -6
  4. package/lib/dist/activation/steering.js.map +1 -1
  5. package/lib/dist/bin/specship.js +232 -40
  6. package/lib/dist/bin/specship.js.map +1 -1
  7. package/lib/dist/commands/specship/learn.md +5 -3
  8. package/lib/dist/commands/specship/spec.md +73 -15
  9. package/lib/dist/db/spec-queries.d.ts +73 -3
  10. package/lib/dist/db/spec-queries.d.ts.map +1 -1
  11. package/lib/dist/db/spec-queries.js +139 -5
  12. package/lib/dist/db/spec-queries.js.map +1 -1
  13. package/lib/dist/enforce/changed-files.d.ts +28 -0
  14. package/lib/dist/enforce/changed-files.d.ts.map +1 -0
  15. package/lib/dist/enforce/changed-files.js +72 -0
  16. package/lib/dist/enforce/changed-files.js.map +1 -0
  17. package/lib/dist/enforce/enforce.d.ts +28 -1
  18. package/lib/dist/enforce/enforce.d.ts.map +1 -1
  19. package/lib/dist/enforce/enforce.js +37 -5
  20. package/lib/dist/enforce/enforce.js.map +1 -1
  21. package/lib/dist/extraction/grammars.d.ts +10 -0
  22. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  23. package/lib/dist/extraction/grammars.js +32 -3
  24. package/lib/dist/extraction/grammars.js.map +1 -1
  25. package/lib/dist/extraction/index.d.ts +7 -0
  26. package/lib/dist/extraction/index.d.ts.map +1 -1
  27. package/lib/dist/extraction/index.js.map +1 -1
  28. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts +7 -0
  29. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
  30. package/lib/dist/extraction/specs/markdown-spec-extractor.js +81 -8
  31. package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
  32. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm.gz +0 -0
  33. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm.gz +0 -0
  34. package/lib/dist/graph/spec-coverage.d.ts +86 -0
  35. package/lib/dist/graph/spec-coverage.d.ts.map +1 -0
  36. package/lib/dist/graph/spec-coverage.js +152 -0
  37. package/lib/dist/graph/spec-coverage.js.map +1 -0
  38. package/lib/dist/index.d.ts +69 -6
  39. package/lib/dist/index.d.ts.map +1 -1
  40. package/lib/dist/index.js +133 -21
  41. package/lib/dist/index.js.map +1 -1
  42. package/lib/dist/installer/index.d.ts +33 -8
  43. package/lib/dist/installer/index.d.ts.map +1 -1
  44. package/lib/dist/installer/index.js +50 -50
  45. package/lib/dist/installer/index.js.map +1 -1
  46. package/lib/dist/installer/init-offer.d.ts +14 -9
  47. package/lib/dist/installer/init-offer.d.ts.map +1 -1
  48. package/lib/dist/installer/init-offer.js +13 -9
  49. package/lib/dist/installer/init-offer.js.map +1 -1
  50. package/lib/dist/installer/install-defaults.d.ts +62 -0
  51. package/lib/dist/installer/install-defaults.d.ts.map +1 -0
  52. package/lib/dist/installer/install-defaults.js +67 -0
  53. package/lib/dist/installer/install-defaults.js.map +1 -0
  54. package/lib/dist/installer/targets/claude.d.ts +14 -0
  55. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  56. package/lib/dist/installer/targets/claude.js +64 -0
  57. package/lib/dist/installer/targets/claude.js.map +1 -1
  58. package/lib/dist/installer/targets/shared.js +4 -4
  59. package/lib/dist/installer/targets/shared.js.map +1 -1
  60. package/lib/dist/mcp/fitness-tool.d.ts +5 -6
  61. package/lib/dist/mcp/fitness-tool.d.ts.map +1 -1
  62. package/lib/dist/mcp/fitness-tool.js +0 -16
  63. package/lib/dist/mcp/fitness-tool.js.map +1 -1
  64. package/lib/dist/mcp/health-tool.d.ts +26 -0
  65. package/lib/dist/mcp/health-tool.d.ts.map +1 -0
  66. package/lib/dist/mcp/health-tool.js +50 -0
  67. package/lib/dist/mcp/health-tool.js.map +1 -0
  68. package/lib/dist/mcp/maintainability-tool.d.ts +5 -7
  69. package/lib/dist/mcp/maintainability-tool.d.ts.map +1 -1
  70. package/lib/dist/mcp/maintainability-tool.js +0 -16
  71. package/lib/dist/mcp/maintainability-tool.js.map +1 -1
  72. package/lib/dist/mcp/model-context.d.ts +13 -2
  73. package/lib/dist/mcp/model-context.d.ts.map +1 -1
  74. package/lib/dist/mcp/model-context.js +39 -12
  75. package/lib/dist/mcp/model-context.js.map +1 -1
  76. package/lib/dist/mcp/proxy.d.ts.map +1 -1
  77. package/lib/dist/mcp/proxy.js +4 -1
  78. package/lib/dist/mcp/proxy.js.map +1 -1
  79. package/lib/dist/mcp/server-instructions.d.ts +1 -1
  80. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  81. package/lib/dist/mcp/server-instructions.js +5 -4
  82. package/lib/dist/mcp/server-instructions.js.map +1 -1
  83. package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
  84. package/lib/dist/mcp/spec-tools.js +213 -28
  85. package/lib/dist/mcp/spec-tools.js.map +1 -1
  86. package/lib/dist/mcp/tools.d.ts +12 -8
  87. package/lib/dist/mcp/tools.d.ts.map +1 -1
  88. package/lib/dist/mcp/tools.js +89 -48
  89. package/lib/dist/mcp/tools.js.map +1 -1
  90. package/lib/dist/resolution/spec-link-resolver.d.ts +44 -2
  91. package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
  92. package/lib/dist/resolution/spec-link-resolver.js +132 -0
  93. package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
  94. package/lib/dist/resolution/test-title-links.d.ts +62 -0
  95. package/lib/dist/resolution/test-title-links.d.ts.map +1 -0
  96. package/lib/dist/resolution/test-title-links.js +108 -0
  97. package/lib/dist/resolution/test-title-links.js.map +1 -0
  98. package/lib/dist/search/query-parser.d.ts.map +1 -1
  99. package/lib/dist/search/query-parser.js +6 -1
  100. package/lib/dist/search/query-parser.js.map +1 -1
  101. package/lib/dist/server/project-registry.js +1 -1
  102. package/lib/dist/server/routes/claude.js +9 -36
  103. package/lib/dist/server/routes/graph.js +53 -7
  104. package/lib/dist/server/routes/mcp.js +4 -3
  105. package/lib/dist/server/routes/projects.js +4 -4
  106. package/lib/dist/server/routes/spec.js +49 -7
  107. package/lib/dist/server/server.js +29 -17
  108. package/lib/dist/skills/spec-author/SKILL.md +164 -0
  109. package/lib/dist/skills/spec-author/references/format.md +210 -0
  110. package/lib/dist/skills/spec-author/references/gap-questions.md +152 -0
  111. package/lib/dist/skills/spec-author/references/quality-rubric.md +125 -0
  112. package/lib/dist/skills/spec-author/references/review-checklist.md +175 -0
  113. package/lib/dist/spec-lint/lint.d.ts +82 -0
  114. package/lib/dist/spec-lint/lint.d.ts.map +1 -0
  115. package/lib/dist/spec-lint/lint.js +430 -0
  116. package/lib/dist/spec-lint/lint.js.map +1 -0
  117. package/lib/dist/statusline/index.js.map +1 -1
  118. package/lib/dist/statusline/render.d.ts +1 -1
  119. package/lib/dist/statusline/render.d.ts.map +1 -1
  120. package/lib/dist/statusline/render.js +3 -1
  121. package/lib/dist/statusline/render.js.map +1 -1
  122. package/lib/dist/ui/assets/index-BzuUWUAE.js +50 -0
  123. package/lib/dist/ui/index.html +1 -1
  124. package/lib/dist/verify/report-ingest.d.ts +88 -0
  125. package/lib/dist/verify/report-ingest.d.ts.map +1 -0
  126. package/lib/dist/verify/report-ingest.js +269 -0
  127. package/lib/dist/verify/report-ingest.js.map +1 -0
  128. package/lib/dist/workflows/defaults/spec-fix.yaml +103 -12
  129. package/lib/dist/workflows/defaults/spec-implement-mixed.yaml +264 -21
  130. package/lib/dist/workflows/defaults/spec-implement.yaml +260 -19
  131. package/lib/dist/workflows/defaults/spec-verify.yaml +60 -9
  132. package/lib/node_modules/.package-lock.json +1 -1
  133. package/lib/package.json +7 -3
  134. package/package.json +1 -1
  135. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  136. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  137. package/lib/dist/server/ingest/impact-query.js +0 -343
  138. package/lib/dist/server/routes/events.js +0 -152
  139. 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 haiku-tier template (LOWMODEL-DOC, REQ-LOWMODEL-002): small models
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 STEERING_TEXT_HAIKU: string;
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
- * haiku sessions get the prescriptive template; sonnet and frontier keep the
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,mBAAmB,QAIqC,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"}
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.STEERING_TEXT_HAIKU = exports.STEERING_TEXT = void 0;
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 haiku-tier template (LOWMODEL-DOC, REQ-LOWMODEL-002): small models
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.STEERING_TEXT_HAIKU = 'This project has a SpecShip index. For any code question, call exactly: ' +
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
- * haiku sessions get the prescriptive template; sonnet and frontier keep the
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) === 'haiku' ? exports.STEERING_TEXT_HAIKU : exports.STEERING_TEXT;
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,mBAAmB,GAC9B,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,OAAO,CAAC,CAAC,CAAC,2BAAmB,CAAC,CAAC,CAAC,qBAAa,CAAC;AAC9F,CAAC"}
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"}
@@ -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 and apply it from the dashboard Improvements page (preview-diff → confirm).');
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('Review and apply proposals from the dashboard Improvements page (preview-diff → confirm).');
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 + apply from the dashboard Improvements page (preview-diff → confirm), or `specship reflect`.');
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 ? cg.getEnforce(enforce.strictEnforceConfig()) : cg.getEnforce();
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
- console.log(` ${mark} ${c.check.padEnd(16)} ${tag} ${c.passed ? 'pass' : `${c.findings.length} finding(s)`}`);
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, auto-allow on')
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 (skips the prompt; never overwrites an existing status line)')
2469
- .option('--skip-statusline', 'Do not add the status-line segment (skips the prompt)')
2470
- .option('--skip-index', 'Do not offer to index the current project (an explicit opt-out for automation)')
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
- // We MUST treat the default-true as "user did not override — let
2514
- // the orchestrator prompt" and only forward an explicit `false`
2515
- // (or `true` when --yes implies it). Otherwise the auto-allow
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.yes
2713
+ : opts.statusline
2521
2714
  ? true
2522
2715
  : undefined;
2523
- // Status-line opt-in: `--statusline` forces on, `--skip-statusline`
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
- // Offer to index the current project (REQ-HANDSHAKE-004) before the smoke
2548
- // check, so an accepted index is reflected by the index-queryable item.
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
- let doIndex = initDecision === 'auto-index';
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`.