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
@@ -0,0 +1,84 @@
1
+ /**
2
+ * delegation-contract — 「编排器必须委派子代理」硬约束块渲染核心(plugin-owned,自包含,无 root 依赖)
3
+ *
4
+ * canonical 源:templates/delegation-contract.md 的 block-start/block-end 之间内容。
5
+ * 与 preference-rules 不同:delegation-contract 是**整块原样注入**(无 per-row tool 过滤),
6
+ * 被注入到 5 个主编排器 SKILL.md 正文(fix/story/feature/implement/resume)。
7
+ *
8
+ * 被两处消费:
9
+ * - plugins/spec-driver/scripts/sync-delegation-contract.mjs(按各 SKILL 锚点注入约束块)
10
+ * - tests(纯函数级断言)
11
+ *
12
+ * 设计意图:plugin 必须自包含(发布后不依赖仓库 root scripts/),故渲染逻辑落在 plugin lib。
13
+ */
14
+
15
+ export const BEGIN_MARKER = '<!-- BEGIN delegation-contract (generated from templates/delegation-contract.md; do not edit) -->';
16
+ export const END_MARKER = '<!-- END delegation-contract -->';
17
+
18
+ const BLOCK_START_RE = /<!--\s*delegation-contract:block-start\s*-->/;
19
+ const BLOCK_END_RE = /<!--\s*delegation-contract:block-end\s*-->/;
20
+
21
+ /**
22
+ * 提取 canonical 块文本:取 block-start/block-end marker 之间的内容。
23
+ * 若无 block-start → 原样返回(兼容单测直接传 block 内容的场景)。
24
+ * 有 block-start 却无 block-end → fail-loud(对齐 preference-rules,禁止静默截断到 EOF)。
25
+ */
26
+ export function extractCanonicalBlock(templateText) {
27
+ const lines = String(templateText ?? '').split('\n');
28
+ const start = lines.findIndex((l) => BLOCK_START_RE.test(l));
29
+ if (start < 0) return String(templateText ?? '');
30
+ let end = -1;
31
+ for (let i = start + 1; i < lines.length; i++) {
32
+ if (BLOCK_END_RE.test(lines[i])) { end = i; break; }
33
+ }
34
+ if (end < 0) {
35
+ throw new Error('[delegation-contract] template 含 block-start 但缺 block-end,拒绝静默截断');
36
+ }
37
+ return lines.slice(start + 1, end).join('\n');
38
+ }
39
+
40
+ /** 将 canonical 块包裹 BEGIN/END marker(供 SKILL 文件嵌入)。 */
41
+ export function wrapWithMarkers(block) {
42
+ return `${BEGIN_MARKER}\n${String(block).trimEnd()}\n${END_MARKER}`;
43
+ }
44
+
45
+ /**
46
+ * 计算 SKILL 文件应有的内容(注入或替换约束块)。
47
+ * - 若已含 BEGIN..END → 仅替换 marker 之间内容(锚点不移动,幂等)。
48
+ * - 否则 → 在 anchorHeading 行**之后**(紧随该标题行的下一行)插入 wrapped 块,前后各留一空行。
49
+ * - anchorHeading 在 skillText 中未找到 → fail-loud(不静默跳过)。
50
+ *
51
+ * @param {string} skillText SKILL.md 全文
52
+ * @param {string} templateText delegation-contract.md 全文(或仅 block 内容)
53
+ * @param {string} anchorHeading 形如 '## 工作流定义',必须与 SKILL 中某行完整匹配
54
+ * @returns {string}
55
+ */
56
+ export function computeExpectedSkillContent(skillText, templateText, anchorHeading) {
57
+ const block = extractCanonicalBlock(templateText);
58
+ const wrapped = wrapWithMarkers(block);
59
+
60
+ const beginIdx = skillText.indexOf(BEGIN_MARKER);
61
+ // 在 BEGIN 之后查找 END,避免匹配到文档引用里的字面 marker
62
+ const endIdx = beginIdx >= 0 ? skillText.indexOf(END_MARKER, beginIdx + BEGIN_MARKER.length) : -1;
63
+ if (beginIdx >= 0 && endIdx > beginIdx) {
64
+ const before = skillText.slice(0, beginIdx);
65
+ const after = skillText.slice(endIdx + END_MARKER.length);
66
+ return before + wrapped + after;
67
+ }
68
+ // fail-loud(codex CRITICAL):孤儿 BEGIN(有 BEGIN 无 END,如手动截断/上次写入中断)若放行
69
+ // 会落入首次插入路径 → 文件出现双 BEGIN 单 END,且此后 --check 稳定假绿,守护永远看不见
70
+ if (beginIdx >= 0) {
71
+ throw new Error('[delegation-contract] SKILL 含 BEGIN marker 但缺 END marker(畸形块),拒绝重复注入;请手动修复或删除残块后重跑 sync');
72
+ }
73
+
74
+ // 首次插入:定位锚点标题行
75
+ const lines = skillText.split('\n');
76
+ const anchorIdx = lines.findIndex((l) => l === anchorHeading);
77
+ if (anchorIdx < 0) {
78
+ throw new Error(`[delegation-contract] 锚点未找到: ${JSON.stringify(anchorHeading)}`);
79
+ }
80
+ // 在锚点标题行之后插入,前后各留一空行
81
+ const head = lines.slice(0, anchorIdx + 1).join('\n');
82
+ const tail = lines.slice(anchorIdx + 1).join('\n').replace(/^\n+/, '');
83
+ return `${head}\n\n${wrapped}\n\n${tail}`;
84
+ }
@@ -23,6 +23,7 @@ import {
23
23
  orchestrationOverridesSchema,
24
24
  orchestrationMergedSchema,
25
25
  formatZodIssue,
26
+ zodAvailable,
26
27
  } from '../contracts/orchestration-schema.mjs';
