spectra-cli 4.2.0 → 4.3.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 (365) hide show
  1. package/README.md +12 -5
  2. package/dist/.spectra-build-meta.json +9 -0
  3. package/dist/adapters/language-adapter.d.ts +7 -12
  4. package/dist/adapters/language-adapter.d.ts.map +1 -1
  5. package/dist/adapters/python-adapter.d.ts.map +1 -1
  6. package/dist/adapters/python-adapter.js +11 -2
  7. package/dist/adapters/python-adapter.js.map +1 -1
  8. package/dist/batch/batch-orchestrator.d.ts +44 -1
  9. package/dist/batch/batch-orchestrator.d.ts.map +1 -1
  10. package/dist/batch/batch-orchestrator.js +312 -76
  11. package/dist/batch/batch-orchestrator.js.map +1 -1
  12. package/dist/batch/delta-regenerator.d.ts.map +1 -1
  13. package/dist/batch/delta-regenerator.js +21 -28
  14. package/dist/batch/delta-regenerator.js.map +1 -1
  15. package/dist/batch/module-grouper.d.ts +7 -0
  16. package/dist/batch/module-grouper.d.ts.map +1 -1
  17. package/dist/batch/module-grouper.js +2 -0
  18. package/dist/batch/module-grouper.js.map +1 -1
  19. package/dist/batch/regen-plan.d.ts +78 -0
  20. package/dist/batch/regen-plan.d.ts.map +1 -0
  21. package/dist/batch/regen-plan.js +93 -0
  22. package/dist/batch/regen-plan.js.map +1 -0
  23. package/dist/cli/commands/batch.d.ts.map +1 -1
  24. package/dist/cli/commands/batch.js +36 -8
  25. package/dist/cli/commands/batch.js.map +1 -1
  26. package/dist/cli/commands/scaffold-kb.d.ts +9 -0
  27. package/dist/cli/commands/scaffold-kb.d.ts.map +1 -0
  28. package/dist/cli/commands/scaffold-kb.js +179 -0
  29. package/dist/cli/commands/scaffold-kb.js.map +1 -0
  30. package/dist/cli/index.js +24 -5
  31. package/dist/cli/index.js.map +1 -1
  32. package/dist/cli/utils/error-handler.d.ts +0 -6
  33. package/dist/cli/utils/error-handler.d.ts.map +1 -1
  34. package/dist/cli/utils/error-handler.js +0 -13
  35. package/dist/cli/utils/error-handler.js.map +1 -1
  36. package/dist/cli/utils/parse-args.d.ts +43 -3
  37. package/dist/cli/utils/parse-args.d.ts.map +1 -1
  38. package/dist/cli/utils/parse-args.js +98 -12
  39. package/dist/cli/utils/parse-args.js.map +1 -1
  40. package/dist/cli/version-meta.d.ts +16 -0
  41. package/dist/cli/version-meta.d.ts.map +1 -0
  42. package/dist/cli/version-meta.js +28 -0
  43. package/dist/cli/version-meta.js.map +1 -0
  44. package/dist/core/ast-analyzer.d.ts.map +1 -1
  45. package/dist/core/ast-analyzer.js +7 -11
  46. package/dist/core/ast-analyzer.js.map +1 -1
  47. package/dist/core/import-resolver.d.ts +70 -67
  48. package/dist/core/import-resolver.d.ts.map +1 -1
  49. package/dist/core/import-resolver.js +341 -200
  50. package/dist/core/import-resolver.js.map +1 -1
  51. package/dist/core/single-spec-orchestrator.d.ts +21 -0
  52. package/dist/core/single-spec-orchestrator.d.ts.map +1 -1
  53. package/dist/core/single-spec-orchestrator.js +41 -14
  54. package/dist/core/single-spec-orchestrator.js.map +1 -1
  55. package/dist/core/skeleton-hash.d.ts +33 -0
  56. package/dist/core/skeleton-hash.d.ts.map +1 -0
  57. package/dist/core/skeleton-hash.js +76 -0
  58. package/dist/core/skeleton-hash.js.map +1 -0
  59. package/dist/core/tree-sitter-analyzer.d.ts +3 -2
  60. package/dist/core/tree-sitter-analyzer.d.ts.map +1 -1
  61. package/dist/core/tree-sitter-analyzer.js +5 -6
  62. package/dist/core/tree-sitter-analyzer.js.map +1 -1
  63. package/dist/core/tree-sitter-fallback.d.ts +7 -2
  64. package/dist/core/tree-sitter-fallback.d.ts.map +1 -1
  65. package/dist/core/tree-sitter-fallback.js +18 -15
  66. package/dist/core/tree-sitter-fallback.js.map +1 -1
  67. package/dist/extraction/image-extractor.d.ts.map +1 -1
  68. package/dist/extraction/image-extractor.js +3 -1
  69. package/dist/extraction/image-extractor.js.map +1 -1
  70. package/dist/extraction/markdown-extractor.d.ts.map +1 -1
  71. package/dist/extraction/markdown-extractor.js +3 -1
  72. package/dist/extraction/markdown-extractor.js.map +1 -1
  73. package/dist/generator/frontmatter.d.ts +5 -0
  74. package/dist/generator/frontmatter.d.ts.map +1 -1
  75. package/dist/generator/frontmatter.js +4 -0
  76. package/dist/generator/frontmatter.js.map +1 -1
  77. package/dist/kb-mcp/index.d.ts +14 -0
  78. package/dist/kb-mcp/index.d.ts.map +1 -0
  79. package/dist/kb-mcp/index.js +24 -0
  80. package/dist/kb-mcp/index.js.map +1 -0
  81. package/dist/kb-mcp/lib/kb-error.d.ts +21 -0
  82. package/dist/kb-mcp/lib/kb-error.d.ts.map +1 -0
  83. package/dist/kb-mcp/lib/kb-error.js +28 -0
  84. package/dist/kb-mcp/lib/kb-error.js.map +1 -0
  85. package/dist/kb-mcp/lib/kb-locator.d.ts +58 -0
  86. package/dist/kb-mcp/lib/kb-locator.d.ts.map +1 -0
  87. package/dist/kb-mcp/lib/kb-locator.js +85 -0
  88. package/dist/kb-mcp/lib/kb-locator.js.map +1 -0
  89. package/dist/kb-mcp/lib/result-merger.d.ts +35 -0
  90. package/dist/kb-mcp/lib/result-merger.d.ts.map +1 -0
  91. package/dist/kb-mcp/lib/result-merger.js +64 -0
  92. package/dist/kb-mcp/lib/result-merger.js.map +1 -0
  93. package/dist/kb-mcp/server.d.ts +12 -0
  94. package/dist/kb-mcp/server.d.ts.map +1 -0
  95. package/dist/kb-mcp/server.js +29 -0
  96. package/dist/kb-mcp/server.js.map +1 -0
  97. package/dist/kb-mcp/tools/kb-api-lookup.d.ts +28 -0
  98. package/dist/kb-mcp/tools/kb-api-lookup.d.ts.map +1 -0
  99. package/dist/kb-mcp/tools/kb-api-lookup.js +237 -0
  100. package/dist/kb-mcp/tools/kb-api-lookup.js.map +1 -0
  101. package/dist/kb-mcp/tools/kb-doc-lookup.d.ts +20 -0
  102. package/dist/kb-mcp/tools/kb-doc-lookup.d.ts.map +1 -0
  103. package/dist/kb-mcp/tools/kb-doc-lookup.js +92 -0
  104. package/dist/kb-mcp/tools/kb-doc-lookup.js.map +1 -0
  105. package/dist/kb-mcp/tools/kb-search.d.ts +23 -0
  106. package/dist/kb-mcp/tools/kb-search.d.ts.map +1 -0
  107. package/dist/kb-mcp/tools/kb-search.js +126 -0
  108. package/dist/kb-mcp/tools/kb-search.js.map +1 -0
  109. package/dist/knowledge-graph/import-resolver.d.ts +11 -86
  110. package/dist/knowledge-graph/import-resolver.d.ts.map +1 -1
  111. package/dist/knowledge-graph/import-resolver.js +10 -333
  112. package/dist/knowledge-graph/import-resolver.js.map +1 -1
  113. package/dist/knowledge-graph/incremental.d.ts +25 -4
  114. package/dist/knowledge-graph/incremental.d.ts.map +1 -1
  115. package/dist/knowledge-graph/incremental.js +37 -15
  116. package/dist/knowledge-graph/incremental.js.map +1 -1
  117. package/dist/knowledge-graph/index.d.ts +11 -0
  118. package/dist/knowledge-graph/index.d.ts.map +1 -1
  119. package/dist/knowledge-graph/index.js +43 -1
  120. package/dist/knowledge-graph/index.js.map +1 -1
  121. package/dist/knowledge-graph/module-derivation.d.ts +14 -0
  122. package/dist/knowledge-graph/module-derivation.d.ts.map +1 -1
  123. package/dist/knowledge-graph/module-derivation.js +44 -44
  124. package/dist/knowledge-graph/module-derivation.js.map +1 -1
  125. package/dist/knowledge-graph/persistence.d.ts +56 -16
  126. package/dist/knowledge-graph/persistence.d.ts.map +1 -1
  127. package/dist/knowledge-graph/persistence.js +86 -32
  128. package/dist/knowledge-graph/persistence.js.map +1 -1
  129. package/dist/knowledge-graph/query-helpers.d.ts +39 -12
  130. package/dist/knowledge-graph/query-helpers.d.ts.map +1 -1
  131. package/dist/knowledge-graph/query-helpers.js +199 -34
  132. package/dist/knowledge-graph/query-helpers.js.map +1 -1
  133. package/dist/knowledge-graph/relativize.d.ts +51 -0
  134. package/dist/knowledge-graph/relativize.d.ts.map +1 -0
  135. package/dist/knowledge-graph/relativize.js +104 -0
  136. package/dist/knowledge-graph/relativize.js.map +1 -0
  137. package/dist/mcp/agent-context-tools.d.ts +3 -54
  138. package/dist/mcp/agent-context-tools.d.ts.map +1 -1
  139. package/dist/mcp/agent-context-tools.js +141 -189
  140. package/dist/mcp/agent-context-tools.js.map +1 -1
  141. package/dist/mcp/file-nav-tools.d.ts +42 -0
  142. package/dist/mcp/file-nav-tools.d.ts.map +1 -0
  143. package/dist/mcp/file-nav-tools.js +318 -0
  144. package/dist/mcp/file-nav-tools.js.map +1 -0
  145. package/dist/mcp/graph-tools.d.ts +2 -0
  146. package/dist/mcp/graph-tools.d.ts.map +1 -1
  147. package/dist/mcp/graph-tools.js +134 -123
  148. package/dist/mcp/graph-tools.js.map +1 -1
  149. package/dist/mcp/lib/file-nav-helpers.d.ts +128 -0
  150. package/dist/mcp/lib/file-nav-helpers.d.ts.map +1 -0
  151. package/dist/mcp/lib/file-nav-helpers.js +305 -0
  152. package/dist/mcp/lib/file-nav-helpers.js.map +1 -0
  153. package/dist/mcp/lib/telemetry.d.ts +69 -0
  154. package/dist/mcp/lib/telemetry.d.ts.map +1 -0
  155. package/dist/mcp/lib/telemetry.js +113 -0
  156. package/dist/mcp/lib/telemetry.js.map +1 -0
  157. package/dist/mcp/lib/tool-response.d.ts +46 -0
  158. package/dist/mcp/lib/tool-response.d.ts.map +1 -0
  159. package/dist/mcp/lib/tool-response.js +83 -0
  160. package/dist/mcp/lib/tool-response.js.map +1 -0
  161. package/dist/mcp/server.d.ts +13 -2
  162. package/dist/mcp/server.d.ts.map +1 -1
  163. package/dist/mcp/server.js +209 -150
  164. package/dist/mcp/server.js.map +1 -1
  165. package/dist/models/code-skeleton.d.ts +4 -2
  166. package/dist/models/code-skeleton.d.ts.map +1 -1
  167. package/dist/models/code-skeleton.js +2 -1
  168. package/dist/models/code-skeleton.js.map +1 -1
  169. package/dist/models/module-spec.d.ts +18 -0
  170. package/dist/models/module-spec.d.ts.map +1 -1
  171. package/dist/models/module-spec.js +6 -0
  172. package/dist/models/module-spec.js.map +1 -1
  173. package/dist/panoramic/builders/doc-graph-builder.d.ts +13 -0
  174. package/dist/panoramic/builders/doc-graph-builder.d.ts.map +1 -1
  175. package/dist/panoramic/builders/doc-graph-builder.js +20 -0
  176. package/dist/panoramic/builders/doc-graph-builder.js.map +1 -1
  177. package/dist/panoramic/graph/graph-builder.d.ts +72 -2
  178. package/dist/panoramic/graph/graph-builder.d.ts.map +1 -1
  179. package/dist/panoramic/graph/graph-builder.js +257 -51
  180. package/dist/panoramic/graph/graph-builder.js.map +1 -1
  181. package/dist/panoramic/graph/graph-query.d.ts +16 -0
  182. package/dist/panoramic/graph/graph-query.d.ts.map +1 -1
  183. package/dist/panoramic/graph/graph-query.js +49 -1
  184. package/dist/panoramic/graph/graph-query.js.map +1 -1
  185. package/dist/panoramic/graph/index.d.ts +2 -1
  186. package/dist/panoramic/graph/index.d.ts.map +1 -1
  187. package/dist/panoramic/graph/index.js +1 -1
  188. package/dist/panoramic/graph/index.js.map +1 -1
  189. package/dist/panoramic/pipelines/adr-evidence-verifier.d.ts.map +1 -1
  190. package/dist/panoramic/pipelines/adr-evidence-verifier.js +1 -29
  191. package/dist/panoramic/pipelines/adr-evidence-verifier.js.map +1 -1
  192. package/dist/panoramic/query.d.ts +1 -0
  193. package/dist/panoramic/query.d.ts.map +1 -1
  194. package/dist/panoramic/query.js +5 -0
  195. package/dist/panoramic/query.js.map +1 -1
  196. package/dist/scaffold-kb/api-entities-serializer.d.ts +12 -0
  197. package/dist/scaffold-kb/api-entities-serializer.d.ts.map +1 -0
  198. package/dist/scaffold-kb/api-entities-serializer.js +144 -0
  199. package/dist/scaffold-kb/api-entities-serializer.js.map +1 -0
  200. package/dist/scaffold-kb/arbitration.d.ts +44 -0
  201. package/dist/scaffold-kb/arbitration.d.ts.map +1 -0
  202. package/dist/scaffold-kb/arbitration.js +144 -0
  203. package/dist/scaffold-kb/arbitration.js.map +1 -0
  204. package/dist/scaffold-kb/chunk-splitter.d.ts +20 -0
  205. package/dist/scaffold-kb/chunk-splitter.d.ts.map +1 -0
  206. package/dist/scaffold-kb/chunk-splitter.js +309 -0
  207. package/dist/scaffold-kb/chunk-splitter.js.map +1 -0
  208. package/dist/scaffold-kb/doc-graph-builder.d.ts +26 -0
  209. package/dist/scaffold-kb/doc-graph-builder.d.ts.map +1 -0
  210. package/dist/scaffold-kb/doc-graph-builder.js +72 -0
  211. package/dist/scaffold-kb/doc-graph-builder.js.map +1 -0
  212. package/dist/scaffold-kb/entity-extractor.d.ts +40 -0
  213. package/dist/scaffold-kb/entity-extractor.d.ts.map +1 -0
  214. package/dist/scaffold-kb/entity-extractor.js +0 -0
  215. package/dist/scaffold-kb/entity-extractor.js.map +1 -0
  216. package/dist/scaffold-kb/entity-heuristic.d.ts +14 -0
  217. package/dist/scaffold-kb/entity-heuristic.d.ts.map +1 -0
  218. package/dist/scaffold-kb/entity-heuristic.js +107 -0
  219. package/dist/scaffold-kb/entity-heuristic.js.map +1 -0
  220. package/dist/scaffold-kb/entity-matcher.d.ts +20 -0
  221. package/dist/scaffold-kb/entity-matcher.d.ts.map +1 -0
  222. package/dist/scaffold-kb/entity-matcher.js +53 -0
  223. package/dist/scaffold-kb/entity-matcher.js.map +1 -0
  224. package/dist/scaffold-kb/entity-util.d.ts +11 -0
  225. package/dist/scaffold-kb/entity-util.d.ts.map +1 -0
  226. package/dist/scaffold-kb/entity-util.js +23 -0
  227. package/dist/scaffold-kb/entity-util.js.map +1 -0
  228. package/dist/scaffold-kb/evidence-envelope.d.ts +17 -0
  229. package/dist/scaffold-kb/evidence-envelope.d.ts.map +1 -0
  230. package/dist/scaffold-kb/evidence-envelope.js +32 -0
  231. package/dist/scaffold-kb/evidence-envelope.js.map +1 -0
  232. package/dist/scaffold-kb/index.d.ts +20 -0
  233. package/dist/scaffold-kb/index.d.ts.map +1 -0
  234. package/dist/scaffold-kb/index.js +102 -0
  235. package/dist/scaffold-kb/index.js.map +1 -0
  236. package/dist/scaffold-kb/ingest/ingest-core.d.ts +47 -0
  237. package/dist/scaffold-kb/ingest/ingest-core.d.ts.map +1 -0
  238. package/dist/scaffold-kb/ingest/ingest-core.js +231 -0
  239. package/dist/scaffold-kb/ingest/ingest-core.js.map +1 -0
  240. package/dist/scaffold-kb/ingest/office-parser.d.ts +33 -0
  241. package/dist/scaffold-kb/ingest/office-parser.d.ts.map +1 -0
  242. package/dist/scaffold-kb/ingest/office-parser.js +187 -0
  243. package/dist/scaffold-kb/ingest/office-parser.js.map +1 -0
  244. package/dist/scaffold-kb/ingest/url-fetcher.d.ts +43 -0
  245. package/dist/scaffold-kb/ingest/url-fetcher.d.ts.map +1 -0
  246. package/dist/scaffold-kb/ingest/url-fetcher.js +245 -0
  247. package/dist/scaffold-kb/ingest/url-fetcher.js.map +1 -0
  248. package/dist/scaffold-kb/ingester.d.ts +38 -0
  249. package/dist/scaffold-kb/ingester.d.ts.map +1 -0
  250. package/dist/scaffold-kb/ingester.js +261 -0
  251. package/dist/scaffold-kb/ingester.js.map +1 -0
  252. package/dist/scaffold-kb/injection-format.d.ts +25 -0
  253. package/dist/scaffold-kb/injection-format.d.ts.map +1 -0
  254. package/dist/scaffold-kb/injection-format.js +55 -0
  255. package/dist/scaffold-kb/injection-format.js.map +1 -0
  256. package/dist/scaffold-kb/kb-writer.d.ts +13 -0
  257. package/dist/scaffold-kb/kb-writer.d.ts.map +1 -0
  258. package/dist/scaffold-kb/kb-writer.js +69 -0
  259. package/dist/scaffold-kb/kb-writer.js.map +1 -0
  260. package/dist/scaffold-kb/keyword-extract.d.ts +16 -0
  261. package/dist/scaffold-kb/keyword-extract.d.ts.map +1 -0
  262. package/dist/scaffold-kb/keyword-extract.js +49 -0
  263. package/dist/scaffold-kb/keyword-extract.js.map +1 -0
  264. package/dist/scaffold-kb/query-sanitizer.d.ts +28 -0
  265. package/dist/scaffold-kb/query-sanitizer.d.ts.map +1 -0
  266. package/dist/scaffold-kb/query-sanitizer.js +38 -0
  267. package/dist/scaffold-kb/query-sanitizer.js.map +1 -0
  268. package/dist/scaffold-kb/recall-eval.d.ts +48 -0
  269. package/dist/scaffold-kb/recall-eval.d.ts.map +1 -0
  270. package/dist/scaffold-kb/recall-eval.js +55 -0
  271. package/dist/scaffold-kb/recall-eval.js.map +1 -0
  272. package/dist/scaffold-kb/schema-compat.d.ts +22 -0
  273. package/dist/scaffold-kb/schema-compat.d.ts.map +1 -0
  274. package/dist/scaffold-kb/schema-compat.js +32 -0
  275. package/dist/scaffold-kb/schema-compat.js.map +1 -0
  276. package/dist/scaffold-kb/search-core.d.ts +42 -0
  277. package/dist/scaffold-kb/search-core.d.ts.map +1 -0
  278. package/dist/scaffold-kb/search-core.js +89 -0
  279. package/dist/scaffold-kb/search-core.js.map +1 -0
  280. package/dist/scaffold-kb/sqlite-engine.d.ts +58 -0
  281. package/dist/scaffold-kb/sqlite-engine.d.ts.map +1 -0
  282. package/dist/scaffold-kb/sqlite-engine.js +61 -0
  283. package/dist/scaffold-kb/sqlite-engine.js.map +1 -0
  284. package/dist/scaffold-kb/sqlite-writer.d.ts +18 -0
  285. package/dist/scaffold-kb/sqlite-writer.d.ts.map +1 -0
  286. package/dist/scaffold-kb/sqlite-writer.js +76 -0
  287. package/dist/scaffold-kb/sqlite-writer.js.map +1 -0
  288. package/dist/scaffold-kb/tokenizer.d.ts +26 -0
  289. package/dist/scaffold-kb/tokenizer.d.ts.map +1 -0
  290. package/dist/scaffold-kb/tokenizer.js +97 -0
  291. package/dist/scaffold-kb/tokenizer.js.map +1 -0
  292. package/dist/scaffold-kb/types.d.ts +157 -0
  293. package/dist/scaffold-kb/types.d.ts.map +1 -0
  294. package/dist/scaffold-kb/types.js +5 -0
  295. package/dist/scaffold-kb/types.js.map +1 -0
  296. package/dist/utils/file-scanner.d.ts +15 -0
  297. package/dist/utils/file-scanner.d.ts.map +1 -1
  298. package/dist/utils/file-scanner.js +17 -0
  299. package/dist/utils/file-scanner.js.map +1 -1
  300. package/dist/utils/string-distance.d.ts +20 -0
  301. package/dist/utils/string-distance.d.ts.map +1 -0
  302. package/dist/utils/string-distance.js +38 -0
  303. package/dist/utils/string-distance.js.map +1 -0
  304. package/package.json +11 -2
  305. package/plugins/demo-kb-en/.claude-plugin/plugin.json +11 -0
  306. package/plugins/demo-kb-en/.mcp.json +8 -0
  307. package/plugins/demo-kb-en/FIXTURE.json +23 -0
  308. package/plugins/demo-kb-en/FIXTURE.md +53 -0
  309. package/plugins/demo-kb-en/ingest-samples/meeting-notes.md +13 -0
  310. package/plugins/demo-kb-en/kb/api-entities.json +3445 -0
  311. package/plugins/demo-kb-en/kb/chunks.sqlite +0 -0
  312. package/plugins/demo-kb-en/kb/doc-graph.json +113 -0
  313. package/plugins/demo-kb-zh/.claude-plugin/plugin.json +11 -0
  314. package/plugins/demo-kb-zh/.mcp.json +8 -0
  315. package/plugins/demo-kb-zh/FIXTURE.json +23 -0
  316. package/plugins/demo-kb-zh/FIXTURE.md +63 -0
  317. package/plugins/demo-kb-zh/ingest-samples/meeting-notes.md +13 -0
  318. package/plugins/demo-kb-zh/kb/api-entities.json +990 -0
  319. package/plugins/demo-kb-zh/kb/chunks.sqlite +0 -0
  320. package/plugins/demo-kb-zh/kb/doc-graph.json +113 -0
  321. package/plugins/spec-driver/.claude-plugin/plugin.json +1 -1
  322. package/plugins/spec-driver/README.md +33 -1
  323. package/plugins/spec-driver/agents/verify.md +88 -0
  324. package/plugins/spec-driver/config/orchestration.yaml +39 -31
  325. package/plugins/spec-driver/contracts/orchestration-overrides-contract.yaml +27 -0
  326. package/plugins/spec-driver/contracts/orchestration-schema.mjs +272 -227
  327. package/plugins/spec-driver/contracts/wrapper-source-of-truth.yaml +3 -0
  328. package/plugins/spec-driver/hooks/hooks.json +9 -0
  329. package/plugins/spec-driver/hooks/stop-fix-compliance-check.sh +36 -0
  330. package/plugins/spec-driver/lib/delegation-contract.mjs +84 -0
  331. package/plugins/spec-driver/lib/orchestration-resolver.mjs +49 -0
  332. package/plugins/spec-driver/lib/orchestrator.mjs +29 -1
  333. package/plugins/spec-driver/scripts/codex-skills.sh +28 -28
  334. package/plugins/spec-driver/scripts/dev/spike-fix-compliance-e2e.mjs +130 -0
  335. package/plugins/spec-driver/scripts/fix-compliance-judge.mjs +415 -0
  336. package/plugins/spec-driver/scripts/generate-adoption-insights.mjs +48 -1
  337. package/plugins/spec-driver/scripts/goal-loop-cli.mjs +307 -0
  338. package/plugins/spec-driver/scripts/init-project.sh +56 -0
  339. package/plugins/spec-driver/scripts/kb-prequery.mjs +125 -0
  340. package/plugins/spec-driver/scripts/lib/config-schema.mjs +200 -94
  341. package/plugins/spec-driver/scripts/lib/ensure-gitignore.sh +277 -0
  342. package/plugins/spec-driver/scripts/lib/extract-wrapper-body.mjs +136 -0
  343. package/plugins/spec-driver/scripts/lib/fix-compliance-core.mjs +434 -0
  344. package/plugins/spec-driver/scripts/lib/fix-compliance-io.mjs +335 -0
  345. package/plugins/spec-driver/scripts/lib/goal-loop-core.mjs +783 -0
  346. package/plugins/spec-driver/scripts/lib/init-project-output.sh +28 -0
  347. package/plugins/spec-driver/scripts/lib/load-zod.mjs +62 -0
  348. package/plugins/spec-driver/scripts/lib/project-profile-resolver.mjs +141 -49
  349. package/plugins/spec-driver/scripts/lib/project-profile-schema.mjs +88 -54
  350. package/plugins/spec-driver/scripts/postinstall.sh +13 -1
  351. package/plugins/spec-driver/scripts/record-workflow-run.mjs +75 -0
  352. package/plugins/spec-driver/scripts/sync-delegation-contract.mjs +187 -0
  353. package/plugins/spec-driver/scripts/validate-orchestrator-models.mjs +156 -0
  354. package/plugins/spec-driver/scripts/validate-wrapper-sources.mjs +46 -1
  355. package/plugins/spec-driver/skills/spec-driver-feature/SKILL.md +292 -0
  356. package/plugins/spec-driver/skills/spec-driver-fix/SKILL.md +87 -8
  357. package/plugins/spec-driver/skills/spec-driver-implement/SKILL.md +8 -0
  358. package/plugins/spec-driver/skills/spec-driver-resume/SKILL.md +12 -1
  359. package/plugins/spec-driver/skills/spec-driver-story/SKILL.md +20 -0
  360. package/plugins/spec-driver/templates/delegation-contract.md +27 -0
  361. package/plugins/spec-driver/templates/goal-loop-override-template.yaml +284 -0
  362. package/plugins/spec-driver/templates/specify-base/project-context-template.yaml +10 -0
  363. package/plugins/spectra/.claude-plugin/plugin.json +1 -1
  364. package/plugins/spectra/README.md +1 -1
  365. package/templates/module-spec.hbs +1 -0
