openlore 2.0.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 (634) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/dist/api/analyze.d.ts +17 -0
  4. package/dist/api/analyze.d.ts.map +1 -0
  5. package/dist/api/analyze.js +143 -0
  6. package/dist/api/analyze.js.map +1 -0
  7. package/dist/api/audit.d.ts +10 -0
  8. package/dist/api/audit.d.ts.map +1 -0
  9. package/dist/api/audit.js +117 -0
  10. package/dist/api/audit.js.map +1 -0
  11. package/dist/api/decisions.d.ts +55 -0
  12. package/dist/api/decisions.d.ts.map +1 -0
  13. package/dist/api/decisions.js +157 -0
  14. package/dist/api/decisions.js.map +1 -0
  15. package/dist/api/drift.d.ts +21 -0
  16. package/dist/api/drift.d.ts.map +1 -0
  17. package/dist/api/drift.js +152 -0
  18. package/dist/api/drift.js.map +1 -0
  19. package/dist/api/generate.d.ts +18 -0
  20. package/dist/api/generate.d.ts.map +1 -0
  21. package/dist/api/generate.js +259 -0
  22. package/dist/api/generate.js.map +1 -0
  23. package/dist/api/index.d.ts +41 -0
  24. package/dist/api/index.d.ts.map +1 -0
  25. package/dist/api/index.js +34 -0
  26. package/dist/api/index.js.map +1 -0
  27. package/dist/api/init.d.ts +18 -0
  28. package/dist/api/init.d.ts.map +1 -0
  29. package/dist/api/init.js +83 -0
  30. package/dist/api/init.js.map +1 -0
  31. package/dist/api/run.d.ts +19 -0
  32. package/dist/api/run.d.ts.map +1 -0
  33. package/dist/api/run.js +312 -0
  34. package/dist/api/run.js.map +1 -0
  35. package/dist/api/specs.d.ts +49 -0
  36. package/dist/api/specs.d.ts.map +1 -0
  37. package/dist/api/specs.js +137 -0
  38. package/dist/api/specs.js.map +1 -0
  39. package/dist/api/types.d.ts +201 -0
  40. package/dist/api/types.d.ts.map +1 -0
  41. package/dist/api/types.js +9 -0
  42. package/dist/api/types.js.map +1 -0
  43. package/dist/api/verify.d.ts +20 -0
  44. package/dist/api/verify.d.ts.map +1 -0
  45. package/dist/api/verify.js +117 -0
  46. package/dist/api/verify.js.map +1 -0
  47. package/dist/cli/commands/analyze.d.ts +30 -0
  48. package/dist/cli/commands/analyze.d.ts.map +1 -0
  49. package/dist/cli/commands/analyze.js +683 -0
  50. package/dist/cli/commands/analyze.js.map +1 -0
  51. package/dist/cli/commands/audit.d.ts +9 -0
  52. package/dist/cli/commands/audit.d.ts.map +1 -0
  53. package/dist/cli/commands/audit.js +98 -0
  54. package/dist/cli/commands/audit.js.map +1 -0
  55. package/dist/cli/commands/decisions.d.ts +16 -0
  56. package/dist/cli/commands/decisions.d.ts.map +1 -0
  57. package/dist/cli/commands/decisions.js +864 -0
  58. package/dist/cli/commands/decisions.js.map +1 -0
  59. package/dist/cli/commands/digest.d.ts +9 -0
  60. package/dist/cli/commands/digest.d.ts.map +1 -0
  61. package/dist/cli/commands/digest.js +61 -0
  62. package/dist/cli/commands/digest.js.map +1 -0
  63. package/dist/cli/commands/doctor.d.ts +9 -0
  64. package/dist/cli/commands/doctor.d.ts.map +1 -0
  65. package/dist/cli/commands/doctor.js +398 -0
  66. package/dist/cli/commands/doctor.js.map +1 -0
  67. package/dist/cli/commands/drift.d.ts +9 -0
  68. package/dist/cli/commands/drift.d.ts.map +1 -0
  69. package/dist/cli/commands/drift.js +550 -0
  70. package/dist/cli/commands/drift.js.map +1 -0
  71. package/dist/cli/commands/generate.d.ts +9 -0
  72. package/dist/cli/commands/generate.d.ts.map +1 -0
  73. package/dist/cli/commands/generate.js +565 -0
  74. package/dist/cli/commands/generate.js.map +1 -0
  75. package/dist/cli/commands/init.d.ts +9 -0
  76. package/dist/cli/commands/init.d.ts.map +1 -0
  77. package/dist/cli/commands/init.js +173 -0
  78. package/dist/cli/commands/init.js.map +1 -0
  79. package/dist/cli/commands/mcp.d.ts +2235 -0
  80. package/dist/cli/commands/mcp.d.ts.map +1 -0
  81. package/dist/cli/commands/mcp.js +1384 -0
  82. package/dist/cli/commands/mcp.js.map +1 -0
  83. package/dist/cli/commands/refresh-stories.d.ts +10 -0
  84. package/dist/cli/commands/refresh-stories.d.ts.map +1 -0
  85. package/dist/cli/commands/refresh-stories.js +314 -0
  86. package/dist/cli/commands/refresh-stories.js.map +1 -0
  87. package/dist/cli/commands/run.d.ts +9 -0
  88. package/dist/cli/commands/run.d.ts.map +1 -0
  89. package/dist/cli/commands/run.js +459 -0
  90. package/dist/cli/commands/run.js.map +1 -0
  91. package/dist/cli/commands/setup.d.ts +19 -0
  92. package/dist/cli/commands/setup.d.ts.map +1 -0
  93. package/dist/cli/commands/setup.js +355 -0
  94. package/dist/cli/commands/setup.js.map +1 -0
  95. package/dist/cli/commands/test.d.ts +22 -0
  96. package/dist/cli/commands/test.d.ts.map +1 -0
  97. package/dist/cli/commands/test.js +180 -0
  98. package/dist/cli/commands/test.js.map +1 -0
  99. package/dist/cli/commands/verify.d.ts +9 -0
  100. package/dist/cli/commands/verify.d.ts.map +1 -0
  101. package/dist/cli/commands/verify.js +383 -0
  102. package/dist/cli/commands/verify.js.map +1 -0
  103. package/dist/cli/commands/view.d.ts +13 -0
  104. package/dist/cli/commands/view.d.ts.map +1 -0
  105. package/dist/cli/commands/view.js +547 -0
  106. package/dist/cli/commands/view.js.map +1 -0
  107. package/dist/cli/index.d.ts +9 -0
  108. package/dist/cli/index.d.ts.map +1 -0
  109. package/dist/cli/index.js +118 -0
  110. package/dist/cli/index.js.map +1 -0
  111. package/dist/cli/tui-approval.d.ts +11 -0
  112. package/dist/cli/tui-approval.d.ts.map +1 -0
  113. package/dist/cli/tui-approval.js +129 -0
  114. package/dist/cli/tui-approval.js.map +1 -0
  115. package/dist/constants.d.ts +314 -0
  116. package/dist/constants.d.ts.map +1 -0
  117. package/dist/constants.js +382 -0
  118. package/dist/constants.js.map +1 -0
  119. package/dist/core/analyzer/ai-config-generator.d.ts +54 -0
  120. package/dist/core/analyzer/ai-config-generator.d.ts.map +1 -0
  121. package/dist/core/analyzer/ai-config-generator.js +98 -0
  122. package/dist/core/analyzer/ai-config-generator.js.map +1 -0
  123. package/dist/core/analyzer/architecture-writer.d.ts +67 -0
  124. package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
  125. package/dist/core/analyzer/architecture-writer.js +209 -0
  126. package/dist/core/analyzer/architecture-writer.js.map +1 -0
  127. package/dist/core/analyzer/artifact-generator.d.ts +261 -0
  128. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
  129. package/dist/core/analyzer/artifact-generator.js +909 -0
  130. package/dist/core/analyzer/artifact-generator.js.map +1 -0
  131. package/dist/core/analyzer/ast-chunker.d.ts +24 -0
  132. package/dist/core/analyzer/ast-chunker.d.ts.map +1 -0
  133. package/dist/core/analyzer/ast-chunker.js +198 -0
  134. package/dist/core/analyzer/ast-chunker.js.map +1 -0
  135. package/dist/core/analyzer/call-graph.d.ts +162 -0
  136. package/dist/core/analyzer/call-graph.d.ts.map +1 -0
  137. package/dist/core/analyzer/call-graph.js +2040 -0
  138. package/dist/core/analyzer/call-graph.js.map +1 -0
  139. package/dist/core/analyzer/code-shaper.d.ts +33 -0
  140. package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
  141. package/dist/core/analyzer/code-shaper.js +154 -0
  142. package/dist/core/analyzer/code-shaper.js.map +1 -0
  143. package/dist/core/analyzer/codebase-digest.d.ts +40 -0
  144. package/dist/core/analyzer/codebase-digest.d.ts.map +1 -0
  145. package/dist/core/analyzer/codebase-digest.js +195 -0
  146. package/dist/core/analyzer/codebase-digest.js.map +1 -0
  147. package/dist/core/analyzer/cpp-header-resolver.d.ts +30 -0
  148. package/dist/core/analyzer/cpp-header-resolver.d.ts.map +1 -0
  149. package/dist/core/analyzer/cpp-header-resolver.js +71 -0
  150. package/dist/core/analyzer/cpp-header-resolver.js.map +1 -0
  151. package/dist/core/analyzer/dependency-graph.d.ts +230 -0
  152. package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
  153. package/dist/core/analyzer/dependency-graph.js +752 -0
  154. package/dist/core/analyzer/dependency-graph.js.map +1 -0
  155. package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
  156. package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
  157. package/dist/core/analyzer/duplicate-detector.js +289 -0
  158. package/dist/core/analyzer/duplicate-detector.js.map +1 -0
  159. package/dist/core/analyzer/embedding-service.d.ts +56 -0
  160. package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
  161. package/dist/core/analyzer/embedding-service.js +118 -0
  162. package/dist/core/analyzer/embedding-service.js.map +1 -0
  163. package/dist/core/analyzer/env-extractor.d.ts +33 -0
  164. package/dist/core/analyzer/env-extractor.d.ts.map +1 -0
  165. package/dist/core/analyzer/env-extractor.js +196 -0
  166. package/dist/core/analyzer/env-extractor.js.map +1 -0
  167. package/dist/core/analyzer/external-packages.d.ts +20 -0
  168. package/dist/core/analyzer/external-packages.d.ts.map +1 -0
  169. package/dist/core/analyzer/external-packages.js +175 -0
  170. package/dist/core/analyzer/external-packages.js.map +1 -0
  171. package/dist/core/analyzer/file-walker.d.ts +78 -0
  172. package/dist/core/analyzer/file-walker.d.ts.map +1 -0
  173. package/dist/core/analyzer/file-walker.js +532 -0
  174. package/dist/core/analyzer/file-walker.js.map +1 -0
  175. package/dist/core/analyzer/function-registry-trie.d.ts +21 -0
  176. package/dist/core/analyzer/function-registry-trie.d.ts.map +1 -0
  177. package/dist/core/analyzer/function-registry-trie.js +39 -0
  178. package/dist/core/analyzer/function-registry-trie.js.map +1 -0
  179. package/dist/core/analyzer/http-route-parser.d.ts +152 -0
  180. package/dist/core/analyzer/http-route-parser.d.ts.map +1 -0
  181. package/dist/core/analyzer/http-route-parser.js +971 -0
  182. package/dist/core/analyzer/http-route-parser.js.map +1 -0
  183. package/dist/core/analyzer/import-parser.d.ts +100 -0
  184. package/dist/core/analyzer/import-parser.d.ts.map +1 -0
  185. package/dist/core/analyzer/import-parser.js +952 -0
  186. package/dist/core/analyzer/import-parser.js.map +1 -0
  187. package/dist/core/analyzer/import-resolver-bridge.d.ts +25 -0
  188. package/dist/core/analyzer/import-resolver-bridge.d.ts.map +1 -0
  189. package/dist/core/analyzer/import-resolver-bridge.js +99 -0
  190. package/dist/core/analyzer/import-resolver-bridge.js.map +1 -0
  191. package/dist/core/analyzer/index.d.ts +10 -0
  192. package/dist/core/analyzer/index.d.ts.map +1 -0
  193. package/dist/core/analyzer/index.js +10 -0
  194. package/dist/core/analyzer/index.js.map +1 -0
  195. package/dist/core/analyzer/middleware-extractor.d.ts +29 -0
  196. package/dist/core/analyzer/middleware-extractor.d.ts.map +1 -0
  197. package/dist/core/analyzer/middleware-extractor.js +195 -0
  198. package/dist/core/analyzer/middleware-extractor.js.map +1 -0
  199. package/dist/core/analyzer/refactor-analyzer.d.ts +83 -0
  200. package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
  201. package/dist/core/analyzer/refactor-analyzer.js +351 -0
  202. package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
  203. package/dist/core/analyzer/repository-mapper.d.ts +150 -0
  204. package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
  205. package/dist/core/analyzer/repository-mapper.js +740 -0
  206. package/dist/core/analyzer/repository-mapper.js.map +1 -0
  207. package/dist/core/analyzer/schema-extractor.d.ts +41 -0
  208. package/dist/core/analyzer/schema-extractor.d.ts.map +1 -0
  209. package/dist/core/analyzer/schema-extractor.js +229 -0
  210. package/dist/core/analyzer/schema-extractor.js.map +1 -0
  211. package/dist/core/analyzer/signature-extractor.d.ts +31 -0
  212. package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
  213. package/dist/core/analyzer/signature-extractor.js +675 -0
  214. package/dist/core/analyzer/signature-extractor.js.map +1 -0
  215. package/dist/core/analyzer/significance-scorer.d.ts +79 -0
  216. package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
  217. package/dist/core/analyzer/significance-scorer.js +407 -0
  218. package/dist/core/analyzer/significance-scorer.js.map +1 -0
  219. package/dist/core/analyzer/spec-snapshot-generator.d.ts +17 -0
  220. package/dist/core/analyzer/spec-snapshot-generator.d.ts.map +1 -0
  221. package/dist/core/analyzer/spec-snapshot-generator.js +201 -0
  222. package/dist/core/analyzer/spec-snapshot-generator.js.map +1 -0
  223. package/dist/core/analyzer/spec-vector-index.d.ts +68 -0
  224. package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -0
  225. package/dist/core/analyzer/spec-vector-index.js +340 -0
  226. package/dist/core/analyzer/spec-vector-index.js.map +1 -0
  227. package/dist/core/analyzer/subgraph-extractor.d.ts +51 -0
  228. package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
  229. package/dist/core/analyzer/subgraph-extractor.js +147 -0
  230. package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
  231. package/dist/core/analyzer/type-inference-engine.d.ts +23 -0
  232. package/dist/core/analyzer/type-inference-engine.d.ts.map +1 -0
  233. package/dist/core/analyzer/type-inference-engine.js +130 -0
  234. package/dist/core/analyzer/type-inference-engine.js.map +1 -0
  235. package/dist/core/analyzer/ui-component-extractor.d.ts +43 -0
  236. package/dist/core/analyzer/ui-component-extractor.d.ts.map +1 -0
  237. package/dist/core/analyzer/ui-component-extractor.js +245 -0
  238. package/dist/core/analyzer/ui-component-extractor.js.map +1 -0
  239. package/dist/core/analyzer/unified-search.d.ts +116 -0
  240. package/dist/core/analyzer/unified-search.d.ts.map +1 -0
  241. package/dist/core/analyzer/unified-search.js +231 -0
  242. package/dist/core/analyzer/unified-search.js.map +1 -0
  243. package/dist/core/analyzer/vector-index.d.ts +92 -0
  244. package/dist/core/analyzer/vector-index.d.ts.map +1 -0
  245. package/dist/core/analyzer/vector-index.js +451 -0
  246. package/dist/core/analyzer/vector-index.js.map +1 -0
  247. package/dist/core/decisions/consolidator.d.ts +14 -0
  248. package/dist/core/decisions/consolidator.d.ts.map +1 -0
  249. package/dist/core/decisions/consolidator.js +169 -0
  250. package/dist/core/decisions/consolidator.js.map +1 -0
  251. package/dist/core/decisions/extractor.d.ts +26 -0
  252. package/dist/core/decisions/extractor.d.ts.map +1 -0
  253. package/dist/core/decisions/extractor.js +156 -0
  254. package/dist/core/decisions/extractor.js.map +1 -0
  255. package/dist/core/decisions/index.d.ts +19 -0
  256. package/dist/core/decisions/index.d.ts.map +1 -0
  257. package/dist/core/decisions/index.js +16 -0
  258. package/dist/core/decisions/index.js.map +1 -0
  259. package/dist/core/decisions/store.d.ts +36 -0
  260. package/dist/core/decisions/store.d.ts.map +1 -0
  261. package/dist/core/decisions/store.js +109 -0
  262. package/dist/core/decisions/store.js.map +1 -0
  263. package/dist/core/decisions/syncer.d.ts +27 -0
  264. package/dist/core/decisions/syncer.d.ts.map +1 -0
  265. package/dist/core/decisions/syncer.js +214 -0
  266. package/dist/core/decisions/syncer.js.map +1 -0
  267. package/dist/core/decisions/verifier.d.ts +20 -0
  268. package/dist/core/decisions/verifier.d.ts.map +1 -0
  269. package/dist/core/decisions/verifier.js +115 -0
  270. package/dist/core/decisions/verifier.js.map +1 -0
  271. package/dist/core/digest/digest-generator.d.ts +29 -0
  272. package/dist/core/digest/digest-generator.d.ts.map +1 -0
  273. package/dist/core/digest/digest-generator.js +181 -0
  274. package/dist/core/digest/digest-generator.js.map +1 -0
  275. package/dist/core/drift/drift-detector.d.ts +102 -0
  276. package/dist/core/drift/drift-detector.d.ts.map +1 -0
  277. package/dist/core/drift/drift-detector.js +598 -0
  278. package/dist/core/drift/drift-detector.js.map +1 -0
  279. package/dist/core/drift/git-diff.d.ts +60 -0
  280. package/dist/core/drift/git-diff.d.ts.map +1 -0
  281. package/dist/core/drift/git-diff.js +383 -0
  282. package/dist/core/drift/git-diff.js.map +1 -0
  283. package/dist/core/drift/index.d.ts +12 -0
  284. package/dist/core/drift/index.d.ts.map +1 -0
  285. package/dist/core/drift/index.js +9 -0
  286. package/dist/core/drift/index.js.map +1 -0
  287. package/dist/core/drift/spec-mapper.d.ts +73 -0
  288. package/dist/core/drift/spec-mapper.d.ts.map +1 -0
  289. package/dist/core/drift/spec-mapper.js +353 -0
  290. package/dist/core/drift/spec-mapper.js.map +1 -0
  291. package/dist/core/drift/test-suggester.d.ts +18 -0
  292. package/dist/core/drift/test-suggester.d.ts.map +1 -0
  293. package/dist/core/drift/test-suggester.js +107 -0
  294. package/dist/core/drift/test-suggester.js.map +1 -0
  295. package/dist/core/generator/adr-generator.d.ts +32 -0
  296. package/dist/core/generator/adr-generator.d.ts.map +1 -0
  297. package/dist/core/generator/adr-generator.js +192 -0
  298. package/dist/core/generator/adr-generator.js.map +1 -0
  299. package/dist/core/generator/index.d.ts +9 -0
  300. package/dist/core/generator/index.d.ts.map +1 -0
  301. package/dist/core/generator/index.js +12 -0
  302. package/dist/core/generator/index.js.map +1 -0
  303. package/dist/core/generator/mapping-generator.d.ts +54 -0
  304. package/dist/core/generator/mapping-generator.d.ts.map +1 -0
  305. package/dist/core/generator/mapping-generator.js +240 -0
  306. package/dist/core/generator/mapping-generator.js.map +1 -0
  307. package/dist/core/generator/openspec-compat.d.ts +160 -0
  308. package/dist/core/generator/openspec-compat.d.ts.map +1 -0
  309. package/dist/core/generator/openspec-compat.js +524 -0
  310. package/dist/core/generator/openspec-compat.js.map +1 -0
  311. package/dist/core/generator/openspec-format-generator.d.ts +131 -0
  312. package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
  313. package/dist/core/generator/openspec-format-generator.js +963 -0
  314. package/dist/core/generator/openspec-format-generator.js.map +1 -0
  315. package/dist/core/generator/openspec-writer.d.ts +130 -0
  316. package/dist/core/generator/openspec-writer.d.ts.map +1 -0
  317. package/dist/core/generator/openspec-writer.js +404 -0
  318. package/dist/core/generator/openspec-writer.js.map +1 -0
  319. package/dist/core/generator/prompts.d.ts +35 -0
  320. package/dist/core/generator/prompts.d.ts.map +1 -0
  321. package/dist/core/generator/prompts.js +212 -0
  322. package/dist/core/generator/prompts.js.map +1 -0
  323. package/dist/core/generator/rag-manifest-generator.d.ts +37 -0
  324. package/dist/core/generator/rag-manifest-generator.d.ts.map +1 -0
  325. package/dist/core/generator/rag-manifest-generator.js +134 -0
  326. package/dist/core/generator/rag-manifest-generator.js.map +1 -0
  327. package/dist/core/generator/schemas.d.ts +365 -0
  328. package/dist/core/generator/schemas.d.ts.map +1 -0
  329. package/dist/core/generator/schemas.js +190 -0
  330. package/dist/core/generator/schemas.js.map +1 -0
  331. package/dist/core/generator/spec-pipeline.d.ts +123 -0
  332. package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
  333. package/dist/core/generator/spec-pipeline.js +699 -0
  334. package/dist/core/generator/spec-pipeline.js.map +1 -0
  335. package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
  336. package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
  337. package/dist/core/generator/stages/stage1-survey.js +171 -0
  338. package/dist/core/generator/stages/stage1-survey.js.map +1 -0
  339. package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
  340. package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
  341. package/dist/core/generator/stages/stage2-entities.js +74 -0
  342. package/dist/core/generator/stages/stage2-entities.js.map +1 -0
  343. package/dist/core/generator/stages/stage3-services.d.ts +11 -0
  344. package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
  345. package/dist/core/generator/stages/stage3-services.js +85 -0
  346. package/dist/core/generator/stages/stage3-services.js.map +1 -0
  347. package/dist/core/generator/stages/stage4-api.d.ts +11 -0
  348. package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
  349. package/dist/core/generator/stages/stage4-api.js +72 -0
  350. package/dist/core/generator/stages/stage4-api.js.map +1 -0
  351. package/dist/core/generator/stages/stage5-architecture.d.ts +11 -0
  352. package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
  353. package/dist/core/generator/stages/stage5-architecture.js +75 -0
  354. package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
  355. package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
  356. package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
  357. package/dist/core/generator/stages/stage6-adr.js +47 -0
  358. package/dist/core/generator/stages/stage6-adr.js.map +1 -0
  359. package/dist/core/services/chat-agent.d.ts +50 -0
  360. package/dist/core/services/chat-agent.d.ts.map +1 -0
  361. package/dist/core/services/chat-agent.js +369 -0
  362. package/dist/core/services/chat-agent.js.map +1 -0
  363. package/dist/core/services/chat-tools.d.ts +32 -0
  364. package/dist/core/services/chat-tools.d.ts.map +1 -0
  365. package/dist/core/services/chat-tools.js +494 -0
  366. package/dist/core/services/chat-tools.js.map +1 -0
  367. package/dist/core/services/config-manager.d.ts +61 -0
  368. package/dist/core/services/config-manager.d.ts.map +1 -0
  369. package/dist/core/services/config-manager.js +149 -0
  370. package/dist/core/services/config-manager.js.map +1 -0
  371. package/dist/core/services/edge-store.d.ts +57 -0
  372. package/dist/core/services/edge-store.d.ts.map +1 -0
  373. package/dist/core/services/edge-store.js +419 -0
  374. package/dist/core/services/edge-store.js.map +1 -0
  375. package/dist/core/services/gitignore-manager.d.ts +29 -0
  376. package/dist/core/services/gitignore-manager.d.ts.map +1 -0
  377. package/dist/core/services/gitignore-manager.js +95 -0
  378. package/dist/core/services/gitignore-manager.js.map +1 -0
  379. package/dist/core/services/index.d.ts +8 -0
  380. package/dist/core/services/index.d.ts.map +1 -0
  381. package/dist/core/services/index.js +8 -0
  382. package/dist/core/services/index.js.map +1 -0
  383. package/dist/core/services/llm-service.d.ts +379 -0
  384. package/dist/core/services/llm-service.d.ts.map +1 -0
  385. package/dist/core/services/llm-service.js +1553 -0
  386. package/dist/core/services/llm-service.js.map +1 -0
  387. package/dist/core/services/mcp-handlers/analysis.d.ts +127 -0
  388. package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
  389. package/dist/core/services/mcp-handlers/analysis.js +1185 -0
  390. package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
  391. package/dist/core/services/mcp-handlers/change.d.ts +14 -0
  392. package/dist/core/services/mcp-handlers/change.d.ts.map +1 -0
  393. package/dist/core/services/mcp-handlers/change.js +416 -0
  394. package/dist/core/services/mcp-handlers/change.js.map +1 -0
  395. package/dist/core/services/mcp-handlers/decisions.d.ts +16 -0
  396. package/dist/core/services/mcp-handlers/decisions.d.ts.map +1 -0
  397. package/dist/core/services/mcp-handlers/decisions.js +239 -0
  398. package/dist/core/services/mcp-handlers/decisions.js.map +1 -0
  399. package/dist/core/services/mcp-handlers/graph.d.ts +94 -0
  400. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
  401. package/dist/core/services/mcp-handlers/graph.js +693 -0
  402. package/dist/core/services/mcp-handlers/graph.js.map +1 -0
  403. package/dist/core/services/mcp-handlers/orient.d.ts +17 -0
  404. package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -0
  405. package/dist/core/services/mcp-handlers/orient.js +357 -0
  406. package/dist/core/services/mcp-handlers/orient.js.map +1 -0
  407. package/dist/core/services/mcp-handlers/semantic.d.ts +66 -0
  408. package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
  409. package/dist/core/services/mcp-handlers/semantic.js +432 -0
  410. package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
  411. package/dist/core/services/mcp-handlers/utils.d.ts +85 -0
  412. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
  413. package/dist/core/services/mcp-handlers/utils.js +262 -0
  414. package/dist/core/services/mcp-handlers/utils.js.map +1 -0
  415. package/dist/core/services/mcp-watcher.d.ts +41 -0
  416. package/dist/core/services/mcp-watcher.d.ts.map +1 -0
  417. package/dist/core/services/mcp-watcher.js +254 -0
  418. package/dist/core/services/mcp-watcher.js.map +1 -0
  419. package/dist/core/services/project-detector.d.ts +32 -0
  420. package/dist/core/services/project-detector.d.ts.map +1 -0
  421. package/dist/core/services/project-detector.js +100 -0
  422. package/dist/core/services/project-detector.js.map +1 -0
  423. package/dist/core/test-generator/coverage-analyzer.d.ts +27 -0
  424. package/dist/core/test-generator/coverage-analyzer.d.ts.map +1 -0
  425. package/dist/core/test-generator/coverage-analyzer.js +285 -0
  426. package/dist/core/test-generator/coverage-analyzer.js.map +1 -0
  427. package/dist/core/test-generator/framework-detector.d.ts +17 -0
  428. package/dist/core/test-generator/framework-detector.d.ts.map +1 -0
  429. package/dist/core/test-generator/framework-detector.js +65 -0
  430. package/dist/core/test-generator/framework-detector.js.map +1 -0
  431. package/dist/core/test-generator/index.d.ts +14 -0
  432. package/dist/core/test-generator/index.d.ts.map +1 -0
  433. package/dist/core/test-generator/index.js +11 -0
  434. package/dist/core/test-generator/index.js.map +1 -0
  435. package/dist/core/test-generator/renderers/catch2.d.ts +8 -0
  436. package/dist/core/test-generator/renderers/catch2.d.ts.map +1 -0
  437. package/dist/core/test-generator/renderers/catch2.js +47 -0
  438. package/dist/core/test-generator/renderers/catch2.js.map +1 -0
  439. package/dist/core/test-generator/renderers/gtest.d.ts +8 -0
  440. package/dist/core/test-generator/renderers/gtest.d.ts.map +1 -0
  441. package/dist/core/test-generator/renderers/gtest.js +45 -0
  442. package/dist/core/test-generator/renderers/gtest.js.map +1 -0
  443. package/dist/core/test-generator/renderers/index.d.ts +20 -0
  444. package/dist/core/test-generator/renderers/index.d.ts.map +1 -0
  445. package/dist/core/test-generator/renderers/index.js +35 -0
  446. package/dist/core/test-generator/renderers/index.js.map +1 -0
  447. package/dist/core/test-generator/renderers/playwright.d.ts +8 -0
  448. package/dist/core/test-generator/renderers/playwright.d.ts.map +1 -0
  449. package/dist/core/test-generator/renderers/playwright.js +44 -0
  450. package/dist/core/test-generator/renderers/playwright.js.map +1 -0
  451. package/dist/core/test-generator/renderers/pytest.d.ts +8 -0
  452. package/dist/core/test-generator/renderers/pytest.d.ts.map +1 -0
  453. package/dist/core/test-generator/renderers/pytest.js +44 -0
  454. package/dist/core/test-generator/renderers/pytest.js.map +1 -0
  455. package/dist/core/test-generator/renderers/shared.d.ts +21 -0
  456. package/dist/core/test-generator/renderers/shared.d.ts.map +1 -0
  457. package/dist/core/test-generator/renderers/shared.js +56 -0
  458. package/dist/core/test-generator/renderers/shared.js.map +1 -0
  459. package/dist/core/test-generator/renderers/vitest.d.ts +8 -0
  460. package/dist/core/test-generator/renderers/vitest.d.ts.map +1 -0
  461. package/dist/core/test-generator/renderers/vitest.js +52 -0
  462. package/dist/core/test-generator/renderers/vitest.js.map +1 -0
  463. package/dist/core/test-generator/scenario-parser.d.ts +33 -0
  464. package/dist/core/test-generator/scenario-parser.d.ts.map +1 -0
  465. package/dist/core/test-generator/scenario-parser.js +244 -0
  466. package/dist/core/test-generator/scenario-parser.js.map +1 -0
  467. package/dist/core/test-generator/test-generator.d.ts +30 -0
  468. package/dist/core/test-generator/test-generator.d.ts.map +1 -0
  469. package/dist/core/test-generator/test-generator.js +174 -0
  470. package/dist/core/test-generator/test-generator.js.map +1 -0
  471. package/dist/core/test-generator/test-writer.d.ts +25 -0
  472. package/dist/core/test-generator/test-writer.d.ts.map +1 -0
  473. package/dist/core/test-generator/test-writer.js +128 -0
  474. package/dist/core/test-generator/test-writer.js.map +1 -0
  475. package/dist/core/test-generator/then-matchers.d.ts +35 -0
  476. package/dist/core/test-generator/then-matchers.d.ts.map +1 -0
  477. package/dist/core/test-generator/then-matchers.js +211 -0
  478. package/dist/core/test-generator/then-matchers.js.map +1 -0
  479. package/dist/core/verifier/index.d.ts +5 -0
  480. package/dist/core/verifier/index.d.ts.map +1 -0
  481. package/dist/core/verifier/index.js +5 -0
  482. package/dist/core/verifier/index.js.map +1 -0
  483. package/dist/core/verifier/verification-engine.d.ts +293 -0
  484. package/dist/core/verifier/verification-engine.d.ts.map +1 -0
  485. package/dist/core/verifier/verification-engine.js +919 -0
  486. package/dist/core/verifier/verification-engine.js.map +1 -0
  487. package/dist/types/index.d.ts +368 -0
  488. package/dist/types/index.d.ts.map +1 -0
  489. package/dist/types/index.js +5 -0
  490. package/dist/types/index.js.map +1 -0
  491. package/dist/types/pipeline.d.ts +167 -0
  492. package/dist/types/pipeline.d.ts.map +1 -0
  493. package/dist/types/pipeline.js +5 -0
  494. package/dist/types/pipeline.js.map +1 -0
  495. package/dist/types/test-generator.d.ts +103 -0
  496. package/dist/types/test-generator.d.ts.map +1 -0
  497. package/dist/types/test-generator.js +17 -0
  498. package/dist/types/test-generator.js.map +1 -0
  499. package/dist/utils/command-helpers.d.ts +68 -0
  500. package/dist/utils/command-helpers.d.ts.map +1 -0
  501. package/dist/utils/command-helpers.js +150 -0
  502. package/dist/utils/command-helpers.js.map +1 -0
  503. package/dist/utils/errors.d.ts +51 -0
  504. package/dist/utils/errors.d.ts.map +1 -0
  505. package/dist/utils/errors.js +129 -0
  506. package/dist/utils/errors.js.map +1 -0
  507. package/dist/utils/logger.d.ts +149 -0
  508. package/dist/utils/logger.d.ts.map +1 -0
  509. package/dist/utils/logger.js +342 -0
  510. package/dist/utils/logger.js.map +1 -0
  511. package/dist/utils/misc.d.ts +10 -0
  512. package/dist/utils/misc.d.ts.map +1 -0
  513. package/dist/utils/misc.js +21 -0
  514. package/dist/utils/misc.js.map +1 -0
  515. package/dist/utils/progress.d.ts +142 -0
  516. package/dist/utils/progress.d.ts.map +1 -0
  517. package/dist/utils/progress.js +283 -0
  518. package/dist/utils/progress.js.map +1 -0
  519. package/dist/utils/prompts.d.ts +53 -0
  520. package/dist/utils/prompts.d.ts.map +1 -0
  521. package/dist/utils/prompts.js +199 -0
  522. package/dist/utils/prompts.js.map +1 -0
  523. package/dist/utils/shutdown.d.ts +89 -0
  524. package/dist/utils/shutdown.d.ts.map +1 -0
  525. package/dist/utils/shutdown.js +238 -0
  526. package/dist/utils/shutdown.js.map +1 -0
  527. package/examples/bmad/README.md +113 -0
  528. package/examples/bmad/agents/architect.md +226 -0
  529. package/examples/bmad/agents/dev-brownfield.md +69 -0
  530. package/examples/bmad/setup/architect.customize.yaml +14 -0
  531. package/examples/bmad/tasks/implement-story.md +254 -0
  532. package/examples/bmad/tasks/onboarding.md +169 -0
  533. package/examples/bmad/tasks/refactor.md +178 -0
  534. package/examples/bmad/tasks/sprint-planning.md +168 -0
  535. package/examples/bmad/templates/story.md +108 -0
  536. package/examples/cline-workflows/openlore-analyze-codebase.md +101 -0
  537. package/examples/cline-workflows/openlore-check-spec-drift.md +102 -0
  538. package/examples/cline-workflows/openlore-execute-refactor.md +212 -0
  539. package/examples/cline-workflows/openlore-implement-feature.md +266 -0
  540. package/examples/cline-workflows/openlore-plan-refactor.md +279 -0
  541. package/examples/cline-workflows/openlore-refactor-codebase.md +16 -0
  542. package/examples/cline-workflows/openlore-write-tests.md +177 -0
  543. package/examples/drift-demo/openspec/config.yaml +14 -0
  544. package/examples/drift-demo/openspec/specs/architecture/spec.md +30 -0
  545. package/examples/drift-demo/openspec/specs/auth/spec.md +71 -0
  546. package/examples/drift-demo/openspec/specs/database/spec.md +33 -0
  547. package/examples/drift-demo/openspec/specs/overview/spec.md +20 -0
  548. package/examples/drift-demo/openspec/specs/projects/spec.md +55 -0
  549. package/examples/drift-demo/openspec/specs/tasks/spec.md +78 -0
  550. package/examples/drift-demo/package.json +21 -0
  551. package/examples/drift-demo/src/auth/auth-middleware.ts +30 -0
  552. package/examples/drift-demo/src/auth/auth-routes.ts +29 -0
  553. package/examples/drift-demo/src/auth/auth-service.ts +45 -0
  554. package/examples/drift-demo/src/database/connection.ts +27 -0
  555. package/examples/drift-demo/src/index.ts +16 -0
  556. package/examples/drift-demo/src/projects/project-model.ts +15 -0
  557. package/examples/drift-demo/src/projects/project-service.ts +34 -0
  558. package/examples/drift-demo/src/tasks/task-model.ts +37 -0
  559. package/examples/drift-demo/src/tasks/task-routes.ts +53 -0
  560. package/examples/drift-demo/src/tasks/task-service.ts +60 -0
  561. package/examples/drift-demo/src/utils/validation.ts +11 -0
  562. package/examples/drift-demo/tests/auth.test.ts +4 -0
  563. package/examples/drift-demo/tests/tasks.test.ts +4 -0
  564. package/examples/drift-demo/tsconfig.json +10 -0
  565. package/examples/drift-test/run-drift-test.sh +1087 -0
  566. package/examples/gsd/README.md +119 -0
  567. package/examples/gsd/commands/gsd/openlore-drift.md +111 -0
  568. package/examples/gsd/commands/gsd/openlore-orient.md +191 -0
  569. package/examples/mistral-vibe/README.md +101 -0
  570. package/examples/mistral-vibe/antipatterns-template.md +18 -0
  571. package/examples/mistral-vibe/skills/openlore-analyze-codebase/SKILL.md +124 -0
  572. package/examples/mistral-vibe/skills/openlore-brainstorm/SKILL.md +379 -0
  573. package/examples/mistral-vibe/skills/openlore-debug/SKILL.md +330 -0
  574. package/examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md +291 -0
  575. package/examples/mistral-vibe/skills/openlore-generate/SKILL.md +245 -0
  576. package/examples/mistral-vibe/skills/openlore-implement-story/SKILL.md +326 -0
  577. package/examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md +365 -0
  578. package/examples/mistral-vibe/skills/openlore-review-changes/SKILL.md +128 -0
  579. package/examples/mistral-vibe/skills/openlore-write-tests/SKILL.md +261 -0
  580. package/examples/opencode/agent-guard.ts +170 -0
  581. package/examples/opencode/plugins/anti-laziness.ts +202 -0
  582. package/examples/opencode/plugins/lib/openlore-context-injector-helpers.ts +116 -0
  583. package/examples/opencode/plugins/lib/openlore-decision-extractor-helpers.ts +65 -0
  584. package/examples/opencode/plugins/openlore-context-injector.test.ts +211 -0
  585. package/examples/opencode/plugins/openlore-context-injector.ts +165 -0
  586. package/examples/opencode/plugins/openlore-decision-extractor.test.ts +131 -0
  587. package/examples/opencode/plugins/openlore-decision-extractor.ts +322 -0
  588. package/examples/opencode/plugins/openlore-enforcer.ts +227 -0
  589. package/examples/opencode/prompts/sisyphus-sdd.md +150 -0
  590. package/examples/opencode-skills/openlore-analyze-codebase/SKILL.md +101 -0
  591. package/examples/opencode-skills/openlore-brainstorm/SKILL.md +354 -0
  592. package/examples/opencode-skills/openlore-debug/SKILL.md +291 -0
  593. package/examples/opencode-skills/openlore-execute-refactor/SKILL.md +241 -0
  594. package/examples/opencode-skills/openlore-generate/SKILL.md +236 -0
  595. package/examples/opencode-skills/openlore-implement-story/SKILL.md +251 -0
  596. package/examples/opencode-skills/openlore-plan-refactor/SKILL.md +298 -0
  597. package/examples/opencode-skills/openlore-review-changes/SKILL.md +134 -0
  598. package/examples/opencode-skills/openlore-write-tests/SKILL.md +230 -0
  599. package/examples/openspec-analysis/README.md +59 -0
  600. package/examples/openspec-analysis/SUMMARY.md +72 -0
  601. package/examples/openspec-analysis/config.json +16 -0
  602. package/examples/openspec-analysis/dependencies.mermaid +35 -0
  603. package/examples/openspec-analysis/dependency-graph.json +12116 -0
  604. package/examples/openspec-analysis/llm-context.json +119 -0
  605. package/examples/openspec-analysis/repo-structure.json +871 -0
  606. package/examples/openspec-cli/README.md +67 -0
  607. package/examples/openspec-cli/openspec/config.yaml +26 -0
  608. package/examples/openspec-cli/openspec/specs/architecture/spec.md +178 -0
  609. package/examples/openspec-cli/openspec/specs/artifact-graph/spec.md +143 -0
  610. package/examples/openspec-cli/openspec/specs/cli/spec.md +138 -0
  611. package/examples/openspec-cli/openspec/specs/overview/spec.md +60 -0
  612. package/examples/openspec-cli/openspec/specs/parsing/spec.md +123 -0
  613. package/examples/openspec-cli/openspec/specs/validation/spec.md +108 -0
  614. package/examples/spec-kit/README.md +104 -0
  615. package/examples/spec-kit/commands/drift.md +87 -0
  616. package/examples/spec-kit/commands/orient.md +138 -0
  617. package/examples/spec-kit/extension.yml +54 -0
  618. package/package.json +125 -0
  619. package/src/viewer/InteractiveGraphViewer.jsx +1600 -0
  620. package/src/viewer/app/index.html +17 -0
  621. package/src/viewer/app/main.jsx +13 -0
  622. package/src/viewer/components/ArchitectureView.jsx +177 -0
  623. package/src/viewer/components/ChatPanel.jsx +450 -0
  624. package/src/viewer/components/ClassGraph.jsx +782 -0
  625. package/src/viewer/components/ClusterGraph.jsx +469 -0
  626. package/src/viewer/components/FilterBar.jsx +179 -0
  627. package/src/viewer/components/FlatGraph.jsx +282 -0
  628. package/src/viewer/components/MicroComponents.jsx +85 -0
  629. package/src/viewer/hooks/usePanZoom.js +79 -0
  630. package/src/viewer/utils/constants.js +64 -0
  631. package/src/viewer/utils/graph-helpers.js +303 -0
  632. package/src/viewer/utils/graph-helpers.test.ts +39 -0
  633. package/src/viewer/utils/themes.js +206 -0
  634. package/stubs/tree-sitter-cli-stub/package.json +6 -0