27
28
 
28
29
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -208,6 +209,54 @@ export async function resolveOrchestrationConfig({ projectRoot, _loadBase, _load
208
209
  };
209
210
  }
210
211
 
212
+ // ── zod 缺失短路(步骤 1 成功之后、步骤 2 safeParse 之前)────────
213
+ // 缺 zod 时全部 schema 为 null,无法 safeParse;此处 best-effort 信任 base、
214
+ // 跳过项目级 overrides(用户输入无法校验),返回结构化结果而非硬崩。
215
+ if (!zodAvailable) {
216
+ // 纯对象检查(复用既有 isPlainObject 守卫模式)
217
+ const isBaseRawPlainObject =
218
+ rawBase !== null &&
219
+ rawBase !== undefined &&
220
+ Object.prototype.toString.call(rawBase) === '[object Object]';
221
+
222
+ if (!isBaseRawPlainObject) {
223
+ // base 解析结果不是纯对象 → 视为 base 损坏,退 generateFallbackConfig
224
+ diagnostics.push(createDiagnostic(
225
+ 'error',
226
+ 'orchestration.base-invalid',
227
+ '[orchestration] base 配置不是合法对象(zod 缺失 + base 非纯对象)',
228
+ ));
229
+ const fallbackConfig = generateFallbackConfig();
230
+ return {
231
+ mergedConfig: fallbackConfig,
232
+ baseConfig: fallbackConfig,
233
+ fieldSources: {},
234
+ diagnostics,
235
+ isFallback: true,
236
+ isBaseInvalid: true,
237
+ };
238
+ }
239
+
240
+ // base 是纯对象 → best-effort 信任,跳过 overrides(无法校验用户输入)
241
+ diagnostics.push(createDiagnostic(
242
+ 'warning',
243
+ 'orchestration.zod-unavailable',
244
+ '[orchestration] 未能加载 zod,已跳过 orchestration schema 校验并 best-effort 信任 base 配置;' +
245
+ '项目级 orchestration-overrides 在缺 zod 时不被应用。' +
246
+ '如需完整校验请在已安装依赖的目录运行(npm i)或从仓内源路径运行 spec-driver 脚本',
247
+ ));
248
+ const baseFieldSources = buildBaseOnlyFieldSources(rawBase);
249
+ return {
250
+ mergedConfig: rawBase,
251
+ baseConfig: rawBase,
252
+ fieldSources: baseFieldSources,
253
+ diagnostics,
254
+ isFallback: true,
255
+ isBaseInvalid: false,
256
+ };
257
+ }
258
+ // ── zod 缺失短路结束 ─────────────────────────────────────────────
259
+
211
260
  // ── 步骤 2:base Zod 校验 ──────────────────────────────────