@@ -120,6 +120,34 @@ print_init_text_result() {
120
120
  echo -e " ⚠️ ${YELLOW}.specify/templates 仍缺少模板: ${missing}${NC}"
121
121
  fi
122
122
  ;;
123
+ git_exclude)
124
+ if [[ "$value" == ready ]]; then
125
+ echo -e " ✅ .git/info/exclude 已含 Spec Driver 忽略规则"
126
+ elif [[ "$value" == created:* ]]; then
127
+ echo -e " ✅ 已创建 .git/info/exclude 并写入 Spec Driver 忽略规则(非 tracked,零 diff)"
128
+ elif [[ "$value" == injected:* ]]; then
129
+ local n="${value#injected:}"
130
+ echo -e " ✅ 已向 .git/info/exclude 追加 ${n} 条 Spec Driver 忽略规则(非 tracked,零 diff)"
131
+ elif [[ "$value" == skipped ]]; then
132
+ echo -e " ℹ️ 非标准 git 目录(非 git / worktree / submodule),跳过 .git/info/exclude 注入(由 .gitignore 覆盖)"
133
+ elif [[ "$value" == skip_error ]]; then
134
+ echo -e " ⚠️ ${YELLOW}.git/info/exclude 注入失败,已跳过(不影响初始化)${NC}"
135
+ fi
136
+ ;;
137
+ gitignore)
138
+ if [[ "$value" == ready ]]; then
139
+ echo -e " ✅ .gitignore 已含 Spec Driver 忽略规则"
140
+ elif [[ "$value" == created:* ]]; then
141
+ echo -e " ✅ 已创建 .gitignore 并写入 Spec Driver 忽略规则"
142
+ elif [[ "$value" == injected:* ]]; then
143
+ local n="${value#injected:}"
144
+ echo -e " ✅ 已向 .gitignore 追加 ${n} 条 Spec Driver 忽略规则"
145
+ elif [[ "$value" == skip_error ]]; then
146
+ echo -e " ⚠️ ${YELLOW}.gitignore 注入失败,已跳过(不影响初始化)${NC}"
147
+ elif [[ "$value" == lock_skipped ]]; then
148
+ echo -e " ⏭️ ${YELLOW}.gitignore 注入被并发会话锁定,本次跳过(下次会话自动补齐)${NC}"
149
+ fi
150
+ ;;
123
151
  esac
