@yasserkhanorg/impact-gate 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 (587) hide show
  1. package/LICENSE +168 -0
  2. package/README.md +520 -0
  3. package/dist/adapters/cypress.d.ts +10 -0
  4. package/dist/adapters/cypress.d.ts.map +1 -0
  5. package/dist/adapters/cypress.js +86 -0
  6. package/dist/adapters/framework_adapter.d.ts +41 -0
  7. package/dist/adapters/framework_adapter.d.ts.map +1 -0
  8. package/dist/adapters/framework_adapter.js +152 -0
  9. package/dist/adapters/playwright.d.ts +10 -0
  10. package/dist/adapters/playwright.d.ts.map +1 -0
  11. package/dist/adapters/playwright.js +86 -0
  12. package/dist/adapters/pytest.d.ts +10 -0
  13. package/dist/adapters/pytest.d.ts.map +1 -0
  14. package/dist/adapters/pytest.js +96 -0
  15. package/dist/adapters/supertest.d.ts +12 -0
  16. package/dist/adapters/supertest.d.ts.map +1 -0
  17. package/dist/adapters/supertest.js +85 -0
  18. package/dist/agent/api_catalog.d.ts +11 -0
  19. package/dist/agent/api_catalog.d.ts.map +1 -0
  20. package/dist/agent/api_catalog.js +210 -0
  21. package/dist/agent/config.d.ts +193 -0
  22. package/dist/agent/config.d.ts.map +1 -0
  23. package/dist/agent/config.js +875 -0
  24. package/dist/agent/feedback.d.ts +91 -0
  25. package/dist/agent/feedback.d.ts.map +1 -0
  26. package/dist/agent/feedback.js +323 -0
  27. package/dist/agent/git.d.ts +19 -0
  28. package/dist/agent/git.d.ts.map +1 -0
  29. package/dist/agent/git.js +257 -0
  30. package/dist/agent/handoff.d.ts +22 -0
  31. package/dist/agent/handoff.d.ts.map +1 -0
  32. package/dist/agent/handoff.js +180 -0
  33. package/dist/agent/llm_agents_flow.d.ts +15 -0
  34. package/dist/agent/llm_agents_flow.d.ts.map +1 -0
  35. package/dist/agent/llm_agents_flow.js +434 -0
  36. package/dist/agent/native_flow.d.ts +6 -0
  37. package/dist/agent/native_flow.d.ts.map +1 -0
  38. package/dist/agent/native_flow.js +179 -0
  39. package/dist/agent/pipeline.d.ts +7 -0
  40. package/dist/agent/pipeline.d.ts.map +1 -0
  41. package/dist/agent/pipeline.js +260 -0
  42. package/dist/agent/pipeline_types.d.ts +54 -0
  43. package/dist/agent/pipeline_types.d.ts.map +1 -0
  44. package/dist/agent/pipeline_types.js +4 -0
  45. package/dist/agent/pipeline_utils.d.ts +12 -0
  46. package/dist/agent/pipeline_utils.d.ts.map +1 -0
  47. package/dist/agent/pipeline_utils.js +156 -0
  48. package/dist/agent/plan.d.ts +170 -0
  49. package/dist/agent/plan.d.ts.map +1 -0
  50. package/dist/agent/plan.js +86 -0
  51. package/dist/agent/playwright_report.d.ts +8 -0
  52. package/dist/agent/playwright_report.d.ts.map +1 -0
  53. package/dist/agent/playwright_report.js +126 -0
  54. package/dist/agent/process_runner.d.ts +10 -0
  55. package/dist/agent/process_runner.d.ts.map +1 -0
  56. package/dist/agent/process_runner.js +92 -0
  57. package/dist/agent/spec_generator.d.ts +5 -0
  58. package/dist/agent/spec_generator.d.ts.map +1 -0
  59. package/dist/agent/spec_generator.js +253 -0
  60. package/dist/agent/test_path.d.ts +2 -0
  61. package/dist/agent/test_path.d.ts.map +1 -0
  62. package/dist/agent/test_path.js +23 -0
  63. package/dist/agent/traceability_capture.d.ts +18 -0
  64. package/dist/agent/traceability_capture.d.ts.map +1 -0
  65. package/dist/agent/traceability_capture.js +313 -0
  66. package/dist/agent/traceability_ingest.d.ts +21 -0
  67. package/dist/agent/traceability_ingest.d.ts.map +1 -0
  68. package/dist/agent/traceability_ingest.js +237 -0
  69. package/dist/agent/types.d.ts +42 -0
  70. package/dist/agent/types.d.ts.map +1 -0
  71. package/dist/agent/types.js +4 -0
  72. package/dist/agent/utils.d.ts +13 -0
  73. package/dist/agent/utils.d.ts.map +1 -0
  74. package/dist/agent/utils.js +152 -0
  75. package/dist/agent/validation_runner.d.ts +5 -0
  76. package/dist/agent/validation_runner.d.ts.map +1 -0
  77. package/dist/agent/validation_runner.js +77 -0
  78. package/dist/agentic/fix_loop.d.ts +26 -0
  79. package/dist/agentic/fix_loop.d.ts.map +1 -0
  80. package/dist/agentic/fix_loop.js +96 -0
  81. package/dist/agentic/playwright_runner.d.ts +43 -0
  82. package/dist/agentic/playwright_runner.d.ts.map +1 -0
  83. package/dist/agentic/playwright_runner.js +165 -0
  84. package/dist/agentic/runner.d.ts +27 -0
  85. package/dist/agentic/runner.d.ts.map +1 -0
  86. package/dist/agentic/runner.js +210 -0
  87. package/dist/agentic/types.d.ts +62 -0
  88. package/dist/agentic/types.d.ts.map +1 -0
  89. package/dist/agentic/types.js +4 -0
  90. package/dist/agents/coverage-evaluator.d.ts +8 -0
  91. package/dist/agents/coverage-evaluator.d.ts.map +1 -0
  92. package/dist/agents/coverage-evaluator.js +41 -0
  93. package/dist/agents/cross-impact.d.ts +13 -0
  94. package/dist/agents/cross-impact.d.ts.map +1 -0
  95. package/dist/agents/cross-impact.js +140 -0
  96. package/dist/agents/executor.d.ts +8 -0
  97. package/dist/agents/executor.d.ts.map +1 -0
  98. package/dist/agents/executor.js +75 -0
  99. package/dist/agents/explorer.d.ts +12 -0
  100. package/dist/agents/explorer.d.ts.map +1 -0
  101. package/dist/agents/explorer.js +43 -0
  102. package/dist/agents/generator.d.ts +8 -0
  103. package/dist/agents/generator.d.ts.map +1 -0
  104. package/dist/agents/generator.js +77 -0
  105. package/dist/agents/healer.d.ts +8 -0
  106. package/dist/agents/healer.d.ts.map +1 -0
  107. package/dist/agents/healer.js +31 -0
  108. package/dist/agents/impact-analyst.d.ts +8 -0
  109. package/dist/agents/impact-analyst.d.ts.map +1 -0
  110. package/dist/agents/impact-analyst.js +38 -0
  111. package/dist/agents/regression-advisor.d.ts +8 -0
  112. package/dist/agents/regression-advisor.d.ts.map +1 -0
  113. package/dist/agents/regression-advisor.js +116 -0
  114. package/dist/agents/strategist.d.ts +9 -0
  115. package/dist/agents/strategist.d.ts.map +1 -0
  116. package/dist/agents/strategist.js +92 -0
  117. package/dist/agents/test-designer.d.ts +8 -0
  118. package/dist/agents/test-designer.d.ts.map +1 -0
  119. package/dist/agents/test-designer.js +111 -0
  120. package/dist/anthropic_provider.d.ts +65 -0
  121. package/dist/anthropic_provider.d.ts.map +1 -0
  122. package/dist/anthropic_provider.js +334 -0
  123. package/dist/api.d.ts +48 -0
  124. package/dist/api.d.ts.map +1 -0
  125. package/dist/api.js +151 -0
  126. package/dist/base_provider.d.ts +109 -0
  127. package/dist/base_provider.d.ts.map +1 -0
  128. package/dist/base_provider.js +203 -0
  129. package/dist/budget_ledger.d.ts +28 -0
  130. package/dist/budget_ledger.d.ts.map +1 -0
  131. package/dist/budget_ledger.js +62 -0
  132. package/dist/cache/cached_provider.d.ts +49 -0
  133. package/dist/cache/cached_provider.d.ts.map +1 -0
  134. package/dist/cache/cached_provider.js +91 -0
  135. package/dist/cache/response_cache.d.ts +79 -0
  136. package/dist/cache/response_cache.d.ts.map +1 -0
  137. package/dist/cache/response_cache.js +177 -0
  138. package/dist/cli/commands/analyze.d.ts +3 -0
  139. package/dist/cli/commands/analyze.d.ts.map +1 -0
  140. package/dist/cli/commands/analyze.js +77 -0
  141. package/dist/cli/commands/bootstrap.d.ts +3 -0
  142. package/dist/cli/commands/bootstrap.d.ts.map +1 -0
  143. package/dist/cli/commands/bootstrap.js +109 -0
  144. package/dist/cli/commands/cost_report.d.ts +3 -0
  145. package/dist/cli/commands/cost_report.d.ts.map +1 -0
  146. package/dist/cli/commands/cost_report.js +115 -0
  147. package/dist/cli/commands/crew.d.ts +3 -0
  148. package/dist/cli/commands/crew.d.ts.map +1 -0
  149. package/dist/cli/commands/crew.js +255 -0
  150. package/dist/cli/commands/feedback.d.ts +3 -0
  151. package/dist/cli/commands/feedback.d.ts.map +1 -0
  152. package/dist/cli/commands/feedback.js +39 -0
  153. package/dist/cli/commands/finalize.d.ts +3 -0
  154. package/dist/cli/commands/finalize.d.ts.map +1 -0
  155. package/dist/cli/commands/finalize.js +41 -0
  156. package/dist/cli/commands/gate.d.ts +3 -0
  157. package/dist/cli/commands/gate.d.ts.map +1 -0
  158. package/dist/cli/commands/gate.js +89 -0
  159. package/dist/cli/commands/generate.d.ts +4 -0
  160. package/dist/cli/commands/generate.d.ts.map +1 -0
  161. package/dist/cli/commands/generate.js +108 -0
  162. package/dist/cli/commands/heal.d.ts +3 -0
  163. package/dist/cli/commands/heal.d.ts.map +1 -0
  164. package/dist/cli/commands/heal.js +60 -0
  165. package/dist/cli/commands/impact.d.ts +4 -0
  166. package/dist/cli/commands/impact.d.ts.map +1 -0
  167. package/dist/cli/commands/impact.js +33 -0
  168. package/dist/cli/commands/init.d.ts +2 -0
  169. package/dist/cli/commands/init.d.ts.map +1 -0
  170. package/dist/cli/commands/init.js +169 -0
  171. package/dist/cli/commands/llm_health.d.ts +2 -0
  172. package/dist/cli/commands/llm_health.d.ts.map +1 -0
  173. package/dist/cli/commands/llm_health.js +22 -0
  174. package/dist/cli/commands/plan.d.ts +4 -0
  175. package/dist/cli/commands/plan.d.ts.map +1 -0
  176. package/dist/cli/commands/plan.js +120 -0
  177. package/dist/cli/commands/plan_crew.d.ts +17 -0
  178. package/dist/cli/commands/plan_crew.d.ts.map +1 -0
  179. package/dist/cli/commands/plan_crew.js +316 -0
  180. package/dist/cli/commands/traceability.d.ts +4 -0
  181. package/dist/cli/commands/traceability.d.ts.map +1 -0
  182. package/dist/cli/commands/traceability.js +77 -0
  183. package/dist/cli/commands/train.d.ts +3 -0
  184. package/dist/cli/commands/train.d.ts.map +1 -0
  185. package/dist/cli/commands/train.js +391 -0
  186. package/dist/cli/defaults.d.ts +35 -0
  187. package/dist/cli/defaults.d.ts.map +1 -0
  188. package/dist/cli/defaults.js +172 -0
  189. package/dist/cli/errors.d.ts +27 -0
  190. package/dist/cli/errors.d.ts.map +1 -0
  191. package/dist/cli/errors.js +57 -0
  192. package/dist/cli/parse_args.d.ts +6 -0
  193. package/dist/cli/parse_args.d.ts.map +1 -0
  194. package/dist/cli/parse_args.js +257 -0
  195. package/dist/cli/types.d.ts +87 -0
  196. package/dist/cli/types.d.ts.map +1 -0
  197. package/dist/cli/types.js +4 -0
  198. package/dist/cli/usage.d.ts +2 -0
  199. package/dist/cli/usage.d.ts.map +1 -0
  200. package/dist/cli/usage.js +109 -0
  201. package/dist/cli.d.ts +3 -0
  202. package/dist/cli.d.ts.map +1 -0
  203. package/dist/cli.js +194 -0
  204. package/dist/crew/context.d.ts +55 -0
  205. package/dist/crew/context.d.ts.map +1 -0
  206. package/dist/crew/context.js +36 -0
  207. package/dist/crew/orchestrator.d.ts +50 -0
  208. package/dist/crew/orchestrator.d.ts.map +1 -0
  209. package/dist/crew/orchestrator.js +329 -0
  210. package/dist/crew/protocol.d.ts +46 -0
  211. package/dist/crew/protocol.d.ts.map +1 -0
  212. package/dist/crew/protocol.js +4 -0
  213. package/dist/crew/provider.d.ts +17 -0
  214. package/dist/crew/provider.d.ts.map +1 -0
  215. package/dist/crew/provider.js +36 -0
  216. package/dist/crew/sanitize.d.ts +3 -0
  217. package/dist/crew/sanitize.d.ts.map +1 -0
  218. package/dist/crew/sanitize.js +31 -0
  219. package/dist/crew/types.d.ts +52 -0
  220. package/dist/crew/types.d.ts.map +1 -0
  221. package/dist/crew/types.js +4 -0
  222. package/dist/crew/workflows.d.ts +52 -0
  223. package/dist/crew/workflows.d.ts.map +1 -0
  224. package/dist/crew/workflows.js +36 -0
  225. package/dist/custom_provider.d.ts +20 -0
  226. package/dist/custom_provider.d.ts.map +1 -0
  227. package/dist/custom_provider.js +277 -0
  228. package/dist/engine/ai_enrichment.d.ts +44 -0
  229. package/dist/engine/ai_enrichment.d.ts.map +1 -0
  230. package/dist/engine/ai_enrichment.js +267 -0
  231. package/dist/engine/diff_loader.d.ts +11 -0
  232. package/dist/engine/diff_loader.d.ts.map +1 -0
  233. package/dist/engine/diff_loader.js +63 -0
  234. package/dist/engine/impact_engine.d.ts +72 -0
  235. package/dist/engine/impact_engine.d.ts.map +1 -0
  236. package/dist/engine/impact_engine.js +298 -0
  237. package/dist/engine/plan_builder.d.ts +11 -0
  238. package/dist/engine/plan_builder.d.ts.map +1 -0
  239. package/dist/engine/plan_builder.js +599 -0
  240. package/dist/esm/adapters/cypress.js +49 -0
  241. package/dist/esm/adapters/framework_adapter.js +114 -0
  242. package/dist/esm/adapters/playwright.js +49 -0
  243. package/dist/esm/adapters/pytest.js +59 -0
  244. package/dist/esm/adapters/supertest.js +48 -0
  245. package/dist/esm/agent/api_catalog.js +199 -0
  246. package/dist/esm/agent/config.js +872 -0
  247. package/dist/esm/agent/feedback.js +317 -0
  248. package/dist/esm/agent/git.js +252 -0
  249. package/dist/esm/agent/handoff.js +177 -0
  250. package/dist/esm/agent/llm_agents_flow.js +421 -0
  251. package/dist/esm/agent/native_flow.js +175 -0
  252. package/dist/esm/agent/pipeline.js +256 -0
  253. package/dist/esm/agent/pipeline_types.js +3 -0
  254. package/dist/esm/agent/pipeline_utils.js +146 -0
  255. package/dist/esm/agent/plan.js +83 -0
  256. package/dist/esm/agent/playwright_report.js +123 -0
  257. package/dist/esm/agent/process_runner.js +83 -0
  258. package/dist/esm/agent/spec_generator.js +249 -0
  259. package/dist/esm/agent/test_path.js +20 -0
  260. package/dist/esm/agent/traceability_capture.js +310 -0
  261. package/dist/esm/agent/traceability_ingest.js +234 -0
  262. package/dist/esm/agent/types.js +3 -0
  263. package/dist/esm/agent/utils.js +138 -0
  264. package/dist/esm/agent/validation_runner.js +73 -0
  265. package/dist/esm/agentic/fix_loop.js +91 -0
  266. package/dist/esm/agentic/playwright_runner.js +161 -0
  267. package/dist/esm/agentic/runner.js +207 -0
  268. package/dist/esm/agentic/types.js +3 -0
  269. package/dist/esm/agents/coverage-evaluator.js +37 -0
  270. package/dist/esm/agents/cross-impact.js +136 -0
  271. package/dist/esm/agents/executor.js +71 -0
  272. package/dist/esm/agents/explorer.js +39 -0
  273. package/dist/esm/agents/generator.js +73 -0
  274. package/dist/esm/agents/healer.js +27 -0
  275. package/dist/esm/agents/impact-analyst.js +34 -0
  276. package/dist/esm/agents/regression-advisor.js +112 -0
  277. package/dist/esm/agents/strategist.js +88 -0
  278. package/dist/esm/agents/test-designer.js +107 -0
  279. package/dist/esm/anthropic_provider.js +326 -0
  280. package/dist/esm/api.js +143 -0
  281. package/dist/esm/base_provider.js +198 -0
  282. package/dist/esm/budget_ledger.js +58 -0
  283. package/dist/esm/cache/cached_provider.js +85 -0
  284. package/dist/esm/cache/response_cache.js +140 -0
  285. package/dist/esm/cli/commands/analyze.js +74 -0
  286. package/dist/esm/cli/commands/bootstrap.js +106 -0
  287. package/dist/esm/cli/commands/cost_report.js +112 -0
  288. package/dist/esm/cli/commands/crew.js +252 -0
  289. package/dist/esm/cli/commands/feedback.js +36 -0
  290. package/dist/esm/cli/commands/finalize.js +38 -0
  291. package/dist/esm/cli/commands/gate.js +86 -0
  292. package/dist/esm/cli/commands/generate.js +105 -0
  293. package/dist/esm/cli/commands/heal.js +57 -0
  294. package/dist/esm/cli/commands/impact.js +30 -0
  295. package/dist/esm/cli/commands/init.js +133 -0
  296. package/dist/esm/cli/commands/llm_health.js +19 -0
  297. package/dist/esm/cli/commands/plan.js +117 -0
  298. package/dist/esm/cli/commands/plan_crew.js +309 -0
  299. package/dist/esm/cli/commands/traceability.js +73 -0
  300. package/dist/esm/cli/commands/train.js +355 -0
  301. package/dist/esm/cli/defaults.js +165 -0
  302. package/dist/esm/cli/errors.js +52 -0
  303. package/dist/esm/cli/parse_args.js +251 -0
  304. package/dist/esm/cli/types.js +3 -0
  305. package/dist/esm/cli/usage.js +106 -0
  306. package/dist/esm/cli.js +192 -0
  307. package/dist/esm/crew/context.js +32 -0
  308. package/dist/esm/crew/orchestrator.js +325 -0
  309. package/dist/esm/crew/protocol.js +3 -0
  310. package/dist/esm/crew/provider.js +33 -0
  311. package/dist/esm/crew/sanitize.js +27 -0
  312. package/dist/esm/crew/types.js +3 -0
  313. package/dist/esm/crew/workflows.js +33 -0
  314. package/dist/esm/custom_provider.js +273 -0
  315. package/dist/esm/engine/ai_enrichment.js +264 -0
  316. package/dist/esm/engine/diff_loader.js +59 -0
  317. package/dist/esm/engine/impact_engine.js +291 -0
  318. package/dist/esm/engine/plan_builder.js +593 -0
  319. package/dist/esm/index.js +72 -0
  320. package/dist/esm/knowledge/api_surface.js +408 -0
  321. package/dist/esm/knowledge/cluster_utils.js +60 -0
  322. package/dist/esm/knowledge/context_loader.js +85 -0
  323. package/dist/esm/knowledge/failure_history.js +121 -0
  324. package/dist/esm/knowledge/kg_bridge.js +381 -0
  325. package/dist/esm/knowledge/kg_types.js +3 -0
  326. package/dist/esm/knowledge/route_families.js +393 -0
  327. package/dist/esm/knowledge/spec_index.js +122 -0
  328. package/dist/esm/logger.js +115 -0
  329. package/dist/esm/mcp-server.js +621 -0
  330. package/dist/esm/metrics/prometheus.js +149 -0
  331. package/dist/esm/model_router.js +59 -0
  332. package/dist/esm/ollama_provider.js +301 -0
  333. package/dist/esm/openai_provider.js +243 -0
  334. package/dist/esm/package.json +3 -0
  335. package/dist/esm/pipeline/orchestrator.js +228 -0
  336. package/dist/esm/pipeline/spec_verifier.js +75 -0
  337. package/dist/esm/pipeline/stage0_preprocess.js +102 -0
  338. package/dist/esm/pipeline/stage1_impact.js +140 -0
  339. package/dist/esm/pipeline/stage2_coverage.js +153 -0
  340. package/dist/esm/pipeline/stage3_generation.js +284 -0
  341. package/dist/esm/pipeline/stage4_heal.js +288 -0
  342. package/dist/esm/progress.js +112 -0
  343. package/dist/esm/prompts/coverage.js +57 -0
  344. package/dist/esm/prompts/cross-impact.js +53 -0
  345. package/dist/esm/prompts/generation.js +297 -0
  346. package/dist/esm/prompts/generation_profile.js +147 -0
  347. package/dist/esm/prompts/heal.js +91 -0
  348. package/dist/esm/prompts/impact.js +63 -0
  349. package/dist/esm/prompts/json_extract.js +36 -0
  350. package/dist/esm/prompts/strategist.js +61 -0
  351. package/dist/esm/prompts/test-designer.js +92 -0
  352. package/dist/esm/provider_factory.js +366 -0
  353. package/dist/esm/provider_interface.js +23 -0
  354. package/dist/esm/provider_utils.js +96 -0
  355. package/dist/esm/qa-agent/cli.js +205 -0
  356. package/dist/esm/qa-agent/orchestrator.js +120 -0
  357. package/dist/esm/qa-agent/phase1/runner.js +139 -0
  358. package/dist/esm/qa-agent/phase1/scope.js +126 -0
  359. package/dist/esm/qa-agent/phase2/agent_browser.js +95 -0
  360. package/dist/esm/qa-agent/phase2/agent_loop.js +351 -0
  361. package/dist/esm/qa-agent/phase2/exploration_state.js +97 -0
  362. package/dist/esm/qa-agent/phase2/tools.js +386 -0
  363. package/dist/esm/qa-agent/phase2/vision.js +75 -0
  364. package/dist/esm/qa-agent/phase3/feedback.js +34 -0
  365. package/dist/esm/qa-agent/phase3/reporter.js +145 -0
  366. package/dist/esm/qa-agent/phase3/spec_generator.js +62 -0
  367. package/dist/esm/qa-agent/phase3/verdict.js +66 -0
  368. package/dist/esm/qa-agent/safe_env.js +23 -0
  369. package/dist/esm/qa-agent/types.js +3 -0
  370. package/dist/esm/reporters/junit.js +86 -0
  371. package/dist/esm/reporters/reporter.js +3 -0
  372. package/dist/esm/reporters/sarif.js +132 -0
  373. package/dist/esm/resilience/circuit_breaker.js +78 -0
  374. package/dist/esm/resilience/retry.js +56 -0
  375. package/dist/esm/sanitize.js +66 -0
  376. package/dist/esm/training/enricher.js +345 -0
  377. package/dist/esm/training/kg_scanner.js +115 -0
  378. package/dist/esm/training/merger.js +204 -0
  379. package/dist/esm/training/scanner.js +923 -0
  380. package/dist/esm/training/types.js +6 -0
  381. package/dist/esm/training/validator.js +254 -0
  382. package/dist/esm/validation/guardrails.js +101 -0
  383. package/dist/esm/validation/output_schema.js +80 -0
  384. package/dist/esm/version.js +33 -0
  385. package/dist/index.d.ts +99 -0
  386. package/dist/index.d.ts.map +1 -0
  387. package/dist/index.js +169 -0
  388. package/dist/knowledge/api_surface.d.ts +37 -0
  389. package/dist/knowledge/api_surface.d.ts.map +1 -0
  390. package/dist/knowledge/api_surface.js +418 -0
  391. package/dist/knowledge/cluster_utils.d.ts +28 -0
  392. package/dist/knowledge/cluster_utils.d.ts.map +1 -0
  393. package/dist/knowledge/cluster_utils.js +67 -0
  394. package/dist/knowledge/context_loader.d.ts +13 -0
  395. package/dist/knowledge/context_loader.d.ts.map +1 -0
  396. package/dist/knowledge/context_loader.js +90 -0
  397. package/dist/knowledge/failure_history.d.ts +39 -0
  398. package/dist/knowledge/failure_history.d.ts.map +1 -0
  399. package/dist/knowledge/failure_history.js +128 -0
  400. package/dist/knowledge/kg_bridge.d.ts +31 -0
  401. package/dist/knowledge/kg_bridge.d.ts.map +1 -0
  402. package/dist/knowledge/kg_bridge.js +388 -0
  403. package/dist/knowledge/kg_types.d.ts +75 -0
  404. package/dist/knowledge/kg_types.d.ts.map +1 -0
  405. package/dist/knowledge/kg_types.js +4 -0
  406. package/dist/knowledge/route_families.d.ts +98 -0
  407. package/dist/knowledge/route_families.d.ts.map +1 -0
  408. package/dist/knowledge/route_families.js +410 -0
  409. package/dist/knowledge/spec_index.d.ts +18 -0
  410. package/dist/knowledge/spec_index.d.ts.map +1 -0
  411. package/dist/knowledge/spec_index.js +128 -0
  412. package/dist/logger.d.ts +31 -0
  413. package/dist/logger.d.ts.map +1 -0
  414. package/dist/logger.js +119 -0
  415. package/dist/mcp-server.d.ts +68 -0
  416. package/dist/mcp-server.d.ts.map +1 -0
  417. package/dist/mcp-server.js +629 -0
  418. package/dist/metrics/prometheus.d.ts +37 -0
  419. package/dist/metrics/prometheus.d.ts.map +1 -0
  420. package/dist/metrics/prometheus.js +153 -0
  421. package/dist/model_router.d.ts +28 -0
  422. package/dist/model_router.d.ts.map +1 -0
  423. package/dist/model_router.js +63 -0
  424. package/dist/ollama_provider.d.ts +65 -0
  425. package/dist/ollama_provider.d.ts.map +1 -0
  426. package/dist/ollama_provider.js +309 -0
  427. package/dist/openai_provider.d.ts +23 -0
  428. package/dist/openai_provider.d.ts.map +1 -0
  429. package/dist/openai_provider.js +251 -0
  430. package/dist/pipeline/orchestrator.d.ts +33 -0
  431. package/dist/pipeline/orchestrator.d.ts.map +1 -0
  432. package/dist/pipeline/orchestrator.js +231 -0
  433. package/dist/pipeline/spec_verifier.d.ts +20 -0
  434. package/dist/pipeline/spec_verifier.d.ts.map +1 -0
  435. package/dist/pipeline/spec_verifier.js +79 -0
  436. package/dist/pipeline/stage0_preprocess.d.ts +31 -0
  437. package/dist/pipeline/stage0_preprocess.d.ts.map +1 -0
  438. package/dist/pipeline/stage0_preprocess.js +105 -0
  439. package/dist/pipeline/stage1_impact.d.ts +19 -0
  440. package/dist/pipeline/stage1_impact.d.ts.map +1 -0
  441. package/dist/pipeline/stage1_impact.js +143 -0
  442. package/dist/pipeline/stage2_coverage.d.ts +19 -0
  443. package/dist/pipeline/stage2_coverage.d.ts.map +1 -0
  444. package/dist/pipeline/stage2_coverage.js +156 -0
  445. package/dist/pipeline/stage3_generation.d.ts +43 -0
  446. package/dist/pipeline/stage3_generation.d.ts.map +1 -0
  447. package/dist/pipeline/stage3_generation.js +287 -0
  448. package/dist/pipeline/stage4_heal.d.ts +62 -0
  449. package/dist/pipeline/stage4_heal.d.ts.map +1 -0
  450. package/dist/pipeline/stage4_heal.js +294 -0
  451. package/dist/progress.d.ts +22 -0
  452. package/dist/progress.d.ts.map +1 -0
  453. package/dist/progress.js +116 -0
  454. package/dist/prompts/coverage.d.ts +39 -0
  455. package/dist/prompts/coverage.d.ts.map +1 -0
  456. package/dist/prompts/coverage.js +61 -0
  457. package/dist/prompts/cross-impact.d.ts +23 -0
  458. package/dist/prompts/cross-impact.d.ts.map +1 -0
  459. package/dist/prompts/cross-impact.js +57 -0
  460. package/dist/prompts/generation.d.ts +25 -0
  461. package/dist/prompts/generation.d.ts.map +1 -0
  462. package/dist/prompts/generation.js +302 -0
  463. package/dist/prompts/generation_profile.d.ts +29 -0
  464. package/dist/prompts/generation_profile.d.ts.map +1 -0
  465. package/dist/prompts/generation_profile.js +151 -0
  466. package/dist/prompts/heal.d.ts +23 -0
  467. package/dist/prompts/heal.d.ts.map +1 -0
  468. package/dist/prompts/heal.js +95 -0
  469. package/dist/prompts/impact.d.ts +31 -0
  470. package/dist/prompts/impact.d.ts.map +1 -0
  471. package/dist/prompts/impact.js +67 -0
  472. package/dist/prompts/json_extract.d.ts +14 -0
  473. package/dist/prompts/json_extract.d.ts.map +1 -0
  474. package/dist/prompts/json_extract.js +39 -0
  475. package/dist/prompts/strategist.d.ts +25 -0
  476. package/dist/prompts/strategist.d.ts.map +1 -0
  477. package/dist/prompts/strategist.js +65 -0
  478. package/dist/prompts/test-designer.d.ts +35 -0
  479. package/dist/prompts/test-designer.d.ts.map +1 -0
  480. package/dist/prompts/test-designer.js +96 -0
  481. package/dist/provider_factory.d.ts +104 -0
  482. package/dist/provider_factory.d.ts.map +1 -0
  483. package/dist/provider_factory.js +371 -0
  484. package/dist/provider_interface.d.ts +365 -0
  485. package/dist/provider_interface.d.ts.map +1 -0
  486. package/dist/provider_interface.js +28 -0
  487. package/dist/provider_utils.d.ts +39 -0
  488. package/dist/provider_utils.d.ts.map +1 -0
  489. package/dist/provider_utils.js +103 -0
  490. package/dist/qa-agent/cli.d.ts +3 -0
  491. package/dist/qa-agent/cli.d.ts.map +1 -0
  492. package/dist/qa-agent/cli.js +207 -0
  493. package/dist/qa-agent/orchestrator.d.ts +3 -0
  494. package/dist/qa-agent/orchestrator.d.ts.map +1 -0
  495. package/dist/qa-agent/orchestrator.js +123 -0
  496. package/dist/qa-agent/phase1/runner.d.ts +3 -0
  497. package/dist/qa-agent/phase1/runner.d.ts.map +1 -0
  498. package/dist/qa-agent/phase1/runner.js +142 -0
  499. package/dist/qa-agent/phase1/scope.d.ts +6 -0
  500. package/dist/qa-agent/phase1/scope.d.ts.map +1 -0
  501. package/dist/qa-agent/phase1/scope.js +129 -0
  502. package/dist/qa-agent/phase2/agent_browser.d.ts +35 -0
  503. package/dist/qa-agent/phase2/agent_browser.d.ts.map +1 -0
  504. package/dist/qa-agent/phase2/agent_browser.js +99 -0
  505. package/dist/qa-agent/phase2/agent_loop.d.ts +3 -0
  506. package/dist/qa-agent/phase2/agent_loop.d.ts.map +1 -0
  507. package/dist/qa-agent/phase2/agent_loop.js +357 -0
  508. package/dist/qa-agent/phase2/exploration_state.d.ts +12 -0
  509. package/dist/qa-agent/phase2/exploration_state.d.ts.map +1 -0
  510. package/dist/qa-agent/phase2/exploration_state.js +109 -0
  511. package/dist/qa-agent/phase2/tools.d.ts +28 -0
  512. package/dist/qa-agent/phase2/tools.d.ts.map +1 -0
  513. package/dist/qa-agent/phase2/tools.js +390 -0
  514. package/dist/qa-agent/phase2/vision.d.ts +3 -0
  515. package/dist/qa-agent/phase2/vision.d.ts.map +1 -0
  516. package/dist/qa-agent/phase2/vision.js +78 -0
  517. package/dist/qa-agent/phase3/feedback.d.ts +3 -0
  518. package/dist/qa-agent/phase3/feedback.d.ts.map +1 -0
  519. package/dist/qa-agent/phase3/feedback.js +37 -0
  520. package/dist/qa-agent/phase3/reporter.d.ts +3 -0
  521. package/dist/qa-agent/phase3/reporter.d.ts.map +1 -0
  522. package/dist/qa-agent/phase3/reporter.js +148 -0
  523. package/dist/qa-agent/phase3/spec_generator.d.ts +3 -0
  524. package/dist/qa-agent/phase3/spec_generator.d.ts.map +1 -0
  525. package/dist/qa-agent/phase3/spec_generator.js +65 -0
  526. package/dist/qa-agent/phase3/verdict.d.ts +3 -0
  527. package/dist/qa-agent/phase3/verdict.d.ts.map +1 -0
  528. package/dist/qa-agent/phase3/verdict.js +69 -0
  529. package/dist/qa-agent/safe_env.d.ts +3 -0
  530. package/dist/qa-agent/safe_env.d.ts.map +1 -0
  531. package/dist/qa-agent/safe_env.js +26 -0
  532. package/dist/qa-agent/types.d.ts +130 -0
  533. package/dist/qa-agent/types.d.ts.map +1 -0
  534. package/dist/qa-agent/types.js +4 -0
  535. package/dist/reporters/junit.d.ts +6 -0
  536. package/dist/reporters/junit.d.ts.map +1 -0
  537. package/dist/reporters/junit.js +89 -0
  538. package/dist/reporters/reporter.d.ts +42 -0
  539. package/dist/reporters/reporter.d.ts.map +1 -0
  540. package/dist/reporters/reporter.js +4 -0
  541. package/dist/reporters/sarif.d.ts +7 -0
  542. package/dist/reporters/sarif.d.ts.map +1 -0
  543. package/dist/reporters/sarif.js +135 -0
  544. package/dist/resilience/circuit_breaker.d.ts +36 -0
  545. package/dist/resilience/circuit_breaker.d.ts.map +1 -0
  546. package/dist/resilience/circuit_breaker.js +82 -0
  547. package/dist/resilience/retry.d.ts +11 -0
  548. package/dist/resilience/retry.d.ts.map +1 -0
  549. package/dist/resilience/retry.js +59 -0
  550. package/dist/sanitize.d.ts +15 -0
  551. package/dist/sanitize.d.ts.map +1 -0
  552. package/dist/sanitize.js +71 -0
  553. package/dist/training/enricher.d.ts +17 -0
  554. package/dist/training/enricher.d.ts.map +1 -0
  555. package/dist/training/enricher.js +350 -0
  556. package/dist/training/kg_scanner.d.ts +13 -0
  557. package/dist/training/kg_scanner.d.ts.map +1 -0
  558. package/dist/training/kg_scanner.js +118 -0
  559. package/dist/training/merger.d.ts +15 -0
  560. package/dist/training/merger.d.ts.map +1 -0
  561. package/dist/training/merger.js +208 -0
  562. package/dist/training/scanner.d.ts +36 -0
  563. package/dist/training/scanner.d.ts.map +1 -0
  564. package/dist/training/scanner.js +932 -0
  565. package/dist/training/types.d.ts +117 -0
  566. package/dist/training/types.d.ts.map +1 -0
  567. package/dist/training/types.js +9 -0
  568. package/dist/training/validator.d.ts +21 -0
  569. package/dist/training/validator.d.ts.map +1 -0
  570. package/dist/training/validator.js +262 -0
  571. package/dist/validation/guardrails.d.ts +31 -0
  572. package/dist/validation/guardrails.d.ts.map +1 -0
  573. package/dist/validation/guardrails.js +112 -0
  574. package/dist/validation/output_schema.d.ts +67 -0
  575. package/dist/validation/output_schema.d.ts.map +1 -0
  576. package/dist/validation/output_schema.js +84 -0
  577. package/dist/version.d.ts +6 -0
  578. package/dist/version.d.ts.map +1 -0
  579. package/dist/version.js +36 -0
  580. package/package.json +126 -0
  581. package/schemas/flow-decision.schema.json +83 -0
  582. package/schemas/gap.schema.json +18 -0
  583. package/schemas/impact.schema.json +455 -0
  584. package/schemas/plan.schema.json +491 -0
  585. package/schemas/route-families.schema.json +137 -0
  586. package/schemas/subsystem-risk-map.schema.json +62 -0
  587. package/schemas/traceability-input.schema.json +122 -0
