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,335 @@
1
+ /**
2
+ * fix-compliance-io.mjs
3
+ * Feature 208 — fix 依从性判定 I/O 边界(全部 fs 操作聚于此,分层参照 goal-loop-cli.mjs)
4
+ *
5
+ * 本文件承载:payload 解析 / transcript 读取 / 配置读取 / 审计事件落盘 / 特性目录磁盘核验。
6
+ * BlockCountState 读写(loadBlockState/saveBlockState)由 T023 追加,刻意不在本文件初版实现
7
+ * (避免与 US4 任务边界重叠)。
8
+ *
9
+ * 关键契约(contracts/fix-compliance-config-field.md):判定路径**不 import config-schema.mjs**,
10
+ * 改用零依赖的 simple-yaml.mjs parseYamlDocument 做非抛出式配置读取,杜绝拉入 zod 间接依赖链。
11
+ */
12
+
13
+ import fs from 'node:fs';
14
+ import os from 'node:os';
15
+ import path from 'node:path';
16
+ import { parseYamlDocument } from './simple-yaml.mjs';
17
+ import { normalizeTranscriptEntry, resolveEnforcementFromConfig } from './fix-compliance-core.mjs';
18
+
19
+ /**
20
+ * transcript 体积上限(research.md D6 / T001 校准:实测 fix 会话 ≤0.31MB,20MB≈60 倍余量)。
21
+ * 超限即判 transcript-too-large 走 FR-013 fail-open,作为主要性能防线(不引入运行时熔断)。
22
+ */
23
+ export const MAX_TRANSCRIPT_BYTES = 20 * 1024 * 1024;
24
+
25
+ // ────────────────────────────────────────
26
+ // payload 组
27
+ // ────────────────────────────────────────
28
+
29
+ /**
30
+ * 解析 Stop hook stdin payload(data-model.md §1),非抛出式。
31
+ * @param {string} stdinRaw
32
+ * @returns {{ ok:boolean, payload:object|null, diagnostics:string[] }}
33
+ */
34
+ export function readHookPayload(stdinRaw) {
35
+ let parsed;
36
+ try {
37
+ parsed = JSON.parse(typeof stdinRaw === 'string' ? stdinRaw : '');
38
+ } catch {
39
+ return { ok: false, payload: null, diagnostics: ['payload-invalid'] };
40
+ }
41
+ const sessionId = parsed && parsed.session_id;
42
+ const transcriptPath = parsed && parsed.transcript_path;
43
+ if (typeof sessionId !== 'string' || sessionId.length === 0
44
+ || typeof transcriptPath !== 'string' || transcriptPath.length === 0) {
45
+ return { ok: false, payload: null, diagnostics: ['payload-invalid'] };
46
+ }
47
+ return { ok: true, payload: parsed, diagnostics: [] };
48
+ }
49
+
50
+ // ────────────────────────────────────────
51
+ // transcript 组
52
+ // ────────────────────────────────────────
53
+
54
+ /**
55
+ * 读取并逐行解析 transcript JSONL(data-model.md §2)。非抛出式 + 逐行容错。
56
+ * @param {string} transcriptPath
57
+ * @param {number} [maxBytes=MAX_TRANSCRIPT_BYTES] - 体积上限(可注入以便测试)
58
+ * @returns {{ entries:object[], diagnostics:string[] }}
59
+ */
60
+ export function readTranscriptEntries(transcriptPath, maxBytes = MAX_TRANSCRIPT_BYTES) {
61
+ let stat;
62
+ try {
63
+ stat = fs.statSync(transcriptPath);
64
+ } catch {
65
+ return { entries: [], diagnostics: ['transcript-unavailable'] };
66
+ }
67
+ if (!stat.isFile()) {
68
+ return { entries: [], diagnostics: ['transcript-unavailable'] };
69
+ }
70
+ if (stat.size > maxBytes) {
71
+ return { entries: [], diagnostics: ['transcript-too-large'] };
72
+ }
73
+ let raw;
74
+ try {
75
+ raw = fs.readFileSync(transcriptPath, 'utf8');
76
+ } catch {
77
+ return { entries: [], diagnostics: ['transcript-unavailable'] };
78
+ }
79
+ const lines = raw.split('\n').filter((line) => line.trim().length > 0);
80
+ const entries = lines.map((line, index) => {
81
+ try {
82
+ return normalizeTranscriptEntry(JSON.parse(line), index, false);
83
+ } catch {
84
+ // 单行损坏不中断整体解析(data-model.md §2 parseError 语义)
85
+ return normalizeTranscriptEntry(null, index, true);
86
+ }
87
+ });
88
+ // 全损坏(非空行存在且全部解析失败)= FR-013 的"格式不可识别":不能静默当非 fix 会话放行,
89
+ // 必须走 fail-open + loud 诊断路径(codex implement 审查 C-1)。部分损坏维持逐行容错。
90
+ if (entries.length > 0 && entries.every((entry) => entry.parseError)) {
91
+ return { entries: [], diagnostics: ['transcript-unavailable'] };
92
+ }
93
+ return { entries, diagnostics: [] };
94
+ }
95
+
96
+ // ────────────────────────────────────────
97
+ // config 组(FR-015 三步顺序,非抛出式,不经 zod)
98
+ // ────────────────────────────────────────
99
+
100
+ /** 查找配置文件:projectRoot 优先,其次 .specify/ 下 */
101
+ function findConfigFile(projectRoot) {
102
+ const primary = path.join(projectRoot, 'spec-driver.config.yaml');
103
+ if (fs.existsSync(primary)) return primary;
104
+ const fallback = path.join(projectRoot, '.specify', 'spec-driver.config.yaml');
105
+ if (fs.existsSync(fallback)) return fallback;
106
+ return null;
107
+ }
108
+
109
+ /**
110
+ * 读取并解析 fix_compliance.enforcement(fix-compliance-config-field.md 三步序)。
111
+ * 类型化区分三态:缺失(默认非降级)/ 损坏或非法值(降级)/ 合法(采用);
112
+ * 禁止 catch-all 合并"配置错误"与"判定异常"——本函数只吞配置层解析异常。
113
+ * @param {string} projectRoot
114
+ * @returns {{ found:boolean, parseFailed:boolean, config:object|null, enforcement:string, configDegraded:boolean, diagnostics:string[] }}
115
+ */
116
+ export function findAndParseConfig(projectRoot) {
117
+ const configPath = findConfigFile(projectRoot);
118
+ if (!configPath) {
119
+ const resolved = resolveEnforcementFromConfig({ found: false, parseFailed: false, config: null });
120
+ return { found: false, parseFailed: false, config: null, ...resolved, diagnostics: [] };
121
+ }
122
+ let config = null;
123
+ let parseFailed = false;
124
+ try {
125
+ config = parseYamlDocument(fs.readFileSync(configPath, 'utf8'));
126
+ } catch {
127
+ // 仅捕获配置文件读取/解析异常(读目录 EISDIR、权限等)→ 归约为 parseFailed(损坏)
128
+ parseFailed = true;
129
+ }
130
+ const resolved = resolveEnforcementFromConfig({ found: true, parseFailed, config });
131
+ const diagnostics = resolved.configDegraded ? ['config-degraded'] : [];
132
+ return { found: true, parseFailed, config, ...resolved, diagnostics };
133
+ }
134
+
135
+ // ────────────────────────────────────────
136
+ // audit 组
137
+ // ────────────────────────────────────────
138
+
139
+ /**
140
+ * 追加审计事件到 .specify/runs/YYYY-MM.jsonl(与 record-workflow-run.mjs 同目录/命名约定)。
141
+ * 非抛出式:写入失败返回 ok:false(FR-013 精神,落盘失败不得让判定崩溃)。
142
+ * @param {string} projectRoot
143
+ * @param {object} event
144
+ * @returns {{ ok:boolean, path:string|null }}
145
+ */
146
+ export function appendAuditEvent(projectRoot, event) {
147
+ try {
148
+ const runsDir = path.join(projectRoot, '.specify', 'runs');
149
+ fs.mkdirSync(runsDir, { recursive: true });
150
+ const month = new Date().toISOString().slice(0, 7);
151
+ const targetFile = path.join(runsDir, `${month}.jsonl`);
152
+ fs.appendFileSync(targetFile, `${JSON.stringify(event)}\n`, 'utf8');
153
+ return { ok: true, path: targetFile };
154
+ } catch {
155
+ return { ok: false, path: null };
156
+ }
157
+ }
158
+
159
+ // ────────────────────────────────────────
160
+ // featureDir 组(磁盘核验才是判据,提名只是候选)
161
+ // ────────────────────────────────────────
162
+
163
+ /**
164
+ * 校验特性目录候选是否真实存在于磁盘(research.md D1:提名≠判据)。
165
+ * @param {string} projectRoot
166
+ * @param {string|null} relPath - resolveFeatureDirCandidate 提名的相对路径
167
+ * @returns {{ existsOnDisk:boolean }}
168
+ */
169
+ export function checkFeatureDirOnDisk(projectRoot, relPath) {
170
+ if (typeof relPath !== 'string' || relPath.length === 0) {
171
+ return { existsOnDisk: false };
172
+ }
173
+ try {
174
+ const full = path.join(projectRoot, relPath);
175
+ return { existsOnDisk: fs.existsSync(full) && fs.statSync(full).isDirectory() };
176
+ } catch {
177
+ return { existsOnDisk: false };
178
+ }
179
+ }
180
+
181
+ /**
182
+ * 读取制品文件内容(ArtifactCheckResult 的磁盘侧输入,data-model.md §6)。
183
+ * @param {string} projectRoot
184
+ * @param {string} relPath
185
+ * @returns {{ exists:boolean, content:string|null, nonEmpty:boolean }}
186
+ */
187
+ export function readArtifactFile(projectRoot, relPath) {
188
+ if (typeof relPath !== 'string' || relPath.length === 0) {
189
+ return { exists: false, content: null, nonEmpty: false };
190
+ }
191
+ try {
192
+ const full = path.join(projectRoot, relPath);
193
+ const stat = fs.statSync(full);
194
+ if (!stat.isFile()) return { exists: false, content: null, nonEmpty: false };
195
+ const content = fs.readFileSync(full, 'utf8');
196
+ return { exists: true, content, nonEmpty: content.replace(/\s/g, '').length > 0 };
197
+ } catch {
198
+ return { exists: false, content: null, nonEmpty: false };
199
+ }
200
+ }
201
+
202
+ // ────────────────────────────────────────
203
+ // BlockCountState 组(T023,FR-006 阻断计数持久态;data-model.md §8 + research.md D2/D4)
204
+ // ────────────────────────────────────────
205
+
206
+ /** 阻断计数状态主目录(相对 projectRoot):.specify/runs/ 已被仓库既有 .gitignore 整段忽略 */
207
+ const STATE_SUBDIR = ['.specify', 'runs', '.fix-compliance-state'];
208
+ /** tmpdir 降级子目录名 */
209
+ const STATE_TMP_SUBDIR = 'spec-driver-fix-compliance';
210
+
211
+ /**
212
+ * tmpdir 降级基路径。支持 env 覆盖以便测试模拟"两级存储均不可用"。
213
+ * @returns {string}
214
+ */
215
+ function stateTmpBase() {
216
+ const override = process.env.SPEC_DRIVER_FIX_COMPLIANCE_STATE_TMP;
217
+ return typeof override === 'string' && override.length > 0 ? override : os.tmpdir();
218
+ }
219
+
220
+ /**
221
+ * session_id 白名单化清洗(research.md D2 [REVISED]):仅保留 [A-Za-z0-9._-],
222
+ * 其余替换为 _;清洗后为空用 unknown-session。杜绝路径穿越/非法文件名。
223
+ * @param {string} sessionId
224
+ * @returns {string}
225
+ */
226
+ export function sanitizeSessionId(sessionId) {
227
+ const raw = typeof sessionId === 'string' ? sessionId : '';
228
+ const cleaned = raw.replace(/[^A-Za-z0-9._-]/g, '_');
229
+ return cleaned.length > 0 ? cleaned : 'unknown-session';
230
+ }
231
+
232
+ /** 主存储文件绝对路径 */
233
+ function primaryStatePath(projectRoot, sanitizedId) {
234
+ return path.join(projectRoot, ...STATE_SUBDIR, `${sanitizedId}.json`);
235
+ }
236
+
237
+ /** tmpdir 降级文件绝对路径 */
238
+ function tmpStatePath(sanitizedId) {
239
+ return path.join(stateTmpBase(), STATE_TMP_SUBDIR, `${sanitizedId}.json`);
240
+ }
241
+
242
+ /** 归一化磁盘读到的状态对象(缺字段按默认,向后兼容) */
243
+ function normalizeState(sessionId, parsed) {
244
+ const src = parsed && typeof parsed === 'object' ? parsed : {};
245
+ const blockCount = Number.isInteger(src.blockCount) && src.blockCount >= 0 ? src.blockCount : 0;
246
+ return {
247
+ sessionId,
248
+ blockCount,
249
+ // 历史文件缺 degradedRecorded 字段 → 按 false(向后兼容,data-model.md §8)
250
+ degradedRecorded: src.degradedRecorded === true,
251
+ };
252
+ }
253
+
254
+ /**
255
+ * 读取阻断计数状态(主路径优先,回落 tmpdir)。文件缺失/损坏均按初始态返回(blockCount 0)。
256
+ * load 不区分"存储不可用"——不可用信号由 saveBlockState 在写入时暴露(research.md D2)。
257
+ * @param {string} projectRoot
258
+ * @param {string} sessionId
259
+ * @returns {{ sessionId:string, blockCount:number, degradedRecorded:boolean }}
260
+ */
261
+ export function loadBlockState(projectRoot, sessionId) {
262
+ const sanitizedId = sanitizeSessionId(sessionId);
263
+ for (const filePath of [primaryStatePath(projectRoot, sanitizedId), tmpStatePath(sanitizedId)]) {
264
+ try {
265
+ const raw = fs.readFileSync(filePath, 'utf8');
266
+ return normalizeState(sanitizedId, JSON.parse(raw));
267
+ } catch {
268
+ // 文件缺失/损坏/不可读 → 尝试下一路径
269
+ }
270
+ }
271
+ return normalizeState(sanitizedId, null);
272
+ }
273
+
274
+ /** 尝试写单一路径,成功返回 true */
275
+ function tryWriteState(filePath, payload) {
276
+ try {
277
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
278
+ fs.writeFileSync(filePath, `${JSON.stringify(payload)}\n`, 'utf8');
279
+ return true;
280
+ } catch {
281
+ return false;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * 持久化阻断计数状态(主路径失败降级 tmpdir,两级均失败 → state-storage-unavailable)。
287
+ * @param {string} projectRoot
288
+ * @param {string} sessionId
289
+ * @param {{ blockCount:number, degradedRecorded:boolean }} state
290
+ * @returns {{ ok:boolean, path:string|null, degraded:boolean, diagnostics:string[] }}
291
+ */
292
+ export function saveBlockState(projectRoot, sessionId, state) {
293
+ const sanitizedId = sanitizeSessionId(sessionId);
294
+ const payload = {
295
+ sessionId: sanitizedId,
296
+ blockCount: Number.isInteger(state && state.blockCount) && state.blockCount >= 0 ? state.blockCount : 0,
297
+ degradedRecorded: Boolean(state && state.degradedRecorded),
298
+ updatedAt: new Date().toISOString(),
299
+ };
300
+
301
+ const primary = primaryStatePath(projectRoot, sanitizedId);
302
+ if (tryWriteState(primary, payload)) {
303
+ return { ok: true, path: primary, degraded: false, diagnostics: [] };
304
+ }
305
+ const fallback = tmpStatePath(sanitizedId);
306
+ if (tryWriteState(fallback, payload)) {
307
+ return { ok: true, path: fallback, degraded: true, diagnostics: [] };
308
+ }
309
+ return { ok: false, path: null, degraded: true, diagnostics: ['state-storage-unavailable'] };
310
+ }
311
+
312
+ /**
313
+ * 重置阻断计数状态(FR-006 增补:补救成功后的清零转移)。
314
+ * 删除两级存储(主路径 + tmpdir 回落)中该 session 对应的状态文件,
315
+ * 与"从未被阻断"状态同构——blockCount 与 degradedRecorded 一并归位,无字段级歧义。
316
+ * 尽力而为、非抛出式:文件不存在(本就未阻断过)或删除失败均静默忽略,
317
+ * 不产生可失败传播的下游(与 sweep 同为旁路维护语义,不同于 saveBlockState 需暴露
318
+ * state-storage-unavailable 诊断——reset 失败的最坏后果只是"旧计数残留",
319
+ * 不影响本次放行判定,无需诊断落盘)。
320
+ * @param {string} projectRoot
321
+ * @param {string} sessionId
322
+ * @returns {void}
323
+ */
324
+ export function resetBlockState(projectRoot, sessionId) {
325
+ const sanitizedId = sanitizeSessionId(sessionId);
326
+ // 两级都无条件尝试删除:不因主路径删除失败就跳过 tmpdir,否则 load 会回落读到
327
+ // tmpdir 残留旧计数导致清零失效(fix-report 影响范围扫描:重置必须两级都清)。
328
+ for (const filePath of [primaryStatePath(projectRoot, sanitizedId), tmpStatePath(sanitizedId)]) {
329
+ try {
330
+ fs.unlinkSync(filePath);
331
+ } catch {
332
+ // 文件不存在 / 不可删 → 忽略(尽力而为,缺一级不影响另一级清除)
333
+ }
334
+ }
335
+ }