124
152
  }
125
153
 
@@ -0,0 +1,62 @@
1
+ /**
2
+ * load-zod.mjs -- 共享的同步 zod 加载 helper。
3
+ *
4
+ * 为什么需要:spec-driver 脚本原本在多个 schema 模块顶层 `import { z } from 'zod'`。
5
+ * ESM 顶层 import 在模块加载期静态解析,缺 zod(如插件缓存目录无 node_modules)时
6
+ * 会抛 ERR_MODULE_NOT_FOUND 硬崩,无法被运行时 try/catch 捕获。
7
+ *
8
+ * 本 helper 用 createRequire + require('zod') 同步加载(zod 自带 CJS 入口,可 require),
9
+ * 把"加载期硬崩"收敛为"可捕获的运行时缺失":缺 zod 时返回 { available: false }
10
+ * 而非抛错,从而让调用方决定降级路径。保持全同步调用链,避免动态 import 的 async 涟漪。
11
+ */
12
+
13
+ import { createRequire } from 'node:module';
14
+
15
+ const require = createRequire(import.meta.url);
16
+
17
+ // memoize:首次加载结果缓存,后续直接返回,避免重复 require / 重复求值
18
+ let _cache = null;
19
+
20
+ /**
21
+ * 同步加载 zod,永不抛错。
22
+ * @returns {{ z: object|null, available: boolean, error: Error|null }}
23
+ * - z:可用时为 zod 命名空间(mod.z ?? mod),缺失时为 null
24
+ * - available:zod 是否成功加载
25
+ * - error:缺失时的捕获错误(用于诊断),可用时为 null
26
+ */
27
+ export function loadZod() {
28
+ if (_cache) return _cache;
29
+
30
+ // 测试 seam:强制走缺失分支,使"缺 zod"路径可在有 node_modules 的仓库内被复现
31
+ if (
32
+ process.env.SPEC_DRIVER_FORCE_ZOD_MISSING === '1' ||
33
+ process.env.SPEC_DRIVER_FORCE_ZOD_MISSING === 'true'
34
+ ) {
35
+ _cache = {
36
+ z: null,
37
+ available: false,
38
+ error: new Error('zod 加载被 SPEC_DRIVER_FORCE_ZOD_MISSING 强制禁用'),
39
+ };
40
+ return _cache;
41
+ }
42
+
43
+ try {
44
+ const mod = require('zod');
45
+ // zod 的 CJS 模块上 `.z` 是命名空间;某些打包形态下退回 mod 本体
46
+ const z = mod.z ?? mod;
47
+ _cache = { z, available: true, error: null };
48
+ } catch (error) {
49
+ // require 缺失抛 MODULE_NOT_FOUND(可捕获),降级为运行时缺失而非加载期硬崩
50
+ _cache = { z: null, available: false, error };
51
+ }
52
+
53
+ return _cache;
54
+ }
55
+
56
+ /**
57
+ * 仅供测试使用:清空 memoize 缓存,使同进程内可在切换环境变量后重新求值。
58
+ * 注意:本函数只清 load-zod.mjs 内的 _cache,不影响已加载 schema 模块固化的 zodAvailable。
59
+ */
60
+ export function __resetZodCacheForTest() {
61
+ _cache = null;
62
+ }
@@ -6,6 +6,7 @@ import {
6
6
  EXCLUDED_EXECUTION_FIELDS,
7
7
  referenceEntrySchema,
8
8
  resolvedProjectProfileSchema,
9
+ zodAvailable,
9
10
  } from './project-profile-schema.mjs';
