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
@@ -16,7 +16,11 @@
16
16
  * - formatZodIssue Zod issue 中文化格式辅助函数
17
17
  */
18
18
 
19
- import { z } from 'zod';
19
+ import { loadZod } from '../scripts/lib/load-zod.mjs';
20
+
21
+ // 经共享 helper 同步加载 zod;缺失时 zodAvailable=false,模块加载不崩
22
+ // (缺 zod 时全部 9 个 schema 求值被守卫跳过,导出为 null,由两消费者降级处理)
23
+ const { z, available: zodAvailable } = loadZod();
20
24
 
21
25
  // ─────────────────────────────────────────────────────────────
22
26
  // Zod issue 中文化格式辅助
@@ -47,161 +51,12 @@ export function formatZodIssue(issue) {
47
51
  }
48
52
  }
49
53
 
50
- // ─────────────────────────────────────────────────────────────
51
- // 共用子 Schema 基础类型(从 orchestration.yaml 实际结构推导)
52
- // ─────────────────────────────────────────────────────────────
53
-
54
- /**
55
- * Phase 定义 Schema(base orchestration.yaml 中 phases 数组元素)
56
- *
57
- * 关键观察(来自 orchestration.yaml 实际字段):
58
- * - agent: null | string | string[](三种形态)
59
- * - agent_mode: inline | single | parallel_group | gate | orchestrator_verify | batch_loop
60
- * - gates_before / gates_after: null | string[](nullable,非 optional)
61
- * - conditional / skip_if_exists: null | string(nullable)
62
- * - is_critical: boolean
63
- */
64
- export const phaseSchema = z.object({
65
- id: z.string({ required_error: 'phase id 为必填字段' }),
66
- name: z.string({ required_error: 'phase name 为必填字段' }),
67
- display_name: z.string({ required_error: 'phase display_name 为必填字段' }),
68
- // agent 可以是 null、string 或 string 数组(parallel_group 模式时)
69
- agent: z.union([
70
- z.null(),
71
- z.string(),
72
- z.array(z.string()),
73
- ]),
74
- // agent_mode 枚举——来自 orchestration.yaml 实际值
75
- agent_mode: z.enum([
76
- 'inline',
77
- 'single',
78
- 'parallel_group',
79
- 'gate',
80
- 'orchestrator_verify',
81
- 'batch_loop',
82
- ], {
83
- error_map: (issue) => {
84
- if (issue.code === 'invalid_enum_value') {
85
- return {
86
- message: `agent_mode 不合法:期望 [inline|single|parallel_group|gate|orchestrator_verify|batch_loop],实际为 "${issue.received}"`,
87
- };
88
- }
89
- return { message: issue.message };
90
- },
91
- }),
92
- // gates_before / gates_after:null 或 string 数组(YAML 中的 null 必须用 .nullable())
93
- gates_before: z.array(z.string()).nullable(),
94
- gates_after: z.array(z.string()).nullable(),
95
- // conditional / skip_if_exists:null 或字符串表达式
96
- conditional: z.string().nullable(),
97
- skip_if_exists: z.string().nullable(),
98
- is_critical: z.boolean(),
99
- });
100
-
101
- /**
102
- * Gate 定义 Schema(base orchestration.yaml 中 gates 块)
103
- *
104
- * 关键观察:
105
- * - type: string(自由文本,如 "research_checkpoint")
106
- * - applicable_modes: string[] 或不存在(非所有 gate 都有此字段)
107
- * - default_behavior: "always" | "auto" | "on_failure" | "skip"(skip 表示跳过该 gate 检查点)
108
- * - severity: "critical" | "non_critical"(实际值,非 spec 定义的 warning/info)
109
- * - hard_gate_modes: null | string[](nullable)
110
- * - insertion_point: null | string(nullable)
111
- */
112
- export const gateDefinitionSchema = z.object({
113
- type: z.string(),
114
- // applicable_modes 在某些 gate 中存在(如 GATE_RESEARCH),某些不存在(如 GATE_VERIFY 有)
115
- // 实际上所有 gate 都有此字段,但为了健壮性设为 optional
116
- applicable_modes: z.array(z.string()).optional(),
117
- description: z.string(),
118
- // 实际 default_behavior 值包含 on_failure 和 skip(override 场景允许 skip 跳过 gate)
119
- default_behavior: z.enum(['always', 'auto', 'on_failure', 'skip'], {
120
- error_map: (issue) => {
121
- if (issue.code === 'invalid_enum_value') {
122
- return {
123
- message: `default_behavior 不合法:期望 [always | auto | on_failure | skip],实际为 "${issue.received}"`,
124
- };
125
- }
126
- return { message: issue.message };
127
- },
128
- }),
129
- // 实际 severity 值为 "critical" 和 "non_critical"(非 spec 文档定义的 warning/info)
130
- severity: z.enum(['critical', 'non_critical', 'warning', 'info'], {
131
- error_map: (issue) => {
132
- if (issue.code === 'invalid_enum_value') {
133
- return {
134
- message: `severity 不合法:期望 [critical | non_critical | warning | info],实际为 "${issue.received}"`,
135
- };
136
- }
137
- return { message: issue.message };
138
- },
139
- }),
140
- // hard_gate_modes:YAML 中显式为 null(必须用 .nullable(),不能用 .optional())
141
- hard_gate_modes: z.array(z.string()).nullable(),
142
- // insertion_point:YAML 中显式为 null 或字符串
143
- insertion_point: z.string().nullable(),
144
- });
145
-
146
- /**
147
- * Gate 覆盖 Schema(overrides 文件中 gates 块,仅允许部分字段)
148
- * 只有 default_behavior / severity / hard_gate_modes 可被覆盖
149
- */
150
- export const gateOverrideSchema = z.object({
151
- // overrides 中的 default_behavior 允许 always/auto/on_failure/skip(与 gateDefinitionSchema 对齐)
152
- default_behavior: z.enum(['always', 'auto', 'on_failure', 'skip']).optional(),
153
- severity: z.enum(['critical', 'non_critical', 'warning', 'info']).optional(),
154
- // hard_gate_modes 整段替换,非追加
155
- hard_gate_modes: z.array(z.string()).optional(),
156
- }).strict();
157
-
158
- /**
159
- * Mode 定义 Schema(base orchestration.yaml 中 modes 块)
160
- */
161
- export const modeDefinitionSchema = z.object({
162
- name: z.string(),
163
- description: z.string(),
164
- phases: z.array(phaseSchema).min(1, { message: 'phases 数组不能为空' }),
165
- });
166
-
167
- /**
168
- * Mode 覆盖 Schema(overrides 文件中 modes 块,整段替换语义)
169
- * 整段替换要求用户提供完整的 mode 定义(name/description/phases)
170
- * extends 字段为 MVP 预留:schema 接受但 resolver 不处理
171
- */
172
- export const modeOverrideSchema = z.object({
173
- name: z.string().optional(), // 与 modeDefinitionSchema 保持一致(整段替换时合并进 mergedModes)
174
- description: z.string().optional(), // 同上
175
- extends: z.string().optional(), // 二期预留,MVP 接受但不处理
176
- // phases 的 agent_mode 在 override 场景下也接受额外值,或直接复用 phaseSchema
177
- phases: z.array(phaseSchema).min(0),
178
- }).strip(); // strip 未知字段(与整体 .strict() 策略一致;二期新增字段时在此处显式声明)
179
-
180
- /**
181
- * 并行组 Schema(base orchestration.yaml 中 parallel_groups 块)
182
- */
183
- export const parallelGroupSchema = z.object({
184
- members: z.array(z.string()),
185
- convergence_point: z.string(),
186
- fallback_strategy: z.string(),
187
- max_concurrent: z.number().int().positive(),
188
- description: z.string(),
189
- });
190
-
191
- /**
192
- * 全局并行调度 Schema(base orchestration.yaml 中 parallel_scheduling 块)
193
- */
194
- export const parallelSchedulingSchema = z.object({
195
- max_concurrent_tasks: z.number().int().positive(),
196
- fallback_to_serial_on_failure: z.boolean(),
197
- fallback_reason_log: z.boolean(),
198
- });
199
-
200
54
  // ─────────────────────────────────────────────────────────────
