spectra-cli 4.1.1 → 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 (682) hide show
  1. package/README.md +12 -5
  2. package/dist/.spectra-build-meta.json +9 -0
  3. package/dist/adapters/go-adapter.d.ts.map +1 -1
  4. package/dist/adapters/go-adapter.js +2 -0
  5. package/dist/adapters/go-adapter.js.map +1 -1
  6. package/dist/adapters/index.d.ts +1 -1
  7. package/dist/adapters/index.d.ts.map +1 -1
  8. package/dist/adapters/java-adapter.d.ts.map +1 -1
  9. package/dist/adapters/java-adapter.js +2 -0
  10. package/dist/adapters/java-adapter.js.map +1 -1
  11. package/dist/adapters/language-adapter.d.ts +42 -9
  12. package/dist/adapters/language-adapter.d.ts.map +1 -1
  13. package/dist/adapters/python-adapter.d.ts +34 -6
  14. package/dist/adapters/python-adapter.d.ts.map +1 -1
  15. package/dist/adapters/python-adapter.js +168 -61
  16. package/dist/adapters/python-adapter.js.map +1 -1
  17. package/dist/adapters/ts-js-adapter.d.ts +13 -5
  18. package/dist/adapters/ts-js-adapter.d.ts.map +1 -1
  19. package/dist/adapters/ts-js-adapter.js +55 -14
  20. package/dist/adapters/ts-js-adapter.js.map +1 -1
  21. package/dist/batch/batch-orchestrator.d.ts +74 -5
  22. package/dist/batch/batch-orchestrator.d.ts.map +1 -1
  23. package/dist/batch/batch-orchestrator.js +583 -70
  24. package/dist/batch/batch-orchestrator.js.map +1 -1
  25. package/dist/batch/delta-regenerator.d.ts +2 -2
  26. package/dist/batch/delta-regenerator.d.ts.map +1 -1
  27. package/dist/batch/delta-regenerator.js +21 -28
  28. package/dist/batch/delta-regenerator.js.map +1 -1
  29. package/dist/batch/module-grouper.d.ts +9 -2
  30. package/dist/batch/module-grouper.d.ts.map +1 -1
  31. package/dist/batch/module-grouper.js +3 -1
  32. package/dist/batch/module-grouper.js.map +1 -1
  33. package/dist/batch/regen-plan.d.ts +78 -0
  34. package/dist/batch/regen-plan.d.ts.map +1 -0
  35. package/dist/batch/regen-plan.js +93 -0
  36. package/dist/batch/regen-plan.js.map +1 -0
  37. package/dist/cli/commands/batch.d.ts.map +1 -1
  38. package/dist/cli/commands/batch.js +36 -8
  39. package/dist/cli/commands/batch.js.map +1 -1
  40. package/dist/cli/commands/graph.js +1 -1
  41. package/dist/cli/commands/graph.js.map +1 -1
  42. package/dist/cli/commands/index.d.ts +8 -0
  43. package/dist/cli/commands/index.d.ts.map +1 -0
  44. package/dist/cli/commands/index.js +274 -0
  45. package/dist/cli/commands/index.js.map +1 -0
  46. package/dist/cli/commands/query.d.ts.map +1 -1
  47. package/dist/cli/commands/query.js +3 -1
  48. package/dist/cli/commands/query.js.map +1 -1
  49. package/dist/cli/commands/scaffold-kb.d.ts +9 -0
  50. package/dist/cli/commands/scaffold-kb.d.ts.map +1 -0
  51. package/dist/cli/commands/scaffold-kb.js +179 -0
  52. package/dist/cli/commands/scaffold-kb.js.map +1 -0
  53. package/dist/cli/index.js +33 -14
  54. package/dist/cli/index.js.map +1 -1
  55. package/dist/cli/utils/error-handler.d.ts +0 -6
  56. package/dist/cli/utils/error-handler.d.ts.map +1 -1
  57. package/dist/cli/utils/error-handler.js +0 -13
  58. package/dist/cli/utils/error-handler.js.map +1 -1
  59. package/dist/cli/utils/parse-args.d.ts +55 -3
  60. package/dist/cli/utils/parse-args.d.ts.map +1 -1
  61. package/dist/cli/utils/parse-args.js +176 -13
  62. package/dist/cli/utils/parse-args.js.map +1 -1
  63. package/dist/cli/version-meta.d.ts +16 -0
  64. package/dist/cli/version-meta.d.ts.map +1 -0
  65. package/dist/cli/version-meta.js +28 -0
  66. package/dist/cli/version-meta.js.map +1 -0
  67. package/dist/core/ast-analyzer.d.ts.map +1 -1
  68. package/dist/core/ast-analyzer.js +75 -5
  69. package/dist/core/ast-analyzer.js.map +1 -1
  70. package/dist/core/import-resolver.d.ts +93 -0
  71. package/dist/core/import-resolver.d.ts.map +1 -0
  72. package/dist/core/import-resolver.js +414 -0
  73. package/dist/core/import-resolver.js.map +1 -0
  74. package/dist/core/query-mappers/base-mapper.d.ts +13 -0
  75. package/dist/core/query-mappers/base-mapper.d.ts.map +1 -1
  76. package/dist/core/query-mappers/go-mapper.d.ts +68 -0
  77. package/dist/core/query-mappers/go-mapper.d.ts.map +1 -1
  78. package/dist/core/query-mappers/go-mapper.js +421 -0
  79. package/dist/core/query-mappers/go-mapper.js.map +1 -1
  80. package/dist/core/query-mappers/java-mapper.d.ts +196 -0
  81. package/dist/core/query-mappers/java-mapper.d.ts.map +1 -1
  82. package/dist/core/query-mappers/java-mapper.js +600 -0
  83. package/dist/core/query-mappers/java-mapper.js.map +1 -1
  84. package/dist/core/query-mappers/python-mapper.d.ts +55 -0
  85. package/dist/core/query-mappers/python-mapper.d.ts.map +1 -1
  86. package/dist/core/query-mappers/python-mapper.js +273 -0
  87. package/dist/core/query-mappers/python-mapper.js.map +1 -1
  88. package/dist/core/query-mappers/typescript-mapper.d.ts +102 -0
  89. package/dist/core/query-mappers/typescript-mapper.d.ts.map +1 -1
  90. package/dist/core/query-mappers/typescript-mapper.js +470 -0
  91. package/dist/core/query-mappers/typescript-mapper.js.map +1 -1
  92. package/dist/core/single-spec-orchestrator.d.ts +21 -0
  93. package/dist/core/single-spec-orchestrator.d.ts.map +1 -1
  94. package/dist/core/single-spec-orchestrator.js +187 -23
  95. package/dist/core/single-spec-orchestrator.js.map +1 -1
  96. package/dist/core/skeleton-hash.d.ts +33 -0
  97. package/dist/core/skeleton-hash.d.ts.map +1 -0
  98. package/dist/core/skeleton-hash.js +76 -0
  99. package/dist/core/skeleton-hash.js.map +1 -0
  100. package/dist/core/tree-sitter-analyzer.d.ts +7 -0
  101. package/dist/core/tree-sitter-analyzer.d.ts.map +1 -1
  102. package/dist/core/tree-sitter-analyzer.js +32 -1
  103. package/dist/core/tree-sitter-analyzer.js.map +1 -1
  104. package/dist/core/tree-sitter-fallback.d.ts +12 -4
  105. package/dist/core/tree-sitter-fallback.d.ts.map +1 -1
  106. package/dist/core/tree-sitter-fallback.js +160 -12
  107. package/dist/core/tree-sitter-fallback.js.map +1 -1
  108. package/dist/extraction/extraction-types.d.ts +25 -25
  109. package/dist/extraction/image-extractor.d.ts.map +1 -1
  110. package/dist/extraction/image-extractor.js +3 -1
  111. package/dist/extraction/image-extractor.js.map +1 -1
  112. package/dist/extraction/markdown-extractor.d.ts.map +1 -1
  113. package/dist/extraction/markdown-extractor.js +3 -1
  114. package/dist/extraction/markdown-extractor.js.map +1 -1
  115. package/dist/generator/frontmatter.d.ts +5 -0
  116. package/dist/generator/frontmatter.d.ts.map +1 -1
  117. package/dist/generator/frontmatter.js +4 -0
  118. package/dist/generator/frontmatter.js.map +1 -1
  119. package/dist/generator/index-generator.d.ts +3 -3
  120. package/dist/generator/index-generator.d.ts.map +1 -1
  121. package/dist/generator/index-generator.js +1 -1
  122. package/dist/generator/index-generator.js.map +1 -1
  123. package/dist/graph/directory-graph.d.ts +14 -4
  124. package/dist/graph/directory-graph.d.ts.map +1 -1
  125. package/dist/graph/directory-graph.js +71 -128
  126. package/dist/graph/directory-graph.js.map +1 -1
  127. package/dist/graph/mermaid-renderer.d.ts +8 -5
  128. package/dist/graph/mermaid-renderer.d.ts.map +1 -1
  129. package/dist/graph/mermaid-renderer.js +3 -3
  130. package/dist/graph/mermaid-renderer.js.map +1 -1
  131. package/dist/graph/topological-sort.d.ts +13 -6
  132. package/dist/graph/topological-sort.d.ts.map +1 -1
  133. package/dist/graph/topological-sort.js +2 -2
  134. package/dist/graph/topological-sort.js.map +1 -1
  135. package/dist/kb-mcp/index.d.ts +14 -0
  136. package/dist/kb-mcp/index.d.ts.map +1 -0
  137. package/dist/kb-mcp/index.js +24 -0
  138. package/dist/kb-mcp/index.js.map +1 -0
  139. package/dist/kb-mcp/lib/kb-error.d.ts +21 -0
  140. package/dist/kb-mcp/lib/kb-error.d.ts.map +1 -0
  141. package/dist/kb-mcp/lib/kb-error.js +28 -0
  142. package/dist/kb-mcp/lib/kb-error.js.map +1 -0
  143. package/dist/kb-mcp/lib/kb-locator.d.ts +58 -0
  144. package/dist/kb-mcp/lib/kb-locator.d.ts.map +1 -0
  145. package/dist/kb-mcp/lib/kb-locator.js +85 -0
  146. package/dist/kb-mcp/lib/kb-locator.js.map +1 -0
  147. package/dist/kb-mcp/lib/result-merger.d.ts +35 -0
  148. package/dist/kb-mcp/lib/result-merger.d.ts.map +1 -0
  149. package/dist/kb-mcp/lib/result-merger.js +64 -0
  150. package/dist/kb-mcp/lib/result-merger.js.map +1 -0
  151. package/dist/kb-mcp/server.d.ts +12 -0
  152. package/dist/kb-mcp/server.d.ts.map +1 -0
  153. package/dist/kb-mcp/server.js +29 -0
  154. package/dist/kb-mcp/server.js.map +1 -0
  155. package/dist/kb-mcp/tools/kb-api-lookup.d.ts +28 -0
  156. package/dist/kb-mcp/tools/kb-api-lookup.d.ts.map +1 -0
  157. package/dist/kb-mcp/tools/kb-api-lookup.js +237 -0
  158. package/dist/kb-mcp/tools/kb-api-lookup.js.map +1 -0
  159. package/dist/kb-mcp/tools/kb-doc-lookup.d.ts +20 -0
  160. package/dist/kb-mcp/tools/kb-doc-lookup.d.ts.map +1 -0
  161. package/dist/kb-mcp/tools/kb-doc-lookup.js +92 -0
  162. package/dist/kb-mcp/tools/kb-doc-lookup.js.map +1 -0
  163. package/dist/kb-mcp/tools/kb-search.d.ts +23 -0
  164. package/dist/kb-mcp/tools/kb-search.d.ts.map +1 -0
  165. package/dist/kb-mcp/tools/kb-search.js +126 -0
  166. package/dist/kb-mcp/tools/kb-search.js.map +1 -0
  167. package/dist/knowledge-graph/call-resolver.d.ts +64 -0
  168. package/dist/knowledge-graph/call-resolver.d.ts.map +1 -0
  169. package/dist/knowledge-graph/call-resolver.js +323 -0
  170. package/dist/knowledge-graph/call-resolver.js.map +1 -0
  171. package/dist/knowledge-graph/import-resolver.d.ts +39 -0
  172. package/dist/knowledge-graph/import-resolver.d.ts.map +1 -0
  173. package/dist/knowledge-graph/import-resolver.js +214 -0
  174. package/dist/knowledge-graph/import-resolver.js.map +1 -0
  175. package/dist/knowledge-graph/incremental.d.ts +131 -0
  176. package/dist/knowledge-graph/incremental.d.ts.map +1 -0
  177. package/dist/knowledge-graph/incremental.js +432 -0
  178. package/dist/knowledge-graph/incremental.js.map +1 -0
  179. package/dist/knowledge-graph/index.d.ts +85 -0
  180. package/dist/knowledge-graph/index.d.ts.map +1 -0
  181. package/dist/knowledge-graph/index.js +198 -0
  182. package/dist/knowledge-graph/index.js.map +1 -0
  183. package/dist/knowledge-graph/module-derivation.d.ts +252 -0
  184. package/dist/knowledge-graph/module-derivation.d.ts.map +1 -0
  185. package/dist/knowledge-graph/module-derivation.js +418 -0
  186. package/dist/knowledge-graph/module-derivation.js.map +1 -0
  187. package/dist/knowledge-graph/persistence.d.ts +310 -0
  188. package/dist/knowledge-graph/persistence.d.ts.map +1 -0
  189. package/dist/knowledge-graph/persistence.js +280 -0
  190. package/dist/knowledge-graph/persistence.js.map +1 -0
  191. package/dist/knowledge-graph/query-helpers.d.ts +191 -0
  192. package/dist/knowledge-graph/query-helpers.d.ts.map +1 -0
  193. package/dist/knowledge-graph/query-helpers.js +559 -0
  194. package/dist/knowledge-graph/query-helpers.js.map +1 -0
  195. package/dist/knowledge-graph/relativize.d.ts +51 -0
  196. package/dist/knowledge-graph/relativize.d.ts.map +1 -0
  197. package/dist/knowledge-graph/relativize.js +104 -0
  198. package/dist/knowledge-graph/relativize.js.map +1 -0
  199. package/dist/knowledge-graph/unified-graph.d.ts +310 -0
  200. package/dist/knowledge-graph/unified-graph.d.ts.map +1 -0
  201. package/dist/knowledge-graph/unified-graph.js +182 -0
  202. package/dist/knowledge-graph/unified-graph.js.map +1 -0
  203. package/dist/mcp/agent-context-tools.d.ts +53 -0
  204. package/dist/mcp/agent-context-tools.d.ts.map +1 -0
  205. package/dist/mcp/agent-context-tools.js +806 -0
  206. package/dist/mcp/agent-context-tools.js.map +1 -0
  207. package/dist/mcp/file-nav-tools.d.ts +42 -0
  208. package/dist/mcp/file-nav-tools.d.ts.map +1 -0
  209. package/dist/mcp/file-nav-tools.js +318 -0
  210. package/dist/mcp/file-nav-tools.js.map +1 -0
  211. package/dist/mcp/graph-tools.d.ts +22 -0
  212. package/dist/mcp/graph-tools.d.ts.map +1 -1
  213. package/dist/mcp/graph-tools.js +204 -133
  214. package/dist/mcp/graph-tools.js.map +1 -1
  215. package/dist/mcp/lib/file-nav-helpers.d.ts +128 -0
  216. package/dist/mcp/lib/file-nav-helpers.d.ts.map +1 -0
  217. package/dist/mcp/lib/file-nav-helpers.js +305 -0
  218. package/dist/mcp/lib/file-nav-helpers.js.map +1 -0
  219. package/dist/mcp/lib/response-helpers.d.ts +67 -0
  220. package/dist/mcp/lib/response-helpers.d.ts.map +1 -0
  221. package/dist/mcp/lib/response-helpers.js +103 -0
  222. package/dist/mcp/lib/response-helpers.js.map +1 -0
  223. package/dist/mcp/lib/telemetry.d.ts +69 -0
  224. package/dist/mcp/lib/telemetry.d.ts.map +1 -0
  225. package/dist/mcp/lib/telemetry.js +113 -0
  226. package/dist/mcp/lib/telemetry.js.map +1 -0
  227. package/dist/mcp/lib/tool-response.d.ts +46 -0
  228. package/dist/mcp/lib/tool-response.d.ts.map +1 -0
  229. package/dist/mcp/lib/tool-response.js +83 -0
  230. package/dist/mcp/lib/tool-response.js.map +1 -0
  231. package/dist/mcp/server.d.ts +14 -1
  232. package/dist/mcp/server.d.ts.map +1 -1
  233. package/dist/mcp/server.js +216 -158
  234. package/dist/mcp/server.js.map +1 -1
  235. package/dist/models/call-site.d.ts +68 -0
  236. package/dist/models/call-site.d.ts.map +1 -0
  237. package/dist/models/call-site.js +60 -0
  238. package/dist/models/call-site.js.map +1 -0
  239. package/dist/models/code-skeleton.d.ts +80 -6
  240. package/dist/models/code-skeleton.d.ts.map +1 -1
  241. package/dist/models/code-skeleton.js +30 -0
  242. package/dist/models/code-skeleton.js.map +1 -1
  243. package/dist/models/drift-item.d.ts +4 -4
  244. package/dist/models/module-spec.d.ts +173 -94
  245. package/dist/models/module-spec.d.ts.map +1 -1
  246. package/dist/models/module-spec.js +6 -0
  247. package/dist/models/module-spec.js.map +1 -1
  248. package/dist/panoramic/adr-decision-pipeline.d.ts +53 -0
  249. package/dist/panoramic/adr-decision-pipeline.d.ts.map +1 -0
  250. package/dist/panoramic/adr-decision-pipeline.js +685 -0
  251. package/dist/panoramic/adr-decision-pipeline.js.map +1 -0
  252. package/dist/panoramic/api-surface-generator.d.ts +56 -0
  253. package/dist/panoramic/api-surface-generator.d.ts.map +1 -0
  254. package/dist/panoramic/api-surface-generator.js +1671 -0
  255. package/dist/panoramic/api-surface-generator.js.map +1 -0
  256. package/dist/panoramic/architecture-ir-builder.d.ts +19 -0
  257. package/dist/panoramic/architecture-ir-builder.d.ts.map +1 -0
  258. package/dist/panoramic/architecture-ir-builder.js +561 -0
  259. package/dist/panoramic/architecture-ir-builder.js.map +1 -0
  260. package/dist/panoramic/architecture-ir-exporters.d.ts +7 -0
  261. package/dist/panoramic/architecture-ir-exporters.d.ts.map +1 -0
  262. package/dist/panoramic/architecture-ir-exporters.js +183 -0
  263. package/dist/panoramic/architecture-ir-exporters.js.map +1 -0
  264. package/dist/panoramic/architecture-ir-generator.d.ts +36 -0
  265. package/dist/panoramic/architecture-ir-generator.d.ts.map +1 -0
  266. package/dist/panoramic/architecture-ir-generator.js +51 -0
  267. package/dist/panoramic/architecture-ir-generator.js.map +1 -0
  268. package/dist/panoramic/architecture-ir-mermaid-adapter.d.ts +6 -0
  269. package/dist/panoramic/architecture-ir-mermaid-adapter.d.ts.map +1 -0
  270. package/dist/panoramic/architecture-ir-mermaid-adapter.js +98 -0
  271. package/dist/panoramic/architecture-ir-mermaid-adapter.js.map +1 -0
  272. package/dist/panoramic/architecture-ir-model.d.ts +87 -0
  273. package/dist/panoramic/architecture-ir-model.d.ts.map +1 -0
  274. package/dist/panoramic/architecture-ir-model.js +20 -0
  275. package/dist/panoramic/architecture-ir-model.js.map +1 -0
  276. package/dist/panoramic/architecture-narrative.d.ts +68 -0
  277. package/dist/panoramic/architecture-narrative.d.ts.map +1 -0
  278. package/dist/panoramic/architecture-narrative.js +401 -0
  279. package/dist/panoramic/architecture-narrative.js.map +1 -0
  280. package/dist/panoramic/architecture-overview-generator.d.ts +31 -0
  281. package/dist/panoramic/architecture-overview-generator.d.ts.map +1 -0
  282. package/dist/panoramic/architecture-overview-generator.js +454 -0
  283. package/dist/panoramic/architecture-overview-generator.js.map +1 -0
  284. package/dist/panoramic/architecture-overview-model.d.ts +77 -0
  285. package/dist/panoramic/architecture-overview-model.d.ts.map +1 -0
  286. package/dist/panoramic/architecture-overview-model.js +31 -0
  287. package/dist/panoramic/architecture-overview-model.js.map +1 -0
  288. package/dist/panoramic/batch-project-docs.d.ts.map +1 -1
  289. package/dist/panoramic/batch-project-docs.js +4 -2
  290. package/dist/panoramic/batch-project-docs.js.map +1 -1
  291. package/dist/panoramic/builders/component-view-builder.d.ts +25 -1
  292. package/dist/panoramic/builders/component-view-builder.d.ts.map +1 -1
  293. package/dist/panoramic/builders/component-view-builder.js +83 -3
  294. package/dist/panoramic/builders/component-view-builder.js.map +1 -1
  295. package/dist/panoramic/builders/doc-graph-builder.d.ts +15 -2
  296. package/dist/panoramic/builders/doc-graph-builder.d.ts.map +1 -1
  297. package/dist/panoramic/builders/doc-graph-builder.js +20 -0
  298. package/dist/panoramic/builders/doc-graph-builder.js.map +1 -1
  299. package/dist/panoramic/component-view-builder.d.ts +17 -0
  300. package/dist/panoramic/component-view-builder.d.ts.map +1 -0
  301. package/dist/panoramic/component-view-builder.js +652 -0
  302. package/dist/panoramic/component-view-builder.js.map +1 -0
  303. package/dist/panoramic/component-view-model.d.ts +127 -0
  304. package/dist/panoramic/component-view-model.d.ts.map +1 -0
  305. package/dist/panoramic/component-view-model.js +65 -0
  306. package/dist/panoramic/component-view-model.js.map +1 -0
  307. package/dist/panoramic/config-reference-generator.d.ts +78 -0
  308. package/dist/panoramic/config-reference-generator.d.ts.map +1 -0
  309. package/dist/panoramic/config-reference-generator.js +199 -0
  310. package/dist/panoramic/config-reference-generator.js.map +1 -0
  311. package/dist/panoramic/coverage-auditor.d.ts +95 -0
  312. package/dist/panoramic/coverage-auditor.d.ts.map +1 -0
  313. package/dist/panoramic/coverage-auditor.js +290 -0
  314. package/dist/panoramic/coverage-auditor.js.map +1 -0
  315. package/dist/panoramic/cross-package-analyzer.d.ts +112 -0
  316. package/dist/panoramic/cross-package-analyzer.d.ts.map +1 -0
  317. package/dist/panoramic/cross-package-analyzer.js +258 -0
  318. package/dist/panoramic/cross-package-analyzer.js.map +1 -0
  319. package/dist/panoramic/data-model-generator.d.ts +528 -0
  320. package/dist/panoramic/data-model-generator.d.ts.map +1 -0
  321. package/dist/panoramic/data-model-generator.js +647 -0
  322. package/dist/panoramic/data-model-generator.js.map +1 -0
  323. package/dist/panoramic/doc-graph-builder.d.ts +80 -0
  324. package/dist/panoramic/doc-graph-builder.d.ts.map +1 -0
  325. package/dist/panoramic/doc-graph-builder.js +352 -0
  326. package/dist/panoramic/doc-graph-builder.js.map +1 -0
  327. package/dist/panoramic/docs-bundle-manifest-reader.d.ts +26 -0
  328. package/dist/panoramic/docs-bundle-manifest-reader.d.ts.map +1 -0
  329. package/dist/panoramic/docs-bundle-manifest-reader.js +106 -0
  330. package/dist/panoramic/docs-bundle-manifest-reader.js.map +1 -0
  331. package/dist/panoramic/docs-bundle-orchestrator.d.ts +14 -0
  332. package/dist/panoramic/docs-bundle-orchestrator.d.ts.map +1 -0
  333. package/dist/panoramic/docs-bundle-orchestrator.js +538 -0
  334. package/dist/panoramic/docs-bundle-orchestrator.js.map +1 -0
  335. package/dist/panoramic/docs-bundle-profiles.d.ts +4 -0
  336. package/dist/panoramic/docs-bundle-profiles.d.ts.map +1 -0
  337. package/dist/panoramic/docs-bundle-profiles.js +82 -0
  338. package/dist/panoramic/docs-bundle-profiles.js.map +1 -0
  339. package/dist/panoramic/docs-bundle-types.d.ts +94 -0
  340. package/dist/panoramic/docs-bundle-types.d.ts.map +1 -0
  341. package/dist/panoramic/docs-bundle-types.js +10 -0
  342. package/dist/panoramic/docs-bundle-types.js.map +1 -0
  343. package/dist/panoramic/docs-quality-evaluator.d.ts +31 -0
  344. package/dist/panoramic/docs-quality-evaluator.d.ts.map +1 -0
  345. package/dist/panoramic/docs-quality-evaluator.js +941 -0
  346. package/dist/panoramic/docs-quality-evaluator.js.map +1 -0
  347. package/dist/panoramic/docs-quality-model.d.ts +125 -0
  348. package/dist/panoramic/docs-quality-model.d.ts.map +1 -0
  349. package/dist/panoramic/docs-quality-model.js +166 -0
  350. package/dist/panoramic/docs-quality-model.js.map +1 -0
  351. package/dist/panoramic/dynamic-scenarios-builder.d.ts +18 -0
  352. package/dist/panoramic/dynamic-scenarios-builder.d.ts.map +1 -0
  353. package/dist/panoramic/dynamic-scenarios-builder.js +316 -0
  354. package/dist/panoramic/dynamic-scenarios-builder.js.map +1 -0
  355. package/dist/panoramic/event-surface-generator.d.ts +52 -0
  356. package/dist/panoramic/event-surface-generator.d.ts.map +1 -0
  357. package/dist/panoramic/event-surface-generator.js +507 -0
  358. package/dist/panoramic/event-surface-generator.js.map +1 -0
  359. package/dist/panoramic/generator-registry.d.ts +1 -1
  360. package/dist/panoramic/generator-registry.d.ts.map +1 -1
  361. package/dist/panoramic/generator-registry.js +5 -2
  362. package/dist/panoramic/generator-registry.js.map +1 -1
  363. package/dist/panoramic/generators/cross-package-analyzer.d.ts +5 -5
  364. package/dist/panoramic/generators/cross-package-analyzer.d.ts.map +1 -1
  365. package/dist/panoramic/generators/cross-package-analyzer.js +1 -1
  366. package/dist/panoramic/generators/cross-package-analyzer.js.map +1 -1
  367. package/dist/panoramic/generators/data-model-generator.d.ts +27 -27
  368. package/dist/panoramic/graph/confidence-mapper.d.ts +18 -0
  369. package/dist/panoramic/graph/confidence-mapper.d.ts.map +1 -1
  370. package/dist/panoramic/graph/confidence-mapper.js +26 -0
  371. package/dist/panoramic/graph/confidence-mapper.js.map +1 -1
  372. package/dist/panoramic/graph/graph-builder.d.ts +72 -2
  373. package/dist/panoramic/graph/graph-builder.d.ts.map +1 -1
  374. package/dist/panoramic/graph/graph-builder.js +330 -46
  375. package/dist/panoramic/graph/graph-builder.js.map +1 -1
  376. package/dist/panoramic/graph/graph-paths.d.ts +8 -0
  377. package/dist/panoramic/graph/graph-paths.d.ts.map +1 -1
  378. package/dist/panoramic/graph/graph-paths.js +10 -0
  379. package/dist/panoramic/graph/graph-paths.js.map +1 -1
  380. package/dist/panoramic/graph/graph-query.d.ts +49 -2
  381. package/dist/panoramic/graph/graph-query.d.ts.map +1 -1
  382. package/dist/panoramic/graph/graph-query.js +117 -19
  383. package/dist/panoramic/graph/graph-query.js.map +1 -1
  384. package/dist/panoramic/graph/graph-types.d.ts +32 -3
  385. package/dist/panoramic/graph/graph-types.d.ts.map +1 -1
  386. package/dist/panoramic/graph/index.d.ts +2 -1
  387. package/dist/panoramic/graph/index.d.ts.map +1 -1
  388. package/dist/panoramic/graph/index.js +1 -1
  389. package/dist/panoramic/graph/index.js.map +1 -1
  390. package/dist/panoramic/hyperedges/schema.d.ts +6 -6
  391. package/dist/panoramic/interface-surface-generator.d.ts +50 -0
  392. package/dist/panoramic/interface-surface-generator.d.ts.map +1 -0
  393. package/dist/panoramic/interface-surface-generator.js +388 -0
  394. package/dist/panoramic/interface-surface-generator.js.map +1 -0
  395. package/dist/panoramic/interfaces.d.ts +2 -2
  396. package/dist/panoramic/mock-readme-generator.d.ts +90 -0
  397. package/dist/panoramic/mock-readme-generator.d.ts.map +1 -0
  398. package/dist/panoramic/mock-readme-generator.js +111 -0
  399. package/dist/panoramic/mock-readme-generator.js.map +1 -0
  400. package/dist/panoramic/models/component-view-model.d.ts +1 -1
  401. package/dist/panoramic/models/component-view-model.d.ts.map +1 -1
  402. package/dist/panoramic/models/component-view-model.js.map +1 -1
  403. package/dist/panoramic/narrative-provenance-adapter.d.ts +8 -0
  404. package/dist/panoramic/narrative-provenance-adapter.d.ts.map +1 -0
  405. package/dist/panoramic/narrative-provenance-adapter.js +99 -0
  406. package/dist/panoramic/narrative-provenance-adapter.js.map +1 -0
  407. package/dist/panoramic/parser-registry.d.ts +1 -1
  408. package/dist/panoramic/parser-registry.js +1 -1
  409. package/dist/panoramic/parsers/types.d.ts +6 -6
  410. package/dist/panoramic/pattern-hints-generator.d.ts +28 -0
  411. package/dist/panoramic/pattern-hints-generator.d.ts.map +1 -0
  412. package/dist/panoramic/pattern-hints-generator.js +218 -0
  413. package/dist/panoramic/pattern-hints-generator.js.map +1 -0
  414. package/dist/panoramic/pattern-hints-model.d.ts +89 -0
  415. package/dist/panoramic/pattern-hints-model.d.ts.map +1 -0
  416. package/dist/panoramic/pattern-hints-model.js +61 -0
  417. package/dist/panoramic/pattern-hints-model.js.map +1 -0
  418. package/dist/panoramic/pattern-knowledge-base.d.ts +31 -0
  419. package/dist/panoramic/pattern-knowledge-base.d.ts.map +1 -0
  420. package/dist/panoramic/pattern-knowledge-base.js +407 -0
  421. package/dist/panoramic/pattern-knowledge-base.js.map +1 -0
  422. package/dist/panoramic/pipelines/adr-evidence-verifier.d.ts.map +1 -1
  423. package/dist/panoramic/pipelines/adr-evidence-verifier.js +1 -29
  424. package/dist/panoramic/pipelines/adr-evidence-verifier.js.map +1 -1
  425. package/dist/panoramic/pipelines/coverage-auditor.d.ts.map +1 -1
  426. package/dist/panoramic/pipelines/coverage-auditor.js +4 -2
  427. package/dist/panoramic/pipelines/coverage-auditor.js.map +1 -1
  428. package/dist/panoramic/pipelines/docs-bundle-orchestrator.js +13 -6
  429. package/dist/panoramic/pipelines/docs-bundle-orchestrator.js.map +1 -1
  430. package/dist/panoramic/pipelines/product-ux-docs.d.ts +5 -0
  431. package/dist/panoramic/pipelines/product-ux-docs.d.ts.map +1 -1
  432. package/dist/panoramic/pipelines/product-ux-docs.js +34 -6
  433. package/dist/panoramic/pipelines/product-ux-docs.js.map +1 -1
  434. package/dist/panoramic/product-ux-docs.d.ts +104 -0
  435. package/dist/panoramic/product-ux-docs.d.ts.map +1 -0
  436. package/dist/panoramic/product-ux-docs.js +817 -0
  437. package/dist/panoramic/product-ux-docs.js.map +1 -0
  438. package/dist/panoramic/qa/__tests__/citation.test.d.ts +2 -0
  439. package/dist/panoramic/qa/__tests__/citation.test.d.ts.map +1 -0
  440. package/dist/panoramic/qa/__tests__/citation.test.js +193 -0
  441. package/dist/panoramic/qa/__tests__/citation.test.js.map +1 -0
  442. package/dist/panoramic/qa/__tests__/debt-context.test.d.ts +2 -0
  443. package/dist/panoramic/qa/__tests__/debt-context.test.d.ts.map +1 -0
  444. package/dist/panoramic/qa/__tests__/debt-context.test.js +154 -0
  445. package/dist/panoramic/qa/__tests__/debt-context.test.js.map +1 -0
  446. package/dist/panoramic/qa/__tests__/graph-retriever.test.d.ts +2 -0
  447. package/dist/panoramic/qa/__tests__/graph-retriever.test.d.ts.map +1 -0
  448. package/dist/panoramic/qa/__tests__/graph-retriever.test.js +148 -0
  449. package/dist/panoramic/qa/__tests__/graph-retriever.test.js.map +1 -0
  450. package/dist/panoramic/qa/__tests__/index.test.d.ts +2 -0
  451. package/dist/panoramic/qa/__tests__/index.test.d.ts.map +1 -0
  452. package/dist/panoramic/qa/__tests__/index.test.js +246 -0
  453. package/dist/panoramic/qa/__tests__/index.test.js.map +1 -0
  454. package/dist/panoramic/qa/__tests__/llm-caller.test.d.ts +2 -0
  455. package/dist/panoramic/qa/__tests__/llm-caller.test.d.ts.map +1 -0
  456. package/dist/panoramic/qa/__tests__/llm-caller.test.js +168 -0
  457. package/dist/panoramic/qa/__tests__/llm-caller.test.js.map +1 -0
  458. package/dist/panoramic/qa/__tests__/prompt-builder.test.d.ts +2 -0
  459. package/dist/panoramic/qa/__tests__/prompt-builder.test.d.ts.map +1 -0
  460. package/dist/panoramic/qa/__tests__/prompt-builder.test.js +150 -0
  461. package/dist/panoramic/qa/__tests__/prompt-builder.test.js.map +1 -0
  462. package/dist/panoramic/qa/__tests__/qa-integration.test.d.ts +2 -0
  463. package/dist/panoramic/qa/__tests__/qa-integration.test.d.ts.map +1 -0
  464. package/dist/panoramic/qa/__tests__/qa-integration.test.js +296 -0
  465. package/dist/panoramic/qa/__tests__/qa-integration.test.js.map +1 -0
  466. package/dist/panoramic/qa/__tests__/rag-reranker.test.d.ts +2 -0
  467. package/dist/panoramic/qa/__tests__/rag-reranker.test.d.ts.map +1 -0
  468. package/dist/panoramic/qa/__tests__/rag-reranker.test.js +136 -0
  469. package/dist/panoramic/qa/__tests__/rag-reranker.test.js.map +1 -0
  470. package/dist/panoramic/qa/index.d.ts.map +1 -1
  471. package/dist/panoramic/qa/index.js +10 -3
  472. package/dist/panoramic/qa/index.js.map +1 -1
  473. package/dist/panoramic/query.d.ts +1 -0
  474. package/dist/panoramic/query.d.ts.map +1 -1
  475. package/dist/panoramic/query.js +5 -0
  476. package/dist/panoramic/query.js.map +1 -1
  477. package/dist/panoramic/runtime-topology-generator.d.ts +77 -0
  478. package/dist/panoramic/runtime-topology-generator.d.ts.map +1 -0
  479. package/dist/panoramic/runtime-topology-generator.js +676 -0
  480. package/dist/panoramic/runtime-topology-generator.js.map +1 -0
  481. package/dist/panoramic/runtime-topology-model.d.ts +145 -0
  482. package/dist/panoramic/runtime-topology-model.d.ts.map +1 -0
  483. package/dist/panoramic/runtime-topology-model.js +300 -0
  484. package/dist/panoramic/runtime-topology-model.js.map +1 -0
  485. package/dist/panoramic/troubleshooting-generator.d.ts +51 -0
  486. package/dist/panoramic/troubleshooting-generator.d.ts.map +1 -0
  487. package/dist/panoramic/troubleshooting-generator.js +451 -0
  488. package/dist/panoramic/troubleshooting-generator.js.map +1 -0
  489. package/dist/panoramic/utils/text-segmenter.d.ts +15 -0
  490. package/dist/panoramic/utils/text-segmenter.d.ts.map +1 -1
  491. package/dist/panoramic/utils/text-segmenter.js +38 -0
  492. package/dist/panoramic/utils/text-segmenter.js.map +1 -1
  493. package/dist/panoramic/workspace-index-generator.d.ts +148 -0
  494. package/dist/panoramic/workspace-index-generator.d.ts.map +1 -0
  495. package/dist/panoramic/workspace-index-generator.js +497 -0
  496. package/dist/panoramic/workspace-index-generator.js.map +1 -0
  497. package/dist/runtime-bootstrap.d.ts +9 -0
  498. package/dist/runtime-bootstrap.d.ts.map +1 -0
  499. package/dist/runtime-bootstrap.js +33 -0
  500. package/dist/runtime-bootstrap.js.map +1 -0
  501. package/dist/scaffold-kb/api-entities-serializer.d.ts +12 -0
  502. package/dist/scaffold-kb/api-entities-serializer.d.ts.map +1 -0
  503. package/dist/scaffold-kb/api-entities-serializer.js +144 -0
  504. package/dist/scaffold-kb/api-entities-serializer.js.map +1 -0
  505. package/dist/scaffold-kb/arbitration.d.ts +44 -0
  506. package/dist/scaffold-kb/arbitration.d.ts.map +1 -0
  507. package/dist/scaffold-kb/arbitration.js +144 -0
  508. package/dist/scaffold-kb/arbitration.js.map +1 -0
  509. package/dist/scaffold-kb/chunk-splitter.d.ts +20 -0
  510. package/dist/scaffold-kb/chunk-splitter.d.ts.map +1 -0
  511. package/dist/scaffold-kb/chunk-splitter.js +309 -0
  512. package/dist/scaffold-kb/chunk-splitter.js.map +1 -0
  513. package/dist/scaffold-kb/doc-graph-builder.d.ts +26 -0
  514. package/dist/scaffold-kb/doc-graph-builder.d.ts.map +1 -0
  515. package/dist/scaffold-kb/doc-graph-builder.js +72 -0
  516. package/dist/scaffold-kb/doc-graph-builder.js.map +1 -0
  517. package/dist/scaffold-kb/entity-extractor.d.ts +40 -0
  518. package/dist/scaffold-kb/entity-extractor.d.ts.map +1 -0
  519. package/dist/scaffold-kb/entity-extractor.js +0 -0
  520. package/dist/scaffold-kb/entity-extractor.js.map +1 -0
  521. package/dist/scaffold-kb/entity-heuristic.d.ts +14 -0
  522. package/dist/scaffold-kb/entity-heuristic.d.ts.map +1 -0
  523. package/dist/scaffold-kb/entity-heuristic.js +107 -0
  524. package/dist/scaffold-kb/entity-heuristic.js.map +1 -0
  525. package/dist/scaffold-kb/entity-matcher.d.ts +20 -0
  526. package/dist/scaffold-kb/entity-matcher.d.ts.map +1 -0
  527. package/dist/scaffold-kb/entity-matcher.js +53 -0
  528. package/dist/scaffold-kb/entity-matcher.js.map +1 -0
  529. package/dist/scaffold-kb/entity-util.d.ts +11 -0
  530. package/dist/scaffold-kb/entity-util.d.ts.map +1 -0
  531. package/dist/scaffold-kb/entity-util.js +23 -0
  532. package/dist/scaffold-kb/entity-util.js.map +1 -0
  533. package/dist/scaffold-kb/evidence-envelope.d.ts +17 -0
  534. package/dist/scaffold-kb/evidence-envelope.d.ts.map +1 -0
  535. package/dist/scaffold-kb/evidence-envelope.js +32 -0
  536. package/dist/scaffold-kb/evidence-envelope.js.map +1 -0
  537. package/dist/scaffold-kb/index.d.ts +20 -0
  538. package/dist/scaffold-kb/index.d.ts.map +1 -0
  539. package/dist/scaffold-kb/index.js +102 -0
  540. package/dist/scaffold-kb/index.js.map +1 -0
  541. package/dist/scaffold-kb/ingest/ingest-core.d.ts +47 -0
  542. package/dist/scaffold-kb/ingest/ingest-core.d.ts.map +1 -0
  543. package/dist/scaffold-kb/ingest/ingest-core.js +231 -0
  544. package/dist/scaffold-kb/ingest/ingest-core.js.map +1 -0
  545. package/dist/scaffold-kb/ingest/office-parser.d.ts +33 -0
  546. package/dist/scaffold-kb/ingest/office-parser.d.ts.map +1 -0
  547. package/dist/scaffold-kb/ingest/office-parser.js +187 -0
  548. package/dist/scaffold-kb/ingest/office-parser.js.map +1 -0
  549. package/dist/scaffold-kb/ingest/url-fetcher.d.ts +43 -0
  550. package/dist/scaffold-kb/ingest/url-fetcher.d.ts.map +1 -0
  551. package/dist/scaffold-kb/ingest/url-fetcher.js +245 -0
  552. package/dist/scaffold-kb/ingest/url-fetcher.js.map +1 -0
  553. package/dist/scaffold-kb/ingester.d.ts +38 -0
  554. package/dist/scaffold-kb/ingester.d.ts.map +1 -0
  555. package/dist/scaffold-kb/ingester.js +261 -0
  556. package/dist/scaffold-kb/ingester.js.map +1 -0
  557. package/dist/scaffold-kb/injection-format.d.ts +25 -0
  558. package/dist/scaffold-kb/injection-format.d.ts.map +1 -0
  559. package/dist/scaffold-kb/injection-format.js +55 -0
  560. package/dist/scaffold-kb/injection-format.js.map +1 -0
  561. package/dist/scaffold-kb/kb-writer.d.ts +13 -0
  562. package/dist/scaffold-kb/kb-writer.d.ts.map +1 -0
  563. package/dist/scaffold-kb/kb-writer.js +69 -0
  564. package/dist/scaffold-kb/kb-writer.js.map +1 -0
  565. package/dist/scaffold-kb/keyword-extract.d.ts +16 -0
  566. package/dist/scaffold-kb/keyword-extract.d.ts.map +1 -0
  567. package/dist/scaffold-kb/keyword-extract.js +49 -0
  568. package/dist/scaffold-kb/keyword-extract.js.map +1 -0
  569. package/dist/scaffold-kb/query-sanitizer.d.ts +28 -0
  570. package/dist/scaffold-kb/query-sanitizer.d.ts.map +1 -0
  571. package/dist/scaffold-kb/query-sanitizer.js +38 -0
  572. package/dist/scaffold-kb/query-sanitizer.js.map +1 -0
  573. package/dist/scaffold-kb/recall-eval.d.ts +48 -0
  574. package/dist/scaffold-kb/recall-eval.d.ts.map +1 -0
  575. package/dist/scaffold-kb/recall-eval.js +55 -0
  576. package/dist/scaffold-kb/recall-eval.js.map +1 -0
  577. package/dist/scaffold-kb/schema-compat.d.ts +22 -0
  578. package/dist/scaffold-kb/schema-compat.d.ts.map +1 -0
  579. package/dist/scaffold-kb/schema-compat.js +32 -0
  580. package/dist/scaffold-kb/schema-compat.js.map +1 -0
  581. package/dist/scaffold-kb/search-core.d.ts +42 -0
  582. package/dist/scaffold-kb/search-core.d.ts.map +1 -0
  583. package/dist/scaffold-kb/search-core.js +89 -0
  584. package/dist/scaffold-kb/search-core.js.map +1 -0
  585. package/dist/scaffold-kb/sqlite-engine.d.ts +58 -0
  586. package/dist/scaffold-kb/sqlite-engine.d.ts.map +1 -0
  587. package/dist/scaffold-kb/sqlite-engine.js +61 -0
  588. package/dist/scaffold-kb/sqlite-engine.js.map +1 -0
  589. package/dist/scaffold-kb/sqlite-writer.d.ts +18 -0
  590. package/dist/scaffold-kb/sqlite-writer.d.ts.map +1 -0
  591. package/dist/scaffold-kb/sqlite-writer.js +76 -0
  592. package/dist/scaffold-kb/sqlite-writer.js.map +1 -0
  593. package/dist/scaffold-kb/tokenizer.d.ts +26 -0
  594. package/dist/scaffold-kb/tokenizer.d.ts.map +1 -0
  595. package/dist/scaffold-kb/tokenizer.js +97 -0
  596. package/dist/scaffold-kb/tokenizer.js.map +1 -0
  597. package/dist/scaffold-kb/types.d.ts +157 -0
  598. package/dist/scaffold-kb/types.d.ts.map +1 -0
  599. package/dist/scaffold-kb/types.js +5 -0
  600. package/dist/scaffold-kb/types.js.map +1 -0
  601. package/dist/utils/file-scanner.d.ts +15 -0
  602. package/dist/utils/file-scanner.d.ts.map +1 -1
  603. package/dist/utils/file-scanner.js +18 -1
  604. package/dist/utils/file-scanner.js.map +1 -1
  605. package/dist/utils/string-distance.d.ts +20 -0
  606. package/dist/utils/string-distance.d.ts.map +1 -0
  607. package/dist/utils/string-distance.js +38 -0
  608. package/dist/utils/string-distance.js.map +1 -0
  609. package/package.json +22 -3
  610. package/plugins/demo-kb-en/.claude-plugin/plugin.json +11 -0
  611. package/plugins/demo-kb-en/.mcp.json +8 -0
  612. package/plugins/demo-kb-en/FIXTURE.json +23 -0
  613. package/plugins/demo-kb-en/FIXTURE.md +53 -0
  614. package/plugins/demo-kb-en/ingest-samples/meeting-notes.md +13 -0
  615. package/plugins/demo-kb-en/kb/api-entities.json +3445 -0
  616. package/plugins/demo-kb-en/kb/chunks.sqlite +0 -0
  617. package/plugins/demo-kb-en/kb/doc-graph.json +113 -0
  618. package/plugins/demo-kb-zh/.claude-plugin/plugin.json +11 -0
  619. package/plugins/demo-kb-zh/.mcp.json +8 -0
  620. package/plugins/demo-kb-zh/FIXTURE.json +23 -0
  621. package/plugins/demo-kb-zh/FIXTURE.md +63 -0
  622. package/plugins/demo-kb-zh/ingest-samples/meeting-notes.md +13 -0
  623. package/plugins/demo-kb-zh/kb/api-entities.json +990 -0
  624. package/plugins/demo-kb-zh/kb/chunks.sqlite +0 -0
  625. package/plugins/demo-kb-zh/kb/doc-graph.json +113 -0
  626. package/plugins/spec-driver/.claude-plugin/plugin.json +1 -1
  627. package/plugins/spec-driver/README.md +50 -1
  628. package/plugins/spec-driver/agents/implement.md +20 -1
  629. package/plugins/spec-driver/agents/plan.md +20 -1
  630. package/plugins/spec-driver/agents/quality-review.md +20 -1
  631. package/plugins/spec-driver/agents/spec-review.md +20 -1
  632. package/plugins/spec-driver/agents/verify.md +108 -1
  633. package/plugins/spec-driver/config/orchestration.yaml +39 -31
  634. package/plugins/spec-driver/contracts/orchestration-overrides-contract.yaml +27 -0
  635. package/plugins/spec-driver/contracts/orchestration-schema.mjs +272 -227
  636. package/plugins/spec-driver/contracts/wrapper-source-of-truth.yaml +3 -0
  637. package/plugins/spec-driver/docs/customization.md +134 -0
  638. package/plugins/spec-driver/docs/spectra-mcp-integration.md +165 -0
  639. package/plugins/spec-driver/hooks/hooks.json +9 -0
  640. package/plugins/spec-driver/hooks/stop-fix-compliance-check.sh +36 -0
  641. package/plugins/spec-driver/lib/delegation-contract.mjs +84 -0
  642. package/plugins/spec-driver/lib/orchestration-resolver.mjs +49 -0
  643. package/plugins/spec-driver/lib/orchestrator.mjs +29 -1
  644. package/plugins/spec-driver/lib/preference-rules.mjs +96 -0
  645. package/plugins/spec-driver/scripts/codex-skills.sh +28 -28
  646. package/plugins/spec-driver/scripts/dev/spike-fix-compliance-e2e.mjs +130 -0
  647. package/plugins/spec-driver/scripts/fix-compliance-judge.mjs +415 -0
  648. package/plugins/spec-driver/scripts/generate-adoption-insights.mjs +48 -1
  649. package/plugins/spec-driver/scripts/goal-loop-cli.mjs +307 -0
  650. package/plugins/spec-driver/scripts/init-project.sh +56 -0
  651. package/plugins/spec-driver/scripts/kb-prequery.mjs +125 -0
  652. package/plugins/spec-driver/scripts/lib/config-schema.mjs +200 -94
  653. package/plugins/spec-driver/scripts/lib/ensure-gitignore.sh +277 -0
  654. package/plugins/spec-driver/scripts/lib/extract-wrapper-body.mjs +136 -0
  655. package/plugins/spec-driver/scripts/lib/fix-compliance-core.mjs +434 -0
  656. package/plugins/spec-driver/scripts/lib/fix-compliance-io.mjs +335 -0
  657. package/plugins/spec-driver/scripts/lib/goal-loop-core.mjs +783 -0
  658. package/plugins/spec-driver/scripts/lib/init-project-output.sh +28 -0
  659. package/plugins/spec-driver/scripts/lib/load-zod.mjs +62 -0
  660. package/plugins/spec-driver/scripts/lib/project-profile-resolver.mjs +141 -49
  661. package/plugins/spec-driver/scripts/lib/project-profile-schema.mjs +88 -54
  662. package/plugins/spec-driver/scripts/postinstall.sh +13 -1
  663. package/plugins/spec-driver/scripts/record-workflow-run.mjs +75 -0
  664. package/plugins/spec-driver/scripts/sync-delegation-contract.mjs +187 -0
  665. package/plugins/spec-driver/scripts/sync-preference-rules.mjs +148 -0
  666. package/plugins/spec-driver/scripts/validate-orchestrator-models.mjs +156 -0
  667. package/plugins/spec-driver/scripts/validate-wrapper-sources.mjs +46 -1
  668. package/plugins/spec-driver/skills/spec-driver-feature/SKILL.md +302 -0
  669. package/plugins/spec-driver/skills/spec-driver-fix/SKILL.md +97 -8
  670. package/plugins/spec-driver/skills/spec-driver-implement/SKILL.md +18 -0
  671. package/plugins/spec-driver/skills/spec-driver-refactor/SKILL.md +10 -0
  672. package/plugins/spec-driver/skills/spec-driver-resume/SKILL.md +12 -1
  673. package/plugins/spec-driver/skills/spec-driver-story/SKILL.md +30 -0
  674. package/plugins/spec-driver/templates/delegation-contract.md +27 -0
  675. package/plugins/spec-driver/templates/goal-loop-override-template.yaml +284 -0
  676. package/plugins/spec-driver/templates/preference-rules.md +39 -0
  677. package/plugins/spec-driver/templates/specify-base/project-context-template.yaml +10 -0
  678. package/plugins/spectra/.claude-plugin/plugin.json +1 -1
  679. package/plugins/spectra/README.md +48 -1
  680. package/plugins/spectra/hooks/post-commit.sh +40 -0
  681. package/templates/module-spec.hbs +1 -0
  682. package/templates/product-overview.hbs +1 -1