@@ -0,0 +1,919 @@
1
+ /**
2
+ * Spec Verification Engine
3
+ *
4
+ * Tests whether generated specs accurately describe the codebase by using
5
+ * the specs to predict code behavior and comparing against actual files.
6
+ */
7
+ import { readFile, writeFile, mkdir, access, readdir } from 'node:fs/promises';
8
+ import { join, basename, relative } from 'node:path';
9
+ import logger from '../../utils/logger.js';
10
+ import { VERIFICATION_PREDICTION_MAX_TOKENS } from '../../constants.js';
11
+ import { ImportExportParser } from '../analyzer/import-parser.js';
12
+ // ============================================================================
13
+ // SYSTEM PROMPTS
14
+ // ============================================================================
15
+ const PREDICTION_SYSTEM_PROMPT = `You are testing the accuracy of OpenSpec specifications.
16
+
17
+ You will be given:
18
+ 1. A set of specifications describing a software system (in OpenSpec format)
19
+ 2. A file path within that system
20
+
21
+ Your task: Based ONLY on the specifications, predict:
22
+ - What this file likely does (purpose)
23
+ - What modules/files it probably imports
24
+ - What it probably exports (functions, classes, etc.)
25
+ - Key logic patterns you'd expect to see based on the spec requirements
26
+
27
+ Be specific. If the specs don't provide enough info, say so.
28
+
29
+ Respond with valid JSON only.`;
30
+ // ============================================================================
31
+ // VERIFICATION ENGINE
32
+ // ============================================================================
33
+ /**
34
+ * Spec Verification Engine
35
+ */
36
+ export class SpecVerificationEngine {
37
+ llm;
38
+ options;
39
+ specs = [];
40
+ fileDomainMap = new Map();
41
+ parser;
42
+ constructor(llm, options) {
43
+ this.llm = llm;
44
+ this.parser = new ImportExportParser();
45
+ this.options = {
46
+ rootPath: options.rootPath,
47
+ openspecPath: options.openspecPath,
48
+ outputDir: options.outputDir,
49
+ minComplexity: options.minComplexity ?? 50,
50
+ maxComplexity: options.maxComplexity ?? 500,
51
+ filesPerDomain: options.filesPerDomain ?? 3,
52
+ passThreshold: options.passThreshold ?? 0.5,
53
+ generationContext: options.generationContext ?? [],
54
+ };
55
+ }
56
+ /**
57
+ * Run full verification
58
+ */
59
+ async verify(depGraph, specVersion) {
60
+ const startTime = Date.now();
61
+ // Load all specs and the file→domain mapping
62
+ await this.loadSpecs();
63
+ await this.loadFileDomainMap();
64
+ if (this.specs.length === 0) {
65
+ throw new Error('No specs found to verify against');
66
+ }
67
+ logger.analysis(`Loaded ${this.specs.length} spec(s) for verification`);
68
+ // Select verification candidates
69
+ const candidates = this.selectCandidates(depGraph);
70
+ logger.discovery(`Selected ${candidates.length} candidate file(s) for verification`);
71
+ if (candidates.length === 0) {
72
+ throw new Error('No suitable verification candidates found');
73
+ }
74
+ // Run verification for each candidate
75
+ const results = [];
76
+ for (let i = 0; i < candidates.length; i++) {
77
+ const candidate = candidates[i];
78
+ logger.analysis(`Verifying ${i + 1}/${candidates.length}: ${candidate.path}`);
79
+ try {
80
+ const result = await this.verifyFile(candidate);
81
+ results.push(result);
82
+ }
83
+ catch (error) {
84
+ logger.warning(`Failed to verify ${candidate.path}: ${error.message}`);
85
+ }
86
+ }
87
+ // Generate report
88
+ const report = this.generateReport(results, specVersion);
89
+ // Save report
90
+ await this.saveReport(report);
91
+ const duration = Date.now() - startTime;
92
+ logger.success(`Verification complete in ${(duration / 1000).toFixed(1)}s`);
93
+ return report;
94
+ }
95
+ /**
96
+ * Load all specs from openspec directory
97
+ */
98
+ async loadSpecs() {
99
+ this.specs = [];
100
+ const specsDir = join(this.options.openspecPath, 'specs');
101
+ try {
102
+ await access(specsDir);
103
+ }
104
+ catch {
105
+ return;
106
+ }
107
+ const entries = await readdir(specsDir, { withFileTypes: true });
108
+ for (const entry of entries) {
109
+ if (!entry.isDirectory())
110
+ continue;
111
+ const specPath = join(specsDir, entry.name, 'spec.md');
112
+ try {
113
+ const content = await readFile(specPath, 'utf-8');
114
+ this.specs.push({
115
+ domain: entry.name,
116
+ path: relative(this.options.rootPath, specPath),
117
+ content,
118
+ });
119
+ }
120
+ catch {
121
+ // Spec doesn't exist for this domain
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * Load file→domain mapping from .openlore/analysis/mapping.json.
127
+ * Falls back silently if the file doesn't exist (e.g. before first analysis run).
128
+ */
129
+ async loadFileDomainMap() {
130
+ this.fileDomainMap = new Map();
131
+ const mappingPath = join(this.options.rootPath, '.openlore', 'analysis', 'mapping.json');
132
+ try {
133
+ const raw = await readFile(mappingPath, 'utf-8');
134
+ const data = JSON.parse(raw);
135
+ // Count how many distinct domains each file appears in
136
+ const fileDomains = new Map();
137
+ for (const entry of data.mappings ?? []) {
138
+ for (const fn of entry.functions ?? []) {
139
+ if (!fn.file || !entry.domain)
140
+ continue;
141
+ if (!fileDomains.has(fn.file))
142
+ fileDomains.set(fn.file, new Set());
143
+ fileDomains.get(fn.file).add(entry.domain);
144
+ }
145
+ }
146
+ // Only map files that belong to exactly one domain — cross-cutting files
147
+ // (e.g. constants.ts, logger.ts) appear in many domains and can't be fairly
148
+ // verified against any single spec.
149
+ for (const [file, domains] of fileDomains) {
150
+ if (domains.size === 1) {
151
+ this.fileDomainMap.set(file, [...domains][0]);
152
+ }
153
+ }
154
+ logger.analysis(`Loaded file→domain mapping for ${this.fileDomainMap.size} file(s)`);
155
+ }
156
+ catch {
157
+ // mapping.json not available — inferDomain falls back to path heuristics
158
+ }
159
+ }
160
+ /**
161
+ * Select verification candidate files
162
+ */
163
+ selectCandidates(depGraph) {
164
+ const candidates = [];
165
+ const usedPaths = new Set(this.options.generationContext);
166
+ // Group files by domain
167
+ const filesByDomain = new Map();
168
+ for (const node of depGraph.nodes) {
169
+ // Skip files used in generation
170
+ if (usedPaths.has(node.file.path))
171
+ continue;
172
+ // Skip test files
173
+ if (node.file.isTest)
174
+ continue;
175
+ // Skip generated files
176
+ if (node.file.isGenerated)
177
+ continue;
178
+ // Skip non-source files (config, manifests, markup, data)
179
+ const ext = node.file.path.split('.').pop()?.toLowerCase() ?? '';
180
+ const sourceExts = new Set(['ts', 'tsx', 'js', 'jsx', 'py', 'go', 'rs', 'rb', 'java', 'cpp', 'c', 'cs', 'swift', 'kt']);
181
+ if (!sourceExts.has(ext))
182
+ continue;
183
+ // Skip files outside complexity range
184
+ if (node.file.lines < this.options.minComplexity)
185
+ continue;
186
+ if (node.file.lines > this.options.maxComplexity)
187
+ continue;
188
+ // Determine domain from path — skip files with no matching spec
189
+ // (only filter misc when specs are loaded; without specs every file maps to misc)
190
+ const domain = this.inferDomain(node.file.path);
191
+ if (domain === 'misc' && this.specs.length > 0)
192
+ continue;
193
+ if (!filesByDomain.has(domain)) {
194
+ filesByDomain.set(domain, []);
195
+ }
196
+ filesByDomain.get(domain).push(node);
197
+ }
198
+ // Select files from each domain
199
+ for (const [domain, nodes] of filesByDomain) {
200
+ // Prefer high-connectivity (core) files — they're what specs actually describe
201
+ // and are more likely to have docstrings. Leaf/utility nodes were previously
202
+ // preferred (ascending sort) but produced systematically low scores.
203
+ const sorted = nodes.sort((a, b) => {
204
+ const aConnectivity = a.metrics.inDegree + a.metrics.outDegree;
205
+ const bConnectivity = b.metrics.inDegree + b.metrics.outDegree;
206
+ return bConnectivity - aConnectivity;
207
+ });
208
+ // Take up to filesPerDomain
209
+ const selected = sorted.slice(0, this.options.filesPerDomain);
210
+ for (const node of selected) {
211
+ candidates.push({
212
+ path: node.file.path,
213
+ absolutePath: node.file.absolutePath,
214
+ domain,
215
+ usedInGeneration: false,
216
+ complexity: node.file.lines,
217
+ lines: node.file.lines,
218
+ imports: node.metrics.outDegree,
219
+ exports: node.exports.length,
220
+ });
221
+ }
222
+ }
223
+ return candidates;
224
+ }
225
+ /**
226
+ * Resolve the spec domain for a file.
227
+ *
228
+ * Priority:
229
+ * 1. mapping.json lookup — deterministic, built from the analysis run.
230
+ * 2. Path heuristic — walk segments, match against known spec domain names
231
+ * (exact, then prefix ≥4 chars to handle utils→utilities etc.).
232
+ * 3. Fallback — first meaningful non-structural segment.
233
+ */
234
+ inferDomain(filePath) {
235
+ // 1. Deterministic lookup from mapping.json
236
+ const mapped = this.fileDomainMap.get(filePath);
237
+ if (mapped)
238
+ return mapped;
239
+ // 2. Path-based matching against known spec domains
240
+ const knownDomains = this.specs.map(s => s.domain);
241
+ const structural = new Set(['src', 'lib', 'app', 'core', 'utils', 'helpers', 'common', 'shared']);
242
+ const rawParts = filePath.split('/');
243
+ const segments = rawParts.map((p, i) => i === rawParts.length - 1 ? p.replace(/\.[^.]+$/, '').toLowerCase() : p.toLowerCase());
244
+ // Exact match against known domains — iterate deepest-first (reverse) so that
245
+ // src/core/services/mcp-handlers/x.ts matches "mcp-handlers" not "services".
246
+ const reversed = [...segments].reverse();
247
+ for (const seg of reversed) {
248
+ if (!structural.has(seg) && knownDomains.includes(seg))
249
+ return seg;
250
+ }
251
+ for (const seg of reversed) {
252
+ if (structural.has(seg) && knownDomains.includes(seg))
253
+ return seg;
254
+ }
255
+ // Shared-prefix match (≥4 chars) — deepest-first, e.g. "utils"→"utilities"
256
+ const commonPrefixLen = (a, b) => {
257
+ let i = 0;
258
+ while (i < a.length && i < b.length && a[i] === b[i])
259
+ i++;
260
+ return i;
261
+ };
262
+ for (const seg of reversed) {
263
+ if (seg.length < 4)
264
+ continue;
265
+ const hit = knownDomains.find(d => commonPrefixLen(seg, d) >= 4);
266
+ if (hit)
267
+ return hit;
268
+ }
269
+ // No match found — return 'misc' rather than inventing a phantom domain
270
+ // from the filename (which would score 0% against a non-existent spec).
271
+ return 'misc';
272
+ }
273
+ /**
274
+ * Verify a single file
275
+ */
276
+ async verifyFile(candidate) {
277
+ // Read actual file first — content is passed to getPrediction for LLM-as-judge scoring
278
+ const fileContent = await readFile(candidate.absolutePath, 'utf-8');
279
+ const fileAnalysis = await this.parser.parseFile(candidate.absolutePath);
280
+ // Get prediction from LLM (includes spec accuracy score via LLM-as-judge)
281
+ const prediction = await this.getPrediction(candidate, fileContent);
282
+ // Compare prediction to actual
283
+ const purposeMatch = this.comparePurpose(prediction.predictedPurpose, fileContent, prediction.specAccuracyScore);
284
+ const importMatch = this.analyzeImportCoverage(fileAnalysis.imports.map(i => i.source), candidate.domain);
285
+ const exportMatch = this.compareExports(prediction.predictedExports, fileAnalysis.exports.map(e => e.name));
286
+ const requirementCoverage = this.analyzeRequirementCoverage(candidate.domain, fileContent, prediction.requirementCoverageScore);
287
+ // Calculate overall score
288
+ const overallScore = this.calculateOverallScore(purposeMatch, importMatch, exportMatch, requirementCoverage);
289
+ // Generate feedback
290
+ const feedback = this.generateFeedback(candidate, prediction, purposeMatch, importMatch, exportMatch, requirementCoverage);
291
+ return {
292
+ filePath: candidate.path,
293
+ domain: candidate.domain,
294
+ purposeMatch,
295
+ importMatch,
296
+ exportMatch,
297
+ requirementCoverage,
298
+ overallScore,
299
+ llmConfidence: prediction.confidence,
300
+ feedback,
301
+ };
302
+ }
303
+ /**
304
+ * Build specs context string capped at maxChars to avoid silent LLM token overflow.
305
+ * Specs are included in order; the last spec may be truncated if the budget is tight.
306
+ */
307
+ buildSpecsContext(maxChars) {
308
+ const parts = [];
309
+ let total = 0;
310
+ for (const s of this.specs) {
311
+ const header = `=== ${s.domain} (${s.path}) ===\n`;
312
+ const budget = maxChars - total - header.length;
313
+ if (budget <= 0)
314
+ break;
315
+ const body = s.content.length > budget
316
+ ? s.content.slice(0, budget) + '\n[truncated]'
317
+ : s.content;
318
+ parts.push(header + body);
319
+ total += header.length + body.length;
320
+ }
321
+ return parts.join('\n\n');
322
+ }
323
+ /**
324
+ * Get prediction from LLM.
325
+ *
326
+ * When fileContent is provided the prompt uses an LLM-as-judge approach:
327
+ * the model sees both the spec and the actual file content, and returns a
328
+ * specAccuracyScore (0–1) measuring how well the spec describes the file.
329
+ * This replaces the brittle Jaccard keyword-overlap used for purposeMatch.
330
+ */
331
+ async getPrediction(candidate, fileContent) {
332
+ // Prefer the candidate's own domain spec; fall back to full context if not found.
333
+ const domainSpec = this.specs.find(s => s.domain === candidate.domain);
334
+ const specsContent = domainSpec
335
+ ? `=== ${domainSpec.domain} (${domainSpec.path}) ===\n${domainSpec.content}`
336
+ : this.buildSpecsContext(24_000);
337
+ // Include a trimmed excerpt of the actual file so the LLM can score spec accuracy
338
+ const fileExcerpt = fileContent
339
+ ? `\n\n=== Actual file content (${candidate.path}) ===\n${fileContent.slice(0, 3000)}${fileContent.length > 3000 ? '\n[truncated]' : ''}`
340
+ : '';
341
+ const judgeInstruction = fileContent
342
+ ? `\nAlso set:
343
+ - "specAccuracyScore": float 0.0–1.0 — how accurately the spec describes this specific file's purpose and behavior (1.0 = spec perfectly describes this file, 0.0 = spec is irrelevant).
344
+ - "requirementCoverageScore": float 0.0–1.0 — of the requirements in the spec that are relevant to THIS file specifically, what fraction does the file actually implement? Ignore requirements that clearly belong to other files in the domain.`
345
+ : '';
346
+ const userPrompt = `Here are the specifications:
347
+
348
+ ${specsContent}${fileExcerpt}
349
+
350
+ Predict the contents of: ${candidate.path}
351
+
352
+ IMPORTANT: The specs may contain entries attributed to specific files using \`> \`path\`\` markers.
353
+ Focus ONLY on entries attributed to \`${candidate.path}\`. Ignore entries attributed to other files.
354
+ If no entries are attributed to this file, use only the general domain purpose.${judgeInstruction}
355
+
356
+ Respond in JSON:
357
+ {
358
+ "predictedPurpose": "...",
359
+ "predictedImports": ["...", "..."],
360
+ "predictedExports": ["...", "..."],
361
+ "predictedLogic": ["...", "..."],
362
+ "relatedRequirements": ["RequirementName1", "RequirementName2"],
363
+ "confidence": 0.0-1.0,
364
+ "specAccuracyScore": 0.0-1.0,
365
+ "requirementCoverageScore": 0.0-1.0,
366
+ "reasoning": "..."
367
+ }`;
368
+ try {
369
+ const prediction = await this.llm.completeJSON({
370
+ systemPrompt: PREDICTION_SYSTEM_PROMPT,
371
+ userPrompt,
372
+ temperature: 0.3,
373
+ maxTokens: VERIFICATION_PREDICTION_MAX_TOKENS,
374
+ });
375
+ return {
376
+ predictedPurpose: prediction.predictedPurpose ?? '',
377
+ predictedImports: prediction.predictedImports ?? [],
378
+ predictedExports: prediction.predictedExports ?? [],
379
+ predictedLogic: prediction.predictedLogic ?? [],
380
+ relatedRequirements: prediction.relatedRequirements ?? [],
381
+ confidence: prediction.confidence ?? 0.5,
382
+ specAccuracyScore: typeof prediction.specAccuracyScore === 'number' ? prediction.specAccuracyScore : undefined,
383
+ requirementCoverageScore: typeof prediction.requirementCoverageScore === 'number' ? prediction.requirementCoverageScore : undefined,
384
+ reasoning: prediction.reasoning ?? '',
385
+ };
386
+ }
387
+ catch (error) {
388
+ logger.warning(`Prediction failed for ${candidate.path}: ${error.message}`);
389
+ // Re-throw so verify() skips this file rather than recording a misleading 0% score
390
+ throw error;
391
+ }
392
+ }
393
+ /**
394
+ * Compare predicted purpose to actual file content.
395
+ *
396
+ * When specAccuracyScore is provided (LLM-as-judge), it is used directly as
397
+ * the similarity score — this is far more reliable than keyword overlap because
398
+ * the LLM has seen the actual file and can assess whether the spec describes it.
399
+ * Falls back to Jaccard keyword overlap when no LLM score is available.
400
+ */
401
+ comparePurpose(predicted, fileContent, specAccuracyScore) {
402
+ const actual = this.extractPurpose(fileContent);
403
+ const similarity = typeof specAccuracyScore === 'number'
404
+ ? specAccuracyScore
405
+ : this.calculateSimilarity(predicted, actual);
406
+ return { predicted, actual, similarity };
407
+ }
408
+ /**
409
+ * Extract purpose from file content (comments, docstrings)
410
+ */
411
+ extractPurpose(content) {
412
+ const lines = content.split('\n');
413
+ const parts = [];
414
+ // 1. Module-level JSDoc block (/** ... */)
415
+ let inBlockComment = false;
416
+ for (let i = 0; i < lines.length; i++) {
417
+ const trimmed = lines[i].trim();
418
+ if (trimmed.startsWith('/**')) {
419
+ inBlockComment = true;
420
+ continue;
421
+ }
422
+ if (trimmed.startsWith('*/') || trimmed.endsWith('*/')) {
423
+ inBlockComment = false;
424
+ break;
425
+ }
426
+ if (inBlockComment) {
427
+ const comment = trimmed.replace(/^\*\s*/, '').trim();
428
+ if (comment && !comment.startsWith('@'))
429
+ parts.push(comment);
430
+ }
431
+ // Single-line // comments near the top
432
+ if (trimmed.startsWith('//') && !inBlockComment && parts.length < 3 && i < 30) {
433
+ parts.push(trimmed.replace(/^\/\/\s*/, ''));
434
+ }
435
+ }
436
+ // 2. Exported identifier names — split camelCase/PascalCase/snake_case into words.
437
+ // This gives the verifier vocabulary to match against even when comments are absent.
438
+ // E.g. "readOpenLoreConfig" → "read Spec Gen Config"; "OPENLORE_DIR" → "spec gen dir".
439
+ const exportMatches = content.matchAll(/^export\s+(?:default\s+)?(?:async\s+)?(?:function|class|const|let|var|interface|type|enum)\s+(\w+)/gm);
440
+ const identWords = [];
441
+ for (const m of exportMatches) {
442
+ const name = m[1];
443
+ // Split on underscores and camelCase boundaries
444
+ const words = name
445
+ .replace(/_+/g, ' ')
446
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
447
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
448
+ .toLowerCase()
449
+ .split(/\s+/)
450
+ .filter(w => w.length > 2);
451
+ identWords.push(...words);
452
+ }
453
+ if (identWords.length > 0) {
454
+ parts.push(identWords.join(' '));
455
+ }
456
+ return parts.join(' ').slice(0, 800);
457
+ }
458
+ /**
459
+ * Calculate text similarity using keyword overlap
460
+ */
461
+ calculateSimilarity(text1, text2) {
462
+ if (!text1 || !text2)
463
+ return 0;
464
+ const words1 = this.extractKeywords(text1);
465
+ const words2 = this.extractKeywords(text2);
466
+ if (words1.size === 0 || words2.size === 0)
467
+ return 0;
468
+ let matches = 0;
469
+ for (const word of words1) {
470
+ if (words2.has(word))
471
+ matches++;
472
+ }
473
+ // Jaccard similarity
474
+ const union = new Set([...words1, ...words2]);
475
+ return matches / union.size;
476
+ }
477
+ /**
478
+ * Normalize a word for similarity comparison by truncating to its first 5
479
+ * characters. This is more robust than suffix-stripping for technical
480
+ * English: "generate/generates/generating/generation" all share the prefix
481
+ * "gener", "verify/verification/verifies" share "verif", etc.
482
+ * Tested against 26 word pairs: 18/26 correct matches, 0 false positives.
483
+ */
484
+ normalize(word) {
485
+ return word.slice(0, 5);
486
+ }
487
+ /**
488
+ * Extract keywords from text
489
+ */
490
+ extractKeywords(text) {
491
+ const words = text
492
+ .toLowerCase()
493
+ .replace(/[^a-z0-9\s]/g, ' ')
494
+ .split(/\s+/)
495
+ .filter(w => w.length > 3);
496
+ // Filter out common words
497
+ const stopwords = new Set(['the', 'and', 'for', 'this', 'that', 'with', 'are', 'from', 'has', 'have', 'will', 'can', 'all', 'each', 'which', 'when', 'there', 'been', 'being', 'their', 'would', 'could', 'should']);
498
+ return new Set(words.filter(w => !stopwords.has(w)).map(w => this.normalize(w)));
499
+ }
500
+ /**
501
+ * Analyze import coverage using spec content rather than LLM predictions.
502
+ * For each actual import (normalized to module name), checks whether it is
503
+ * mentioned in the domain's spec text (exact name or hyphen→space variant).
504
+ * This is a spec-completeness check: are the modules the file depends on
505
+ * actually described in the spec?
506
+ *
507
+ * Returns a SetMatch where:
508
+ * - actual = all normalized actual import module names
509
+ * - predicted = subset of actual imports that appear in the spec text
510
+ * - f1Score = recall = fraction of actual imports covered by spec
511
+ */
512
+ analyzeImportCoverage(actualImports, domain) {
513
+ const normalized = actualImports.map(a => this.normalizeImport(a));
514
+ const spec = this.specs.find(s => s.domain === domain);
515
+ const specLower = spec ? spec.content.toLowerCase() : '';
516
+ const covered = [];
517
+ if (specLower.length > 0) {
518
+ for (const name of normalized) {
519
+ if (!name || name.length < 2)
520
+ continue;
521
+ // Match literal (e.g. "config-manager") or with spaces (e.g. "config manager")
522
+ if (specLower.includes(name) || specLower.includes(name.replace(/-/g, ' '))) {
523
+ covered.push(name);
524
+ }
525
+ }
526
+ }
527
+ const total = normalized.length;
528
+ const coverage = total > 0 ? covered.length / total : 0;
529
+ return {
530
+ predicted: covered, // imports mentioned in spec
531
+ actual: normalized, // all actual imports
532
+ precision: coverage,
533
+ recall: coverage,
534
+ f1Score: coverage,
535
+ };
536
+ }
537
+ /**
538
+ * Normalize import path for comparison.
539
+ * Strips file extensions and the first leading `./` or `../` prefix,
540
+ * then extracts the final path segment (module name) in lowercase.
541
+ * Deeply-nested relative paths (e.g. `../../foo`) are handled correctly
542
+ * because only the last segment is used for comparison.
543
+ */
544
+ normalizeImport(importPath) {
545
+ const normalized = importPath
546
+ .replace(/\.(js|ts|jsx|tsx|mjs|cjs)$/, '')
547
+ .replace(/^\.\//, '')
548
+ .replace(/^\.\.\//, '');
549
+ const parts = normalized.split('/');
550
+ return parts[parts.length - 1].toLowerCase();
551
+ }
552
+ /**
553
+ * Compare predicted exports to actual
554
+ */
555
+ compareExports(predicted, actual) {
556
+ return this.calculateSetMatch(predicted.map(p => p.toLowerCase()), actual.map(a => a.toLowerCase()));
557
+ }
558
+ /**
559
+ * Calculate precision, recall, F1 for set comparison
560
+ */
561
+ calculateSetMatch(predicted, actual) {
562
+ const predictedSet = new Set(predicted);
563
+ const actualSet = new Set(actual);
564
+ let truePositives = 0;
565
+ for (const p of predictedSet) {
566
+ if (actualSet.has(p))
567
+ truePositives++;
568
+ }
569
+ const precision = predictedSet.size > 0 ? truePositives / predictedSet.size : 0;
570
+ const recall = actualSet.size > 0 ? truePositives / actualSet.size : 0;
571
+ const f1Score = precision + recall > 0 ? (2 * precision * recall) / (precision + recall) : 0;
572
+ return {
573
+ predicted,
574
+ actual,
575
+ precision,
576
+ recall,
577
+ f1Score,
578
+ };
579
+ }
580
+ /**
581
+ * Parse requirements from a spec's markdown content.
582
+ * Returns an array of { name, description } extracted from
583
+ * "### Requirement: Name\n\nThe system SHALL ..." blocks.
584
+ */
585
+ parseSpecRequirements(specContent) {
586
+ const requirements = [];
587
+ const lines = specContent.split('\n');
588
+ for (let i = 0; i < lines.length; i++) {
589
+ const m = lines[i].match(/^###\s+Requirement:\s+(.+)/i);
590
+ if (!m)
591
+ continue;
592
+ const name = m[1].trim();
593
+ // Look ahead for the description line (first non-empty line after the heading)
594
+ let description = '';
595
+ for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
596
+ const l = lines[j].trim();
597
+ if (l.length > 0) {
598
+ description = l;
599
+ break;
600
+ }
601
+ }
602
+ if (name)
603
+ requirements.push({ name, description });
604
+ }
605
+ return requirements;
606
+ }
607
+ /**
608
+ * Analyze requirement coverage.
609
+ *
610
+ * When llmScore is provided (LLM-as-judge), it is used directly — the LLM
611
+ * has seen both the spec and the file and scores only the requirements
612
+ * relevant to this specific file, avoiding the false penalty of a domain
613
+ * spec covering many files where each file implements only a small subset.
614
+ *
615
+ * Falls back to keyword matching when no LLM score is available.
616
+ */
617
+ analyzeRequirementCoverage(domain, fileContent, llmScore) {
618
+ const spec = this.specs.find(s => s.domain === domain);
619
+ if (!spec) {
620
+ return { relatedRequirements: [], actuallyImplements: [], coverage: 0 };
621
+ }
622
+ const requirements = this.parseSpecRequirements(spec.content);
623
+ const relatedRequirements = requirements.map(r => r.name);
624
+ // LLM-as-judge: use the score directly, synthesize actuallyImplements proportionally
625
+ if (typeof llmScore === 'number') {
626
+ const implementedCount = Math.round(llmScore * requirements.length);
627
+ return {
628
+ relatedRequirements,
629
+ actuallyImplements: relatedRequirements.slice(0, implementedCount),
630
+ coverage: llmScore,
631
+ };
632
+ }
633
+ if (requirements.length === 0) {
634
+ return { relatedRequirements: [], actuallyImplements: [], coverage: 0 };
635
+ }
636
+ const contentLower = fileContent.toLowerCase();
637
+ const actuallyImplements = [];
638
+ for (const req of requirements) {
639
+ const source = req.description.length > 0 ? req.description : req.name;
640
+ const keywords = source
641
+ .toLowerCase()
642
+ .replace(/[^a-z0-9\s]/g, ' ')
643
+ .split(/\s+/)
644
+ .filter(w => w.length > 3 && !['shall', 'system', 'when', 'given', 'then', 'that', 'this', 'with', 'from', 'have', 'will'].includes(w));
645
+ if (keywords.length === 0)
646
+ continue;
647
+ const matched = keywords.filter(w => contentLower.includes(w));
648
+ if (matched.length >= Math.ceil(keywords.length * 0.5)) {
649
+ actuallyImplements.push(req.name);
650
+ }
651
+ }
652
+ const coverage = actuallyImplements.length / requirements.length;
653
+ return { relatedRequirements, actuallyImplements, coverage };
654
+ }
655
+ /**
656
+ * Calculate overall score (weighted combination)
657
+ */
658
+ calculateOverallScore(purposeMatch, importMatch, exportMatch, requirementCoverage) {
659
+ // Weighted combination (total = 1.0):
660
+ // Purpose: 50% — LLM-as-judge: how well the spec describes this file
661
+ // Requirements: 35% — LLM-as-judge: fraction of file-relevant requirements covered
662
+ // Imports: 5% — fraction of actual imports mentioned in spec
663
+ // (low weight: library deps are never in specs, so ceiling ~20%)
664
+ // Exports: 10% — F1 of LLM-predicted vs actual exports
665
+ return (purposeMatch.similarity * 0.50 +
666
+ requirementCoverage.coverage * 0.35 +
667
+ importMatch.f1Score * 0.05 +
668
+ exportMatch.f1Score * 0.10);
669
+ }
670
+ /**
671
+ * Generate feedback for gaps
672
+ */
673
+ generateFeedback(candidate, prediction, purposeMatch, importMatch, exportMatch, requirementCoverage) {
674
+ const feedback = [];
675
+ // Low purpose match
676
+ if (purposeMatch.similarity < 0.3) {
677
+ feedback.push(`Purpose mismatch: specs don't clearly describe what ${basename(candidate.path)} does`);
678
+ }
679
+ // Missing imports
680
+ const missingImports = importMatch.actual.filter(a => !importMatch.predicted.includes(a));
681
+ if (missingImports.length > 0) {
682
+ feedback.push(`Missing dependencies: specs don't mention ${missingImports.slice(0, 3).join(', ')}`);
683
+ }
684
+ // Missing exports
685
+ const missingExports = exportMatch.actual.filter(a => !exportMatch.predicted.includes(a));
686
+ if (missingExports.length > 0) {
687
+ feedback.push(`Undocumented exports: ${missingExports.slice(0, 3).join(', ')} not described in specs`);
688
+ }
689
+ // Low requirement coverage
690
+ if (requirementCoverage.coverage < 0.5 && prediction.relatedRequirements.length > 0) {
691
+ const missing = prediction.relatedRequirements.filter(r => !requirementCoverage.actuallyImplements.includes(r));
692
+ if (missing.length > 0) {
693
+ feedback.push(`Requirements ${missing.slice(0, 2).join(', ')} don't appear to be implemented in this file`);
694
+ }
695
+ }
696
+ // Low confidence from LLM
697
+ if (prediction.confidence < 0.5) {
698
+ feedback.push(`LLM had low confidence: "${prediction.reasoning}"`);
699
+ }
700
+ return feedback;
701
+ }
702
+ /**
703
+ * Generate verification report
704
+ */
705
+ generateReport(results, specVersion) {
706
+ const passedFiles = results.filter(r => r.overallScore >= this.options.passThreshold).length;
707
+ const overallConfidence = results.length > 0
708
+ ? results.reduce((sum, r) => sum + r.overallScore, 0) / results.length
709
+ : 0;
710
+ // Domain breakdown
711
+ const domainResults = new Map();
712
+ for (const result of results) {
713
+ if (!domainResults.has(result.domain)) {
714
+ domainResults.set(result.domain, []);
715
+ }
716
+ domainResults.get(result.domain).push(result);
717
+ }
718
+ const domainBreakdown = [];
719
+ for (const [domain, domainRes] of domainResults) {
720
+ const avgScore = domainRes.reduce((sum, r) => sum + r.overallScore, 0) / domainRes.length;
721
+ // Find weakest area
722
+ const avgPurpose = domainRes.reduce((sum, r) => sum + r.purposeMatch.similarity, 0) / domainRes.length;
723
+ const avgImport = domainRes.reduce((sum, r) => sum + r.importMatch.f1Score, 0) / domainRes.length;
724
+ const avgExport = domainRes.reduce((sum, r) => sum + r.exportMatch.f1Score, 0) / domainRes.length;
725
+ const avgReq = domainRes.reduce((sum, r) => sum + r.requirementCoverage.coverage, 0) / domainRes.length;
726
+ const areas = [
727
+ { name: 'purpose', score: avgPurpose },
728
+ { name: 'imports', score: avgImport },
729
+ { name: 'exports', score: avgExport },
730
+ { name: 'requirements', score: avgReq },
731
+ ];
732
+ const weakest = areas.sort((a, b) => a.score - b.score)[0];
733
+ domainBreakdown.push({
734
+ domain,
735
+ specPath: `openspec/specs/${domain}/spec.md`,
736
+ filesVerified: domainRes.length,
737
+ averageScore: avgScore,
738
+ weakestArea: weakest.name,
739
+ });
740
+ }
741
+ // Common gaps
742
+ const allFeedback = results.flatMap(r => r.feedback);
743
+ const feedbackCounts = new Map();
744
+ for (const fb of allFeedback) {
745
+ // Normalize feedback for grouping
746
+ const key = fb.split(':')[0];
747
+ feedbackCounts.set(key, (feedbackCounts.get(key) ?? 0) + 1);
748
+ }
749
+ const commonGaps = Array.from(feedbackCounts.entries())
750
+ .filter(([_, count]) => count >= 2)
751
+ .sort((a, b) => b[1] - a[1])
752
+ .slice(0, 5)
753
+ .map(([gap, _]) => gap);
754
+ // Suggested improvements
755
+ const suggestedImprovements = [];
756
+ for (const breakdown of domainBreakdown) {
757
+ if (breakdown.averageScore < 0.7) {
758
+ suggestedImprovements.push({
759
+ domain: breakdown.domain,
760
+ issue: `Low verification score (${(breakdown.averageScore * 100).toFixed(0)}%)`,
761
+ suggestion: `Review and enhance ${breakdown.specPath}, especially ${breakdown.weakestArea} descriptions`,
762
+ });
763
+ }
764
+ }
765
+ // Recommendation
766
+ let recommendation;
767
+ if (overallConfidence >= 0.75) {
768
+ recommendation = 'ready';
769
+ }
770
+ else if (overallConfidence >= 0.5) {
771
+ recommendation = 'needs-review';
772
+ }
773
+ else {
774
+ recommendation = 'regenerate';
775
+ }
776
+ return {
777
+ timestamp: new Date().toLocaleString(),
778
+ specVersion,
779
+ sampledFiles: results.length,
780
+ passedFiles,
781
+ overallConfidence,
782
+ domainBreakdown,
783
+ commonGaps,
784
+ recommendation,
785
+ suggestedImprovements,
786
+ results,
787
+ };
788
+ }
789
+ /**
790
+ * Save verification report
791
+ */
792
+ async saveReport(report) {
793
+ await mkdir(this.options.outputDir, { recursive: true });
794
+ // Save JSON report
795
+ const jsonPath = join(this.options.outputDir, 'report.json');
796
+ await writeFile(jsonPath, JSON.stringify(report, null, 2), 'utf-8');
797
+ logger.discovery(`Saved JSON report to ${relative(this.options.rootPath, jsonPath)}`);
798
+ // Save Markdown report
799
+ const mdPath = join(this.options.outputDir, 'REPORT.md');
800
+ const markdown = this.generateMarkdownReport(report);
801
+ await writeFile(mdPath, markdown, 'utf-8');
802
+ logger.discovery(`Saved Markdown report to ${relative(this.options.rootPath, mdPath)}`);
803
+ }
804
+ /**
805
+ * Generate markdown report
806
+ */
807
+ generateMarkdownReport(report) {
808
+ const lines = [];
809
+ lines.push('# Spec Verification Report');
810
+ lines.push('');
811
+ lines.push(`Generated: ${report.timestamp}`);
812
+ lines.push(`Spec Version: ${report.specVersion}`);
813
+ lines.push('');
814
+ // Summary
815
+ lines.push('## Summary');
816
+ lines.push('');
817
+ lines.push(`| Metric | Value |`);
818
+ lines.push(`|--------|-------|`);
819
+ lines.push(`| Files Verified | ${report.sampledFiles} |`);
820
+ lines.push(`| Files Passed | ${report.passedFiles} (${report.sampledFiles > 0 ? ((report.passedFiles / report.sampledFiles) * 100).toFixed(0) : 'N/A'}%) |`);
821
+ lines.push(`| Overall Confidence | ${(report.overallConfidence * 100).toFixed(1)}% |`);
822
+ lines.push(`| Recommendation | **${report.recommendation}** |`);
823
+ lines.push('');
824
+ // Recommendation explanation
825
+ lines.push('### Recommendation');
826
+ if (report.recommendation === 'ready') {
827
+ lines.push('✅ Specs accurately describe the codebase and are ready for use.');
828
+ }
829
+ else if (report.recommendation === 'needs-review') {
830
+ lines.push('⚠️ Specs need review. Some gaps were identified that should be addressed.');
831
+ }
832
+ else {
833
+ lines.push('❌ Specs have significant gaps. Consider regenerating with improved context.');
834
+ }
835
+ lines.push('');
836
+ // Domain breakdown
837
+ lines.push('## Domain Breakdown');
838
+ lines.push('');
839
+ lines.push('| Domain | Spec Path | Files | Avg Score | Weakest Area |');
840
+ lines.push('|--------|-----------|-------|-----------|--------------|');
841
+ for (const domain of report.domainBreakdown) {
842
+ const scorePercent = (domain.averageScore * 100).toFixed(0);
843
+ lines.push(`| ${domain.domain} | ${domain.specPath} | ${domain.filesVerified} | ${scorePercent}% | ${domain.weakestArea} |`);
844
+ }
845
+ lines.push('');
846
+ // Common gaps
847
+ if (report.commonGaps.length > 0) {
848
+ lines.push('## Common Gaps');
849
+ lines.push('');
850
+ for (const gap of report.commonGaps) {
851
+ lines.push(`- ${gap}`);
852
+ }
853
+ lines.push('');
854
+ }
855
+ // Suggested improvements
856
+ if (report.suggestedImprovements.length > 0) {
857
+ lines.push('## Suggested Improvements');
858
+ lines.push('');
859
+ for (const improvement of report.suggestedImprovements) {
860
+ lines.push(`### ${improvement.domain}`);
861
+ lines.push(`- **Issue**: ${improvement.issue}`);
862
+ lines.push(`- **Suggestion**: ${improvement.suggestion}`);
863
+ lines.push('');
864
+ }
865
+ }
866
+ // Detailed results
867
+ lines.push('## Detailed Results');
868
+ lines.push('');
869
+ for (const result of report.results) {
870
+ const scorePercent = (result.overallScore * 100).toFixed(0);
871
+ const status = result.overallScore >= this.options.passThreshold ? '✅' : '❌';
872
+ lines.push(`### ${status} ${result.filePath}`);
873
+ lines.push('');
874
+ lines.push(`- **Domain**: ${result.domain}`);
875
+ lines.push(`- **Overall Score**: ${scorePercent}%`);
876
+ lines.push(`- **LLM Confidence**: ${(result.llmConfidence * 100).toFixed(0)}%`);
877
+ lines.push('');
878
+ lines.push('| Category | Score |');
879
+ lines.push('|----------|-------|');
880
+ lines.push(`| Purpose Match | ${(result.purposeMatch.similarity * 100).toFixed(0)}% |`);
881
+ lines.push(`| Import Match (F1) | ${(result.importMatch.f1Score * 100).toFixed(0)}% |`);
882
+ lines.push(`| Export Match (F1) | ${(result.exportMatch.f1Score * 100).toFixed(0)}% |`);
883
+ lines.push(`| Requirement Coverage | ${(result.requirementCoverage.coverage * 100).toFixed(0)}% |`);
884
+ lines.push('');
885
+ if (result.feedback.length > 0) {
886
+ lines.push('**Feedback:**');
887
+ for (const fb of result.feedback) {
888
+ lines.push(`- ${fb}`);
889
+ }
890
+ lines.push('');
891
+ }
892
+ }
893
+ lines.push('---');
894
+ lines.push('*Generated by openlore verify*');
895
+ return lines.join('\n');
896
+ }
897
+ /**
898
+ * Get list of domains from loaded specs.
899
+ * If specs have not been loaded yet (i.e., verify() has not been called),
900
+ * triggers an eager load so callers can preview domains without a full LLM run.
901
+ */
902
+ async getDomains() {
903
+ if (this.specs.length === 0) {
904
+ await this.loadSpecs();
905
+ }
906
+ return this.specs.map(s => s.domain);
907
+ }
908
+ }
909
+ // ============================================================================
910
+ // CONVENIENCE FUNCTIONS
911
+ // ============================================================================
912
+ /**
913
+ * Run verification on a project
914
+ */
915
+ export async function verifySpecs(llm, depGraph, options, specVersion) {
916
+ const engine = new SpecVerificationEngine(llm, options);
917
+ return engine.verify(depGraph, specVersion);
918
+ }
919
+ //# sourceMappingURL=verification-engine.js.map