@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,57 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { resolveConfig } from '../../agent/config.js';
4
+ import { runTargetedSpecHeal } from '../../agent/pipeline.js';
5
+ import { extractPlaywrightUnstableSpecs } from '../../agent/playwright_report.js';
6
+ export function runHealCommand(args, autoConfig) {
7
+ if (!args.path && !autoConfig) {
8
+ console.error('Error: --path is required for heal command');
9
+ process.exit(1);
10
+ }
11
+ if (!args.traceabilityReportPath) {
12
+ console.error('Error: --traceability-report <path> is required for heal command');
13
+ process.exit(1);
14
+ }
15
+ const { config } = resolveConfig(process.cwd(), autoConfig, {
16
+ path: args.path,
17
+ profile: args.profile,
18
+ testsRoot: args.testsRoot,
19
+ mode: 'gap',
20
+ framework: args.framework,
21
+ pipeline: {
22
+ enabled: true,
23
+ scenarios: args.pipelineScenarios,
24
+ outputDir: args.pipelineOutput,
25
+ baseUrl: args.pipelineBaseUrl,
26
+ browser: args.pipelineBrowser,
27
+ headless: args.pipelineHeadless,
28
+ project: args.pipelineProject,
29
+ parallel: args.pipelineParallel,
30
+ dryRun: args.pipelineDryRun,
31
+ mcp: args.pipelineMcp,
32
+ mcpAllowFallback: args.pipelineMcpAllowFallback,
33
+ mcpOnly: args.pipelineMcpOnly,
34
+ },
35
+ llmProvider: args.llmProvider,
36
+ });
37
+ const reportRoot = config.testsRoot || config.path;
38
+ const unstableSpecs = extractPlaywrightUnstableSpecs(args.traceabilityReportPath, [reportRoot, config.path]);
39
+ if (unstableSpecs.length === 0) {
40
+ console.log('Heal targeted unstable specs: 0');
41
+ return;
42
+ }
43
+ const targetedSummary = runTargetedSpecHeal(reportRoot, unstableSpecs.map((spec) => ({
44
+ specPath: spec.specPath,
45
+ status: spec.status,
46
+ reason: `Playwright report: failingTests=${spec.failingTests}, flakyTests=${spec.flakyTests}`,
47
+ })), {
48
+ ...config.pipeline,
49
+ enabled: true,
50
+ heal: true,
51
+ });
52
+ const healedCount = targetedSummary.results.filter((result) => result.healStatus === 'success').length;
53
+ console.log(`Heal targeted unstable specs: ${unstableSpecs.length} (healed=${healedCount})`);
54
+ if (targetedSummary.warnings.length > 0) {
55
+ console.log(`Heal warnings: ${targetedSummary.warnings.join(' | ')}`);
56
+ }
57
+ }
@@ -0,0 +1,30 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { mkdirSync, writeFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { getChangedFiles } from '../../agent/git.js';
6
+ import { analyzeImpact as analyzeImpactV2 } from '../../engine/impact_engine.js';
7
+ export function runImpactCommand(args, config) {
8
+ const reportRoot = config.testsRoot || config.path;
9
+ const gitResult = getChangedFiles(config.path, config.git.since, { includeUncommitted: config.git.includeUncommitted });
10
+ const impactResult = analyzeImpactV2(gitResult.files, {
11
+ testsRoot: reportRoot,
12
+ routeFamilies: config.routeFamilies,
13
+ });
14
+ console.log(`Impact: ${impactResult.changedFiles.length} changed files → ${impactResult.impactedFeatures.length} features impacted`);
15
+ console.log(`Unbound files: ${impactResult.unboundFiles.length}`);
16
+ for (const f of impactResult.impactedFeatures) {
17
+ const label = f.featureId || f.familyId;
18
+ console.log(` [${f.priority}] ${label}: ${f.coverageStatus} (PW=${f.playwrightSpecs.length}, Cy=${f.cypressSpecs.length})`);
19
+ }
20
+ if (impactResult.warnings.length > 0) {
21
+ for (const w of impactResult.warnings) {
22
+ console.warn(` Warning: ${w}`);
23
+ }
24
+ }
25
+ const outputDir = join(reportRoot, '.e2e-ai-agents');
26
+ mkdirSync(outputDir, { recursive: true });
27
+ const planPath = join(outputDir, 'plan.json');
28
+ writeFileSync(planPath, JSON.stringify(impactResult, null, 2));
29
+ console.log(`Wrote ${planPath}`);
30
+ }
@@ -0,0 +1,133 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { existsSync, writeFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import * as readline from 'readline';
6
+ import { detectFramework, detectTestsRoot, detectGitDefaultBranch } from '../defaults.js';
7
+ const CONFIG_FILENAME = 'impact-gate.config.json';
8
+ const LEGACY_CONFIG_FILENAMES = ['e2e-ai-agents.config.json', '.e2e-ai-agents.config.json'];
9
+ function createInterface() {
10
+ return readline.createInterface({
11
+ input: process.stdin,
12
+ output: process.stdout,
13
+ });
14
+ }
15
+ function ask(rl, question, defaultValue) {
16
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
17
+ return new Promise((resolve) => {
18
+ rl.question(`${question}${suffix}: `, (answer) => {
19
+ resolve(answer.trim() || defaultValue || '');
20
+ });
21
+ });
22
+ }
23
+ function buildConfig(answers) {
24
+ const config = {
25
+ path: answers.path,
26
+ framework: answers.framework,
27
+ git: { since: answers.gitSince },
28
+ impact: {
29
+ dependencyGraph: { enabled: true, maxDepth: 3 },
30
+ traceability: { enabled: true },
31
+ aiFlow: {
32
+ enabled: answers.enableAi,
33
+ provider: answers.enableAi ? answers.provider : undefined,
34
+ },
35
+ },
36
+ policy: {
37
+ enforcementMode: answers.enforcementMode,
38
+ blockOnActions: ['must-add-tests'],
39
+ },
40
+ };
41
+ if (answers.testsRoot && answers.testsRoot !== '.' && answers.testsRoot !== answers.path) {
42
+ config.testsRoot = answers.testsRoot;
43
+ }
44
+ return config;
45
+ }
46
+ function printNextSteps() {
47
+ console.log('');
48
+ console.log(' Next steps:');
49
+ console.log(' 1. Start with impact analysis: npx impact-gate impact --path .');
50
+ console.log(' 2. Build a coverage plan: npx impact-gate plan --path .');
51
+ console.log(' 3. Optional AI setup: export ANTHROPIC_API_KEY=sk-ant-...');
52
+ console.log(' 4. Verify provider health: npx impact-gate llm-health');
53
+ console.log('');
54
+ }
55
+ export async function runInitCommand(yes = false) {
56
+ const targetDir = process.cwd();
57
+ const configPath = join(targetDir, CONFIG_FILENAME);
58
+ const existingLegacyConfig = LEGACY_CONFIG_FILENAMES.find((filename) => existsSync(join(targetDir, filename)));
59
+ if (existsSync(configPath) || existingLegacyConfig) {
60
+ const existingName = existsSync(configPath) ? CONFIG_FILENAME : existingLegacyConfig;
61
+ console.error(`${existingName} already exists in this directory.`);
62
+ console.error('Remove it first if you want to re-initialize.');
63
+ process.exit(1);
64
+ }
65
+ // Non-interactive mode: auto-detect everything and write immediately
66
+ if (yes) {
67
+ const appPath = '.';
68
+ const answers = {
69
+ path: appPath,
70
+ testsRoot: detectTestsRoot(appPath) || '.',
71
+ framework: detectFramework(appPath),
72
+ gitSince: detectGitDefaultBranch(appPath),
73
+ provider: 'auto',
74
+ enableAi: true,
75
+ enforcementMode: 'advisory',
76
+ };
77
+ const config = buildConfig(answers);
78
+ const json = JSON.stringify(config, null, 2) + '\n';
79
+ writeFileSync(configPath, json, 'utf-8');
80
+ console.log(`Created ${CONFIG_FILENAME}`);
81
+ printNextSteps();
82
+ return;
83
+ }
84
+ console.log('');
85
+ console.log(' impact-gate init');
86
+ console.log(' ==================');
87
+ console.log('');
88
+ console.log(' This will create an impact-gate.config.json in the current directory.');
89
+ console.log(' Legacy e2e-ai-agents config filenames are still supported during migration.');
90
+ console.log('');
91
+ const rl = createInterface();
92
+ try {
93
+ const appPath = await ask(rl, ' Path to your web app root', '.');
94
+ const detectedFramework = detectFramework(appPath);
95
+ const framework = await ask(rl, ' Test framework (auto | playwright | cypress | pytest | supertest | selenium)', detectedFramework);
96
+ const detectedTestsRoot = detectTestsRoot(appPath);
97
+ const testsRoot = await ask(rl, ' Path to tests root (relative to app root, "." if same)', detectedTestsRoot || '.');
98
+ const detectedBranch = detectGitDefaultBranch(appPath);
99
+ const gitSince = await ask(rl, ' Git ref to diff against', detectedBranch);
100
+ const providerAnswer = await ask(rl, ' LLM provider for AI features (anthropic | openai | ollama | auto)', 'auto');
101
+ const enableAi = providerAnswer !== 'none';
102
+ const enforcementMode = await ask(rl, ' Policy enforcement mode (advisory | warn | block)', 'advisory');
103
+ const answers = {
104
+ path: appPath,
105
+ testsRoot,
106
+ framework,
107
+ gitSince,
108
+ provider: providerAnswer,
109
+ enableAi,
110
+ enforcementMode,
111
+ };
112
+ const config = buildConfig(answers);
113
+ const json = JSON.stringify(config, null, 2) + '\n';
114
+ console.log('');
115
+ console.log(' Generated config:');
116
+ console.log('');
117
+ for (const line of json.split('\n')) {
118
+ console.log(` ${line}`);
119
+ }
120
+ const confirm = await ask(rl, ' Write this config? (Y/n)', 'Y');
121
+ if (confirm.toLowerCase() !== 'y' && confirm !== '') {
122
+ console.log(' Aborted.');
123
+ process.exit(0);
124
+ }
125
+ writeFileSync(configPath, json, 'utf-8');
126
+ console.log('');
127
+ console.log(` Created ${CONFIG_FILENAME}`);
128
+ printNextSteps();
129
+ }
130
+ finally {
131
+ rl.close();
132
+ }
133
+ }
@@ -0,0 +1,19 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { LLMProviderFactory } from '../../provider_factory.js';
4
+ export async function runLlmHealth() {
5
+ try {
6
+ const provider = await LLMProviderFactory.createFromEnv();
7
+ const health = await provider.checkHealth();
8
+ if (!health.healthy) {
9
+ console.error(`${provider.name} failed: ${health.message}`);
10
+ process.exit(1);
11
+ }
12
+ console.log(`${provider.name} OK -> ${health.message}`);
13
+ }
14
+ catch (error) {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ console.error(`LLM health check failed: ${message}`);
17
+ process.exit(1);
18
+ }
19
+ }
@@ -0,0 +1,117 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { appendFileSync, writeFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { writeCiSummary } from '../../engine/plan_builder.js';
6
+ import { recommendTestsAI, recommendTestsDeterministic } from '../../api.js';
7
+ import { appendCrewToSummary, runPlanCrewAnalysis, writeCrewArtifacts } from './plan_crew.js';
8
+ export async function runPlanCommand(args, autoConfig, config) {
9
+ const reportRoot = config.testsRoot || config.path;
10
+ const apiOptions = {
11
+ cwd: process.cwd(),
12
+ configPath: autoConfig,
13
+ path: args.path,
14
+ profile: args.profile,
15
+ testsRoot: args.testsRoot,
16
+ gitSince: args.gitSince,
17
+ llmProvider: args.llmProvider,
18
+ policy: args.policyMinConfidence !== undefined ||
19
+ args.policySafeMergeConfidence !== undefined ||
20
+ args.policyWarningsThreshold !== undefined ||
21
+ (args.policyRiskyPatterns && args.policyRiskyPatterns.length > 0) ||
22
+ args.policyEnforcementMode !== undefined ||
23
+ (args.policyBlockActions && args.policyBlockActions.length > 0)
24
+ ? {
25
+ minConfidenceForTargeted: args.policyMinConfidence,
26
+ safeMergeMinConfidence: args.policySafeMergeConfidence,
27
+ forceFullOnWarningsAtOrAbove: args.policyWarningsThreshold,
28
+ riskyFilePatterns: args.policyRiskyPatterns,
29
+ enforcementMode: args.policyEnforcementMode,
30
+ blockOnActions: args.policyBlockActions,
31
+ }
32
+ : undefined,
33
+ };
34
+ let result;
35
+ if (args.noAi) {
36
+ result = recommendTestsDeterministic(apiOptions);
37
+ }
38
+ else {
39
+ result = await recommendTestsAI(apiOptions);
40
+ if (result.aiEnrichment) {
41
+ const { aiEnrichment } = result;
42
+ console.log(`AI enrichment: ${aiEnrichment.enrichedFeatures.length} features enriched (${aiEnrichment.tokenUsage.input + aiEnrichment.tokenUsage.output} tokens)`);
43
+ }
44
+ else if (!process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY && !process.env.LLM_PROVIDER) {
45
+ console.log('Tip: configure ANTHROPIC_API_KEY, OPENAI_API_KEY, or LLM_PROVIDER to enable AI-powered enrichment');
46
+ }
47
+ }
48
+ const { plan, planPath, ciSummaryMarkdown, ciSummaryPath } = result;
49
+ let planReport = plan;
50
+ let combinedSummaryMarkdown = ciSummaryMarkdown;
51
+ let crewSummaryPath = '';
52
+ let crewMarkdownPath = '';
53
+ let crewTestPlanPath = '';
54
+ if (args.crew) {
55
+ try {
56
+ const crew = await runPlanCrewAnalysis(plan, config, args);
57
+ planReport = {
58
+ ...plan,
59
+ crew,
60
+ };
61
+ combinedSummaryMarkdown = appendCrewToSummary(ciSummaryMarkdown, crew, plan);
62
+ const artifacts = writeCrewArtifacts(reportRoot, crew, plan);
63
+ crewSummaryPath = artifacts.crewSummaryPath;
64
+ crewMarkdownPath = artifacts.crewMarkdownPath;
65
+ crewTestPlanPath = artifacts.crewTestPlanPath;
66
+ writeFileSync(planPath, JSON.stringify(planReport, null, 2), 'utf-8');
67
+ }
68
+ catch (error) {
69
+ const message = error instanceof Error ? error.message : String(error);
70
+ console.warn(`Crew analysis unavailable: ${message}`);
71
+ }
72
+ }
73
+ writeCiSummary(reportRoot, combinedSummaryMarkdown);
74
+ const summaryPath = args.ciCommentPath
75
+ ? writeCiSummary(reportRoot, combinedSummaryMarkdown, args.ciCommentPath)
76
+ : ciSummaryPath;
77
+ // Compute metrics paths (api already wrote metrics; derive paths for GHA output)
78
+ const metricsEventsPath = join(reportRoot, '.e2e-ai-agents/metrics.jsonl');
79
+ const metricsSummaryPath = join(reportRoot, '.e2e-ai-agents/metrics-summary.json');
80
+ const ghaOutput = args.githubOutputPath || process.env.GITHUB_OUTPUT;
81
+ if (ghaOutput) {
82
+ appendFileSync(ghaOutput, `run_set=${planReport.runSet}\n`);
83
+ appendFileSync(ghaOutput, `action=${planReport.decision.action}\n`);
84
+ appendFileSync(ghaOutput, `confidence=${planReport.confidence}\n`);
85
+ appendFileSync(ghaOutput, `enforcement_mode=${planReport.enforcement.mode}\n`);
86
+ appendFileSync(ghaOutput, `enforcement_should_fail=${planReport.enforcement.shouldFail}\n`);
87
+ appendFileSync(ghaOutput, `recommended_tests_count=${planReport.recommendedTests.length}\n`);
88
+ appendFileSync(ghaOutput, `required_new_tests_count=${planReport.requiredNewTests.length}\n`);
89
+ appendFileSync(ghaOutput, `plan_path=${planPath}\n`);
90
+ appendFileSync(ghaOutput, `summary_path=${summaryPath}\n`);
91
+ appendFileSync(ghaOutput, `metrics_events_path=${metricsEventsPath}\n`);
92
+ appendFileSync(ghaOutput, `metrics_summary_path=${metricsSummaryPath}\n`);
93
+ appendFileSync(ghaOutput, `crew_enabled=${planReport.crew ? 'true' : 'false'}\n`);
94
+ appendFileSync(ghaOutput, `crew_workflow=${planReport.crew?.workflow || ''}\n`);
95
+ appendFileSync(ghaOutput, `crew_summary_path=${crewSummaryPath}\n`);
96
+ appendFileSync(ghaOutput, `crew_markdown_path=${crewMarkdownPath}\n`);
97
+ appendFileSync(ghaOutput, `crew_test_plan_path=${crewTestPlanPath}\n`);
98
+ appendFileSync(ghaOutput, `crew_impacted_flows=${planReport.crew?.summary.impactedFlows || 0}\n`);
99
+ appendFileSync(ghaOutput, `crew_strategy_entries=${planReport.crew?.summary.strategyEntries || 0}\n`);
100
+ appendFileSync(ghaOutput, `crew_test_designs=${planReport.crew?.summary.testDesigns || 0}\n`);
101
+ }
102
+ console.log(`Suggested run set: ${planReport.runSet} (confidence ${planReport.confidence})`);
103
+ console.log(`Decision: ${planReport.decision.action} - ${planReport.decision.summary}`);
104
+ console.log(`Enforcement: ${planReport.enforcement.mode} (shouldFail=${planReport.enforcement.shouldFail})`);
105
+ console.log(`Plan data: ${planPath}`);
106
+ console.log(`CI summary: ${summaryPath}`);
107
+ if (planReport.crew) {
108
+ console.log(`Crew workflow: ${planReport.crew.workflow} (impactedFlows=${planReport.crew.summary.impactedFlows}, strategyEntries=${planReport.crew.summary.strategyEntries}, testDesigns=${planReport.crew.summary.testDesigns})`);
109
+ console.log(`Crew summary: ${crewSummaryPath}`);
110
+ console.log(`Crew test plan: ${crewTestPlanPath}`);
111
+ }
112
+ console.log(`Plan metrics: ${metricsSummaryPath}`);
113
+ const failOnLegacyFlag = args.failOnMustAddTests && planReport.decision.action === 'must-add-tests';
114
+ if (failOnLegacyFlag || planReport.enforcement.shouldFail) {
115
+ process.exit(2);
116
+ }
117
+ }
@@ -0,0 +1,309 @@
1
+ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2
+ // See LICENSE.txt for license information.
3
+ import { mkdirSync, writeFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { CrossImpactAgent } from '../../agents/cross-impact.js';
6
+ import { ImpactAnalystAgent } from '../../agents/impact-analyst.js';
7
+ import { RegressionAdvisorAgent } from '../../agents/regression-advisor.js';
8
+ import { StrategistAgent } from '../../agents/strategist.js';
9
+ import { TestDesignerAgent } from '../../agents/test-designer.js';
10
+ import { CrewOrchestrator } from '../../crew/orchestrator.js';
11
+ const VALID_WORKFLOWS = new Set(['full-qa', 'quick-check', 'design-only']);
12
+ function uniqueStrings(values) {
13
+ return Array.from(new Set(values.filter(Boolean)));
14
+ }
15
+ function singleLine(value) {
16
+ return value.replace(/\s+/g, ' ').trim();
17
+ }
18
+ function chooseCrewWorkflow(explicitWorkflow, plan) {
19
+ if (explicitWorkflow && VALID_WORKFLOWS.has(explicitWorkflow)) {
20
+ return explicitWorkflow;
21
+ }
22
+ if (plan.decision.action === 'must-add-tests' || plan.metrics.uncoveredP0P1Flows > 0) {
23
+ return 'design-only';
24
+ }
25
+ return 'quick-check';
26
+ }
27
+ function registerCrewAgents(orchestrator) {
28
+ orchestrator.registerAgent(new ImpactAnalystAgent());
29
+ orchestrator.registerAgent(new StrategistAgent());
30
+ orchestrator.registerAgent(new TestDesignerAgent());
31
+ orchestrator.registerAgent(new CrossImpactAgent());
32
+ orchestrator.registerAgent(new RegressionAdvisorAgent());
33
+ }
34
+ export async function runPlanCrewAnalysis(plan, config, args) {
35
+ const reportRoot = config.testsRoot || config.path;
36
+ const workflow = chooseCrewWorkflow(args.crewWorkflow, plan);
37
+ const normalizedProvider = config.llm.provider?.trim().toLowerCase();
38
+ const providerOverride = normalizedProvider && normalizedProvider !== 'auto' ? normalizedProvider : 'auto';
39
+ const orchestrator = new CrewOrchestrator();
40
+ registerCrewAgents(orchestrator);
41
+ const result = await orchestrator.run({
42
+ appPath: config.path,
43
+ testsRoot: reportRoot,
44
+ gitSince: args.gitSince || config.git.since,
45
+ routeFamilies: config.routeFamilies,
46
+ apiSurface: config.apiSurface,
47
+ workflow,
48
+ providerOverride: providerOverride === 'auto' ? undefined : providerOverride,
49
+ budgetUSD: args.budgetUSD,
50
+ dryRun: args.dryRun,
51
+ });
52
+ const ctx = result.context;
53
+ const highRiskCrossImpacts = ctx.crossImpacts.filter((entry) => entry.riskLevel === 'high');
54
+ const manualReviewEntries = ctx.strategyEntries.filter((entry) => entry.approach === 'manual-review');
55
+ return {
56
+ workflow,
57
+ providerOverride,
58
+ summary: {
59
+ impactedFlows: ctx.impactedFlows.length,
60
+ strategyEntries: ctx.strategyEntries.length,
61
+ testDesigns: ctx.testDesigns.length,
62
+ crossImpacts: ctx.crossImpacts.length,
63
+ highRiskCrossImpacts: highRiskCrossImpacts.length,
64
+ regressionRisks: ctx.regressionRisks.length,
65
+ findings: ctx.findings.length,
66
+ generatedSpecs: ctx.generatedSpecs.length,
67
+ manualReviewEntries: manualReviewEntries.length,
68
+ totalCostUSD: Number(ctx.usage.totalCost.toFixed(4)),
69
+ totalTokens: ctx.usage.totalTokens,
70
+ },
71
+ impactedFlows: ctx.impactedFlows,
72
+ strategyEntries: ctx.strategyEntries,
73
+ testDesigns: ctx.testDesigns,
74
+ crossImpacts: ctx.crossImpacts,
75
+ regressionRisks: ctx.regressionRisks,
76
+ findings: ctx.findings,
77
+ warnings: uniqueStrings([...ctx.warnings, ...result.warnings]),
78
+ timings: result.timings,
79
+ };
80
+ }
81
+ /**
82
+ * Match a strategy/design entry against a set of gap family IDs.
83
+ */
84
+ function matchesGapFamily(flowId, flowName, gapFamilies) {
85
+ return Array.from(gapFamilies).some((fam) => flowId.startsWith(fam) || flowName.toLowerCase().includes(fam.replace(/_/g, ' ')));
86
+ }
87
+ export function buildCrewMarkdown(crew, plan) {
88
+ const totalCases = crew.testDesigns.reduce((n, td) => n + td.testCases.length, 0);
89
+ const gapFamilies = new Set((plan?.gapDetails ?? []).map((g) => g.id));
90
+ const lines = [
91
+ '### Crew Analysis — What to Test',
92
+ '',
93
+ `Crew analyzed the diff and recommends what to verify before merging.`,
94
+ '',
95
+ `Impacted flows: **${crew.summary.impactedFlows}** | Strategy entries: **${crew.summary.strategyEntries}**`,
96
+ ];
97
+ if (totalCases > 0) {
98
+ const gapDesigns = gapFamilies.size > 0
99
+ ? crew.testDesigns.filter((td) => matchesGapFamily(td.flowId, td.flowName, gapFamilies))
100
+ : [];
101
+ const gapCases = gapDesigns.reduce((n, td) => n + td.testCases.length, 0);
102
+ const gapP0Cases = gapDesigns.reduce((n, td) => n + td.testCases.filter((tc) => tc.priority === 'P0').length, 0);
103
+ lines.push(`Structured test designs: **${crew.summary.testDesigns}** flows, **${totalCases}** test cases`);
104
+ if (gapDesigns.length > 0) {
105
+ lines.push(`Gap-focused: **${gapDesigns.length}** flows, **${gapCases}** test cases (**${gapP0Cases}** P0)`);
106
+ }
107
+ }
108
+ if (crew.summary.crossImpacts > 0) {
109
+ lines.push(`Cross-impacts: **${crew.summary.crossImpacts}** (${crew.summary.highRiskCrossImpacts} high risk)`);
110
+ }
111
+ lines.push(`Estimated AI cost: **$${crew.summary.totalCostUSD.toFixed(4)}**`);
112
+ if (crew.strategyEntries.length > 0) {
113
+ lines.push('');
114
+ lines.push('Top Strategy Recommendations:');
115
+ for (const entry of crew.strategyEntries.slice(0, 5)) {
116
+ lines.push(`- ${entry.priority} ${entry.flowName} -> ${entry.approach} (${entry.crossImpactRisk} cross-impact risk)`);
117
+ }
118
+ }
119
+ if (crew.testDesigns.length > 0) {
120
+ lines.push('');
121
+ lines.push('Structured Test Designs:');
122
+ for (const design of crew.testDesigns.slice(0, 3)) {
123
+ lines.push(`- ${design.flowName}: ${design.testCases.length} designed test case(s)`);
124
+ }
125
+ }
126
+ const riskyCrossImpacts = crew.crossImpacts.filter((entry) => entry.riskLevel === 'high');
127
+ if (riskyCrossImpacts.length > 0) {
128
+ lines.push('');
129
+ lines.push('High-Risk Cross-Impacts:');
130
+ for (const entry of riskyCrossImpacts.slice(0, 5)) {
131
+ lines.push(`- ${entry.sourceFamily} -> ${entry.affectedFamily}: ${entry.sharedDependency}`);
132
+ }
133
+ }
134
+ if (crew.findings.length > 0) {
135
+ lines.push('');
136
+ lines.push('Crew Findings:');
137
+ for (const finding of crew.findings.slice(0, 5)) {
138
+ lines.push(`- ${finding.severity} ${finding.type}: ${finding.summary}`);
139
+ }
140
+ }
141
+ if (crew.warnings.length > 0) {
142
+ lines.push('');
143
+ lines.push('Crew Warnings:');
144
+ for (const warning of crew.warnings.slice(0, 5)) {
145
+ lines.push(`- ${singleLine(warning)}`);
146
+ }
147
+ }
148
+ return lines.join('\n');
149
+ }
150
+ export function appendCrewToSummary(baseMarkdown, crew, plan) {
151
+ return `${baseMarkdown}\n\n---\n\n${buildCrewMarkdown(crew, plan)}`;
152
+ }
153
+ /**
154
+ * Build a full structured test plan as a Markdown document.
155
+ * Sections: gap flows first (P0 cases expanded), then covered-flow smoke tests.
156
+ */
157
+ export function buildCrewTestPlan(crew, plan) {
158
+ const gapFamilies = new Set((plan?.gapDetails ?? []).map((g) => g.id));
159
+ const hasTestDesigns = crew.testDesigns.length > 0;
160
+ const totalCases = crew.testDesigns.reduce((n, td) => n + td.testCases.length, 0);
161
+ // Split strategy entries into gap-related and covered
162
+ const gapStrategies = gapFamilies.size > 0
163
+ ? crew.strategyEntries.filter((s) => matchesGapFamily(s.flowId, s.flowName, gapFamilies))
164
+ : [];
165
+ const coveredStrategies = crew.strategyEntries.filter((s) => !gapStrategies.includes(s));
166
+ // Split test designs (if present) into gap-related and covered
167
+ const gapDesigns = [];
168
+ const coveredDesigns = [];
169
+ for (const td of crew.testDesigns) {
170
+ if (matchesGapFamily(td.flowId, td.flowName, gapFamilies)) {
171
+ gapDesigns.push(td);
172
+ }
173
+ else {
174
+ coveredDesigns.push(td);
175
+ }
176
+ }
177
+ const gapCases = gapDesigns.reduce((n, td) => n + td.testCases.length, 0);
178
+ const coveredCases = coveredDesigns.reduce((n, td) => n + td.testCases.length, 0);
179
+ const lines = [
180
+ '# Crew Test Plan — What to Verify',
181
+ '',
182
+ '> **This is a test recommendation, not a test execution report.**',
183
+ '> Crew analyzed the code diff and identified what needs to be tested.',
184
+ '> Use this plan to guide manual QA or write automated E2E tests.',
185
+ '',
186
+ `_Auto-generated by impact-gate crew (\`${crew.workflow}\` workflow)_`,
187
+ '',
188
+ '## Summary',
189
+ '',
190
+ `| Metric | Count |`,
191
+ `|--------|-------|`,
192
+ `| Gap flows — **no existing tests, must verify** | ${gapStrategies.length} flows${hasTestDesigns ? `, **${gapCases} test cases**` : ''} |`,
193
+ `| Covered flows — **has tests, verify no regressions** | ${coveredStrategies.length} flows${hasTestDesigns ? `, ${coveredCases} test cases` : ''} |`,
194
+ `| Total | ${crew.strategyEntries.length} flows${hasTestDesigns ? `, ${totalCases} test cases` : ''} |`,
195
+ `| High-risk cross-impacts | ${crew.summary.highRiskCrossImpacts} |`,
196
+ `| AI cost | $${crew.summary.totalCostUSD.toFixed(4)} |`,
197
+ '',
198
+ ];
199
+ // ── Gap flows ──
200
+ if (gapStrategies.length > 0) {
201
+ lines.push('## Action Required: Gap Flows (No Existing Tests)');
202
+ lines.push('');
203
+ lines.push('These flows have **no automated E2E coverage**. Before merging, either:');
204
+ lines.push('1. **Write E2E tests** for the critical scenarios below, or');
205
+ lines.push('2. **Manually verify** each flow works as expected');
206
+ lines.push('');
207
+ for (const strategy of gapStrategies) {
208
+ const td = crew.testDesigns.find((d) => d.flowId === strategy.flowId);
209
+ lines.push(`### ${strategy.flowName}`);
210
+ lines.push('');
211
+ const actionVerb = strategy.approach === 'full-test' ? 'Write full E2E test or verify manually'
212
+ : strategy.approach === 'smoke-test' ? 'Smoke-test manually or add basic E2E coverage'
213
+ : strategy.approach === 'manual-review' ? 'Manual review required'
214
+ : 'Can skip — low risk';
215
+ lines.push(`**${strategy.priority}** | Recommended: **${strategy.approach}** | Cross-impact risk: **${strategy.crossImpactRisk}**`);
216
+ lines.push(`> ${actionVerb}`);
217
+ if (strategy.rationale && !strategy.rationale.includes('Default strategy')) {
218
+ lines.push(`> Rationale: ${strategy.rationale}`);
219
+ }
220
+ if (strategy.testCategories.length > 0) {
221
+ lines.push(`> Test types to cover: ${strategy.testCategories.join(', ')}`);
222
+ }
223
+ lines.push('');
224
+ // If test designs exist, show P0/P1 cases
225
+ if (td && td.testCases.length > 0) {
226
+ const p0Cases = td.testCases.filter((tc) => tc.priority === 'P0');
227
+ const p1Cases = td.testCases.filter((tc) => tc.priority === 'P1');
228
+ if (p0Cases.length > 0) {
229
+ lines.push('**P0 — Must test:**');
230
+ lines.push('');
231
+ for (const tc of p0Cases) {
232
+ lines.push(`- [ ] **${tc.name}** (${tc.type})`);
233
+ if (tc.preconditions.length > 0) {
234
+ lines.push(` - Preconditions: ${tc.preconditions.join('; ')}`);
235
+ }
236
+ lines.push(` - Steps: ${tc.steps.join(' → ')}`);
237
+ lines.push(` - Expected: ${tc.expectedOutcome}`);
238
+ }
239
+ lines.push('');
240
+ }
241
+ if (p1Cases.length > 0) {
242
+ lines.push(`<details><summary>P1 — Should test (${p1Cases.length})</summary>`);
243
+ lines.push('');
244
+ for (const tc of p1Cases) {
245
+ lines.push(`- [ ] **${tc.name}** (${tc.type}) — ${tc.expectedOutcome}`);
246
+ }
247
+ lines.push('');
248
+ lines.push('</details>');
249
+ lines.push('');
250
+ }
251
+ }
252
+ }
253
+ }
254
+ // ── Covered flows ──
255
+ if (coveredStrategies.length > 0) {
256
+ lines.push('## Regression Check: Covered Flows (Already Have Tests)');
257
+ lines.push('');
258
+ lines.push('These flows have existing E2E specs. The existing tests should catch regressions automatically.');
259
+ lines.push('**No manual action required** unless CI tests fail on these flows.');
260
+ lines.push('');
261
+ for (const strategy of coveredStrategies) {
262
+ const td = crew.testDesigns.find((d) => d.flowId === strategy.flowId);
263
+ const caseCount = td ? td.testCases.length : 0;
264
+ const detail = caseCount > 0 ? ` | ${caseCount} cases` : '';
265
+ lines.push(`<details><summary><strong>${strategy.flowName}</strong> — ${strategy.approach}${detail} (${strategy.priority})</summary>`);
266
+ lines.push('');
267
+ lines.push(`Existing tests should cover this. Cross-impact risk: ${strategy.crossImpactRisk}`);
268
+ if (strategy.rationale && !strategy.rationale.includes('Default strategy')) {
269
+ lines.push(`> ${strategy.rationale}`);
270
+ }
271
+ if (strategy.testCategories.length > 0) {
272
+ lines.push(`Test categories: ${strategy.testCategories.join(', ')}`);
273
+ }
274
+ if (td && td.testCases.length > 0) {
275
+ lines.push('');
276
+ for (const tc of td.testCases) {
277
+ lines.push(`- [ ] **${tc.name}** (${tc.priority}, ${tc.type}) — ${tc.expectedOutcome}`);
278
+ }
279
+ }
280
+ lines.push('');
281
+ lines.push('</details>');
282
+ lines.push('');
283
+ }
284
+ }
285
+ // ── Cross-impacts ──
286
+ const highRisk = crew.crossImpacts.filter((ci) => ci.riskLevel === 'high');
287
+ if (highRisk.length > 0) {
288
+ lines.push('## High-Risk Cross-Impacts — Verify Before Release');
289
+ lines.push('');
290
+ lines.push('Changes in one area may break these related areas. Manually verify or ensure E2E tests cover both sides:');
291
+ lines.push('');
292
+ for (const ci of highRisk) {
293
+ lines.push(`- **${ci.sourceFamily}** → **${ci.affectedFamily}**: ${ci.sharedDependency}`);
294
+ }
295
+ lines.push('');
296
+ }
297
+ return lines.join('\n');
298
+ }
299
+ export function writeCrewArtifacts(reportRoot, crew, plan) {
300
+ const outputDir = join(reportRoot, '.e2e-ai-agents');
301
+ mkdirSync(outputDir, { recursive: true });
302
+ const crewSummaryPath = join(outputDir, 'crew-summary.json');
303
+ const crewMarkdownPath = join(outputDir, 'crew-summary.md');
304
+ const crewTestPlanPath = join(outputDir, 'crew-test-plan.md');
305
+ writeFileSync(crewSummaryPath, JSON.stringify(crew, null, 2), 'utf-8');
306
+ writeFileSync(crewMarkdownPath, buildCrewMarkdown(crew, plan), 'utf-8');
307
+ writeFileSync(crewTestPlanPath, buildCrewTestPlan(crew, plan), 'utf-8');
308
+ return { crewSummaryPath, crewMarkdownPath, crewTestPlanPath };
309
+ }