@@ -0,0 +1,345 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { lstatSync, readdirSync, readFileSync } from 'fs';
4
+ import { join, relative, resolve } from 'path';
5
+ import { logger } from '../logger.js';
6
+ import { isGuessedRoute } from './types.js';
7
+ const MAX_FILES_PER_FAMILY = 20;
8
+ const MAX_LINES_PER_FILE = 50;
9
+ const LLM_TIMEOUT_MS = 60000;
10
+ const MAX_PROMPT_CHARS = 100000;
11
+ const SENSITIVE_PATTERNS = [
12
+ /[._]env/, /secret/i, /credential/i, /\.pem$/, /\.key$/, /password/i,
13
+ /config\/secrets/, /fixtures\/.*auth/i, /\.npmrc/, /\.netrc/,
14
+ /id_rsa/, /id_ed25519/, /\.p12$/, /\.pfx$/, /tokens?\.json/i,
15
+ ];
16
+ const SKIP_DIRS = new Set([
17
+ 'node_modules', '.git', 'dist', 'build', '.next', '.nuxt', 'coverage',
18
+ ]);
19
+ function sampleFiles(dir, maxFiles) {
20
+ const files = [];
21
+ function walk(d, depth = 0, maxDepth = 10) {
22
+ if (files.length >= maxFiles)
23
+ return;
24
+ if (depth > maxDepth)
25
+ return;
26
+ try {
27
+ for (const entry of readdirSync(d)) {
28
+ if (files.length >= maxFiles)
29
+ return;
30
+ // Skip dot-dirs and known heavy directories
31
+ if (entry.startsWith('.') || SKIP_DIRS.has(entry))
32
+ continue;
33
+ const full = join(d, entry);
34
+ try {
35
+ // Skip symlinks
36
+ const lstat = lstatSync(full);
37
+ if (lstat.isSymbolicLink())
38
+ continue;
39
+ // Skip sensitive files (test against relative path from scan root)
40
+ const relPath = relative(dir, full);
41
+ if (SENSITIVE_PATTERNS.some((p) => p.test(relPath) || p.test(entry)))
42
+ continue;
43
+ if (lstat.isDirectory()) {
44
+ walk(full, depth + 1, maxDepth);
45
+ }
46
+ else if (lstat.isFile() && lstat.size < 50000) {
47
+ const ext = entry.slice(entry.lastIndexOf('.'));
48
+ if (['.ts', '.tsx', '.js', '.jsx', '.go', '.py', '.rs'].includes(ext)) {
49
+ const content = readFileSync(full, 'utf-8');
50
+ const lines = content.split('\n').slice(0, MAX_LINES_PER_FILE).join('\n');
51
+ files.push({ path: full, content: lines });
52
+ }
53
+ }
54
+ }
55
+ catch { /* skip */ }
56
+ }
57
+ }
58
+ catch { /* skip */ }
59
+ }
60
+ walk(dir);
61
+ return files;
62
+ }
63
+ /**
64
+ * Build a shallow directory listing of the source tree (depth 2-3) so the LLM
65
+ * can suggest accurate webappPaths / serverPaths for test-derived families.
66
+ */
67
+ function getSourceTreeListing(projectRoot, maxDepth = 3) {
68
+ const lines = [];
69
+ function walk(dir, depth, prefix) {
70
+ if (depth > maxDepth || lines.length > 200)
71
+ return;
72
+ let entries;
73
+ try {
74
+ entries = readdirSync(dir).sort();
75
+ }
76
+ catch {
77
+ return;
78
+ }
79
+ const dirs = entries.filter((e) => {
80
+ if (e.startsWith('.') || SKIP_DIRS.has(e))
81
+ return false;
82
+ try {
83
+ const stat = lstatSync(join(dir, e));
84
+ return !stat.isSymbolicLink() && stat.isDirectory();
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ });
90
+ for (const d of dirs) {
91
+ lines.push(`${prefix}${d}/`);
92
+ walk(join(dir, d), depth + 1, prefix + ' ');
93
+ }
94
+ }
95
+ walk(resolve(projectRoot), 0, '');
96
+ return lines.join('\n');
97
+ }
98
+ function buildEnrichPrompt(families, projectRoot, testsRoot) {
99
+ const sections = [];
100
+ const hasTestOnlyFamilies = families.some((f) => f.webappPaths.length === 0 && f.serverPaths.length === 0);
101
+ const resolvedTestsRoot = testsRoot ? resolve(testsRoot) : resolve(projectRoot);
102
+ for (const family of families) {
103
+ const isTestOnly = family.webappPaths.length === 0 && family.serverPaths.length === 0;
104
+ const allDirs = [
105
+ ...family.webappPaths.map((p) => p.replace(/\/?\*.*$/, '')),
106
+ ...family.serverPaths.map((p) => p.replace(/\/?\*.*$/, '')),
107
+ ];
108
+ const samples = [];
109
+ for (const dir of allDirs) {
110
+ if (!dir)
111
+ continue;
112
+ const fullDir = join(resolve(projectRoot), dir);
113
+ samples.push(...sampleFiles(fullDir, MAX_FILES_PER_FAMILY - samples.length));
114
+ if (samples.length >= MAX_FILES_PER_FAMILY)
115
+ break;
116
+ }
117
+ // For test-only families, sample the test files themselves for richer context
118
+ if (isTestOnly) {
119
+ for (const specDir of family.specDirs) {
120
+ if (samples.length >= MAX_FILES_PER_FAMILY)
121
+ break;
122
+ const fullDir = join(resolvedTestsRoot, specDir);
123
+ samples.push(...sampleFiles(fullDir, MAX_FILES_PER_FAMILY - samples.length));
124
+ }
125
+ }
126
+ // Sample spec descriptions
127
+ const specSamples = [];
128
+ for (const specDir of family.specDirs) {
129
+ const fullDir = join(resolvedTestsRoot, specDir);
130
+ const specFiles = sampleFiles(fullDir, 5);
131
+ for (const sf of specFiles) {
132
+ const matches = sf.content.match(/(?:test|it|describe)\s*\(\s*['"`]([^'"`]+)/g);
133
+ if (matches) {
134
+ specSamples.push(...matches.map((m) => m.replace(/(?:test|it|describe)\s*\(\s*['"`]/, '')));
135
+ }
136
+ }
137
+ }
138
+ sections.push(`## Family: ${family.id}${isTestOnly ? ' [TEST-ONLY — needs webappPaths/serverPaths]' : ''}
139
+ Routes (guessed): ${JSON.stringify(family.routes)}
140
+ Webapp paths: ${JSON.stringify(family.webappPaths)}
141
+ Server paths: ${JSON.stringify(family.serverPaths)}
142
+ Spec dirs: ${JSON.stringify(family.specDirs)}
143
+ Tags: ${JSON.stringify(family.tags)}
144
+ Features: ${family.features.map((f) => f.id).join(', ') || 'none'}
145
+
146
+ Sample files (${samples.length}):
147
+ ${samples.map((s) => `### ${relative(projectRoot, s.path)}\n\`\`\`\n${s.content}\n\`\`\``).join('\n')}
148
+
149
+ Test descriptions:
150
+ ${specSamples.length > 0 ? specSamples.map((d) => `- ${d}`).join('\n') : '(none found)'}
151
+ `);
152
+ }
153
+ // Include source tree listing when we have test-only families
154
+ const sourceTreeSection = hasTestOnlyFamilies
155
+ ? `\n## Source Directory Structure\nUse this to suggest accurate webappPaths and serverPaths for test-only families:\n\`\`\`\n${getSourceTreeListing(projectRoot)}\n\`\`\`\n`
156
+ : '';
157
+ return `You are analyzing a codebase to enrich route-family definitions for an E2E test impact analysis tool.
158
+
159
+ For each family below, provide:
160
+ 1. **priority**: P0 (critical user flow), P1 (important), or P2 (nice-to-have)
161
+ 2. **userFlows**: Array of human-readable flow names (e.g., "Create channel", "Search messages")
162
+ 3. **routes**: Improved URL patterns (e.g., "/{team}/channels/{channel}" instead of "/channels")
163
+ 4. **pageObjects**: Array of page object class names found in the code
164
+ 5. **components**: Array of UI component names relevant to this family
165
+ 6. **webappPaths**: Array of glob patterns for frontend source directories (e.g., "src/components/drafts/**"). REQUIRED for families marked [TEST-ONLY].
166
+ 7. **serverPaths**: Array of glob patterns for backend source directories. REQUIRED for families marked [TEST-ONLY].
167
+
168
+ Respond in JSON format:
169
+ \`\`\`json
170
+ [
171
+ {
172
+ "id": "family_id",
173
+ "priority": "P0",
174
+ "userFlows": ["Flow name 1", "Flow name 2"],
175
+ "routes": ["/improved/route/{param}"],
176
+ "pageObjects": ["PageName"],
177
+ "components": ["ComponentName"],
178
+ "webappPaths": ["src/components/feature_name/**"],
179
+ "serverPaths": ["server/channels/api4/feature.go"]
180
+ }
181
+ ]
182
+ \`\`\`
183
+ ${sourceTreeSection}
184
+ ${sections.join('\n---\n')}`;
185
+ }
186
+ export function validateEntries(parsed) {
187
+ const filterStrings = (arr, maxLen) => {
188
+ if (!Array.isArray(arr))
189
+ return undefined;
190
+ const filtered = arr.filter((v) => typeof v === 'string' && v.length < maxLen);
191
+ return filtered.length > 0 ? filtered : undefined;
192
+ };
193
+ return parsed
194
+ .filter((e) => !!e && typeof e.id === 'string')
195
+ .map((entry) => ({
196
+ id: entry.id,
197
+ priority: ['P0', 'P1', 'P2'].includes(entry.priority) ? entry.priority : undefined,
198
+ routes: filterStrings(entry.routes, 200),
199
+ userFlows: filterStrings(entry.userFlows, 500),
200
+ pageObjects: filterStrings(entry.pageObjects, 200),
201
+ components: filterStrings(entry.components, 200),
202
+ webappPaths: filterStrings(entry.webappPaths, 300),
203
+ serverPaths: filterStrings(entry.serverPaths, 300),
204
+ }));
205
+ }
206
+ export function parseEnrichResponse(response) {
207
+ // Extract JSON from response (may be wrapped in markdown code block)
208
+ const jsonMatch = response.match(/```(?:json)?\s*([\s\S]*?)```/) || [null, response];
209
+ const jsonStr = jsonMatch[1]?.trim() || response.trim();
210
+ try {
211
+ const parsed = JSON.parse(jsonStr);
212
+ if (Array.isArray(parsed)) {
213
+ return validateEntries(parsed);
214
+ }
215
+ }
216
+ catch {
217
+ // Try to find any JSON array in the response
218
+ const arrayMatch = response.match(/\[[\s\S]*\]/);
219
+ if (arrayMatch) {
220
+ try {
221
+ const parsed = JSON.parse(arrayMatch[0]);
222
+ if (Array.isArray(parsed)) {
223
+ return validateEntries(parsed);
224
+ }
225
+ }
226
+ catch {
227
+ // give up
228
+ }
229
+ }
230
+ }
231
+ return [];
232
+ }
233
+ function applyEnrichment(family, enriched) {
234
+ const result = { ...family };
235
+ if (enriched.priority && !family.priority) {
236
+ result.priority = enriched.priority;
237
+ }
238
+ if (enriched.userFlows && (!family.userFlows || family.userFlows.length === 0)) {
239
+ result.userFlows = enriched.userFlows;
240
+ }
241
+ if (enriched.routes && enriched.routes.length > 0) {
242
+ // Only replace if current routes look like guesses
243
+ if (isGuessedRoute(family.routes)) {
244
+ result.routes = enriched.routes;
245
+ }
246
+ }
247
+ if (enriched.pageObjects && (!family.pageObjects || family.pageObjects.length === 0)) {
248
+ result.pageObjects = enriched.pageObjects;
249
+ }
250
+ if (enriched.components && (!family.components || family.components.length === 0)) {
251
+ result.components = enriched.components;
252
+ }
253
+ // Only fill source paths when the family has none (test-derived families)
254
+ if (enriched.webappPaths && (!family.webappPaths || family.webappPaths.length === 0)) {
255
+ result.webappPaths = enriched.webappPaths;
256
+ }
257
+ if (enriched.serverPaths && (!family.serverPaths || family.serverPaths.length === 0)) {
258
+ result.serverPaths = enriched.serverPaths;
259
+ }
260
+ return result;
261
+ }
262
+ export async function enrichFamilies(families, scanned, projectRoot, provider, budgetUSD, testsRoot) {
263
+ const scannedMap = new Map(scanned.map((s) => [s.id, s]));
264
+ const enriched = [];
265
+ let totalTokens = 0;
266
+ let totalCost = 0;
267
+ let requestCount = 0;
268
+ let totalResponseMs = 0;
269
+ const skipped = [];
270
+ // Process in chunks of 4 families
271
+ const chunkSize = 4;
272
+ for (let i = 0; i < families.length; i += chunkSize) {
273
+ if (totalCost >= budgetUSD) {
274
+ for (let j = i; j < families.length; j++) {
275
+ skipped.push(families[j].id);
276
+ enriched.push(families[j]);
277
+ }
278
+ break;
279
+ }
280
+ const chunk = families.slice(i, i + chunkSize);
281
+ const scannedChunk = chunk
282
+ .map((f) => scannedMap.get(f.id))
283
+ .filter((s) => s !== undefined);
284
+ if (scannedChunk.length === 0) {
285
+ enriched.push(...chunk);
286
+ continue;
287
+ }
288
+ let prompt = buildEnrichPrompt(scannedChunk, projectRoot, testsRoot);
289
+ if (prompt.length > MAX_PROMPT_CHARS) {
290
+ // Truncate at the last complete section boundary to avoid malformed input
291
+ const lastSectionEnd = prompt.lastIndexOf('\n---\n', MAX_PROMPT_CHARS);
292
+ if (lastSectionEnd > 0) {
293
+ logger.warn(`[train] Prompt truncated from ${prompt.length} chars at section boundary`);
294
+ prompt = prompt.slice(0, lastSectionEnd);
295
+ }
296
+ else {
297
+ logger.warn(`[train] Prompt truncated from ${prompt.length} to ${MAX_PROMPT_CHARS} chars`);
298
+ prompt = prompt.slice(0, MAX_PROMPT_CHARS);
299
+ }
300
+ }
301
+ let timeoutTimer;
302
+ try {
303
+ const timeoutPromise = new Promise((_, reject) => {
304
+ timeoutTimer = setTimeout(() => reject(new Error('LLM request timed out')), LLM_TIMEOUT_MS);
305
+ });
306
+ const reqStart = performance.now();
307
+ const response = await Promise.race([
308
+ provider.generateText(prompt, { maxTokens: 4096, temperature: 0.3 }),
309
+ timeoutPromise,
310
+ ]);
311
+ totalResponseMs += performance.now() - reqStart;
312
+ requestCount++;
313
+ totalTokens += (response.usage?.inputTokens ?? 0) + (response.usage?.outputTokens ?? 0);
314
+ totalCost += response.cost ?? 0;
315
+ const entries = parseEnrichResponse(response.text);
316
+ const entryMap = new Map(entries.map((e) => [e.id, e]));
317
+ for (const family of chunk) {
318
+ const entry = entryMap.get(family.id);
319
+ if (entry) {
320
+ enriched.push(applyEnrichment(family, entry));
321
+ }
322
+ else {
323
+ enriched.push(family);
324
+ }
325
+ }
326
+ }
327
+ catch (error) {
328
+ // On LLM failure, keep families unchanged
329
+ logger.warn(`[train] LLM enrichment failed for chunk: ${error instanceof Error ? error.message : String(error)}`);
330
+ enriched.push(...chunk);
331
+ }
332
+ finally {
333
+ if (timeoutTimer)
334
+ clearTimeout(timeoutTimer);
335
+ }
336
+ }
337
+ return {
338
+ enrichedFamilies: enriched,
339
+ tokensUsed: totalTokens,
340
+ costUSD: Math.round(totalCost * 100) / 100,
341
+ skippedFamilies: skipped,
342
+ requestCount,
343
+ avgResponseMs: requestCount > 0 ? Math.round(totalResponseMs / requestCount) : 0,
344
+ };
345
+ }
@@ -0,0 +1,115 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { deriveClusterId, SKIP_DIRS_WITH_TESTS } from '../knowledge/cluster_utils.js';
4
+ /**
5
+ * Converts KG nodes/edges into a ScanResult compatible with the filesystem scanner output.
6
+ * Groups nodes by their containing module/directory to form families.
7
+ */
8
+ export function scanFromKnowledgeGraph(kg) {
9
+ const clusters = new Map();
10
+ // Group nodes into clusters by directory/module
11
+ for (const node of kg.nodes) {
12
+ if (node.layer === 'infra')
13
+ continue; // skip infrastructure nodes
14
+ const clusterId = deriveClusterId(node, SKIP_DIRS_WITH_TESTS);
15
+ if (!clusterId)
16
+ continue;
17
+ if (!clusters.has(clusterId)) {
18
+ clusters.set(clusterId, []);
19
+ }
20
+ clusters.get(clusterId).push(node);
21
+ }
22
+ let totalSourceFiles = 0;
23
+ let totalTestFiles = 0;
24
+ const families = [];
25
+ for (const [id, nodes] of clusters) {
26
+ const webappPaths = [];
27
+ const serverPaths = [];
28
+ const specDirs = [];
29
+ const tags = [];
30
+ const seenDirs = new Set();
31
+ for (const node of nodes) {
32
+ if (!node.filePath)
33
+ continue;
34
+ const normalized = node.filePath.replace(/\\/g, '/');
35
+ if (node.layer === 'test') {
36
+ totalTestFiles++;
37
+ const dir = normalized.split('/').slice(0, -1).join('/');
38
+ if (dir && !seenDirs.has(dir)) {
39
+ seenDirs.add(dir);
40
+ specDirs.push(dir + '/');
41
+ }
42
+ continue;
43
+ }
44
+ totalSourceFiles++;
45
+ const glob = buildGlobFromPath(normalized);
46
+ if (node.layer === 'api' || node.layer === 'service' || node.layer === 'data') {
47
+ serverPaths.push(glob);
48
+ }
49
+ else if (node.layer === 'ui') {
50
+ webappPaths.push(glob);
51
+ }
52
+ else {
53
+ // Default assignment based on file path heuristics
54
+ if (isLikelyServerPath(normalized)) {
55
+ serverPaths.push(glob);
56
+ }
57
+ else {
58
+ webappPaths.push(glob);
59
+ }
60
+ }
61
+ // Extract tags from node metadata
62
+ if (node.tags) {
63
+ tags.push(...node.tags);
64
+ }
65
+ }
66
+ if (webappPaths.length === 0 && serverPaths.length === 0 && specDirs.length === 0) {
67
+ continue;
68
+ }
69
+ families.push({
70
+ id,
71
+ routes: [`/${id}`],
72
+ webappPaths: [...new Set(webappPaths)],
73
+ serverPaths: [...new Set(serverPaths)],
74
+ specDirs: [...new Set(specDirs)],
75
+ cypressSpecDirs: [],
76
+ tags: [...new Set(tags)],
77
+ features: [],
78
+ routesGuessed: true,
79
+ });
80
+ }
81
+ return {
82
+ families,
83
+ unmatchedSourceDirs: [],
84
+ unmatchedTestDirs: [],
85
+ stats: {
86
+ totalSourceFiles,
87
+ totalTestFiles,
88
+ familyCount: families.length,
89
+ },
90
+ };
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // Internal helpers
94
+ // ---------------------------------------------------------------------------
95
+ // deriveClusterId and deriveClusterIdFromPath imported from cluster_utils.ts
96
+ function buildGlobFromPath(filePath) {
97
+ // Reject paths with traversal or null bytes
98
+ if (filePath.includes('..') || filePath.includes('\0')) {
99
+ return '';
100
+ }
101
+ // Convert a file path to a glob pattern matching the directory
102
+ const dir = filePath.split('/').slice(0, -1).join('/');
103
+ return dir ? `${dir}/*` : `${filePath}*`;
104
+ }
105
+ function isLikelyServerPath(filePath) {
106
+ const lower = filePath.toLowerCase();
107
+ return lower.includes('/server/') ||
108
+ lower.includes('/api/') ||
109
+ lower.includes('/routes/') ||
110
+ lower.includes('/controllers/') ||
111
+ lower.includes('/services/') ||
112
+ lower.includes('/models/') ||
113
+ lower.endsWith('.go') ||
114
+ lower.endsWith('.py');
115
+ }
@@ -0,0 +1,204 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { execFileSync } from 'child_process';
4
+ import { existsSync } from 'fs';
5
+ import { join, resolve } from 'path';
6
+ import { isGuessedRoute } from './types.js';
7
+ function unionArrays(existing, incoming) {
8
+ const set = new Set(existing || []);
9
+ for (const item of incoming) {
10
+ set.add(item);
11
+ }
12
+ return Array.from(set);
13
+ }
14
+ function mergeFamily(existing, scanned) {
15
+ const merged = { ...existing };
16
+ // Structural fields: union arrays
17
+ merged.webappPaths = unionArrays(existing.webappPaths, scanned.webappPaths);
18
+ merged.serverPaths = unionArrays(existing.serverPaths, scanned.serverPaths);
19
+ merged.specDirs = unionArrays(existing.specDirs, scanned.specDirs);
20
+ merged.cypressSpecDirs = unionArrays(existing.cypressSpecDirs, scanned.cypressSpecDirs);
21
+ merged.tags = unionArrays(existing.tags, scanned.tags);
22
+ // Routes: only update if existing looks like a guess
23
+ if (isGuessedRoute(existing.routes) && !isGuessedRoute(scanned.routes)) {
24
+ merged.routes = scanned.routes;
25
+ }
26
+ // Human-curated fields: never overwrite (priority, userFlows, pageObjects, components)
27
+ // Merge features
28
+ if (scanned.features.length > 0) {
29
+ const existingFeatures = existing.features || [];
30
+ const existingIds = new Set(existingFeatures.map((f) => f.id));
31
+ const newFeatures = scanned.features.filter((f) => !existingIds.has(f.id));
32
+ if (newFeatures.length > 0) {
33
+ merged.features = [
34
+ ...existingFeatures,
35
+ ...newFeatures.map((f) => ({
36
+ id: f.id,
37
+ webappPaths: f.webappPaths,
38
+ serverPaths: f.serverPaths,
39
+ specDirs: f.specDirs,
40
+ })),
41
+ ];
42
+ }
43
+ }
44
+ return merged;
45
+ }
46
+ function scannedToRouteFamily(scanned) {
47
+ const family = {
48
+ id: scanned.id,
49
+ routes: scanned.routes,
50
+ };
51
+ if (scanned.webappPaths.length > 0)
52
+ family.webappPaths = scanned.webappPaths;
53
+ if (scanned.serverPaths.length > 0)
54
+ family.serverPaths = scanned.serverPaths;
55
+ if (scanned.specDirs.length > 0)
56
+ family.specDirs = scanned.specDirs;
57
+ if (scanned.cypressSpecDirs.length > 0)
58
+ family.cypressSpecDirs = scanned.cypressSpecDirs;
59
+ if (scanned.tags.length > 0)
60
+ family.tags = scanned.tags;
61
+ if (scanned.features.length > 0) {
62
+ family.features = scanned.features.map((f) => ({
63
+ id: f.id,
64
+ webappPaths: f.webappPaths.length > 0 ? f.webappPaths : undefined,
65
+ serverPaths: f.serverPaths.length > 0 ? f.serverPaths : undefined,
66
+ specDirs: f.specDirs.length > 0 ? f.specDirs : undefined,
67
+ }));
68
+ }
69
+ return family;
70
+ }
71
+ /**
72
+ * Try to find a matching family ID with singular/plural normalization.
73
+ * "team" matches "teams", "emoji" matches "emoji", etc.
74
+ */
75
+ function findFuzzyMatch(id, idMap) {
76
+ if (idMap.has(id))
77
+ return id;
78
+ // Try adding 's'
79
+ if (!id.endsWith('s') && idMap.has(id + 's'))
80
+ return id + 's';
81
+ // Try removing 's'
82
+ if (id.endsWith('s') && idMap.has(id.slice(0, -1)))
83
+ return id.slice(0, -1);
84
+ return undefined;
85
+ }
86
+ export function mergeFamilies(existing, scanned) {
87
+ const existingFamilies = existing?.families || [];
88
+ const existingMap = new Map(existingFamilies.map((f) => [f.id, f]));
89
+ const scannedMap = new Map(scanned.map((f) => [f.id, f]));
90
+ const newFamilies = [];
91
+ const updatedFamilies = [];
92
+ const mergedFamilies = [];
93
+ // Process existing families — match scanned by exact or fuzzy ID
94
+ for (const ef of existingFamilies) {
95
+ let sf = scannedMap.get(ef.id);
96
+ // Try singular/plural match if exact match failed
97
+ if (!sf) {
98
+ const fuzzyId = findFuzzyMatch(ef.id, scannedMap);
99
+ if (fuzzyId)
100
+ sf = scannedMap.get(fuzzyId);
101
+ }
102
+ if (sf) {
103
+ mergedFamilies.push(mergeFamily(ef, sf));
104
+ updatedFamilies.push(ef.id);
105
+ }
106
+ else {
107
+ // Keep untouched
108
+ mergedFamilies.push({ ...ef });
109
+ }
110
+ }
111
+ // Add new families from scanner (if no existing family matched)
112
+ for (const sf of scanned) {
113
+ const matchedExisting = findFuzzyMatch(sf.id, existingMap);
114
+ if (!matchedExisting) {
115
+ mergedFamilies.push(scannedToRouteFamily(sf));
116
+ newFamilies.push(sf.id);
117
+ }
118
+ }
119
+ const parts = [];
120
+ if (updatedFamilies.length > 0)
121
+ parts.push(`${updatedFamilies.length} families updated`);
122
+ if (newFamilies.length > 0)
123
+ parts.push(`${newFamilies.length} new families added`);
124
+ if (parts.length === 0)
125
+ parts.push('no changes');
126
+ return {
127
+ manifest: { families: mergedFamilies, source: existing?.source || 'train-scan' },
128
+ newFamilies,
129
+ updatedFamilies,
130
+ staleFamilies: [],
131
+ summary: parts.join(', '),
132
+ };
133
+ }
134
+ /**
135
+ * Detect families whose paths no longer exist on disk.
136
+ *
137
+ * Paths in the manifest may be relative to different roots:
138
+ * - webappPaths / serverPaths are typically relative to the repo root
139
+ * - specDirs may be relative to the tests root
140
+ *
141
+ * We try each pattern against all provided roots (and the git repo root
142
+ * if discoverable) to avoid false positives from path-prefix mismatches.
143
+ */
144
+ export function detectStaleFamilies(manifest, projectRoot, testsRoot) {
145
+ const roots = new Set([resolve(projectRoot)]);
146
+ if (testsRoot)
147
+ roots.add(resolve(testsRoot));
148
+ // Also try to discover the git repo root — manifest paths may be repo-relative
149
+ try {
150
+ const gitRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
151
+ cwd: projectRoot,
152
+ encoding: 'utf-8',
153
+ stdio: ['pipe', 'pipe', 'pipe'],
154
+ }).trim();
155
+ if (gitRoot)
156
+ roots.add(resolve(gitRoot));
157
+ }
158
+ catch {
159
+ // Not a git repo or git not available — that's fine
160
+ }
161
+ const stale = [];
162
+ for (const family of manifest.families) {
163
+ const allPatterns = [
164
+ ...(family.webappPaths || []),
165
+ ...(family.serverPaths || []),
166
+ ...(family.specDirs || []),
167
+ ];
168
+ if (allPatterns.length === 0)
169
+ continue;
170
+ // Check if any pattern resolves to existing files/dirs in any root
171
+ let hasAny = false;
172
+ for (const pattern of allPatterns) {
173
+ // Strip trailing glob (* or **) to get the directory
174
+ const dirPart = pattern.replace(/\/?\*.*$/, '');
175
+ if (!dirPart)
176
+ continue;
177
+ // For file-level patterns like "server/channels/api4/draft*.go",
178
+ // dirPart is "server/channels/api4/draft" — check the parent dir instead
179
+ const isFileGlob = /\.\w+$/.test(pattern);
180
+ const pathsToCheck = [dirPart];
181
+ if (isFileGlob) {
182
+ const parentDir = dirPart.split('/').slice(0, -1).join('/');
183
+ if (parentDir)
184
+ pathsToCheck.push(parentDir);
185
+ }
186
+ for (const checkPath of pathsToCheck) {
187
+ for (const root of roots) {
188
+ if (existsSync(join(root, checkPath))) {
189
+ hasAny = true;
190
+ break;
191
+ }
192
+ }
193
+ if (hasAny)
194
+ break;
195
+ }
196
+ if (hasAny)
197
+ break;
198
+ }
199
+ if (!hasAny) {
200
+ stale.push(family.id);
201
+ }
202
+ }
203
+ return stale;
204
+ }