10
11
 
11
12
  function createDiagnostic(level, code, message) {
@@ -69,19 +70,51 @@ function normalizeReferenceEntry(entry, source, projectRoot, diagnostics) {
69
70
  return null;
70
71
  }
71
72
 
72
- const parsed = referenceEntrySchema.safeParse(entry);
73
- if (!parsed.success) {
74
- diagnostics.push(
75
- createDiagnostic(
76
- 'warning',
77
- 'project-context.invalid-reference',
78
- `reference 条目无效:${parsed.error.issues.map((issue) => issue.message).join('; ')}`,
79
- ),
80
- );
81
- return null;
73
+ let normalized;
74
+ if (zodAvailable) {
75
+ // zod 在场:保持原有 schema 校验逻辑逐字节不变
76
+ const parsed = referenceEntrySchema.safeParse(entry);
77
+ if (!parsed.success) {
78
+ diagnostics.push(
79
+ createDiagnostic(
80
+ 'warning',
81
+ 'project-context.invalid-reference',
82
+ `reference 条目无效:${parsed.error.issues.map((issue) => issue.message).join('; ')}`,
83
+ ),
84
+ );
85
+ return null;
86
+ }
87
+ normalized = { ...parsed.data, source };
88
+ } else {
89
+ // 缺 zod 降级:手写构造已知字段。必须复现 referenceEntrySchema 的 .trim() 语义,
90
+ // 否则 `path: " docs/a.md "` 在降级路径会带空格、与正常路径 shape 分叉(W1)。
91
+ // 不浅拷贝 entry(会绕过 trim),逐字段 trim;空串视为缺省(不带入)。
92
+ // 这 4 个字段在 zod schema 里均为 z.string(),非字符串值(如 simple-yaml 把
93
+ // `path: 123` 强转成 number)本就应被拒绝;故仅接受 string 且 trim 后非空,
94
+ // 不再原样带入非字符串值(否则后续 path.resolve 会因 number 抛 ERR_INVALID_ARG_TYPE)。
95
+ normalized = { source };
96
+ for (const key of ['label', 'path', 'url', 'purpose']) {
97
+ const raw = entry[key];
98
+ if (typeof raw === 'string') {
99
+ const trimmed = raw.trim();
100
+ if (trimmed) normalized[key] = trimmed;
101
+ }
102
+ }
103
+ if (typeof entry.required === 'boolean') {
104
+ normalized.required = entry.required;
105
+ }
106
+ // 有效性判定基于 trim 后的 path/url(与 referenceEntrySchema.refine 等价)
107
+ if (!normalized.path && !normalized.url) {
108
+ diagnostics.push(
109
+ createDiagnostic(
110
+ 'warning',
111
+ 'project-context.invalid-reference',
112
+ 'reference 条目无效:reference entry requires either path or url',
113
+ ),
114
+ );
115
+ return null;
116
+ }
82
117
  }
83
-
84
- const normalized = { ...parsed.data, source };
85
118
  if (normalized.path) {
86
119
  normalized.resolvedPath = path.resolve(projectRoot, normalized.path);
87
120
  normalized.exists = fs.existsSync(normalized.resolvedPath);
@@ -482,6 +515,8 @@ function normalizeLegacyMarkdown(content, projectRoot, diagnostics) {
482
515
  .map((line) => line.replace(/^[-*]\s*/, '').trim())
483
516
  .filter(Boolean),
484
517
  ),
518
+ // F191:legacy-md 不支持 knowledge_sources → 稳定默认形态(修 Codex W1)
519
+ knowledgeSources: defaultKnowledgeSources(),
485
520
  };
486
521
  }
487
522
 
@@ -521,6 +556,46 @@ function normalizeYamlInput(raw, projectRoot, diagnostics) {
521
556
  workflowPreferences: normalizeWorkflowPreferences(raw.workflow_preferences),
522
557
  forbiddenChanges: normalizeStringList(raw.forbidden_changes),
523
558
  notes: normalizeStringList(raw.notes),
559
+ // F191:scaffold-kb 预查注入配置(仅 yaml 路径设置;schema optional,其他路径省略合法)
560
+ knowledgeSources: normalizeKnowledgeSources(raw.knowledge_sources, projectRoot, diagnostics),
561
+ };
562
+ }
563
+
564
+ /**
565
+ * F191 — 归一化 knowledge_sources(scaffold-kb 预查注入配置)。
566
+ * 缺省/非对象 → 默认形态(enabled:false);路径相对 projectRoot 解析为绝对路径(不校验存在性,
567
+ * 存在性降级交给 kb-prequery/query,FR-005);非法 top_k/max_inject_chars → 回落默认 + diagnostics。
568
+ */
569
+ /** F191 — knowledgeSources 稳定默认形态(4 条 resolver 路径共用,修 Codex W1) */
570
+ function defaultKnowledgeSources() {
571
+ return { enabled: false, vendorKb: null, projectKb: null, topK: 3, maxInjectChars: 6000 };
572
+ }
573
+
574
+ function normalizeKnowledgeSources(raw, projectRoot, diagnostics) {
575
+ if (!raw || typeof raw !== 'object') return defaultKnowledgeSources();
576
+
577
+ const resolvePath = (p) =>
578
+ typeof p === 'string' && p.trim().length > 0 ? path.resolve(projectRoot, p.trim()) : null;
579
+
580
+ const posInt = (value, fallback, field) => {
581
+ if (value === undefined) return fallback;
582
+ if (Number.isInteger(value) && value > 0) return value;
583
+ diagnostics.push(
584
+ createDiagnostic(
585
+ 'warning',
586
+ 'project-context.knowledge-sources-invalid',
587
+ `knowledge_sources.${field} 必须为正整数,已回落默认 ${fallback}`,
588
+ ),
589
+ );
590
+ return fallback;
591
+ };
592
+
593
+ return {
594
+ enabled: raw.enabled === true,
595
+ vendorKb: resolvePath(raw.vendor_kb),
596
+ projectKb: resolvePath(raw.project_kb),
597
+ topK: posInt(raw.top_k, 3, 'top_k'),
598
+ maxInjectChars: posInt(raw.max_inject_chars, 6000, 'max_inject_chars'),
524
599
  };
525
600
  }