201
55
  // Base reserved mode names(FR-007-A,CL-001)
202
56
  // ─────────────────────────────────────────────────────────────
203
57
 
204
58
  /** Base 保留 mode 名称列表(overrides schema 中做 enum 校验)*/
59
+ // 纯常量数组,不依赖 zod,保持顶层导出(不进 zodAvailable 守卫,D4)
205
60
  export const BASE_RESERVED_MODE_NAMES = [
206
61
  'feature',
207
62
  'story',
@@ -214,88 +69,278 @@ export const BASE_RESERVED_MODE_NAMES = [
214
69
  ];
215
70
 
216
71
  // ─────────────────────────────────────────────────────────────
217
- // 三件套 Schema
72
+ // 共用子 Schema + 三件套 Schema(全部包进 zodAvailable 守卫)
218
73
  // ─────────────────────────────────────────────────────────────
74
+ //
75
+ // schema 求值必须全部包进 zodAvailable 守卫:缺 zod 时模块体完全不触碰 z,
76
+ // 否则会从 MODULE_NOT_FOUND 退化为 ReferenceError —— 等于没修。
77
+ // ESM 语法限制 export const 不能进 if 块,故用 let 顶层声明 + 守卫内赋值 + 末尾统一 export。
78
+ // 赋值顺序严格遵循依赖关系:叶子 schema → 依赖 phaseSchema 的中层 → 顶层 → 别名。
219
79
 
220
- /**
221
- * orchestrationBaseSchema — 校验 plugin base orchestration.yaml
222
- *
223
- * 设计原则(FR-024 / R11):先以现有 orchestration.yaml 内容为准定义 schema,
224
- * 确保现有文件 100% 通过 Zod 校验,再扩展为 overrides 使用。
225
- *
226
- * 字段清单(来自 orchestration.yaml 实际结构):
227
- * - version: string,必填
228
- * - parallel_scheduling: 并行调度配置
229
- * - gates: Record<GATE_ID, GateDefinition>
230
- * - parallel_groups: Record<GROUP_ID, ParallelGroup>
231
- * - modes: Record<ModeName, ModeDefinition>(所有 8 个模式)
232
- */
233
- export const orchestrationBaseSchema = z.object({
234
- version: z.string({ required_error: 'version 为必填字段' }),
235
- parallel_scheduling: parallelSchedulingSchema,
236
- gates: z.record(z.string(), gateDefinitionSchema),
237
- parallel_groups: z.record(z.string(), parallelGroupSchema),
238
- modes: z.record(z.string(), modeDefinitionSchema),
239
- });
80
+ let phaseSchema = null;
81
+ let gateDefinitionSchema = null;
82
+ let gateOverrideSchema = null;
83
+ let modeDefinitionSchema = null; // 依赖 phaseSchema
84
+ let modeOverrideSchema = null; // 依赖 phaseSchema
85
+ let parallelGroupSchema = null;
86
+ let parallelSchedulingSchema = null;
87
+ let orchestrationBaseSchema = null; // 依赖 parallelSchedulingSchema/gateDefinitionSchema/parallelGroupSchema/modeDefinitionSchema
88
+ let orchestrationOverridesSchema = null; // 依赖 modeOverrideSchema/gateOverrideSchema
89
+ let orchestrationMergedSchema = null; // = orchestrationBaseSchema(别名)
240
90
 
241
- /**
242
- * orchestrationOverridesSchema 校验项目级 .specify/orchestration-overrides.yaml
243
- *
244
- * 关键设计决策(GATE_DESIGN CL-001/010):
245
- * - version 必填(CL-008,resolver 比对 base version)
246
- * - modes key 使用显式 z.object() 列出 8 个 mode(CL-001 enum 校验)
247
- * - parallel_groups 字段:schema 接受但 transform 时 strip,发出 unsupported-field warning
248
- * - $schema_version 和 modes.<m>.extends 字段:接受但 resolver 不特殊处理(FR-014)
249
- *
250
- * 注意:parallel_groups 由 resolver 步骤 6 手动检测并 strip;schema 层仅声明该字段以免被 .strict() 拒绝
251
- */
252
- export const orchestrationOverridesSchema = z.object({
253
- // 二期预留字段:schema 接受但 MVP resolver 不处理(FR-014)
254
- $schema_version: z.string().optional(),
91
+ if (zodAvailable) {
92
+ // ── 1. 叶子 schema(无内部依赖)─────────────────────────────
255
93
 
256
- // version 必填(CL-008)——resolver 层做 base/overrides version 比对
257
- version: z.string({ required_error: 'overrides 文件必须包含 version 字段' }),
94
+ /**
95
+ * Phase 定义 Schema(base orchestration.yaml phases 数组元素)
96
+ *
97
+ * 关键观察(来自 orchestration.yaml 实际字段):
98
+ * - agent: null | string | string[](三种形态)
99
+ * - agent_mode: inline | single | parallel_group | gate | orchestrator_verify | batch_loop | goal_loop
100
+ * - gates_before / gates_after: null | string[](nullable,非 optional)
101
+ * - conditional / skip_if_exists: null | string(nullable)
102
+ * - is_critical: boolean
103
+ */
104
+ phaseSchema = z.object({
105
+ id: z.string({ required_error: 'phase id 为必填字段' }),
106
+ name: z.string({ required_error: 'phase name 为必填字段' }),
107
+ display_name: z.string({ required_error: 'phase display_name 为必填字段' }),
108
+ // agent 可以是 null、string 或 string 数组(parallel_group 模式时)
109
+ agent: z.union([
110
+ z.null(),
111
+ z.string(),
112
+ z.array(z.string()),
113
+ ]),
114
+ // agent_mode 枚举——来自 orchestration.yaml 实际值
115
+ // goal_loop(Feature 201):feature mode implement phase 的可迭代闭环模式,
116
+ // base 默认不启用(feature implement 仍为 single),仅经 overrides 整段替换激活
117
+ agent_mode: z.enum([
118
+ 'inline',
119
+ 'single',
120
+ 'parallel_group',
121
+ 'gate',
122
+ 'orchestrator_verify',
123
+ 'batch_loop',
124
+ 'goal_loop',
125
+ ], {
126
+ error_map: (issue) => {
127
+ if (issue.code === 'invalid_enum_value') {
128
+ return {
129
+ message: `agent_mode 不合法:期望 [inline|single|parallel_group|gate|orchestrator_verify|batch_loop|goal_loop],实际为 "${issue.received}"`,
130
+ };
131
+ }
132
+ return { message: issue.message };
133
+ },
134
+ }),
135
+ // gates_before / gates_after:null 或 string 数组(YAML 中的 null 必须用 .nullable())
136
+ gates_before: z.array(z.string()).nullable(),
137
+ gates_after: z.array(z.string()).nullable(),
138
+ // conditional / skip_if_exists:null 或字符串表达式
139
+ conditional: z.string().nullable(),
140
+ skip_if_exists: z.string().nullable(),
141
+ is_critical: z.boolean(),
142
+ });
258
143
 
259
- // modes key 使用显式枚举——拒绝非 reserved name(CL-001,FR-007-A)
260
- // reserved safeParse 返回 error → 触发 schema-fallback → 整体 overrides 降级到 base
261
- // 使用 .strict() 拒绝任何不在 8 个 reserved name 中的 mode key
262
- modes: z.object({
263
- feature: modeOverrideSchema.optional(),
264
- story: modeOverrideSchema.optional(),
265
- implement: modeOverrideSchema.optional(),
266
- fix: modeOverrideSchema.optional(),
267
- resume: modeOverrideSchema.optional(),
268
- sync: modeOverrideSchema.optional(),
269
- doc: modeOverrideSchema.optional(),
270
- refactor: modeOverrideSchema.optional(),
271
- }).strict({
272
- message: 'modes 字段包含非法的 mode 名称,合法值为 [feature|story|implement|fix|resume|sync|doc|refactor]',
273
- }).optional(),
144
+ /**
145
+ * Gate 定义 Schema(base orchestration.yaml gates 块)
146
+ *
147
+ * 关键观察:
148
+ * - type: string(自由文本,如 "research_checkpoint")
149
+ * - applicable_modes: string[] 或不存在(非所有 gate 都有此字段)
150
+ * - default_behavior: "always" | "auto" | "on_failure" | "skip"(skip 表示跳过该 gate 检查点)
151
+ * - severity: "critical" | "non_critical"(实际值,非 spec 定义的 warning/info)
152
+ * - hard_gate_modes: null | string[](nullable)
153
+ * - insertion_point: null | string(nullable)
154
+ */
155
+ gateDefinitionSchema = z.object({
156
+ type: z.string(),
157
+ // applicable_modes 在某些 gate 中存在(如 GATE_RESEARCH),某些不存在(如 GATE_VERIFY 有)
158
+ // 实际上所有 gate 都有此字段,但为了健壮性设为 optional
159
+ applicable_modes: z.array(z.string()).optional(),
160
+ description: z.string(),
161
+ // 实际 default_behavior 值包含 on_failure 和 skip(override 场景允许 skip 跳过 gate)
162
+ default_behavior: z.enum(['always', 'auto', 'on_failure', 'skip'], {
163
+ error_map: (issue) => {
164
+ if (issue.code === 'invalid_enum_value') {
165
+ return {
166
+ message: `default_behavior 不合法:期望 [always | auto | on_failure | skip],实际为 "${issue.received}"`,
167
+ };
168
+ }
169
+ return { message: issue.message };
170
+ },
171
+ }),
172
+ // 实际 severity 值为 "critical" 和 "non_critical"(非 spec 文档定义的 warning/info)
173
+ severity: z.enum(['critical', 'non_critical', 'warning', 'info'], {
174
+ error_map: (issue) => {
175
+ if (issue.code === 'invalid_enum_value') {
176
+ return {
177
+ message: `severity 不合法:期望 [critical | non_critical | warning | info],实际为 "${issue.received}"`,
178
+ };
179
+ }
180
+ return { message: issue.message };
181
+ },
182
+ }),
183
+ // hard_gate_modes:YAML 中显式为 null(必须用 .nullable(),不能用 .optional())
184
+ hard_gate_modes: z.array(z.string()).nullable(),
185
+ // insertion_point:YAML 中显式为 null 或字符串
186
+ insertion_point: z.string().nullable(),
187
+ });
274
188
 
275
- // gates 字段:Record<GATE_ID, GateOverride>,对象级字段合并
276
- gates: z.record(z.string(), gateOverrideSchema).optional(),
189
+ /**
190
+ * Gate 覆盖 Schema(overrides 文件中 gates 块,仅允许部分字段)
191
+ * 只有 default_behavior / severity / hard_gate_modes 可被覆盖
192
+ */
193
+ gateOverrideSchema = z.object({
194
+ // overrides 中的 default_behavior 允许 always/auto/on_failure/skip(与 gateDefinitionSchema 对齐)
195
+ default_behavior: z.enum(['always', 'auto', 'on_failure', 'skip']).optional(),
196
+ severity: z.enum(['critical', 'non_critical', 'warning', 'info']).optional(),
197
+ // hard_gate_modes 整段替换,非追加
198
+ hard_gate_modes: z.array(z.string()).optional(),
199
+ }).strict();
277
200
 
278
- // parallel_scheduling:标量覆盖
279
- parallel_scheduling: z.object({
280
- max_concurrent_tasks: z.number().int().positive().optional(),
281
- fallback_to_serial_on_failure: z.boolean().optional(),
282
- fallback_reason_log: z.boolean().optional(),
283
- }).optional(),
201
+ /**
202
+ * 并行组 Schema(base orchestration.yaml 中 parallel_groups 块)
203
+ */
204
+ parallelGroupSchema = z.object({
205
+ members: z.array(z.string()),
206
+ convergence_point: z.string(),
207
+ fallback_strategy: z.string(),
208
+ max_concurrent: z.number().int().positive(),
209
+ description: z.string(),
210
+ });
284
211
 
285
- // parallel_groups:MVP 不支持覆盖,schema 接受但 resolver 层 strip + warning
286
- // reject 整个 overrides,其余合法字段照常生效(CL-010,FR-022)
287
- // 使用 z.record() 保留类型信息,同时接受任意结构;由 resolver 在 parse 前检测并 strip
288
- parallel_groups: z.record(z.string(), z.unknown()).optional(),
289
- }).strict({
290
- // 顶层真正未知字段使用 .strict() 策略拒绝(NFR-003)
291
- // 注意:parallel_groups 已显式声明,不会被 .strict() 拒绝
292
- message: 'overrides 文件包含未识别的顶层字段,请检查字段名称',
293
- });
212
+ /**
213
+ * 全局并行调度 Schema(base orchestration.yaml 中 parallel_scheduling 块)
214
+ */
215
+ parallelSchedulingSchema = z.object({
216
+ max_concurrent_tasks: z.number().int().positive(),
217
+ fallback_to_serial_on_failure: z.boolean(),
218
+ fallback_reason_log: z.boolean(),
219
+ });
294
220
 
295
- /**
296
- * orchestrationMergedSchema — 校验合并后的 config
297
- *
298
- * 合并结果必须满足 base schema 的全部约束(FR-013)
299
- * 复用 orchestrationBaseSchema 实现 DRY 原则(NFR-006)
300
- */
301
- export const orchestrationMergedSchema = orchestrationBaseSchema;
221
+ // ── 2. 依赖 phaseSchema 的中层 schema ──────────────────────
222
+
223
+ /**
224
+ * Mode 定义 Schema(base orchestration.yaml 中 modes 块)
225
+ */
226
+ modeDefinitionSchema = z.object({
227
+ name: z.string(),
228
+ description: z.string(),
229
+ phases: z.array(phaseSchema).min(1, { message: 'phases 数组不能为空' }),
230
+ });
231
+
232
+ /**
233
+ * Mode 覆盖 Schema(overrides 文件中 modes 块,整段替换语义)
234
+ * 整段替换要求用户提供完整的 mode 定义(name/description/phases)
235
+ * extends 字段为 MVP 预留:schema 接受但 resolver 不处理
236
+ */
237
+ modeOverrideSchema = z.object({
238
+ name: z.string().optional(), // 与 modeDefinitionSchema 保持一致(整段替换时合并进 mergedModes)
239
+ description: z.string().optional(), // 同上
240
+ extends: z.string().optional(), // 二期预留,MVP 接受但不处理
241
+ // phases 的 agent_mode 在 override 场景下也接受额外值,或直接复用 phaseSchema
242
+ phases: z.array(phaseSchema).min(0),
243
+ }).strip(); // strip 未知字段(与整体 .strict() 策略一致;二期新增字段时在此处显式声明)
244
+
245
+ // ── 3. 依赖多个子 schema 的顶层 schema ─────────────────────
246
+
247
+ /**
248
+ * orchestrationBaseSchema — 校验 plugin base orchestration.yaml
249
+ *
250
+ * 设计原则(FR-024 / R11):先以现有 orchestration.yaml 内容为准定义 schema,
251
+ * 确保现有文件 100% 通过 Zod 校验,再扩展为 overrides 使用。
252
+ *
253
+ * 字段清单(来自 orchestration.yaml 实际结构):
254
+ * - version: string,必填
255
+ * - parallel_scheduling: 并行调度配置
256
+ * - gates: Record<GATE_ID, GateDefinition>
257
+ * - parallel_groups: Record<GROUP_ID, ParallelGroup>
258
+ * - modes: Record<ModeName, ModeDefinition>(所有 8 个模式)
259
+ */
260
+ orchestrationBaseSchema = z.object({
261
+ version: z.string({ required_error: 'version 为必填字段' }),
262
+ parallel_scheduling: parallelSchedulingSchema,
263
+ gates: z.record(z.string(), gateDefinitionSchema),
264
+ parallel_groups: z.record(z.string(), parallelGroupSchema),
265
+ modes: z.record(z.string(), modeDefinitionSchema),
266
+ });
267
+
268
+ /**
269
+ * orchestrationOverridesSchema — 校验项目级 .specify/orchestration-overrides.yaml
270
+ *
271
+ * 关键设计决策(GATE_DESIGN CL-001/010):
272
+ * - version 必填(CL-008,resolver 比对 base version)
273
+ * - modes key 使用显式 z.object() 列出 8 个 mode(CL-001 enum 校验)
274
+ * - parallel_groups 字段:schema 接受但 transform 时 strip,发出 unsupported-field warning
275
+ * - $schema_version 和 modes.<m>.extends 字段:接受但 resolver 不特殊处理(FR-014)
276
+ *
277
+ * 注意:parallel_groups 由 resolver 步骤 6 手动检测并 strip;schema 层仅声明该字段以免被 .strict() 拒绝
278
+ */
279
+ orchestrationOverridesSchema = z.object({
280
+ // 二期预留字段:schema 接受但 MVP resolver 不处理(FR-014)
281
+ $schema_version: z.string().optional(),
282
+
283
+ // version 必填(CL-008)——resolver 层做 base/overrides version 比对
284
+ version: z.string({ required_error: 'overrides 文件必须包含 version 字段' }),
285
+
286
+ // modes key 使用显式枚举——拒绝非 reserved name(CL-001,FR-007-A)
287
+ // 非 reserved 名 → safeParse 返回 error → 触发 schema-fallback → 整体 overrides 降级到 base
288
+ // 使用 .strict() 拒绝任何不在 8 个 reserved name 中的 mode key
289
+ modes: z.object({
290
+ feature: modeOverrideSchema.optional(),
291
+ story: modeOverrideSchema.optional(),
292
+ implement: modeOverrideSchema.optional(),
293
+ fix: modeOverrideSchema.optional(),
294
+ resume: modeOverrideSchema.optional(),
295
+ sync: modeOverrideSchema.optional(),
296
+ doc: modeOverrideSchema.optional(),
297
+ refactor: modeOverrideSchema.optional(),
298
+ }).strict({
299
+ message: 'modes 字段包含非法的 mode 名称,合法值为 [feature|story|implement|fix|resume|sync|doc|refactor]',
300
+ }).optional(),
301
+
302
+ // gates 字段:Record<GATE_ID, GateOverride>,对象级字段合并
303
+ gates: z.record(z.string(), gateOverrideSchema).optional(),
304
+
305
+ // parallel_scheduling:标量覆盖
306
+ parallel_scheduling: z.object({
307
+ max_concurrent_tasks: z.number().int().positive().optional(),
308
+ fallback_to_serial_on_failure: z.boolean().optional(),
309
+ fallback_reason_log: z.boolean().optional(),
310
+ }).optional(),
311
+
312
+ // parallel_groups:MVP 不支持覆盖,schema 接受但 resolver 层 strip + warning
313
+ // 不 reject 整个 overrides,其余合法字段照常生效(CL-010,FR-022)
314
+ // 使用 z.record() 保留类型信息,同时接受任意结构;由 resolver 在 parse 前检测并 strip
315
+ parallel_groups: z.record(z.string(), z.unknown()).optional(),
316
+ }).strict({
317
+ // 顶层真正未知字段使用 .strict() 策略拒绝(NFR-003)
318
+ // 注意:parallel_groups 已显式声明,不会被 .strict() 拒绝
319
+ message: 'overrides 文件包含未识别的顶层字段,请检查字段名称',
320
+ });
321
+
322
+ // ── 4. 别名(orchestrationMergedSchema === orchestrationBaseSchema)─
323
+
324
+ /**
325
+ * orchestrationMergedSchema — 校验合并后的 config
326
+ *
327
+ * 合并结果必须满足 base schema 的全部约束(FR-013)
328
+ * 复用 orchestrationBaseSchema 实现 DRY 原则(NFR-006)
329
+ */
330
+ orchestrationMergedSchema = orchestrationBaseSchema;
331
+ }
332
+
333
+ // ── 末尾统一 export(ESM export const 不能进 if 块,故用末尾 export 语句)──
334
+ export {
335
+ zodAvailable,
336
+ phaseSchema,
337
+ gateDefinitionSchema,
338
+ gateOverrideSchema,
339
+ modeDefinitionSchema,
340
+ modeOverrideSchema,
341
+ parallelGroupSchema,
342
+ parallelSchedulingSchema,
343
+ orchestrationBaseSchema,
344
+ orchestrationOverridesSchema,
345
+ orchestrationMergedSchema,
346
+ };
@@ -11,6 +11,9 @@ codexWrappers:
11
11
  generator:
12
12
  script: "plugins/spec-driver/scripts/codex-skills.sh"
13
13
  command: "npm run codex:spec-driver:install"
14
+ # F186 T2:每个 wrapper header 内嵌 `- Source SHA256: <hash>`(source body 的 sha256,
15
+ # 由 scripts/lib/extract-wrapper-body.mjs 计算,shell 生成端与 JS 校验端共用同一 helper)。
16
+ # validate-wrapper-sources.mjs 重算比对:不匹配 / 缺 sha 行 → fail(防 source 漂移或 wrapper 被手改)。
14
17
  entries:
15
18
  - id: "spec-driver-constitution"
16
19
  source: "plugins/spec-driver/skills/spec-driver-constitution/SKILL.md"
@@ -42,6 +42,15 @@
42
42
  "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/stop-task-check.sh"
43
43
  }