@@ -8,7 +8,7 @@ import * as fs from 'node:fs';
8
8
  import * as path from 'node:path';
9
9
  import pLimit from 'p-limit';
10
10
  import { findReadmePath } from '../extraction/index.js';
11
- import { buildGraph } from '../graph/dependency-graph.js';
11
+ import { buildModuleGraphForProject } from '../knowledge-graph/module-derivation.js';
12
12
  import { generateSpec } from '../core/single-spec-orchestrator.js';
13
13
  import { generateIndex } from '../generator/index-generator.js';
14
14
  import { renderIndex, initRenderer } from '../generator/spec-renderer.js';
@@ -20,10 +20,11 @@ import { estimateModuleCost, buildDryRunReport, renderDryRunReport, runBudgetGat
20
20
  import { decideModelOverride } from './model-override-decision.js';
21
21
  import { createLogger } from '../panoramic/utils/logger.js';
22
22
  import { groupFilesToModules } from './module-grouper.js';
23
+ import { buildSpecCacheKey, normalizeProjectPath, resolveRegenPlan, resolveSourceTarget } from './regen-plan.js';
23
24
  import { groupFilesByLanguage } from './language-grouper.js';
24
- import { scanFiles } from '../utils/file-scanner.js';
25
+ import { scanFiles, createGitignoreFilter } from '../utils/file-scanner.js';
25
26
  import { LanguageAdapterRegistry } from '../adapters/language-adapter-registry.js';
26
- import { buildDocGraph, runAnchorIntegration, runHyperedgeIntegration, scanStoredModuleSpecs, } from '../panoramic/builders/doc-graph-builder.js';
27
+ import { buildDocGraph, isBatchGenerated, runAnchorIntegration, runHyperedgeIntegration, scanStoredModuleSpecs, } from '../panoramic/builders/doc-graph-builder.js';
27
28
  import { chunkMarkdownFiles } from '../panoramic/anchoring/chunker.js';
28
29
  import { createEmbeddingProvider } from '../panoramic/anchoring/providers/factory.js';
29
30
  import { DOC_NODE_KINDS } from '../panoramic/hyperedges/constants.js';
@@ -37,10 +38,13 @@ import { detectAuth } from '../auth/auth-detector.js';
37
38
  import { orchestrateDocsBundle } from '../panoramic/pipelines/docs-bundle-orchestrator.js';
38
39
  import { generateDebtIntelligence, } from '../panoramic/pipelines/debt-intelligence-pipeline.js';
39
40
  import { BATCH_OUTPUT_SUBDIRS } from '../panoramic/output-filenames.js';
40
- import { buildKnowledgeGraph, writeKnowledgeGraph } from '../panoramic/graph/index.js';
41
+ import { buildKnowledgeGraph, writeKnowledgeGraph, normalizeGraphForWrite } from '../panoramic/graph/index.js';
42
+ import { buildUnifiedGraph, setCurrentUnifiedGraph, getCurrentUnifiedGraph, } from '../knowledge-graph/index.js';
41
43
  import { buildHtmlTemplate } from '../panoramic/exporters/html-template.js';
42
44
  import { SpecStore } from '../spec-store/index.js';
43
45
  import { createRequire } from 'node:module';
46
+ import { resolvePythonImport } from '../knowledge-graph/import-resolver.js';
47
+ import { resolveTsJsImport, findNearestTsConfig, buildTsConfigContext, } from '../core/import-resolver.js';
44
48
  // 从 package.json 读取版本号(避免硬编码)
45
49
  const _require = createRequire(import.meta.url);
46
50
  const SPECTRA_VERSION = _require('../../package.json').version;
@@ -63,7 +67,40 @@ const logger = createLogger('batch-orchestrator');
63
67
  // 内部辅助函数
64
68
  // ============================================================
65
69
  /**
66
- * 合并多个语言的 DependencyGraph 用于全局拓扑排序
70
+ * Feature 182 修复面 4:checkpoint replace 语义(completed/failed 互斥去重)。
71
+ *
72
+ * 背景:F175 给 checkpoint 加了「mustRegen 失效重跑」(fall-through)语义,但 completedModules
73
+ * 仍沿用 append-only 写法——已完成 module 被重跑后会二次 push,导致 resume 进度超 totalModules,
74
+ * 且 completed / failed 可能交叉污染(同一 module 同时出现在两个集合)。
75
+ *
76
+ * 本 helper 以 path 为身份键先剔除两个集合中的同名旧条目,再 push 到目标集合,保证:
77
+ * (1) 同一 module 在每个集合最多出现一次;(2) completed 与 failed 互斥。
78
+ *
79
+ * 注意:helper 内不得有 await——JS 单线程下同步段不被 pLimit 并发交错,
80
+ * 保持纯同步可防未来插入 await 后语义退化为「读-改-写」竞态。
81
+ */
82
+ function upsertCompletedModule(state, entry) {
83
+ state.completedModules = state.completedModules.filter((m) => m.path !== entry.path);
84
+ state.failedModules = state.failedModules.filter((m) => m.path !== entry.path);
85
+ state.completedModules.push(entry);
86
+ }
87
+ function recordFailedModule(state, entry) {
88
+ state.failedModules = state.failedModules.filter((m) => m.path !== entry.path);
89
+ state.completedModules = state.completedModules.filter((m) => m.path !== entry.path);
90
+ state.failedModules.push(entry);
91
+ }
92
+ /**
93
+ * F175 FR-017/EC-009:判定 absPath 是否位于受管 modules/ 输出目录内(孤儿删除 ownership 必要条件2)。
94
+ *
95
+ * 用 path.relative 判定目录归属,禁用字符串 startsWith——否则 `specs/modules-old/...` 这类
96
+ * sibling 目录会被前缀匹配误判为受管目录(目录穿越)。同时校验 .spec.md 后缀。
97
+ */
98
+ function isInManagedOutputDir(absPath, modulesDir) {
99
+ const rel = path.relative(modulesDir, path.resolve(absPath));
100
+ return !rel.startsWith('..') && !path.isAbsolute(rel) && absPath.endsWith('.spec.md');
101
+ }
102
+ /**
103
+ * 合并多个语言的 ModuleGraph 用于全局拓扑排序
67
104
  * 仅合并 modules 和 edges,SCC/Mermaid 按语言独立保留(TD-002 选项 C)
68
105
  */
69
106
  export function mergeGraphsForTopologicalSort(graphs, projectRoot) {
@@ -170,7 +207,14 @@ export async function runBatch(projectRoot, options = {}) {
170
207
  const effectiveMode = options.mode ?? 'full';
171
208
  // F5:FR-006 — 日志输出当前 mode
172
209
  logger.info(`[info] batch mode: ${effectiveMode}`);
173
- const { force = false, incremental = false, maxRetries = 3, outputDir = 'specs', } = options;
210
+ const { maxRetries = 3, outputDir = 'specs', } = options;
211
+ // F175 FR-002:唯一默认值来源——对直接调用方兜底解析 RegenPlan(CLI/MCP 已在各自入口解析后传入有效值)。
212
+ // 删除原 `incremental = false` 硬编码,默认增量(regenPlan.incremental)由 resolveRegenPlan 决定(FR-001)。
213
+ const regenPlan = resolveRegenPlan({
214
+ incremental: options.incremental,
215
+ full: options.full,
216
+ force: options.force,
217
+ });
174
218
  const startTime = Date.now();
175
219
  const resolvedRoot = path.resolve(projectRoot);
176
220
  const resolvedOutputDir = path.isAbsolute(outputDir)
@@ -185,7 +229,6 @@ export async function runBatch(projectRoot, options = {}) {
185
229
  const rel = path.relative(resolvedRoot, absPath);
186
230
  return rel.startsWith('..') ? absPath : rel;
187
231
  };
188
- const normalizeProjectPath = (inputPath) => inputPath.split(path.sep).join('/');
189
232
  // 步骤 1:扫描文件获取 languageStats
190
233
  const scanResult = scanFiles(resolvedRoot, { projectRoot: resolvedRoot });
191
234
  const languageStats = scanResult.languageStats;
@@ -214,14 +257,14 @@ export async function runBatch(projectRoot, options = {}) {
214
257
  // 步骤 1.7:合并拓扑排序
215
258
  mergedGraph = perLangGraphs.length > 0
216
259
  ? mergeGraphsForTopologicalSort(perLangGraphs, resolvedRoot)
217
- : await buildGraph(resolvedRoot);
260
+ : await buildModuleGraphForProject(resolvedRoot);
218
261
  }
219
262
  else if (isSingleNonTsJs && languageGroupsList[0]) {
220
263
  mergedGraph = await buildGraphForLanguageGroup(languageGroupsList[0], resolvedRoot);
221
264
  }
222
265
  else {
223
- // 纯 TS/JS 或未识别受支持语言:保持 dependency-cruiser 现有路径
224
- mergedGraph = await buildGraph(resolvedRoot);
266
+ // 纯 TS/JS 或未识别受支持语言:走 UnifiedGraph 派生路径(Feature 156 删除 dependency-cruiser
267
+ mergedGraph = await buildModuleGraphForProject(resolvedRoot);
225
268
  }
226
269
  // 步骤 2:文件→模块聚合 + 模块级拓扑排序
227
270
  const groupingOptions = {
@@ -235,42 +278,34 @@ export async function runBatch(projectRoot, options = {}) {
235
278
  const moduleGroups = new Map(groupResult.groups.map((g) => [g.name, g]));
236
279
  const rootModuleName = options.grouping?.rootModuleName ?? 'root';
237
280
  const existingStoredSpecs = scanStoredModuleSpecs(resolvedOutputDir, resolvedRoot);
238
- const storedSpecByTarget = new Map(existingStoredSpecs.map((spec) => [spec.sourceTarget, spec]));
281
+ // Feature 182:以 cache key 入索引(languageSplit 组带 `::language` 后缀),与 processOneModule
282
+ // 的 moduleCacheKey 口径对齐;旧 spec 无 sourceTargetKey 时回落纯路径 sourceTarget。
283
+ const storedSpecByTarget = new Map(existingStoredSpecs.map((spec) => [spec.sourceTargetKey ?? spec.sourceTarget, spec]));
284
+ // F175:全量逃生口(--full / --force 合并为 regenPlan.full)打可观测日志,
285
+ // 替代原 fallbackReason='force-enabled' 内部信号(W-1 取舍)。
286
+ if (regenPlan.full) {
287
+ logger.info(`[regen] full regeneration (source=${regenPlan.source})`);
288
+ }
289
+ // F175:仅增量路径(regenPlan.incremental)调用 DeltaRegenerator;full 路径直接走全量。
239
290
  let deltaReport;
240
- if (incremental) {
241
- if (force) {
242
- deltaReport = {
243
- title: 'Delta Regeneration Report',
244
- generatedAt: new Date().toISOString(),
245
- projectRoot: resolvedRoot,
246
- mode: 'full',
247
- totalTargets: groupResult.groups.reduce((sum, group) => {
248
- const isRootGroup = group.name === rootModuleName || group.name.startsWith(`${rootModuleName}--`);
249
- return sum + (isRootGroup ? group.files.length : 1);
250
- }, 0),
251
- regenerateTargets: [],
252
- directChanges: [],
253
- propagatedChanges: [],
254
- unchangedTargets: [],
255
- fallbackReason: 'force-enabled',
256
- };
257
- }
258
- else {
259
- const deltaRegenerator = new DeltaRegenerator();
260
- deltaReport = await deltaRegenerator.plan({
261
- projectRoot: resolvedRoot,
262
- dependencyGraph: mergedGraph,
263
- moduleGroups: groupResult.groups,
264
- storedSpecs: existingStoredSpecs,
265
- // Bug 142:传入 effectiveMode 启用 mode-aware cache,
266
- // 旧 spec(无 generatedByMode)或 mode 不匹配时强制 cache miss。
267
- effectiveMode,
268
- });
269
- }
291
+ if (regenPlan.incremental) {
292
+ const deltaRegenerator = new DeltaRegenerator();
293
+ deltaReport = await deltaRegenerator.plan({
294
+ projectRoot: resolvedRoot,
295
+ dependencyGraph: mergedGraph,
296
+ moduleGroups: groupResult.groups,
297
+ storedSpecs: existingStoredSpecs,
298
+ // Bug 142:传入 effectiveMode 启用 mode-aware cache,
299
+ // spec(无 generatedByMode)或 mode 不匹配时强制 cache miss。
300
+ effectiveMode,
301
+ });
270
302
  }
271
303
  const regenerateTargets = new Set(deltaReport?.regenerateTargets ?? []);
272
- const forceFullRegeneration = force || (incremental && deltaReport?.mode === 'full');
273
- const shouldUseIncrementalPlan = incremental && !force && deltaReport?.mode === 'incremental';
304
+ // EC-001 force/full 优先:regenPlan.full 直接全量;增量路径 deltaReport.mode==='full'
305
+ //(首次运行 / 无历史 spec / mode 切换)退化全量仍走 forceFullRegeneration。
306
+ // Feature 182:改为 let——full-resume 时序修复需在 checkpoint 加载后回写(见下方 isResume 块)。
307
+ let forceFullRegeneration = regenPlan.full || (regenPlan.incremental && deltaReport?.mode === 'full');
308
+ let shouldUseIncrementalPlan = regenPlan.incremental && deltaReport?.mode === 'incremental';
274
309
  console.log(`发现 ${mergedGraph.modules.length} 个文件,聚合为 ${processingOrder.length} 个模块`);
275
310
  if (isMultiLang) {
276
311
  console.log(`检测到 ${processedLanguages.length} 种语言: ${processedLanguages.join(', ')}`);
@@ -361,6 +396,18 @@ export async function runBatch(projectRoot, options = {}) {
361
396
  }
362
397
  // 步骤 3:检查是否存在检查点
363
398
  let state = loadCheckpoint(checkpointPath);
399
+ // F175 FR-016/EC-007:full/force 启动时丢弃已加载的 checkpoint completed + failed state,
400
+ // 防止残留 checkpoint 命中导致全量语义被绕过(必须在 completedPaths 构建前清空——
401
+ // 加载时即清,而非在 processOneModule 内"忽略",避免中途崩溃 resume 复用脏 set)。
402
+ // W-1:必须同时清空 failedModules——否则 full 后 summary(:1749 起用 checkedState.failedModules)
403
+ // 仍混入上一轮的旧失败详情,与"full 是干净全量"语义矛盾。
404
+ if (state &&
405
+ regenPlan.full &&
406
+ (state.completedModules.length > 0 || state.failedModules.length > 0)) {
407
+ logger.info(`[regen] full regeneration 丢弃残留 checkpoint state(completed ${state.completedModules.length} / failed ${state.failedModules.length} 模块)`);
408
+ state = { ...state, completedModules: [], failedModules: [] };
409
+ saveCheckpoint(state, checkpointPath);
410
+ }
364
411
  const isResume = state !== null;
365
412
  if (!state) {
366
413
  state = {
@@ -372,7 +419,7 @@ export async function runBatch(projectRoot, options = {}) {
372
419
  processingOrder,
373
420
  completedModules: [],
374
421
  failedModules: [],
375
- forceRegenerate: force,
422
+ forceRegenerate: regenPlan.full,
376
423
  // 多语言扩展字段
377
424
  languageGroups: isMultiLang
378
425
  ? Object.fromEntries(languageGroupsList.map((g) => [g.adapterId, g.files]))
@@ -382,6 +429,17 @@ export async function runBatch(projectRoot, options = {}) {
382
429
  }
383
430
  if (isResume) {
384
431
  console.log(`恢复断点: 已完成 ${state.completedModules.length}/${state.totalModules} 模块`);
432
+ // Feature 182 修复面 5:恢复中断的 --full run 意图。
433
+ // 背景:首轮 --full 把 forceRegenerate=true 落盘到 checkpoint(:660),但 runtime 仅用本轮
434
+ // regenPlan 重算的 forceFullRegeneration——裸增量 resume 时为 false,剩余模块静默降级为增量,
435
+ // 产出半新半旧混合产物。此处从 checkpoint 恢复 full 意图,剩余模块继续全量。
436
+ // 时序安全:两变量消费点全在 processOneModule 内(在此回写之后才被调用)。
437
+ // 注意:full-resume 不清空 completed(:636 清空条件用 regenPlan.full 而非本变量),正是 resume 语义。
438
+ if (state.forceRegenerate && !forceFullRegeneration) {
439
+ forceFullRegeneration = true;
440
+ shouldUseIncrementalPlan = false;
441
+ logger.info('[resume] 检测到中断的 full run,剩余模块继续全量');
442
+ }
385
443
  }
386
444
  // 步骤 4:按模块级拓扑顺序处理
387
445
  // Feature 146 FR-010/FR-011:通过 getActiveCount getter 注入 limit.activeCount,
@@ -441,16 +499,30 @@ export async function runBatch(projectRoot, options = {}) {
441
499
  const group = moduleGroups.get(moduleName);
442
500
  if (!group)
443
501
  return;
444
- if (completedPaths.has(moduleName))
445
- return;
446
502
  const isRoot = moduleName === rootModuleName || moduleName.startsWith(`${rootModuleName}--`);
447
503
  const specPath = path.join(modulesDir, `${moduleName}.spec.md`);
448
- // H4 修复:文件级降级场景下 moduleSourceTarget 须与 targetPath 保持一致(文件路径)
449
- // 否则 --incremental 的 regenerateTargets 查询和 storedSpecByTarget 查询全部错位
450
- const hasDirPathConflict = !isRoot && group.files.length === 1 && conflictingDirPaths.has(group.dirPath);
451
- const moduleSourceTarget = hasDirPathConflict
452
- ? normalizeProjectPath(group.files[0])
453
- : normalizeProjectPath(group.dirPath);
504
+ // FR-019:target 口径与 DeltaRegenerator 共用 resolveSourceTarget(文件级降级分支一致),
505
+ // 否则 --incremental 的 regenerateTargets 查询和 storedSpecByTarget 查询全部错位。
506
+ // T020:target 计算前移到 checkpoint 判定之前,供增量 resume 失效判定使用。
507
+ const moduleSourceTarget = resolveSourceTarget(group, conflictingDirPaths, isRoot);
508
+ // Feature 182:cache key(languageSplit 组带 `::language` 后缀),用于 regenerateTargets /
509
+ // storedSpecByTarget / existingVersion 查询,消除同目录多语言组键碰撞;targetPath 与
510
+ // frontmatter 继续用纯路径 moduleSourceTarget(statSync 路径 + panoramic 匹配口径不变)。
511
+ const moduleCacheKey = buildSpecCacheKey(moduleSourceTarget, group);
512
+ // F175 FR-016:checkpoint 判定。full 路径已在加载时清空 completedPaths(必为干净);
513
+ // 增量 resume 下若 checkpoint 命中但本轮 delta 要求重生成该 target,则失效重跑。
514
+ // C-3:root 模块的 regenerateTargets 是文件级(DeltaRegenerator 对 root 按每个文件
515
+ // 产出 snapshot),但 moduleSourceTarget(root 时为 group.dirPath)不在文件级集合里,
516
+ // 用 dirPath 查永远 miss → mustRegen=false → root 文件变更时 checkpoint 命中被错误跳过。
517
+ // 故 root 模块改判 group.files 中任一文件 target 是否命中 regenerateTargets。
518
+ if (completedPaths.has(moduleName)) {
519
+ const mustRegen = shouldUseIncrementalPlan &&
520
+ (isRoot
521
+ ? group.files.some((filePath) => regenerateTargets.has(normalizeProjectPath(filePath)))
522
+ : regenerateTargets.has(moduleCacheKey));
523
+ if (!mustRegen)
524
+ return;
525
+ }
454
526
  const rootTargetsToGenerate = isRoot
455
527
  ? group.files
456
528
  .map((filePath) => normalizeProjectPath(filePath))
@@ -473,7 +545,7 @@ export async function runBatch(projectRoot, options = {}) {
473
545
  else {
474
546
  const shouldGenerate = forceFullRegeneration
475
547
  || (shouldUseIncrementalPlan
476
- ? regenerateTargets.has(moduleSourceTarget)
548
+ ? regenerateTargets.has(moduleCacheKey)
477
549
  : !fs.existsSync(specPath));
478
550
  if (!shouldGenerate) {
479
551
  skipped.push(moduleName);
@@ -587,24 +659,32 @@ export async function runBatch(projectRoot, options = {}) {
587
659
  }
588
660
  successful.push(moduleName);
589
661
  reporter.complete(moduleName, 'success');
590
- checkedState.completedModules.push({
662
+ // Feature 182:replace 语义(失效重跑时去重 + completed/failed 互斥)
663
+ upsertCompletedModule(checkedState, {
591
664
  path: moduleName,
592
665
  specPath: generatedRootSpecs[0],
593
666
  completedAt: new Date().toISOString(),
594
667
  });
595
668
  }
596
669
  else {
597
- // BUG-A 修复:同一 dirPath 下有多个单文件模块时(如 graphify/ 下有 a.py/b.py),
598
- // 使用文件路径避免多个模块覆盖同一个 {dirName}.spec.md;
599
- // 否则仍使用目录路径(每个目录只有一个文件时,目录名才是有意义的模块标识)
600
- // 注意:hasDirPathConflict 已在上方计算(H4 修复)
601
- const targetPath = hasDirPathConflict
602
- ? path.join(resolvedRoot, group.files[0])
603
- : path.join(resolvedRoot, group.dirPath);
670
+ // BUG-A / H4 修复:targetPath 直接由 moduleSourceTarget 推导(FR-019:resolveSourceTarget
671
+ // 已处理"同一 dirPath 多单文件冲突走文件路径"的降级分支),与 sourceTarget 口径保持一致。
672
+ const targetPath = path.join(resolvedRoot, moduleSourceTarget);
604
673
  // genOptions.skipEnrichment 已在 L648 处理 reading/code-only 模式分派
605
674
  const result = await generateSpec(targetPath, {
606
675
  ...genOptions,
607
- existingVersion: storedSpecByTarget.get(moduleSourceTarget)?.version,
676
+ // Feature 182:注入 group.files(语言限定子集,绝对路径),使写侧只分析本语言文件,
677
+ // 与读侧 group.files 文件集口径一致(消除混语言 hash 分叉 + 双倍分析)。
678
+ files: group.files.map((f) => path.join(resolvedRoot, f)),
679
+ existingVersion: storedSpecByTarget.get(moduleCacheKey)?.version,
680
+ // Feature 182 修复 1:仅 languageSplit 组按 moduleName 显式命名输出文件,
681
+ // 避免同目录多语言组(ts / py)都派生为同名 `<dir>.spec.md` 互相覆盖;
682
+ // 非拆分组不传 → basename 派生,存量单语言仓库命名零变化。
683
+ ...(group.languageSplit ? { outputFileName: `${moduleName}.spec.md` } : {}),
684
+ // Feature 182 修复 2:languageSplit 组首写即落入 frontmatter.sourceTargetKey,
685
+ // 消除「post-mutation 依赖 doc-graph re-render 落盘晚于 checkpoint save」崩溃窗口;
686
+ // in-memory moduleSpec.frontmatter 由 generateFrontmatter 返回自然带上。
687
+ sourceTargetKey: group.languageSplit ? moduleCacheKey : undefined,
608
688
  });
609
689
  // Bug 142:非 root 分支累积 input token,供 catch 块统一做预算检查。
610
690
  if (result.costMetadata?.tokenUsage.input) {
@@ -629,7 +709,8 @@ export async function runBatch(projectRoot, options = {}) {
629
709
  successful.push(moduleName);
630
710
  reporter.complete(moduleName, 'success');
631
711
  }
632
- checkedState.completedModules.push({
712
+ // Feature 182:replace 语义(失效重跑时去重 + completed/failed 互斥)
713
+ upsertCompletedModule(checkedState, {
633
714
  path: moduleName,
634
715
  specPath: toProjectPath(path.resolve(result.specPath)),
635
716
  completedAt: new Date().toISOString(),
@@ -684,7 +765,8 @@ export async function runBatch(projectRoot, options = {}) {
684
765
  reason: 'retry-budget-exceeded',
685
766
  };
686
767
  failed.push(failedModule);
687
- checkedState.failedModules.push(failedModule);
768
+ // Feature 182:replace 语义(失效重跑时去重 + completed/failed 互斥)
769
+ recordFailedModule(checkedState, failedModule);
688
770
  reporter.complete(moduleName, 'failed');
689
771
  break;
690
772
  }
@@ -698,7 +780,8 @@ export async function runBatch(projectRoot, options = {}) {
698
780
  degradedToAstOnly: false,
699
781
  };
700
782
  failed.push(failedModule);
701
- checkedState.failedModules.push(failedModule);
783
+ // Feature 182:replace 语义(失效重跑时去重 + completed/failed 互斥)
784
+ recordFailedModule(checkedState, failedModule);
702
785
  reporter.complete(moduleName, 'failed');
703
786
  }
704
787
  }
@@ -743,7 +826,8 @@ export async function runBatch(projectRoot, options = {}) {
743
826
  reason: 'unhandled-exception',
744
827
  };
745
828
  failed.push(failedModule);
746
- checkedState.failedModules.push(failedModule);
829
+ // Feature 182:replace 语义(失效重跑时去重 + completed/failed 互斥)
830
+ recordFailedModule(checkedState, failedModule);
747
831
  try {
748
832
  reporter.complete(moduleName, 'failed');
749
833
  }
@@ -762,10 +846,63 @@ export async function runBatch(projectRoot, options = {}) {
762
846
  let projectDocsResult;
763
847
  let docsBundleManifestPath;
764
848
  let docsBundleProfiles;
765
- // SpecStore 统一查询入口:所有消费方(README、graph、coverage、index、cross-ref)共享此源
849
+ // F175 FR-017/EC-008/EC-009:删除孤儿 spec(源文件已删除的 batch 产物),使增量产物文件集与全量一致。
850
+ // 必须在构造 SpecStore 之前执行 + 从 storedSpecs 集合中剔除已删项(C-2)——否则后续
851
+ // 所有聚合(index/graph/coverage/cross-ref)仍引用陈旧的已删 spec 视图,产生 stale 输出。
852
+ // 仅在增量/全量路径执行;ownership 边界缺一不删:(1) generatedByMode 存在(batch 专属标记)+
853
+ // (2) 位于受管 modules/ 输出目录内(path.relative 防目录穿越,禁字符串 startsWith)+
854
+ // (3) sourceTarget 已无任何在库源文件(EC-008:删除模块最后一个源文件后,空目录仍 existsSync=true,
855
+ // 故不能仅靠目录存在性判定——以当前扫描到的源文件集合为准)。
856
+ let storedSpecsForStore = existingStoredSpecs;
857
+ if (regenPlan.incremental || regenPlan.full) {
858
+ // 当前扫描到的源文件(项目相对,正斜杠口径),用于判定 sourceTarget 是否仍有活跃源文件
859
+ const liveSourceFiles = new Set(scanResult.files.map((f) => normalizeProjectPath(f)));
860
+ const hasLiveSource = (sourceTarget) => {
861
+ const prefix = `${sourceTarget}/`;
862
+ for (const f of liveSourceFiles) {
863
+ if (f === sourceTarget || f.startsWith(prefix))
864
+ return true;
865
+ }
866
+ return false;
867
+ };
868
+ // 已确认删除的孤儿 outputPath 集合,用于从 storedSpecs 中剔除,使 SpecStore 不持有陈旧视图。
869
+ const deletedOrphanPaths = new Set();
870
+ for (const orphan of existingStoredSpecs) {
871
+ if (!isBatchGenerated(orphan))
872
+ continue; // 必要条件1:无 generatedByMode → 跳过(防误删手写 / 单文件 generate 产物)
873
+ if (hasLiveSource(orphan.sourceTarget))
874
+ continue; // 必要条件3:仍有活跃源文件 → 非孤儿
875
+ const absPath = path.isAbsolute(orphan.outputPath)
876
+ ? orphan.outputPath
877
+ : path.join(resolvedRoot, orphan.outputPath);
878
+ if (!isInManagedOutputDir(absPath, modulesDir))
879
+ continue; // 必要条件2:受管 modules/ 目录外 → 跳过
880
+ // 磁盘文件已被本轮其它逻辑清理:视为已删除,从内存视图剔除(保持内存与磁盘一致)。
881
+ if (!fs.existsSync(absPath)) {
882
+ deletedOrphanPaths.add(orphan.outputPath);
883
+ continue;
884
+ }
885
+ // W-1(quality-review):rmSync 是破坏性新操作,权限/OS 错误不应让整个 batch 崩溃。
886
+ // 失败时 warn + 跳过,且【不】加入 deletedOrphanPaths——文件仍在磁盘,内存视图须保留它,
887
+ // 避免"内存说已删、磁盘还在"的不一致。仅删除成功才剔除内存视图。
888
+ try {
889
+ logger.info(`[orphan-cleanup] 删除孤儿 spec: ${orphan.outputPath}`);
890
+ fs.rmSync(absPath, { force: true });
891
+ deletedOrphanPaths.add(orphan.outputPath);
892
+ }
893
+ catch (err) {
894
+ logger.warn(`[orphan-cleanup] 删除孤儿 spec 失败,保守跳过: ${orphan.outputPath}(${err.message})`);
895
+ }
896
+ }
897
+ if (deletedOrphanPaths.size > 0) {
898
+ storedSpecsForStore = existingStoredSpecs.filter((spec) => !deletedOrphanPaths.has(spec.outputPath));
899
+ }
900
+ }
901
+ // SpecStore 统一查询入口:所有消费方(README、graph、coverage、index、cross-ref)共享此源。
902
+ // 使用已剔除孤儿的 storedSpecsForStore,确保后续聚合不引用已删 spec(C-2)。
766
903
  const specStore = new SpecStore({
767
904
  currentSpecs: collectedModuleSpecs,
768
- storedSpecs: existingStoredSpecs,
905
+ storedSpecs: storedSpecsForStore,
769
906
  projectRoot: resolvedRoot,
770
907
  toProjectPath,
771
908
  });
@@ -824,6 +961,34 @@ export async function runBatch(projectRoot, options = {}) {
824
961
  logger.warn(`README 早期读取失败(不阻断 narrative 主流程): ${String(readmeErr)}`);
825
962
  }
826
963
  }
964
+ // Feature 151 Codex Final C-1 修订 — 在 generateBatchProjectDocs 之前构建 UnifiedGraph
965
+ // 让 component-view-builder 能在生成时获取 graph(DI provider getCurrentUnifiedGraph)
966
+ //
967
+ // Feature 152 Codex final C-1 修复 — 同时合并 Python + TS/JS codeSkeletons,
968
+ // 否则用户跑 spectra batch 后 TS 项目 graph.json 中没有 calls 边(US-001 失败)
969
+ setCurrentUnifiedGraph(null);
970
+ try {
971
+ const pythonSkeletons = await collectPythonCodeSkeletons(resolvedRoot);
972
+ const tsJsSkeletons = await collectTsJsCodeSkeletons(resolvedRoot, {
973
+ extractCallSites: true,
974
+ });
975
+ // 合并两个 Map(projectRoot 都已 normalize 为绝对路径,key 形态一致)
976
+ const earlyCodeSkeletons = new Map([...pythonSkeletons, ...tsJsSkeletons]);
977
+ if (earlyCodeSkeletons.size > 0) {
978
+ const earlyUg = buildUnifiedGraph({
979
+ projectRoot: resolvedRoot,
980
+ codeSkeletons: earlyCodeSkeletons,
981
+ });
982
+ setCurrentUnifiedGraph(earlyUg);
983
+ const callEdges = earlyUg.edges.filter((e) => e.relation === 'calls').length;
984
+ logger.info(`[Feature 151+152] 早期 UnifiedGraph 构建:${earlyUg.nodes.length} 节点 / ${callEdges} calls 边 ` +
985
+ `(${pythonSkeletons.size} .py + ${tsJsSkeletons.size} .ts/.tsx/.js/.jsx 文件)`);
986
+ }
987
+ }
988
+ catch (ugErr) {
989
+ setCurrentUnifiedGraph(null);
990
+ logger.warn(`[Feature 151+152] 早期 UnifiedGraph 构建失败(不阻塞): ${String(ugErr)}`);
991
+ }
827
992
  try {
828
993
  projectDocsResult = await generateBatchProjectDocs({
829
994
  projectRoot: resolvedRoot,
@@ -913,11 +1078,22 @@ export async function runBatch(projectRoot, options = {}) {
913
1078
  ...pythonSymbolResults,
914
1079
  ...(extractionResults ?? []),
915
1080
  ];
1081
+ // Feature 151 — UnifiedGraph 接入 graph-builder 第五路(早期已在 generateBatchProjectDocs 之前构建,此处复用 cache)
1082
+ // 复用早期构建结果(Codex Final C-1 修订):cache 在 generateBatchProjectDocs 之前已 set
1083
+ const unifiedGraph = getCurrentUnifiedGraph();
1084
+ if (unifiedGraph) {
1085
+ const callEdgeCount = unifiedGraph.edges.filter((e) => e.relation === 'calls').length;
1086
+ const dependEdgeCount = unifiedGraph.edges.filter((e) => e.relation === 'depends-on').length;
1087
+ logger.info(`[Feature 151] graph-builder 注入 UnifiedGraph:${unifiedGraph.nodes.length} 节点,` +
1088
+ `${callEdgeCount} calls 边,${dependEdgeCount} depends-on 边`);
1089
+ }
916
1090
  const graphJson = buildKnowledgeGraph({
917
1091
  architectureIR: projectDocsResult?.architectureIR,
918
1092
  docGraph,
919
1093
  crossReferenceLinks,
920
1094
  extractionResults: mergedResults.length > 0 ? mergedResults : undefined, // Feature 107 + 143 第四路数据源
1095
+ // Feature 151 T-012a:UnifiedGraph 第五路(calls + depends-on 边 + per-file callSitesCount metadata)
1096
+ ...(unifiedGraph ? { unifiedGraph } : {}),
921
1097
  });
922
1098
  // Feature 133 P1-1(adversarial-review post-fix):anchor + hyperedge 集成
923
1099
  // F4 提供了 runAnchorIntegration / runHyperedgeIntegration 集成函数,
@@ -1074,6 +1250,11 @@ export async function runBatch(projectRoot, options = {}) {
1074
1250
  catch (communityErr) {
1075
1251
  logger.warn(`community-analysis: 社区分析失败,跳过报告生成: ${communityErr instanceof Error ? communityErr.message : String(communityErr)}`);
1076
1252
  }
1253
+ // F175 FR-006/FR-007:写盘前归一化(byte-stable)——nodes/links/hyperedges 确定性排序,
1254
+ // 在追加 semantic edges + 社区分析之后调用,覆盖完整边集。
1255
+ // F179:stripTimestamps:true 使 graph.generatedAt 固定为 epoch,落盘 graph.json 真 byte-stable
1256
+ // (此前 F175 仅在测试读取侧 delete generatedAt,是 over-claim;现在落盘侧直接固定)。
1257
+ normalizeGraphForWrite(graphJson, { stripTimestamps: true });
1077
1258
  // 社区分析完成后写盘(graphJson 已含 degree metadata)
1078
1259
  const graphWrittenPath = writeKnowledgeGraph(graphJson, resolvedOutputDir);
1079
1260
  docGraphPath = toProjectPath(graphWrittenPath);
@@ -1333,6 +1514,7 @@ export async function runBatch(projectRoot, options = {}) {
1333
1514
  docGraphPath,
1334
1515
  coverageReportPath,
1335
1516
  deltaReportPath,
1517
+ deltaReport,
1336
1518
  projectDocs,
1337
1519
  docsBundleManifestPath,
1338
1520
  docsBundleProfiles,
@@ -1345,9 +1527,9 @@ export async function runBatch(projectRoot, options = {}) {
1345
1527
  async function buildGraphForLanguageGroup(langGroup, projectRoot) {
1346
1528
  const registry = LanguageAdapterRegistry.getInstance();
1347
1529
  const adapter = registry.getAllAdapters().find((item) => item.id === langGroup.adapterId);
1348
- if (adapter?.buildDependencyGraph) {
1530
+ if (adapter?.buildModuleGraph) {
1349
1531
  try {
1350
- const langGraph = await adapter.buildDependencyGraph(projectRoot);
1532
+ const langGraph = await adapter.buildModuleGraph(projectRoot);
1351
1533
  for (const node of langGraph.modules) {
1352
1534
  node.language = langGroup.adapterId;
1353
1535
  }
@@ -1549,4 +1731,335 @@ function collectMdRecursive(dir, out) {
1549
1731
  }
1550
1732
  }
1551
1733
  }
1734
+ // ============================================================
1735
+ // Feature 151 — Python CodeSkeleton 收集(含 callSites 抽取)
1736
+ // ============================================================
1737
+ const PY_SKELETON_IGNORE_DIRS = new Set([
1738
+ 'node_modules', '.git', '__pycache__', '.venv', 'venv',
1739
+ 'build', 'dist', 'coverage', 'out', 'target', '.tox',
1740
+ ]);
1741
+ /**
1742
+ * Feature 151 T-008c — 收集 .py 文件 CodeSkeleton(含 callSites + 本地 import 解析)。
1743
+ *
1744
+ * 与 PythonLanguageAdapter.extractSymbolNodes / buildModuleGraph 不同:
1745
+ * - 显式传 extractCallSites=true,让 graph.json 含 callSites 字段
1746
+ * - 在 PythonMapper 输出基础上补充 import 的 resolvedPath(基于项目内 .py 模块 basename map)
1747
+ * — Codex P1 C-1 修订:mapper 当前只输出 moduleSpecifier,resolvedPath 始终 null,
1748
+ * 导致 deriveImportEdges / call-resolver Stage 3 cross-module 全部失效
1749
+ *
1750
+ * 单文件解析失败 / 大文件 / 非 UTF-8 都按 EC-14 兜底(mapper 已处理),不影响整体 collection。
1751
+ */
1752
+ export async function collectPythonCodeSkeletons(projectRoot) {
1753
+ const out = new Map();
1754
+ const { PythonLanguageAdapter } = await import('../adapters/python-adapter.js');
1755
+ const adapter = new PythonLanguageAdapter();
1756
+ // Codex P3+P4 复审 C-2 修复:projectRoot 显式 normalize 为绝对路径
1757
+ // 避免调用方传相对路径 → Map key 与 imports[].resolvedPath 形态不一致
1758
+ // → call-resolver buildImportIndex lookup miss
1759
+ const resolvedProjectRoot = path.resolve(projectRoot);
1760
+ // F194:构建 .gitignore 过滤器,基准 = resolvedProjectRoot(与 walk 内 path.relative 同口径)
1761
+ const isGitignored = createGitignoreFilter(resolvedProjectRoot);
1762
+ const pyFiles = [];
1763
+ walkPyFiles(resolvedProjectRoot, pyFiles, isGitignored, resolvedProjectRoot);
1764
+ // Feature 152 P3 T-017:大文件 size guard 提前到 parse 之前(避免 tree-sitter 解析阻塞)
1765
+ // 1MB 阈值与 PythonMapper.CALLSITES_MAX_FILE_BYTES 对齐(EC-14)
1766
+ const MAX_FILE_BYTES = 1_000_000;
1767
+ for (const filePath of pyFiles) {
1768
+ try {
1769
+ let stat;
1770
+ try {
1771
+ stat = fs.statSync(filePath);
1772
+ }
1773
+ catch {
1774
+ continue; // 文件读不到 stat → 跳过
1775
+ }
1776
+ if (stat.size > MAX_FILE_BYTES) {
1777
+ // 大文件 skip — 不调 analyzeFile(避免 tree-sitter parse 巨型文件)
1778
+ continue;
1779
+ }
1780
+ const skeleton = await adapter.analyzeFile(filePath, { extractCallSites: true });
1781
+ // Feature 152 P3 T-017:使用 resolvePythonImport 替换 basename map(plan §5.1)
1782
+ // C-1 修复:from . import X1, X2 形态(moduleSpecifier='.'/'..') 需对每个 namedImport 单独调用
1783
+ // EC-10 修复:resolvedPath 转为绝对路径,与 Map key 格式对齐
1784
+ const resolvedSkeleton = {
1785
+ ...skeleton,
1786
+ imports: skeleton.imports.flatMap((imp) => {
1787
+ if (imp.resolvedPath)
1788
+ return [imp];
1789
+ const spec = imp.moduleSpecifier;
1790
+ // C-1:裸相对 import —— moduleSpecifier 仅为纯点('.' / '..' / '...' / 更深)
1791
+ // "from . import nn, Value" → imp.namedImports=['nn','Value'],逐个拆解
1792
+ // quality-review W-2 修复:扩展到任意点深度(PEP 328 不限层数),
1793
+ // 避免 'from ... import a, b' 时 a/b 都映射到同一 resolvedPath(namedImports 污染)
1794
+ if (/^\.+$/.test(spec)) {
1795
+ const namedImports = Array.isArray(imp.namedImports)
1796
+ ? imp.namedImports
1797
+ : [];
1798
+ if (namedImports.length === 0) {
1799
+ // 没有 namedImports(罕见),仅尝试解析包 __init__
1800
+ const result = resolvePythonImport(spec, filePath, resolvedProjectRoot);
1801
+ const resolvedPath = result.resolvedPath
1802
+ ? path.resolve(resolvedProjectRoot, result.resolvedPath)
1803
+ : null;
1804
+ return [{ ...imp, resolvedPath }];
1805
+ }
1806
+ // 每个 namedImport 单独解析为独立 import 记录
1807
+ // Codex P3+P4 复审 C-1 修复:每条拆出记录的 namedImports 必须**只**含
1808
+ // 当前拆出的 name,否则 buildImportIndex 会把所有 namedImports 都映射到
1809
+ // 同一 resolvedPath(最后一条胜出),导致 alias 污染
1810
+ return namedImports.map((name) => {
1811
+ const combinedSpec = `${spec}${name}`; // '.' + 'nn' → '.nn'
1812
+ const result = resolvePythonImport(combinedSpec, filePath, resolvedProjectRoot);
1813
+ const resolvedPath = result.resolvedPath
1814
+ ? path.resolve(resolvedProjectRoot, result.resolvedPath)
1815
+ : null;
1816
+ return {
1817
+ ...imp,
1818
+ moduleSpecifier: combinedSpec,
1819
+ namedImports: [name], // 关键:仅含本次拆出的 name,避免 alias 污染
1820
+ resolvedPath,
1821
+ };
1822
+ });
1823
+ }
1824
+ // 常规形态:直接调用 resolver(from pkg.engine import Value / import os)
1825
+ const result = resolvePythonImport(spec, filePath, resolvedProjectRoot);
1826
+ // EC-10:resolver 返回相对 projectRoot 的 POSIX 路径,需转绝对路径与 Map key 对齐
1827
+ const resolvedPath = result.resolvedPath
1828
+ ? path.resolve(resolvedProjectRoot, result.resolvedPath)
1829
+ : null;
1830
+ return [{ ...imp, resolvedPath }];
1831
+ }),
1832
+ };
1833
+ out.set(filePath, resolvedSkeleton);
1834
+ }
1835
+ catch {
1836
+ // 单文件失败不影响整体
1837
+ }
1838
+ }
1839
+ return out;
1840
+ }
1841
+ /**
1842
+ * F194:isGitignored 与 resolvedRoot 由 collectPythonCodeSkeletons 构建并通过参数传入,
1843
+ * 在自写 walk 上叠加 .gitignore 过滤层(保留 PY_SKELETON_IGNORE_DIRS 与点前缀剪枝不变)。
1844
+ */
1845
+ function walkPyFiles(dir, out, isGitignored, resolvedRoot) {
1846
+ let entries;
1847
+ try {
1848
+ entries = fs.readdirSync(dir, { withFileTypes: true });
1849
+ }
1850
+ catch {
1851
+ return;
1852
+ }
1853
+ for (const entry of entries) {
1854
+ // 基准 = resolvedRoot,path.relative 输出不做 sep 转换(与 file-scanner walkDir 一致)
1855
+ const relPath = path.relative(resolvedRoot, path.join(dir, entry.name));
1856
+ if (entry.isDirectory()) {
1857
+ if (entry.name.startsWith('.'))
1858
+ continue;
1859
+ if (PY_SKELETON_IGNORE_DIRS.has(entry.name))
1860
+ continue;
1861
+ if (isGitignored(relPath))
1862
+ continue; // 目录命中 .gitignore → 剪枝
1863
+ walkPyFiles(path.join(dir, entry.name), out, isGitignored, resolvedRoot);
1864
+ }
1865
+ else if (entry.isFile() && (entry.name.endsWith('.py') || entry.name.endsWith('.pyi'))) {
1866
+ if (isGitignored(relPath))
1867
+ continue; // 文件命中 .gitignore → 跳过
1868
+ out.push(path.join(dir, entry.name));
1869
+ }
1870
+ }
1871
+ }
1872
+ // ============================================================
1873
+ // Feature 152 — TypeScript/JavaScript CodeSkeleton 收集
1874
+ // ============================================================
1875
+ /** T-020:TS/JS 文件扫描时忽略的目录集合(与 Python 对齐,增加 .next / .nuxt 等前端产物目录) */
1876
+ const TSJS_SKELETON_IGNORE_DIRS = new Set([
1877
+ 'node_modules', '.git', 'dist', 'build', 'coverage', 'out', 'target',
1878
+ '.next', '.nuxt', '.turbo', '.cache', 'tmp', '.tmp',
1879
+ '__pycache__', '.pytest_cache', '.tox',
1880
+ ]);
1881
+ /**
1882
+ * Feature 152 T-020 — 收集 .ts/.tsx/.js/.jsx 文件 CodeSkeleton(含 callSites + import 路径解析)。
1883
+ *
1884
+ * 与 collectPythonCodeSkeletons 设计对齐:
1885
+ * - 可选 extractCallSites,走 TsJsLanguageAdapter 双路径 merge(Feature 152 T-013/T-014)
1886
+ * - 解析 imports[].resolvedPath:findNearestTsConfig + buildTsConfigContext + resolveTsJsImport
1887
+ * - EC-10:resolvedPath 转绝对路径与 Map key 格式对齐
1888
+ * - T-021a:tsconfig context 按 configDir 缓存,避免每文件重复读
1889
+ * - 单文件失败不阻塞整体(catch 吞掉,同 Python 版本 EC-14 兜底)
1890
+ */
1891
+ export async function collectTsJsCodeSkeletons(projectRoot, options) {
1892
+ const out = new Map();
1893
+ const { TsJsLanguageAdapter } = await import('../adapters/ts-js-adapter.js');
1894
+ const adapter = new TsJsLanguageAdapter();
1895
+ // Codex P3+P4 复审 C-2 修复:projectRoot 显式 normalize 为绝对路径
1896
+ // 避免调用方传相对路径 → Map key 与 imports[].resolvedPath 形态不一致
1897
+ const resolvedProjectRoot = path.resolve(projectRoot);
1898
+ // F194:构建 .gitignore 过滤器,基准 = resolvedProjectRoot(与 walk 内 path.relative 同口径)
1899
+ const isGitignored = createGitignoreFilter(resolvedProjectRoot);
1900
+ const tsJsFiles = [];
1901
+ walkTsJsFiles(resolvedProjectRoot, tsJsFiles, isGitignored, resolvedProjectRoot);
1902
+ // T-021a:tsconfig context 缓存(by configDir),避免每个文件重复读
1903
+ const tsConfigCache = new Map();
1904
+ // 大文件 size guard 与 Python 版本对齐(EC-14:1MB 阈值)
1905
+ const MAX_FILE_BYTES = 1_000_000;
1906
+ for (const filePath of tsJsFiles) {
1907
+ try {
1908
+ let stat;
1909
+ try {
1910
+ stat = fs.statSync(filePath);
1911
+ }
1912
+ catch {
1913
+ continue; // 文件读不到 stat → 跳过
1914
+ }
1915
+ if (stat.size > MAX_FILE_BYTES) {
1916
+ continue; // 大文件 skip
1917
+ }
1918
+ // 主分析(含 callSites if options.extractCallSites)
1919
+ const skeleton = await adapter.analyzeFile(filePath, {
1920
+ extractCallSites: options?.extractCallSites,
1921
+ });
1922
+ // T-021a:查找最近的 tsconfig.json 并缓存 context(Feature 181:loader 收口为 configPath 单参)
1923
+ const configPath = findNearestTsConfig(filePath, resolvedProjectRoot);
1924
+ let tsConfigContext = null;
1925
+ if (configPath) {
1926
+ if (!tsConfigCache.has(configPath)) {
1927
+ tsConfigCache.set(configPath, buildTsConfigContext(configPath));
1928
+ }
1929
+ tsConfigContext = tsConfigCache.get(configPath) ?? null;
1930
+ }
1931
+ // 解析 imports[].resolvedPath(EC-10:转绝对路径)
1932
+ const resolvedSkeleton = {
1933
+ ...skeleton,
1934
+ imports: skeleton.imports.map((imp) => {
1935
+ if (imp.resolvedPath)
1936
+ return imp;
1937
+ const result = resolveTsJsImport(imp.moduleSpecifier, filePath, resolvedProjectRoot, tsConfigContext);
1938
+ // EC-10:resolver 返回相对 projectRoot 的 POSIX 路径,需转绝对路径与 Map key 对齐
1939
+ const resolvedPath = result.resolvedPath
1940
+ ? path.resolve(resolvedProjectRoot, result.resolvedPath)
1941
+ : null;
1942
+ return { ...imp, resolvedPath };
1943
+ }),
1944
+ };
1945
+ out.set(filePath, resolvedSkeleton);
1946
+ }
1947
+ catch {
1948
+ // 单文件失败不影响整体(与 collectPythonCodeSkeletons EC-14 一致)
1949
+ }
1950
+ }
1951
+ return out;
1952
+ }
1953
+ /**
1954
+ * 递归扫描 .ts/.tsx/.js/.jsx 文件(排除产物目录)。
1955
+ * 复用 walkPyFiles 的扫描模式,扩展 TS/JS 扩展名集合。
1956
+ *
1957
+ * F194:isGitignored 与 resolvedRoot 由 collectTsJsCodeSkeletons 构建并通过参数传入,
1958
+ * 在自写 walk 上叠加 .gitignore 过滤层(保留 TSJS_SKELETON_IGNORE_DIRS 与点前缀剪枝不变)。
1959
+ */
1960
+ function walkTsJsFiles(dir, out, isGitignored, resolvedRoot) {
1961
+ let entries;
1962
+ try {
1963
+ entries = fs.readdirSync(dir, { withFileTypes: true });
1964
+ }
1965
+ catch {
1966
+ return;
1967
+ }
1968
+ for (const entry of entries) {
1969
+ // 基准 = resolvedRoot,path.relative 输出不做 sep 转换(与 file-scanner walkDir 一致)
1970
+ const relPath = path.relative(resolvedRoot, path.join(dir, entry.name));
1971
+ if (entry.isDirectory()) {
1972
+ if (entry.name.startsWith('.'))
1973
+ continue;
1974
+ if (TSJS_SKELETON_IGNORE_DIRS.has(entry.name))
1975
+ continue;
1976
+ if (isGitignored(relPath))
1977
+ continue; // 目录命中 .gitignore → 剪枝
1978
+ walkTsJsFiles(path.join(dir, entry.name), out, isGitignored, resolvedRoot);
1979
+ }
1980
+ else if (entry.isFile()) {
1981
+ const name = entry.name;
1982
+ if (name.endsWith('.ts') ||
1983
+ name.endsWith('.tsx') ||
1984
+ name.endsWith('.js') ||
1985
+ name.endsWith('.jsx')) {
1986
+ if (isGitignored(relPath))
1987
+ continue; // 文件命中 .gitignore → 跳过
1988
+ out.push(path.join(dir, entry.name));
1989
+ }
1990
+ }
1991
+ }
1992
+ }
1993
+ /**
1994
+ * Feature 195:全仓纯 AST、零 LLM 的 graph-only 建图。
1995
+ *
1996
+ * 与 runBatch 解耦的姊妹管线——只跑 AST 采集 → unifiedGraph(call graph + depends-on)
1997
+ * + Python 符号节点 → buildKnowledgeGraph → writeKnowledgeGraph,全程不调用任何
1998
+ * spec-gen / enrichment / hyperedge LLM,也不需要认证。
1999
+ *
2000
+ * why 复用 collectPython/collectTsJs 而非 scanFiles:batch 的 unifiedGraph(见 runBatch
2001
+ * 内早期 UnifiedGraph 构建段)正是用这两个采集器构建,复用它们使 graph-only 的 unifiedGraph
2002
+ * 与 batch 口径逐一对齐(扩展名集合 + ignore 规则一致)。不按 languages 过滤,与 batch
2003
+ * unifiedGraph 行为一致。
2004
+ *
2005
+ * why writeKnowledgeGraph 传 stripTimestamps:true:与 batch 写盘一致,剥除 generatedAt
2006
+ * 实时戳,保 byte-stable 与跨 worktree 一致(F193/NFR-002)。
2007
+ */
2008
+ export async function buildAstGraphOnly(projectRoot, options) {
2009
+ const startTime = Date.now();
2010
+ const resolvedRoot = path.resolve(projectRoot);
2011
+ const outputDir = options?.outputDir ?? 'specs';
2012
+ const resolvedOutputDir = path.isAbsolute(outputDir)
2013
+ ? outputDir
2014
+ : path.join(resolvedRoot, outputDir);
2015
+ logger.info('[Feature 195] graph-only 建图:纯 AST / 零 LLM');
2016
+ // 步骤 1:AST 采集(与 batch unifiedGraph 同款采集器,合并 Python + TS/JS)
2017
+ const pythonSkeletons = await collectPythonCodeSkeletons(resolvedRoot);
2018
+ const tsJsSkeletons = await collectTsJsCodeSkeletons(resolvedRoot, {
2019
+ extractCallSites: true,
2020
+ });
2021
+ const codeSkeletons = new Map([...pythonSkeletons, ...tsJsSkeletons]);
2022
+ if (codeSkeletons.size === 0) {
2023
+ // EC-001:无可解析源码 → 仍产出 schema 合法的空图,不崩溃
2024
+ logger.warn('[Feature 195] 未发现可建图的源码(将产出空图)');
2025
+ }
2026
+ // 步骤 2:UnifiedGraph(calls + depends-on,纯 AST)
2027
+ const unifiedGraph = codeSkeletons.size > 0
2028
+ ? buildUnifiedGraph({ projectRoot: resolvedRoot, codeSkeletons })
2029
+ : undefined;
2030
+ // 步骤 3:Python 符号节点(纯 AST,extractionResults 第四路)
2031
+ // EC-003:extractSymbolNodes 内部已 best-effort(单文件失败仍产文件级节点),此处仅兜底捕获
2032
+ let pythonSymbolResults = [];
2033
+ try {
2034
+ const { PythonLanguageAdapter } = await import('../adapters/python-adapter.js');
2035
+ pythonSymbolResults = await new PythonLanguageAdapter().extractSymbolNodes(resolvedRoot);
2036
+ }
2037
+ catch (pyErr) {
2038
+ logger.warn(`[Feature 195] Python 符号提取失败,跳过: ${String(pyErr)}`);
2039
+ }
2040
+ const pythonSymbolCount = pythonSymbolResults.reduce((sum, r) => sum + r.nodes.filter((n) => n.kind === 'component').length, 0);
2041
+ // 步骤 4:合并构建 knowledge graph(不传 docGraph/architectureIR/crossReferenceLinks——
2042
+ // 那三路依赖 spec-gen 产物;EC-004:buildKnowledgeGraph 对缺席数据源 graceful skip)
2043
+ const graphJson = buildKnowledgeGraph({
2044
+ ...(unifiedGraph ? { unifiedGraph } : {}),
2045
+ ...(pythonSymbolResults.length > 0 ? { extractionResults: pythonSymbolResults } : {}),
2046
+ });
2047
+ // 步骤 5:复用 F183 写盘出口(内部 portable 守卫扫描 → normalizeGraphForWrite → 原子写)
2048
+ const graphPath = writeKnowledgeGraph(graphJson, resolvedOutputDir, {
2049
+ stripTimestamps: true,
2050
+ });
2051
+ const callEdgeCount = graphJson.links.filter((e) => e.relation === 'calls').length;
2052
+ const dependsOnEdgeCount = graphJson.links.filter((e) => e.relation === 'depends-on').length;
2053
+ logger.info(`[Feature 195] graph-only 完成:${graphJson.nodes.length} 节点 / ${graphJson.links.length} 边 ` +
2054
+ `(${callEdgeCount} calls, ${dependsOnEdgeCount} depends-on, ${pythonSymbolCount} Python 符号)`);
2055
+ return {
2056
+ graphPath,
2057
+ nodeCount: graphJson.nodes.length,
2058
+ edgeCount: graphJson.links.length,
2059
+ callEdgeCount,
2060
+ dependsOnEdgeCount,
2061
+ pythonSymbolCount,
2062
+ durationMs: Date.now() - startTime,
2063
+ };
2064
+ }
1552
2065
  //# sourceMappingURL=batch-orchestrator.js.map