212
261
  const baseParseResult = orchestrationBaseSchema.safeParse(rawBase);
213
262
  if (!baseParseResult.success) {
@@ -10,7 +10,7 @@ import path from 'path';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { parseYamlDocument } from '../scripts/lib/simple-yaml.mjs';
12
12
  import { generateFallbackConfig } from './orchestrator-fallback.mjs';
13
- import { orchestrationBaseSchema, formatZodIssue } from '../contracts/orchestration-schema.mjs';
13
+ import { orchestrationBaseSchema, formatZodIssue, zodAvailable } from '../contracts/orchestration-schema.mjs';
14
14
 
15
15
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
16
 
@@ -58,6 +58,34 @@ export class Orchestrator {
58
58
  const content = fs.readFileSync(configPath, 'utf-8');
59
59
  const parsed = parseYamlDocument(content);
60
60
 
61
+ // ── zod 缺失守卫(YAML 解析成功之后、safeParse 之前)────────────
62
+ // 缺 zod 时 orchestrationBaseSchema 为 null,调用 .safeParse 会抛 TypeError 被外层
63
+ // catch 误判为加载失败并丢弃真实配置。显式守卫在此 best-effort 信任已解析 YAML。
64
+ if (!zodAvailable) {
65
+ const isParsedPlainObject =
66
+ parsed !== null &&
67
+ parsed !== undefined &&
68
+ Object.prototype.toString.call(parsed) === '[object Object]';
69
+
70
+ if (isParsedPlainObject) {
71
+ // parsed 是纯对象 → best-effort 信任已解析的 YAML
72
+ this.logger.warn(
73
+ '[ORCHESTRATOR] zod 未加载,已跳过 orchestration schema 校验并 best-effort 信任已解析配置',
74
+ );
75
+ this.config = parsed;
76
+ this.isFallback = false; // 真实配置,非最小桩
77
+ } else {
78
+ // parsed 不是纯对象(YAML 解析为 null/数组/标量等)→ 退 generateFallbackConfig
79
+ this.logger.warn(
80
+ '[ORCHESTRATOR] zod 未加载且 YAML 解析结果非纯对象,使用 fallback 配置',
81
+ );
82
+ this.config = generateFallbackConfig();
83
+ this.isFallback = true;
84
+ }
85
+ return; // 跳过 safeParse,退出 loadAndValidateConfig
86
+ }
87
+ // ── zod 缺失守卫结束 ─────────────────────────────────────────────
88
+
61
89
  // 使用 orchestrationBaseSchema.safeParse 替代手写校验(CL-016,T-011)
62
90
  const zodResult = orchestrationBaseSchema.safeParse(parsed);
63
91
  if (!zodResult.success) {
@@ -76,18 +76,8 @@ ensure_source_exists() {
76
76
  fi
77
77
  }
78
78
 
79
- rewrite_codex_runtime_text() {
80
- sed \
81
- -e 's|/spec-driver:spec-driver-feature|$spec-driver-feature|g' \
82
- -e 's|/spec-driver:spec-driver-implement|$spec-driver-implement|g' \
83
- -e 's|/spec-driver:spec-driver-story|$spec-driver-story|g' \
84
- -e 's|/spec-driver:spec-driver-fix|$spec-driver-fix|g' \
85
- -e 's|/spec-driver:spec-driver-resume|$spec-driver-resume|g' \
86
- -e 's|/spec-driver:spec-driver-sync|$spec-driver-sync|g' \
87
- -e 's|/spec-driver:spec-driver-doc|$spec-driver-doc|g' \
88
- -e 's|Claude Code 的 Task tool|Task tool(Codex 下按内联子代理执行)|g' \
89
- -e 's|在同一消息中同时发出多个 Task tool 调用。Claude Code 的 function calling 机制支持在单个 assistant 消息中发出多个 tool calls,这些 tool calls 会被并行执行。|若当前环境支持并行工具调用,则在同一消息中并行执行;否则按本 Skill 的回退规则串行执行。|g'
90
- }
79
+ # F186 T2:原 rewrite_codex_runtime_text(awk|sed 管道的 9 条替换)已迁移到单一 Node helper
80
+ # plugins/spec-driver/scripts/lib/extract-wrapper-body.mjs,与 JS 校验端共用,杜绝双实现漂移。
91
81
 
92
82
  write_frontmatter() {
93
83
  local skill_name="$1"
@@ -134,11 +124,24 @@ EOF_ADAPTER
134
124
 
135
125
  write_wrapper_source_contract() {
136
126
  local source_skill_name="$1"
127
+ local source_skill_path="$2"
128
+
129
+ # F186 T2:盖印 source body sha256(与 JS 校验端共用同一 helper,杜绝双实现漂移)。
130
+ local body_sha
131
+ body_sha="$(node "$PLUGIN_DIR/scripts/lib/extract-wrapper-body.mjs" "$source_skill_path" --sha256)"
132
+
133
+ # CRITICAL-1:helper 在安装路径含空格/非 ASCII 时若 isDirectExecution 误判会输出空 stdout 但退出码 0,
134
+ # 静默生成空 sha → 空 wrapper。此处对非 64 位十六进制 sha 做 hard fail,杜绝静默降级。
135
+ if [[ ! "$body_sha" =~ ^[0-9a-f]{64}$ ]]; then
136
+ echo "[错误] extract-wrapper-body.mjs 未产出合法 sha256(got: '${body_sha}'),source: $source_skill_path" >&2
137
+ exit 1
138
+ fi
137
139
 
138
140
  cat <<EOF_CONTRACT
139
141
  ## Wrapper Source Contract
140
142
 
141
143
  - Canonical source: \`\$PLUGIN_DIR/skills/$source_skill_name/SKILL.md\`
144
+ - Source SHA256: $body_sha
142
145
  - Generated by: \`bash \$PLUGIN_DIR/scripts/codex-skills.sh install\`
143
146
  - Contract: \`\$PLUGIN_DIR/contracts/wrapper-source-of-truth.yaml\`
144
147
  - Maintenance rule: edit the source skill, then reinstall; do not edit this wrapper directly
@@ -150,21 +153,18 @@ EOF_CONTRACT
150
153
  write_skill_body() {
151
154
  local source_skill_path="$1"
152
155
 
153
- awk '
154
- NR == 1 && $0 == "---" {
155
- in_frontmatter = 1
156
- next
157
- }
158
- in_frontmatter {
159
- if ($0 == "---") {
160
- in_frontmatter = 0
161
- }
162
- next
163
- }
164
- {
165
- print
166
- }
167
- ' "$source_skill_path" | rewrite_codex_runtime_text
156
+ # F186 T2:frontmatter 剥除 + runtime text 替换改由单一 Node helper 产出,
157
+ # validate-wrapper-sources.mjs 共用同一提取逻辑(防双实现逐字节漂移)。
158
+ local body
159
+ body="$(node "$PLUGIN_DIR/scripts/lib/extract-wrapper-body.mjs" "$source_skill_path")"
160
+
161
+ # CRITICAL-1:helper 误判 isDirectExecution(含空格/非 ASCII 安装路径)时会输出空 body 但退出码 0,
162
+ # 静默生成空正文 wrapper。source skill 正文恒非空,故空 body 必为 helper 故障 → hard fail。
163
+ if [[ -z "$body" ]]; then
164
+ echo "[错误] extract-wrapper-body.mjs 产出空 body,source: $source_skill_path" >&2
165
+ exit 1
166
+ fi
167
+ printf '%s\n' "$body"
168
168
  }
169
169
 
170
170
  write_wrapper() {
@@ -179,7 +179,7 @@ write_wrapper() {
179
179
  {
180
180
  write_frontmatter "$skill_name" "$source_skill_path"
181
181
  printf '\n'
182
- write_wrapper_source_contract "$source_skill_name"
182
+ write_wrapper_source_contract "$source_skill_name" "$source_skill_path"
183
183
  printf '\n'
184
184
  write_codex_adapter "$skill_name" "$source_skill_name"
185
185
  printf '\n'
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * spike-fix-compliance-e2e.mjs
5
+ * Feature 208 / T028 — fix 依从性 Stop hook 的手工 headless E2E spike。
6
+ *
7
+ * ⚠️ 手工触发脚本:会 spawn `claude --print` 消耗真实订阅凭据(haiku + 极简任务,
8
+ * 单次 <$0.05),**不计入 `npm test`**、不在 CI 自动跑。用途 = 在与评测同构的
9
+ * headless 入口(--plugin-dir 注入插件副本 + Stop hook 挂载)下,观测
10
+ * `stop-fix-compliance-check.sh` 的 exit code 与 `[FIX-COMPLIANCE]` 反馈闭环。
11
+ *
12
+ * 手法复刻 specs/208-.../research/harness-verification.md 的"插件副本 + hook-trace"实锤:
13
+ * 1. 拷贝 plugins/spec-driver 到 scratchpad/os.tmpdir 副本(不污染源码)
14
+ * 2. 副本 hooks.json 追加 Stop → stop-fix-compliance-check.sh
15
+ * 3. claude --print --plugin-dir <副本> 跑 collapsed / compliant 极简场景
16
+ * 4. 打印 hook-trace 时间线与最终 exit code
17
+ *
18
+ * 用法:
19
+ * node plugins/spec-driver/scripts/dev/spike-fix-compliance-e2e.mjs --scenario collapsed
20
+ * node plugins/spec-driver/scripts/dev/spike-fix-compliance-e2e.mjs --scenario compliant
21
+ * node plugins/spec-driver/scripts/dev/spike-fix-compliance-e2e.mjs --scenario collapsed --keep # 保留副本供排查
22
+ *
23
+ * 执行属 T029 主编排器职责;本文件仅提供可重跑脚本。
24
+ */
25
+
26
+ import fs from 'node:fs';
27
+ import os from 'node:os';
28
+ import path from 'node:path';
29
+ import { spawnSync } from 'node:child_process';
30
+ import { fileURLToPath } from 'node:url';
31
+
32
+ const SCENARIOS = new Set(['collapsed', 'compliant']);
33
+
34
+ function parseArgs(argv) {
35
+ const args = { scenario: 'collapsed', keep: false, model: 'claude-haiku-4-5' };
36
+ for (let i = 0; i < argv.length; i += 1) {
37
+ if (argv[i] === '--scenario') { args.scenario = argv[i + 1] ?? args.scenario; i += 1; } else if (argv[i] === '--keep') { args.keep = true; } else if (argv[i] === '--model') { args.model = argv[i + 1] ?? args.model; i += 1; }
38
+ }
39
+ if (!SCENARIOS.has(args.scenario)) {
40
+ throw new Error(`未知场景: ${args.scenario}(可选: ${[...SCENARIOS].join(', ')})`);
41
+ }
42
+ return args;
43
+ }
44
+
45
+ /** 递归拷贝插件源码到副本目录(node 20 fs.cpSync) */
46
+ function copyPluginTo(destRoot) {
47
+ const pluginSrc = fileURLToPath(new URL('../..', import.meta.url)); // plugins/spec-driver
48
+ const dest = path.join(destRoot, 'spec-driver');
49
+ fs.cpSync(pluginSrc, dest, {
50
+ recursive: true,
51
+ filter: (src) => !src.includes(`${path.sep}node_modules${path.sep}`) && !src.endsWith(`${path.sep}node_modules`),
52
+ });
53
+ return dest;
54
+ }
55
+
56
+ /** 在副本 hooks.json 的 Stop 数组挂载新 hook(幂等:T026 之后源码已自带该条目,重复追加会双挂双计数) */
57
+ function mountStopHook(pluginDest) {
58
+ const hooksPath = path.join(pluginDest, 'hooks', 'hooks.json');
59
+ const hooks = JSON.parse(fs.readFileSync(hooksPath, 'utf8'));
60
+ hooks.hooks.Stop = hooks.hooks.Stop || [];
61
+ const alreadyMounted = hooks.hooks.Stop.some((entry) =>
62
+ (entry.hooks || []).some((h) => typeof h.command === 'string' && h.command.includes('stop-fix-compliance-check.sh')));
63
+ if (!alreadyMounted) {
64
+ hooks.hooks.Stop.push({
65
+ matcher: '',
66
+ hooks: [{ type: 'command', command: 'bash ${CLAUDE_PLUGIN_ROOT}/hooks/stop-fix-compliance-check.sh' }],
67
+ });
68
+ fs.writeFileSync(hooksPath, `${JSON.stringify(hooks, null, 2)}\n`, 'utf8');
69
+ }
70
+ return alreadyMounted;
71
+ }
72
+
73
+ /**
74
+ * 构造极简 headless prompt。
75
+ * collapsed:诱导模型直接输出"完成"而不走委派(观测 exit 2 + [FIX-COMPLIANCE])。
76
+ * compliant:仅要求回一个字(不会触发 fix 展开 → 非 fix 会话零接触,作为对照)。
77
+ *
78
+ * 说明:真正的 fix 展开由 slash 命令 harness 注入,spike 无法在 --print 位置参数里
79
+ * 完美复刻 SKILL 展开;本 spike 主要观测 hook 是否执行 + 退出码转发是否成立,
80
+ * 完整 opus + 真实展开的端到端可靠性属评测阶段(harness-verification.md 外推边界)。
81
+ */
82
+ function buildPrompt(scenario) {
83
+ if (scenario === 'collapsed') {
84
+ return 'Base directory for this skill: /w/plugins/spec-driver/skills/spec-driver-fix\n请直接回复"已修复",不要调用任何工具。';
85
+ }
86
+ return 'say only ok';
87
+ }
88
+
89
+ function main() {
90
+ const args = parseArgs(process.argv.slice(2));
91
+ const scratchBase = process.env.SCRATCHPAD || os.tmpdir();
92
+ const workdir = fs.mkdtempSync(path.join(scratchBase, 'fix-compliance-spike-'));
93
+ const pluginCopyRoot = path.join(workdir, 'plugins');
94
+ fs.mkdirSync(pluginCopyRoot, { recursive: true });
95
+
96
+ console.log(`[spike] scenario=${args.scenario} model=${args.model}`);
97
+ console.log(`[spike] workdir=${workdir}`);
98
+
99
+ const pluginDest = copyPluginTo(pluginCopyRoot);
100
+ mountStopHook(pluginDest);
101
+ console.log(`[spike] 插件副本已就绪 + Stop hook 已挂载: ${pluginDest}/hooks/hooks.json`);
102
+
103
+ const prompt = buildPrompt(args.scenario);
104
+ const started = Date.now();
105
+ const res = spawnSync('claude', [
106
+ '--print',
107
+ '--model', args.model,
108
+ '--plugin-dir', pluginDest,
109
+ '--permission-mode', 'acceptEdits',
110
+ '--', prompt,
111
+ ], { encoding: 'utf8', cwd: workdir });
112
+
113
+ const elapsedMs = Date.now() - started;
114
+ console.log('──────── hook-trace / 输出 ────────');
115
+ console.log(`[spike] exit=${res.status} elapsedMs=${elapsedMs}`);
116
+ console.log('[spike] stdout:');
117
+ console.log(res.stdout || '(空)');
118
+ console.log('[spike] stderr(关注 [FIX-COMPLIANCE] / [GATE-DEGRADED] 前缀):');
119
+ console.log(res.stderr || '(空)');
120
+ console.log('───────────────────────────────────');
121
+
122
+ if (args.keep) {
123
+ console.log(`[spike] --keep:保留副本 ${workdir}`);
124
+ } else {
125
+ fs.rmSync(workdir, { recursive: true, force: true });
126
+ console.log('[spike] 已清理副本');
127
+ }
128
+ }
129
+
130
+ main();