44
44
  ]
45
+ },
46
+ {
47
+ "matcher": "",
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/stop-fix-compliance-check.sh"
52
+ }
53
+ ]
45
54
  }
46
55
  ],
47
56
  "WorktreeCreate": [
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # Stop Hook: fix 模式流程依从性判定(Feature 208,阻断型)
3
+ #
4
+ # 薄壳职责(research.md D3):读 stdin payload → 转发给 Node CLI → 按 CLI 退出码原样转发 0/2,
5
+ # 任何非 0/2 的异常退出码一律兜底为 0(放行)。自身无判定逻辑,构成 FR-013 fail-open 的第二道保险
6
+ # (即便 Node 判定器抛出未捕获异常,本层仍兜底放行,不把崩溃泄漏为 Claude Code 侧未定义阻断)。
7
+
8
+ set -euo pipefail
9
+
10
+ # 定位插件根:优先 CLAUDE_PLUGIN_ROOT,缺省时按脚本自身相对路径推导(hooks/ 的上一级)
11
+ PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-}"
12
+ if [ -z "$PLUGIN_ROOT" ]; then
13
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ PLUGIN_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
15
+ fi
16
+
17
+ CLI="${FIX_COMPLIANCE_CLI:-$PLUGIN_ROOT/scripts/fix-compliance-judge.mjs}"
18
+
19
+ # node 不可用 → 放行(无法判定不阻断,FR-013)
20
+ if ! command -v node >/dev/null 2>&1; then
21
+ exit 0
22
+ fi
23
+
24
+ # stdin 转发给 CLI;临时禁用 errexit 以便捕获非零退出码做兜底判定
25
+ STDIN_PAYLOAD="$(cat)"
26
+
27
+ set +e
28
+ printf '%s' "$STDIN_PAYLOAD" | node "$CLI" --mode hook --project-root "$(pwd)"
29
+ CLI_EXIT=$?
30
+ set -e
31
+
32
+ # 退出码转发:0/2 原样转发,其余(崩溃/信号)兜底放行
33
+ if [ "$CLI_EXIT" -eq 2 ]; then
34
+ exit 2
35
+ fi
36
+ exit 0