526
601
 
@@ -529,6 +604,16 @@ export function resolveProjectContext({ projectRoot }) {
529
604
  const yamlPath = path.join(specifyDir, 'project-context.yaml');
530
605
  const markdownPath = path.join(specifyDir, 'project-context.md');
531
606
  const diagnostics = [];
607
+ // 缺 zod 降级:仅在入口 push 一条 warning(避免 reference 多条目时重复)
608
+ if (!zodAvailable) {
609
+ diagnostics.push(
610
+ createDiagnostic(
611
+ 'warning',
612
+ 'project-context.zod-unavailable',
613
+ '未能加载 zod,已跳过 project-context schema 校验并使用手写归一化结果;如需完整校验请在已安装依赖的目录运行(npm i)或从仓内源路径运行 spec-driver 脚本',
614
+ ),
615
+ );
616
+ }
532
617
  const sourceLayers = [
533
618
  'user-input',
534
619
  'skill-contract',
@@ -565,6 +650,7 @@ export function resolveProjectContext({ projectRoot }) {
565
650
  },
566
651
  forbiddenChanges: [],
567
652
  notes: [],
653
+ knowledgeSources: defaultKnowledgeSources(),
568
654
  };
569
655
 
570
656
  if (yamlExists && markdownExists) {
@@ -610,45 +696,51 @@ export function resolveProjectContext({ projectRoot }) {
610
696
  );
611
697
  }
612
698
 
613
- const parsedProfile = resolvedProjectProfileSchema.safeParse(normalized);
614
- if (!parsedProfile.success) {
615
- diagnostics.push(
616
- createDiagnostic(
617
- 'warning',
618
- 'project-context.schema-fallback',
619
- `project-context 解析结果存在结构问题,已回退到安全默认值:${parsedProfile.error.issues
620
- .map((issue) => issue.message)
621
- .join('; ')}`,
622
- ),
623
- );
624
- normalized = resolvedProjectProfileSchema.parse({
625
- product: null,
626
- owner: null,
627
- references: [],
628
- architectureConstraints: [],
629
- verificationPolicy: {
630
- requireRealExecution: true,
631
- requiredCommands: [],
632
- notes: [],
633
- },
634
- researchPolicy: {
635
- onlineRequired: false,
636
- minPoints: 0,
637
- maxPoints: 5,
638
- preferredTools: [],
639
- notes: [],
640
- },
641
- workflowPreferences: {
642
- defaultMode: null,
643
- preferredPreset: null,
699
+ if (zodAvailable) {
700
+ // zod 在场:保持原有 safeParse + schema-fallback 兜底逻辑逐字节不变
701
+ const parsedProfile = resolvedProjectProfileSchema.safeParse(normalized);
702
+ if (!parsedProfile.success) {
703
+ diagnostics.push(
704
+ createDiagnostic(
705
+ 'warning',
706
+ 'project-context.schema-fallback',
707
+ `project-context 解析结果存在结构问题,已回退到安全默认值:${parsedProfile.error.issues
708
+ .map((issue) => issue.message)
709
+ .join('; ')}`,
710
+ ),
711
+ );
712
+ normalized = resolvedProjectProfileSchema.parse({
713
+ product: null,
714
+ owner: null,
715
+ references: [],
716
+ architectureConstraints: [],
717
+ verificationPolicy: {
718
+ requireRealExecution: true,
719
+ requiredCommands: [],
720
+ notes: [],
721
+ },
722
+ researchPolicy: {
723
+ onlineRequired: false,
724
+ minPoints: 0,
725
+ maxPoints: 5,
726
+ preferredTools: [],
727
+ notes: [],
728
+ },
729
+ workflowPreferences: {
730
+ defaultMode: null,
731
+ preferredPreset: null,
732
+ notes: [],
733
+ },
734
+ forbiddenChanges: [],
644
735
  notes: [],
645
- },
646
- forbiddenChanges: [],
647
- notes: [],
648
- });
649
- } else {
650
- normalized = parsedProfile.data;
736
+ knowledgeSources: defaultKnowledgeSources(),
737
+ });
738
+ } else {
739
+ normalized = parsedProfile.data;
740
+ }
651
741
  }
742
+ // 缺 zod 降级:跳过 safeParse(normalized 由手写 normalizeYamlInput / normalizeLegacyMarkdown
743
+ // 构建,结构可信),直接沿用 normalized —— 等价于 zod 在场时 parse 成功分支的效果。
652
744
 
653
745
  const existingReferences = normalized.references.filter((entry) => entry.exists !== false || entry.url);
654
746
  const missingReferences = normalized.references.filter((entry) => entry.exists === false);
@@ -1,4 +1,7 @@
1
- import { z } from 'zod';
1
+ import { loadZod } from './load-zod.mjs';
2
+
3
+ // 经共享 helper 同步加载 zod;缺失时 zodAvailable=false,模块加载不崩
4
+ const { z, available: zodAvailable } = loadZod();
2
5
 
3
6
  export const EXCLUDED_EXECUTION_FIELDS = new Set([
4
7
  'phase_focus',
@@ -17,62 +20,93 @@ export const ALLOWED_TOP_LEVEL_FIELDS = new Set([
17
20
  'workflow_preferences',
18
21
  'forbidden_changes',
19
22
  'notes',
23
+ // F191:scaffold-kb 预查注入配置(加入白名单避免 unknown-field 警告)
24
+ 'knowledge_sources',
20
25
  ]);
21
26
 
22
- export const referenceEntryObjectSchema = z.object({
23
- label: z.string().trim().min(1).optional(),
24
- path: z.string().trim().min(1).optional(),
25
- url: z.string().trim().url().optional(),
26
- required: z.boolean().optional(),
27
- purpose: z.string().trim().min(1).optional(),
28
- });
27
+ // schema 求值必须全部包进 zodAvailable 守卫:缺 zod 时模块体完全不触碰 z
28
+ // 否则会从 MODULE_NOT_FOUND 退化为 ReferenceError —— 等于没修。
29
+ // ESM 语法限制 export const 不能进 if 块,故用 let 顶层声明 + 守卫内赋值 + 末尾统一 export。
30
+ let referenceEntryObjectSchema = null;
31
+ let referenceEntrySchema = null;
32
+ let resolvedReferenceEntrySchema = null;
33
+ let resolvedProjectProfileSchema = null;
29
34
 
30
- export const referenceEntrySchema = referenceEntryObjectSchema.refine(
31
- (value) => Boolean(value.path || value.url),
32
- {
33
- message: 'reference entry requires either path or url',
34
- },
35
- );
35
+ if (zodAvailable) {
36
+ referenceEntryObjectSchema = z.object({
37
+ label: z.string().trim().min(1).optional(),
38
+ path: z.string().trim().min(1).optional(),
39
+ url: z.string().trim().url().optional(),
40
+ required: z.boolean().optional(),
41
+ purpose: z.string().trim().min(1).optional(),
42
+ });
36
43
 
37
- export const resolvedReferenceEntrySchema = referenceEntryObjectSchema.extend({
38
- exists: z.boolean().optional(),
39
- resolvedPath: z.string().trim().min(1).optional(),
40
- source: z.enum(['yaml', 'markdown']).optional(),
41
- });
44
+ referenceEntrySchema = referenceEntryObjectSchema.refine(
45
+ (value) => Boolean(value.path || value.url),
46
+ {
47
+ message: 'reference entry requires either path or url',
48
+ },
49
+ );
42
50
 
43
- export const resolvedProjectProfileSchema = z.object({
44
- product: z
45
- .object({
46
- name: z.string().trim().min(1).optional(),
47
- summary: z.string().trim().min(1).optional(),
48
- })
49
- .nullable(),
50
- owner: z
51
- .object({
52
- name: z.string().trim().min(1).optional(),
53
- team: z.string().trim().min(1).optional(),
54
- email: z.string().trim().email().optional(),
55
- })
56
- .nullable(),
57
- references: z.array(resolvedReferenceEntrySchema),
58
- architectureConstraints: z.array(z.string().trim().min(1)),
59
- verificationPolicy: z.object({
60
- requireRealExecution: z.boolean(),
61
- requiredCommands: z.array(z.string().trim().min(1)),
62
- notes: z.array(z.string().trim().min(1)),
63
- }),
64
- researchPolicy: z.object({
65
- onlineRequired: z.boolean(),
66
- minPoints: z.number().int().min(0),
67
- maxPoints: z.number().int().min(0),
68
- preferredTools: z.array(z.string().trim().min(1)),
69
- notes: z.array(z.string().trim().min(1)),
70
- }),
71
- workflowPreferences: z.object({
72
- defaultMode: z.string().trim().min(1).nullable(),
73
- preferredPreset: z.string().trim().min(1).nullable(),
51
+ resolvedReferenceEntrySchema = referenceEntryObjectSchema.extend({
52
+ exists: z.boolean().optional(),
53
+ resolvedPath: z.string().trim().min(1).optional(),
54
+ source: z.enum(['yaml', 'markdown']).optional(),
55
+ });
56
+
57
+ resolvedProjectProfileSchema = z.object({
58
+ product: z
59
+ .object({
60
+ name: z.string().trim().min(1).optional(),
61
+ summary: z.string().trim().min(1).optional(),
62
+ })
63
+ .nullable(),
64
+ owner: z
65
+ .object({
66
+ name: z.string().trim().min(1).optional(),
67
+ team: z.string().trim().min(1).optional(),
68
+ email: z.string().trim().email().optional(),
69
+ })
70
+ .nullable(),
71
+ references: z.array(resolvedReferenceEntrySchema),
72
+ architectureConstraints: z.array(z.string().trim().min(1)),
73
+ verificationPolicy: z.object({
74
+ requireRealExecution: z.boolean(),
75
+ requiredCommands: z.array(z.string().trim().min(1)),
76
+ notes: z.array(z.string().trim().min(1)),
77
+ }),
78
+ researchPolicy: z.object({
79
+ onlineRequired: z.boolean(),
80
+ minPoints: z.number().int().min(0),
81
+ maxPoints: z.number().int().min(0),
82
+ preferredTools: z.array(z.string().trim().min(1)),
83
+ notes: z.array(z.string().trim().min(1)),
84
+ }),
85
+ workflowPreferences: z.object({
86
+ defaultMode: z.string().trim().min(1).nullable(),
87
+ preferredPreset: z.string().trim().min(1).nullable(),
88
+ notes: z.array(z.string().trim().min(1)),
89
+ }),
90
+ forbiddenChanges: z.array(z.string().trim().min(1)),
74
91
  notes: z.array(z.string().trim().min(1)),
75
- }),
76
- forbiddenChanges: z.array(z.string().trim().min(1)),
77
- notes: z.array(z.string().trim().min(1)),
78
- });
92
+ // F191:scaffold-kb 预查注入配置。OPTIONAL —— 仅 yaml 路径设置;md/fallback 路径省略仍合法,
93
+ // 避免新增 required 字段触发 safeParse 失败 → whole-profile fallback 清空旧字段(Codex 零回归关注点)
94
+ knowledgeSources: z
95
+ .object({
96
+ enabled: z.boolean(),
97
+ vendorKb: z.string().nullable(),
98
+ projectKb: z.string().nullable(),
99
+ topK: z.number().int().positive(),
100
+ maxInjectChars: z.number().int().positive(),
101
+ })
102
+ .optional(),
103
+ });
104
+ }
105
+
106
+ export {
107
+ zodAvailable,
108
+ referenceEntryObjectSchema,
109
+ referenceEntrySchema,
110
+ resolvedReferenceEntrySchema,
111
+ resolvedProjectProfileSchema,
112
+ };
@@ -14,7 +14,7 @@ BOLD='\033[1m'
14
14
 
15
15
  # Plugin 信息
16
16
  PLUGIN_NAME="Spec Driver"
17
- PLUGIN_VERSION="4.2.0"
17
+ PLUGIN_VERSION="4.3.0"
18
18
  MIN_CLAUDE_VERSION="1.0.0"
19
19
 
20
20
  # 推算插件根目录
@@ -42,6 +42,18 @@ write_plugin_path() {
42
42
  # 幂等写入路径文件
43
43
  if [[ -d "$PROJECT_DIR/.specify" ]]; then
44
44
  echo -n "$PLUGIN_DIR" > "$PROJECT_DIR/.specify/.spec-driver-path"
45
+
46
+ # 确保 ignore 规则就位,避免绝对路径泄漏进用户 git diff(Feature 207)。
47
+ # SessionStart hook 只写 .git/info/exclude(非 tracked,零 diff)——伦理面:hook 不应
48
+ # 静默修改用户 tracked 的 .gitignore;.gitignore 注入交由 init-project.sh(显式初始化)。
49
+ # 非 git repo 时 exclude 注入自动 skip(无 .git 目录);但注意上方 .spec-driver-path 落盘
50
+ # 是路径发现机制的本职行为(7 个 SKILL 依赖此文件定位插件),不在 ignore 注入范围——
51
+ # 非 git 场景无 git diff 概念,该文件的 ignore 保护要等用户后续 git init + 跑 init-project 才补上。
52
+ # postinstall 先于 init 触发,须静默执行;全程 || true 防御 set -euo pipefail 下的连锁失败
53
+ if [[ -f "$SCRIPT_DIR/lib/ensure-gitignore.sh" ]]; then
54
+ source "$SCRIPT_DIR/lib/ensure-gitignore.sh"
55
+ ensure_spec_driver_git_exclude "$PROJECT_DIR" >/dev/null 2>&1 || true
56
+ fi
45
57
  else
46
58
  echo "[警告] 无法创建 $PROJECT_DIR/.specify/ 目录,跳过路径写入" >&2
47
59
  fi
@@ -25,6 +25,14 @@ function parseArgs(argv) {
25
25
  artifacts: [],
26
26
  warnings: [],
27
27
  json: false,
28
+ complianceVerdict: null,
29
+ };
30
+
31
+ // FR-014:仅当出现任意 --compliance-* flag 时才实例化 complianceVerdict 对象,
32
+ // 未传参时保持 null → 事件对象不含该键(向后兼容,字节级不变)。
33
+ const ensureComplianceVerdict = () => {
34
+ if (!args.complianceVerdict) args.complianceVerdict = {};
35
+ return args.complianceVerdict;
28
36
  };
29
37
 
30
38
  for (let index = 0; index < argv.length; index += 1) {
@@ -89,6 +97,26 @@ function parseArgs(argv) {
89
97
  args.warnings.push(...splitList(argv[index + 1]));
90
98
  index += 1;
91
99
  break;
100
+ case '--compliance-closure-form':
101
+ ensureComplianceVerdict().closureForm = argv[index + 1] ?? null;
102
+ index += 1;
103
+ break;
104
+ case '--compliance-compliant':
105
+ ensureComplianceVerdict().compliant = parseBooleanFlag(argv[index + 1]);
106
+ index += 1;
107
+ break;
108
+ case '--compliance-missing':
109
+ ensureComplianceVerdict().missing = splitList(argv[index + 1]);
110
+ index += 1;
111
+ break;
112
+ case '--compliance-degraded':
113
+ ensureComplianceVerdict().degraded = parseBooleanFlag(argv[index + 1]);
114
+ index += 1;
115
+ break;
116
+ case '--compliance-block-count':
117
+ ensureComplianceVerdict().blockCount = parseOptionalNumber(argv[index + 1]);
118
+ index += 1;
119
+ break;
92
120
  case '--json':
93
121
  args.json = true;
94
122
  break;
@@ -147,6 +175,12 @@ export function recordWorkflowRun(options = {}) {
147
175
  warnings,
148
176
  };
149
177
 
178
+ // FR-014:仅当调用方显式提供 complianceVerdict 时才追加该键(未提供 → 事件字节级不变)。
179
+ const complianceVerdict = normalizeComplianceVerdict(options.complianceVerdict);
180
+ if (complianceVerdict) {
181
+ event.complianceVerdict = complianceVerdict;
182
+ }
183
+
150
184
  const runsDir = path.join(projectRoot, '.specify', 'runs');
151
185
  fs.mkdirSync(runsDir, { recursive: true });
152
186
  const targetFile = path.join(runsDir, `${finishedAt.slice(0, 7)}.jsonl`);
@@ -182,6 +216,47 @@ function resolveDurationMs(durationMs, startedAt, finishedAt) {
182
216
  return Number.isFinite(delta) && delta >= 0 ? delta : null;
183
217
  }
184
218
 
219
+ function parseBooleanFlag(rawValue) {
220
+ const value = normalizeString(rawValue);
221
+ if (value === null) {
222
+ return null;
223
+ }
224
+ if (value.toLowerCase() === 'true') {
225
+ return true;
226
+ }
227
+ if (value.toLowerCase() === 'false') {
228
+ return false;
229
+ }
230
+ return null;
231
+ }
232
+
233
+ /**
234
+ * 归一化 complianceVerdict(FR-014)。传入 null/undefined/非对象 → 返回 null(不追加事件键)。
235
+ * 仅保留契约字段(closureForm/compliant/missing/degraded/blockCount),忽略未知字段。
236
+ */
237
+ function normalizeComplianceVerdict(raw) {
238
+ if (!raw || typeof raw !== 'object') {
239
+ return null;
240
+ }
241
+ const verdict = {};
242
+ if (typeof raw.closureForm === 'string') {
243
+ verdict.closureForm = raw.closureForm;
244
+ }
245
+ if (typeof raw.compliant === 'boolean') {
246
+ verdict.compliant = raw.compliant;
247
+ }
248
+ if (Array.isArray(raw.missing)) {
249
+ verdict.missing = raw.missing.map((entry) => normalizeString(entry)).filter(Boolean);
250
+ }
251
+ if (typeof raw.degraded === 'boolean') {
252
+ verdict.degraded = raw.degraded;
253
+ }
254
+ if (typeof raw.blockCount === 'number' && Number.isFinite(raw.blockCount)) {
255
+ verdict.blockCount = raw.blockCount;
256
+ }
257
+ return Object.keys(verdict).length > 0 ? verdict : null;
258
+ }
259
+
185
260
  function parseOptionalNumber(rawValue) {
186
261
  if (rawValue === undefined || rawValue === null) {
187
262
  return null;