@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,73 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { readFileSync } from 'fs';
4
+ import { resolveConfig } from '../../agent/config.js';
5
+ import { captureTraceabilityInput } from '../../agent/traceability_capture.js';
6
+ import { ingestTraceabilityInput } from '../../agent/traceability_ingest.js';
7
+ export function runTraceabilityCaptureCommand(args, autoConfig) {
8
+ if (!args.path && !autoConfig) {
9
+ console.error('Error: --path is required for traceability-capture command');
10
+ process.exit(1);
11
+ }
12
+ if (!args.traceabilityReportPath) {
13
+ console.error('Error: --traceability-report <path> is required for traceability-capture command');
14
+ process.exit(1);
15
+ }
16
+ const { config } = resolveConfig(process.cwd(), autoConfig, {
17
+ path: args.path,
18
+ profile: args.profile,
19
+ testsRoot: args.testsRoot,
20
+ mode: 'impact',
21
+ gitSince: args.gitSince,
22
+ llmProvider: args.llmProvider,
23
+ });
24
+ const reportRoot = config.testsRoot || config.path;
25
+ const output = captureTraceabilityInput({
26
+ appPath: config.path,
27
+ testsRoot: reportRoot,
28
+ reportPath: args.traceabilityReportPath,
29
+ sinceRef: args.gitSince || config.git.since,
30
+ outputPath: args.traceabilityCaptureOutputPath,
31
+ coverageMapPath: args.traceabilityCoverageMapPath,
32
+ changedFilesPath: args.traceabilityChangedFilesPath,
33
+ });
34
+ console.log(`Traceability input: ${output.outputPath}`);
35
+ console.log(`Traceability tests seen: ${output.testsSeen}`);
36
+ console.log(`Traceability runs generated: ${output.runsGenerated}`);
37
+ console.log(`Traceability changed files used: ${output.changedFilesUsed}`);
38
+ if (output.warnings.length > 0) {
39
+ console.log(`Traceability warnings: ${output.warnings.join(' | ')}`);
40
+ }
41
+ }
42
+ export function runTraceabilityIngestCommand(args, autoConfig) {
43
+ if (!args.path && !autoConfig) {
44
+ console.error('Error: --path is required for traceability-ingest command');
45
+ process.exit(1);
46
+ }
47
+ if (!args.traceabilityInputPath) {
48
+ console.error('Error: --traceability-input <path> is required for traceability-ingest command');
49
+ process.exit(1);
50
+ }
51
+ const { config } = resolveConfig(process.cwd(), autoConfig, {
52
+ path: args.path,
53
+ profile: args.profile,
54
+ testsRoot: args.testsRoot,
55
+ mode: 'impact',
56
+ llmProvider: args.llmProvider,
57
+ });
58
+ const reportRoot = config.testsRoot || config.path;
59
+ const raw = JSON.parse(readFileSync(args.traceabilityInputPath, 'utf-8'));
60
+ const output = ingestTraceabilityInput(reportRoot, config.impact.traceability, raw, {
61
+ minHits: args.traceabilityMinHits,
62
+ maxFilesPerTest: args.traceabilityMaxFilesPerTest,
63
+ maxAgeDays: args.traceabilityMaxAgeDays,
64
+ });
65
+ console.log(`Traceability manifest: ${output.manifestPath}`);
66
+ console.log(`Traceability state: ${output.statePath}`);
67
+ console.log(`Traceability ingested entries: ${output.entriesIngested}`);
68
+ console.log(`Traceability tracked tests: ${output.testsTracked}`);
69
+ console.log(`Traceability tracked edges: ${output.edgesTracked}`);
70
+ if (output.warnings.length > 0) {
71
+ console.log(`Traceability warnings: ${output.warnings.join(' | ')}`);
72
+ }
73
+ }
@@ -0,0 +1,355 @@
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, mkdirSync, renameSync, writeFileSync } from 'fs';
5
+ import { dirname, join, resolve } from 'path';
6
+ import * as readline from 'readline';
7
+ import { resolveConfig } from '../../agent/config.js';
8
+ import { loadRouteFamilyManifest, serializeManifest } from '../../knowledge/route_families.js';
9
+ import { LLMProviderFactory } from '../../provider_factory.js';
10
+ import { logger, LogLevel } from '../../logger.js';
11
+ import { getVersion } from '../../version.js';
12
+ import { scanProject } from '../../training/scanner.js';
13
+ import { scanFromKnowledgeGraph } from '../../training/kg_scanner.js';
14
+ import { mergeFamilies, detectStaleFamilies } from '../../training/merger.js';
15
+ import { enrichFamilies } from '../../training/enricher.js';
16
+ import { getCommitFiles, validateCommit, buildValidationReport, formatValidationReport } from '../../training/validator.js';
17
+ import { loadKnowledgeGraph } from '../../knowledge/kg_bridge.js';
18
+ class TrainError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = 'TrainError';
22
+ }
23
+ }
24
+ const MAX_BUDGET_USD = 10;
25
+ /**
26
+ * Resolves train-specific options from CLI args.
27
+ * Unlike other commands (analyze, plan, heal) that use the shared resolveConfig()
28
+ * for full pipeline configuration, train only needs appPath and testsRoot.
29
+ * We call resolveConfig() solely to extract testsRoot from the config file.
30
+ */
31
+ function resolveTrainOptions(args, autoConfig) {
32
+ const appPath = args.path || '.';
33
+ let testsRoot = args.testsRoot || appPath;
34
+ // Try to resolve testsRoot from config
35
+ if (autoConfig) {
36
+ try {
37
+ const { config } = resolveConfig(process.cwd(), autoConfig, {
38
+ path: appPath,
39
+ testsRoot: args.testsRoot,
40
+ });
41
+ testsRoot = config.testsRoot || config.path || appPath;
42
+ }
43
+ catch {
44
+ // use defaults
45
+ }
46
+ }
47
+ const outputPath = args.trainOutput ||
48
+ join(testsRoot, '.e2e-ai-agents', 'route-families.json');
49
+ // Validate --pr is a positive integer
50
+ if (args.trainPr !== undefined && (!Number.isInteger(args.trainPr) || args.trainPr <= 0)) {
51
+ throw new TrainError('--pr must be a positive integer');
52
+ }
53
+ // Validate --pr and --since are mutually exclusive
54
+ if (args.trainPr && args.gitSince) {
55
+ throw new TrainError('--pr and --since are mutually exclusive.');
56
+ }
57
+ // Validate --since format (reject leading '-' to prevent git flag injection)
58
+ const since = args.gitSince || 'HEAD~20';
59
+ if (/^-/.test(since) || !/^[a-zA-Z0-9_.~^@\/-]+$/.test(since)) {
60
+ throw new TrainError(`Invalid git ref: ${since}`);
61
+ }
62
+ // Validate budget bounds
63
+ const budget = args.budgetUSD || 0.50;
64
+ if (budget <= 0) {
65
+ throw new TrainError('--budget-usd must be a positive number');
66
+ }
67
+ if (budget > MAX_BUDGET_USD) {
68
+ throw new TrainError(`Budget exceeds maximum of $${MAX_BUDGET_USD}. Use a lower --budget-usd value.`);
69
+ }
70
+ const resolvedAppPath = resolve(appPath);
71
+ const resolvedTestsRoot = resolve(testsRoot);
72
+ const resolvedOutputPath = resolve(outputPath);
73
+ // Validate --path is a real project
74
+ if (!existsSync(resolvedAppPath)) {
75
+ throw new TrainError(`Project root not found: ${resolvedAppPath}`);
76
+ }
77
+ // Validate --output is within project boundary (append separator to prevent prefix attacks)
78
+ const inApp = resolvedOutputPath === resolvedAppPath || resolvedOutputPath.startsWith(resolvedAppPath + '/');
79
+ const inTests = resolvedOutputPath === resolvedTestsRoot || resolvedOutputPath.startsWith(resolvedTestsRoot + '/');
80
+ if (!inApp && !inTests) {
81
+ throw new TrainError(`Output path must be within the project root or tests root: ${resolvedOutputPath}`);
82
+ }
83
+ // Discover git repo root for monorepo-aware scanning and validation
84
+ let gitRepoRoot;
85
+ try {
86
+ gitRepoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
87
+ cwd: resolvedAppPath,
88
+ encoding: 'utf-8',
89
+ stdio: ['pipe', 'pipe', 'pipe'],
90
+ }).trim();
91
+ }
92
+ catch {
93
+ // Not a git repo or git not available
94
+ }
95
+ // Resolve serverRoot: explicit flag, or auto-detect from git repo root
96
+ let serverRoot = args.serverPath;
97
+ if (!serverRoot && gitRepoRoot) {
98
+ const serverDir = join(gitRepoRoot, 'server');
99
+ if (existsSync(serverDir)) {
100
+ serverRoot = serverDir;
101
+ }
102
+ }
103
+ const resolvedServerRoot = serverRoot ? resolve(serverRoot) : undefined;
104
+ return {
105
+ appPath: resolvedAppPath,
106
+ testsRoot: resolvedTestsRoot,
107
+ serverRoot: resolvedServerRoot,
108
+ gitRepoRoot: gitRepoRoot ? resolve(gitRepoRoot) : undefined,
109
+ enrich: args.trainEnrich !== false,
110
+ validate: args.trainValidate || false,
111
+ since,
112
+ pr: args.trainPr,
113
+ outputPath: resolvedOutputPath,
114
+ dryRun: args.dryRun || false,
115
+ yes: args.trainYes || false,
116
+ budgetUSD: budget,
117
+ };
118
+ }
119
+ function ask(rl, question, defaultValue) {
120
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
121
+ return new Promise((res) => {
122
+ rl.question(`${question}${suffix}: `, (answer) => {
123
+ res(answer.trim() || defaultValue || '');
124
+ });
125
+ });
126
+ }
127
+ export async function runTrainCommand(args, autoConfig) {
128
+ const opts = resolveTrainOptions(args, autoConfig);
129
+ const totalTimer = logger.timer('train-total');
130
+ const timings = {};
131
+ // Configure observability from CLI flags
132
+ if (args.verbose)
133
+ logger.setLevel(LogLevel.DEBUG);
134
+ if (args.jsonOutput)
135
+ logger.setJsonMode(true);
136
+ logger.info('impact-gate train');
137
+ logger.info('===================');
138
+ // ---------- Phase 1: Deterministic scan ----------
139
+ // Prefer knowledge graph when available
140
+ const kg = loadKnowledgeGraph(opts.appPath);
141
+ logger.info('Scanning project structure...');
142
+ if (opts.serverRoot) {
143
+ logger.info(`Server root: ${opts.serverRoot}`);
144
+ }
145
+ const scanTimer = logger.timer('scan');
146
+ let scanResult;
147
+ if (kg) {
148
+ logger.info('Using knowledge graph for scanning (found .understand-anything/knowledge-graph.json)');
149
+ scanResult = scanFromKnowledgeGraph(kg);
150
+ logger.info(`KG: ${kg.nodes.length} nodes, ${kg.edges.length} edges`);
151
+ }
152
+ else {
153
+ scanResult = scanProject(opts.appPath, opts.testsRoot !== opts.appPath ? opts.testsRoot : undefined, opts.serverRoot, opts.gitRepoRoot);
154
+ }
155
+ timings.scan = scanTimer.end();
156
+ logger.info(`Found ${scanResult.stats.totalSourceFiles} source files, ${scanResult.stats.totalTestFiles} test files`);
157
+ logger.info(`Discovered ${scanResult.families.length} candidate families`);
158
+ if (scanResult.families.length === 0) {
159
+ logger.info('No families discovered. Make sure your project has recognizable');
160
+ logger.info('source directories (src/, server/, app/) and test directories');
161
+ logger.info('(tests/, e2e/, specs/) with matching names.');
162
+ return;
163
+ }
164
+ // ---------- Phase 2: Merge with existing ----------
165
+ const mergeTimer = logger.timer('merge');
166
+ const existing = loadRouteFamilyManifest(opts.testsRoot);
167
+ if (existing) {
168
+ logger.info(`Found existing manifest with ${existing.families.length} families`);
169
+ }
170
+ let mergeResult = mergeFamilies(existing, scanResult.families);
171
+ timings.merge = mergeTimer.end();
172
+ logger.info(`Merge: ${mergeResult.summary}`);
173
+ // ---------- Phase 3: Stale detection ----------
174
+ if (mergeResult.manifest.families.length > 0) {
175
+ const stale = detectStaleFamilies(mergeResult.manifest, opts.appPath, opts.testsRoot);
176
+ if (stale.length > 0) {
177
+ logger.info(`Stale families detected (${stale.length}):`);
178
+ for (const id of stale) {
179
+ logger.info(` ${id} — paths no longer exist`);
180
+ }
181
+ if (!opts.yes && !opts.dryRun && process.stdin.isTTY) {
182
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
183
+ try {
184
+ const answer = await ask(rl, ' Remove stale families? [y/N]', 'N');
185
+ if (answer.toLowerCase() === 'y') {
186
+ const staleSet = new Set(stale);
187
+ mergeResult.manifest.families = mergeResult.manifest.families.filter((f) => !staleSet.has(f.id));
188
+ mergeResult.staleFamilies = stale;
189
+ logger.info(`Removed ${stale.length} stale families`);
190
+ }
191
+ }
192
+ finally {
193
+ rl.close();
194
+ }
195
+ }
196
+ }
197
+ }
198
+ // ---------- Phase 4: LLM Enrichment ----------
199
+ let enrichTokens = 0;
200
+ let enrichCost = 0;
201
+ let enrichRequests = 0;
202
+ let enrichAvgResponseMs = 0;
203
+ if (opts.enrich) {
204
+ logger.info('Enriching with LLM...');
205
+ const enrichTimer = logger.timer('enrich');
206
+ try {
207
+ const provider = await LLMProviderFactory.createFromEnv();
208
+ const enrichResult = await enrichFamilies(mergeResult.manifest.families, scanResult.families, opts.appPath, provider, opts.budgetUSD, opts.testsRoot !== opts.appPath ? opts.testsRoot : undefined);
209
+ mergeResult.manifest.families = enrichResult.enrichedFamilies;
210
+ enrichTokens = enrichResult.tokensUsed;
211
+ enrichCost = enrichResult.costUSD;
212
+ enrichRequests = enrichResult.requestCount ?? 0;
213
+ enrichAvgResponseMs = enrichResult.avgResponseMs ?? 0;
214
+ logger.info(`Enriched ${enrichResult.enrichedFamilies.length} families`, {
215
+ tokens: enrichResult.tokensUsed,
216
+ cost: enrichResult.costUSD,
217
+ requests: enrichRequests,
218
+ avgResponseMs: enrichAvgResponseMs,
219
+ });
220
+ if (enrichResult.skippedFamilies.length > 0) {
221
+ logger.info(`Skipped ${enrichResult.skippedFamilies.length} families (budget limit)`);
222
+ }
223
+ }
224
+ catch (error) {
225
+ logger.warn(`LLM enrichment failed: ${error instanceof Error ? error.message : String(error)}`);
226
+ logger.warn('Continuing with deterministic results. Use --no-enrich to skip LLM.');
227
+ }
228
+ timings.enrich = enrichTimer.end();
229
+ }
230
+ // ---------- Phase 5: Write manifest ----------
231
+ const json = serializeManifest(mergeResult.manifest);
232
+ if (opts.dryRun) {
233
+ logger.info('Dry run — proposed manifest:');
234
+ console.log(json);
235
+ }
236
+ else {
237
+ const dir = dirname(opts.outputPath);
238
+ if (!existsSync(dir)) {
239
+ mkdirSync(dir, { recursive: true });
240
+ }
241
+ const tmpPath = `${opts.outputPath}.tmp`;
242
+ writeFileSync(tmpPath, json, 'utf-8');
243
+ renameSync(tmpPath, opts.outputPath);
244
+ logger.info(`Wrote ${opts.outputPath}`);
245
+ logger.info(`${mergeResult.manifest.families.length} families`);
246
+ }
247
+ // ---------- Phase 6: Report unmatched ----------
248
+ if (scanResult.unmatchedSourceDirs.length > 0 || scanResult.unmatchedTestDirs.length > 0) {
249
+ logger.info('Unmatched (review manually):');
250
+ for (const dir of scanResult.unmatchedSourceDirs.slice(0, 10)) {
251
+ logger.info(` source: ${dir.relativePath}`);
252
+ }
253
+ for (const dir of scanResult.unmatchedTestDirs.slice(0, 10)) {
254
+ logger.info(` test: ${dir.relativePath}`);
255
+ }
256
+ if (scanResult.unmatchedSourceDirs.length + scanResult.unmatchedTestDirs.length > 20) {
257
+ logger.info(' ... and more');
258
+ }
259
+ }
260
+ // ---------- Phase 7: Validation (optional) ----------
261
+ let validationReport;
262
+ if (opts.validate) {
263
+ const validateTimer = logger.timer('validate');
264
+ // Build path prefixes for monorepo-aware path normalization.
265
+ // Git log returns repo-root-relative paths, but manifest globs are
266
+ // relative to appPath, testsRoot, or serverRoot.
267
+ const pathPrefixes = [];
268
+ if (opts.gitRepoRoot) {
269
+ const { relative: relPath } = await import('path');
270
+ for (const root of [opts.appPath, opts.testsRoot, opts.serverRoot].filter(Boolean)) {
271
+ const rel = relPath(opts.gitRepoRoot, root).replace(/\\/g, '/');
272
+ if (rel && !rel.startsWith('..') && rel !== '.') {
273
+ pathPrefixes.push(rel.endsWith('/') ? rel : rel + '/');
274
+ }
275
+ }
276
+ }
277
+ logger.debug('Validation path prefixes', { prefixes: pathPrefixes });
278
+ if (opts.pr) {
279
+ logger.info(`Validating against PR #${opts.pr}...`);
280
+ // Check for gh CLI
281
+ const { execFileSync } = await import('child_process');
282
+ try {
283
+ execFileSync('gh', ['--version'], { stdio: 'pipe' });
284
+ }
285
+ catch {
286
+ throw new TrainError('--pr requires the GitHub CLI (gh). Install: https://cli.github.com/');
287
+ }
288
+ // Fetch PR changed files via gh CLI
289
+ let prFiles;
290
+ try {
291
+ const output = execFileSync('gh', ['pr', 'view', String(opts.pr), '--json', 'files', '-q', '.files[].path'], {
292
+ cwd: opts.appPath,
293
+ encoding: 'utf-8',
294
+ stdio: ['pipe', 'pipe', 'pipe'],
295
+ });
296
+ prFiles = output.trim().split('\n').filter(Boolean);
297
+ }
298
+ catch (error) {
299
+ throw new TrainError(`Error fetching PR #${opts.pr}: ${error instanceof Error ? error.message : String(error)}`);
300
+ }
301
+ if (prFiles.length === 0) {
302
+ logger.info('No files found in PR.');
303
+ }
304
+ else {
305
+ const validation = validateCommit(mergeResult.manifest, prFiles, `PR#${opts.pr}`, `PR #${opts.pr}`, pathPrefixes);
306
+ validationReport = buildValidationReport([validation], mergeResult.manifest);
307
+ logger.info(formatValidationReport(validationReport));
308
+ }
309
+ }
310
+ else {
311
+ logger.info(`Validating against git history (${opts.since})...`);
312
+ const commits = getCommitFiles(opts.gitRepoRoot || opts.appPath, opts.since);
313
+ if (commits.length === 0) {
314
+ logger.info('No commits found in range.');
315
+ }
316
+ else {
317
+ const validations = commits.map((c) => validateCommit(mergeResult.manifest, c.files, c.hash, c.message, pathPrefixes));
318
+ validationReport = buildValidationReport(validations, mergeResult.manifest);
319
+ logger.info(formatValidationReport(validationReport));
320
+ }
321
+ }
322
+ timings.validate = validateTimer.end();
323
+ }
324
+ timings.total = totalTimer.end();
325
+ // ---------- Write train report ----------
326
+ if (!opts.dryRun) {
327
+ const reportDir = dirname(opts.outputPath);
328
+ const trainReport = {
329
+ timestamp: new Date().toISOString(),
330
+ version: getVersion(),
331
+ timings,
332
+ families: {
333
+ total: mergeResult.manifest.families.length,
334
+ new: mergeResult.newFamilies.length,
335
+ updated: mergeResult.updatedFamilies.length,
336
+ stale: mergeResult.staleFamilies.length,
337
+ },
338
+ coverage: validationReport ? {
339
+ percent: validationReport.coveragePercent,
340
+ boundFiles: validationReport.boundFiles,
341
+ totalFiles: validationReport.totalFiles,
342
+ } : undefined,
343
+ llm: opts.enrich ? {
344
+ tokensUsed: enrichTokens,
345
+ costUSD: enrichCost,
346
+ requests: enrichRequests,
347
+ avgResponseMs: enrichAvgResponseMs,
348
+ } : undefined,
349
+ };
350
+ const reportPath = join(reportDir, 'train-report.json');
351
+ writeFileSync(reportPath, JSON.stringify(trainReport, null, 2) + '\n', 'utf-8');
352
+ logger.debug('Wrote train report', { path: reportPath });
353
+ }
354
+ logger.info('Done.');
355
+ }
@@ -0,0 +1,165 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { existsSync, readFileSync } from 'fs';
4
+ import { execFileSync } from 'child_process';
5
+ import { join, resolve } from 'path';
6
+ function detectPytestFramework(appPath) {
7
+ const resolvedPath = resolve(appPath);
8
+ const pytestIni = join(resolvedPath, 'pytest.ini');
9
+ if (existsSync(pytestIni)) {
10
+ return 'pytest';
11
+ }
12
+ const conftest = join(resolvedPath, 'conftest.py');
13
+ if (existsSync(conftest)) {
14
+ return 'pytest';
15
+ }
16
+ const pyproject = join(resolvedPath, 'pyproject.toml');
17
+ if (existsSync(pyproject)) {
18
+ try {
19
+ const content = readFileSync(pyproject, 'utf-8');
20
+ if (content.includes('pytest')) {
21
+ return 'pytest';
22
+ }
23
+ }
24
+ catch {
25
+ // ignore malformed or unreadable file
26
+ }
27
+ }
28
+ const setupCfg = join(resolvedPath, 'setup.cfg');
29
+ if (existsSync(setupCfg)) {
30
+ try {
31
+ const content = readFileSync(setupCfg, 'utf-8');
32
+ if (content.includes('[tool:pytest]') || content.includes('[pytest]')) {
33
+ return 'pytest';
34
+ }
35
+ }
36
+ catch {
37
+ // ignore malformed or unreadable file
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+ /**
43
+ * Detect the test framework from package.json dependencies.
44
+ */
45
+ export function detectFramework(appPath) {
46
+ const resolvedPath = resolve(appPath);
47
+ const pkgPath = join(resolvedPath, 'package.json');
48
+ if (!existsSync(pkgPath)) {
49
+ const pytestFramework = detectPytestFramework(resolvedPath);
50
+ if (pytestFramework) {
51
+ return pytestFramework;
52
+ }
53
+ return 'auto';
54
+ }
55
+ try {
56
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
57
+ const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
58
+ if (allDeps['@playwright/test'] || allDeps.playwright) {
59
+ return 'playwright';
60
+ }
61
+ if (allDeps.cypress) {
62
+ return 'cypress';
63
+ }
64
+ if (allDeps.supertest) {
65
+ return 'supertest';
66
+ }
67
+ if (allDeps['selenium-webdriver'] || allDeps.webdriverio) {
68
+ return 'selenium';
69
+ }
70
+ }
71
+ catch {
72
+ // ignore malformed package.json
73
+ }
74
+ const pytestFramework = detectPytestFramework(resolvedPath);
75
+ if (pytestFramework) {
76
+ return pytestFramework;
77
+ }
78
+ return 'auto';
79
+ }
80
+ /**
81
+ * Detect the tests root directory by scanning common conventions.
82
+ */
83
+ export function detectTestsRoot(appPath) {
84
+ const resolvedPath = resolve(appPath);
85
+ const candidates = [
86
+ 'e2e-tests/playwright',
87
+ 'e2e-tests',
88
+ 'e2e',
89
+ 'tests/e2e',
90
+ 'test/e2e',
91
+ 'tests',
92
+ 'test',
93
+ 'specs',
94
+ 'playwright',
95
+ 'cypress',
96
+ ];
97
+ for (const candidate of candidates) {
98
+ if (existsSync(join(resolvedPath, candidate))) {
99
+ return candidate;
100
+ }
101
+ }
102
+ return undefined;
103
+ }
104
+ /**
105
+ * Detect the git default branch for diffing.
106
+ * Returns origin/<branch> format.
107
+ */
108
+ export function detectGitDefaultBranch(appPath) {
109
+ try {
110
+ // Try to find the remote HEAD branch first
111
+ const remoteInfo = execFileSync('git', ['remote', 'show', 'origin'], {
112
+ cwd: resolve(appPath),
113
+ encoding: 'utf-8',
114
+ stdio: ['pipe', 'pipe', 'pipe'],
115
+ timeout: 5000,
116
+ });
117
+ const headMatch = remoteInfo.match(/HEAD branch:\s*(.+)/);
118
+ if (headMatch) {
119
+ return `origin/${headMatch[1].trim()}`;
120
+ }
121
+ }
122
+ catch {
123
+ // fallback to current branch
124
+ }
125
+ try {
126
+ const result = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
127
+ cwd: resolve(appPath),
128
+ encoding: 'utf-8',
129
+ stdio: ['pipe', 'pipe', 'pipe'],
130
+ timeout: 5000,
131
+ }).trim();
132
+ return `origin/${result}`;
133
+ }
134
+ catch {
135
+ return 'origin/main';
136
+ }
137
+ }
138
+ /**
139
+ * Detect the project root by walking up to find package.json or .git.
140
+ */
141
+ export function detectProjectRoot(startDir) {
142
+ let current = resolve(startDir);
143
+ while (true) {
144
+ if (existsSync(join(current, 'package.json')) || existsSync(join(current, '.git'))) {
145
+ return current;
146
+ }
147
+ const parent = resolve(current, '..');
148
+ if (parent === current) {
149
+ break;
150
+ }
151
+ current = parent;
152
+ }
153
+ return startDir;
154
+ }
155
+ /**
156
+ * Resolve defaults for CLI commands that need path/testsRoot/framework/since.
157
+ * Explicit values from CLI flags take precedence over detected values.
158
+ */
159
+ export function resolveDefaults(explicit) {
160
+ const path = explicit.path || detectProjectRoot(process.cwd());
161
+ const testsRoot = explicit.testsRoot || detectTestsRoot(path) || '.';
162
+ const framework = explicit.framework || detectFramework(path);
163
+ const since = explicit.gitSince || detectGitDefaultBranch(path);
164
+ return { path, testsRoot, framework, since };
165
+ }
@@ -0,0 +1,52 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ /**
4
+ * CLI Error types with structured exit codes.
5
+ *
6
+ * Exit codes:
7
+ * 0 = success
8
+ * 1 = general/user error (bad args, missing config, invalid input)
9
+ * 2 = budget exceeded
10
+ * 3 = LLM provider unavailable (API down, auth failure)
11
+ * 4 = invalid manifest or config file
12
+ */
13
+ export const EXIT_CODES = {
14
+ SUCCESS: 0,
15
+ GENERAL_ERROR: 1,
16
+ BUDGET_EXCEEDED: 2,
17
+ PROVIDER_UNAVAILABLE: 3,
18
+ INVALID_CONFIG: 4,
19
+ };
20
+ export class CliError extends Error {
21
+ constructor(message, exitCode = EXIT_CODES.GENERAL_ERROR) {
22
+ super(message);
23
+ this.exitCode = exitCode;
24
+ this.name = 'CliError';
25
+ }
26
+ }
27
+ /**
28
+ * Classify an unknown error into the appropriate exit code.
29
+ */
30
+ export function classifyError(error) {
31
+ if (error instanceof CliError)
32
+ return error.exitCode;
33
+ const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
34
+ // Budget errors
35
+ if (msg.includes('budget exceeded') || msg.includes('budget limit')) {
36
+ return EXIT_CODES.BUDGET_EXCEEDED;
37
+ }
38
+ // Provider/auth errors
39
+ if (msg.includes('api key') || msg.includes('authentication') ||
40
+ msg.includes('unauthorized') || msg.includes('403') ||
41
+ (msg.includes('provider') && msg.includes('unavailable')) ||
42
+ msg.includes('econnrefused') || msg.includes('econnreset')) {
43
+ return EXIT_CODES.PROVIDER_UNAVAILABLE;
44
+ }
45
+ // Config/manifest errors
46
+ if ((msg.includes('manifest') && (msg.includes('invalid') || msg.includes('not found') || msg.includes('parse'))) ||
47
+ (msg.includes('config') && msg.includes('invalid')) ||
48
+ (msg.includes('route-families') && msg.includes('invalid'))) {
49
+ return EXIT_CODES.INVALID_CONFIG;
50
+ }
51
+ return EXIT_CODES.GENERAL_ERROR;
52
+ }