@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.
- package/LICENSE +168 -0
- package/README.md +520 -0
- package/dist/adapters/cypress.d.ts +10 -0
- package/dist/adapters/cypress.d.ts.map +1 -0
- package/dist/adapters/cypress.js +86 -0
- package/dist/adapters/framework_adapter.d.ts +41 -0
- package/dist/adapters/framework_adapter.d.ts.map +1 -0
- package/dist/adapters/framework_adapter.js +152 -0
- package/dist/adapters/playwright.d.ts +10 -0
- package/dist/adapters/playwright.d.ts.map +1 -0
- package/dist/adapters/playwright.js +86 -0
- package/dist/adapters/pytest.d.ts +10 -0
- package/dist/adapters/pytest.d.ts.map +1 -0
- package/dist/adapters/pytest.js +96 -0
- package/dist/adapters/supertest.d.ts +12 -0
- package/dist/adapters/supertest.d.ts.map +1 -0
- package/dist/adapters/supertest.js +85 -0
- package/dist/agent/api_catalog.d.ts +11 -0
- package/dist/agent/api_catalog.d.ts.map +1 -0
- package/dist/agent/api_catalog.js +210 -0
- package/dist/agent/config.d.ts +193 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +875 -0
- package/dist/agent/feedback.d.ts +91 -0
- package/dist/agent/feedback.d.ts.map +1 -0
- package/dist/agent/feedback.js +323 -0
- package/dist/agent/git.d.ts +19 -0
- package/dist/agent/git.d.ts.map +1 -0
- package/dist/agent/git.js +257 -0
- package/dist/agent/handoff.d.ts +22 -0
- package/dist/agent/handoff.d.ts.map +1 -0
- package/dist/agent/handoff.js +180 -0
- package/dist/agent/llm_agents_flow.d.ts +15 -0
- package/dist/agent/llm_agents_flow.d.ts.map +1 -0
- package/dist/agent/llm_agents_flow.js +434 -0
- package/dist/agent/native_flow.d.ts +6 -0
- package/dist/agent/native_flow.d.ts.map +1 -0
- package/dist/agent/native_flow.js +179 -0
- package/dist/agent/pipeline.d.ts +7 -0
- package/dist/agent/pipeline.d.ts.map +1 -0
- package/dist/agent/pipeline.js +260 -0
- package/dist/agent/pipeline_types.d.ts +54 -0
- package/dist/agent/pipeline_types.d.ts.map +1 -0
- package/dist/agent/pipeline_types.js +4 -0
- package/dist/agent/pipeline_utils.d.ts +12 -0
- package/dist/agent/pipeline_utils.d.ts.map +1 -0
- package/dist/agent/pipeline_utils.js +156 -0
- package/dist/agent/plan.d.ts +170 -0
- package/dist/agent/plan.d.ts.map +1 -0
- package/dist/agent/plan.js +86 -0
- package/dist/agent/playwright_report.d.ts +8 -0
- package/dist/agent/playwright_report.d.ts.map +1 -0
- package/dist/agent/playwright_report.js +126 -0
- package/dist/agent/process_runner.d.ts +10 -0
- package/dist/agent/process_runner.d.ts.map +1 -0
- package/dist/agent/process_runner.js +92 -0
- package/dist/agent/spec_generator.d.ts +5 -0
- package/dist/agent/spec_generator.d.ts.map +1 -0
- package/dist/agent/spec_generator.js +253 -0
- package/dist/agent/test_path.d.ts +2 -0
- package/dist/agent/test_path.d.ts.map +1 -0
- package/dist/agent/test_path.js +23 -0
- package/dist/agent/traceability_capture.d.ts +18 -0
- package/dist/agent/traceability_capture.d.ts.map +1 -0
- package/dist/agent/traceability_capture.js +313 -0
- package/dist/agent/traceability_ingest.d.ts +21 -0
- package/dist/agent/traceability_ingest.d.ts.map +1 -0
- package/dist/agent/traceability_ingest.js +237 -0
- package/dist/agent/types.d.ts +42 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +4 -0
- package/dist/agent/utils.d.ts +13 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +152 -0
- package/dist/agent/validation_runner.d.ts +5 -0
- package/dist/agent/validation_runner.d.ts.map +1 -0
- package/dist/agent/validation_runner.js +77 -0
- package/dist/agentic/fix_loop.d.ts +26 -0
- package/dist/agentic/fix_loop.d.ts.map +1 -0
- package/dist/agentic/fix_loop.js +96 -0
- package/dist/agentic/playwright_runner.d.ts +43 -0
- package/dist/agentic/playwright_runner.d.ts.map +1 -0
- package/dist/agentic/playwright_runner.js +165 -0
- package/dist/agentic/runner.d.ts +27 -0
- package/dist/agentic/runner.d.ts.map +1 -0
- package/dist/agentic/runner.js +210 -0
- package/dist/agentic/types.d.ts +62 -0
- package/dist/agentic/types.d.ts.map +1 -0
- package/dist/agentic/types.js +4 -0
- package/dist/agents/coverage-evaluator.d.ts +8 -0
- package/dist/agents/coverage-evaluator.d.ts.map +1 -0
- package/dist/agents/coverage-evaluator.js +41 -0
- package/dist/agents/cross-impact.d.ts +13 -0
- package/dist/agents/cross-impact.d.ts.map +1 -0
- package/dist/agents/cross-impact.js +140 -0
- package/dist/agents/executor.d.ts +8 -0
- package/dist/agents/executor.d.ts.map +1 -0
- package/dist/agents/executor.js +75 -0
- package/dist/agents/explorer.d.ts +12 -0
- package/dist/agents/explorer.d.ts.map +1 -0
- package/dist/agents/explorer.js +43 -0
- package/dist/agents/generator.d.ts +8 -0
- package/dist/agents/generator.d.ts.map +1 -0
- package/dist/agents/generator.js +77 -0
- package/dist/agents/healer.d.ts +8 -0
- package/dist/agents/healer.d.ts.map +1 -0
- package/dist/agents/healer.js +31 -0
- package/dist/agents/impact-analyst.d.ts +8 -0
- package/dist/agents/impact-analyst.d.ts.map +1 -0
- package/dist/agents/impact-analyst.js +38 -0
- package/dist/agents/regression-advisor.d.ts +8 -0
- package/dist/agents/regression-advisor.d.ts.map +1 -0
- package/dist/agents/regression-advisor.js +116 -0
- package/dist/agents/strategist.d.ts +9 -0
- package/dist/agents/strategist.d.ts.map +1 -0
- package/dist/agents/strategist.js +92 -0
- package/dist/agents/test-designer.d.ts +8 -0
- package/dist/agents/test-designer.d.ts.map +1 -0
- package/dist/agents/test-designer.js +111 -0
- package/dist/anthropic_provider.d.ts +65 -0
- package/dist/anthropic_provider.d.ts.map +1 -0
- package/dist/anthropic_provider.js +334 -0
- package/dist/api.d.ts +48 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +151 -0
- package/dist/base_provider.d.ts +109 -0
- package/dist/base_provider.d.ts.map +1 -0
- package/dist/base_provider.js +203 -0
- package/dist/budget_ledger.d.ts +28 -0
- package/dist/budget_ledger.d.ts.map +1 -0
- package/dist/budget_ledger.js +62 -0
- package/dist/cache/cached_provider.d.ts +49 -0
- package/dist/cache/cached_provider.d.ts.map +1 -0
- package/dist/cache/cached_provider.js +91 -0
- package/dist/cache/response_cache.d.ts +79 -0
- package/dist/cache/response_cache.d.ts.map +1 -0
- package/dist/cache/response_cache.js +177 -0
- package/dist/cli/commands/analyze.d.ts +3 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +77 -0
- package/dist/cli/commands/bootstrap.d.ts +3 -0
- package/dist/cli/commands/bootstrap.d.ts.map +1 -0
- package/dist/cli/commands/bootstrap.js +109 -0
- package/dist/cli/commands/cost_report.d.ts +3 -0
- package/dist/cli/commands/cost_report.d.ts.map +1 -0
- package/dist/cli/commands/cost_report.js +115 -0
- package/dist/cli/commands/crew.d.ts +3 -0
- package/dist/cli/commands/crew.d.ts.map +1 -0
- package/dist/cli/commands/crew.js +255 -0
- package/dist/cli/commands/feedback.d.ts +3 -0
- package/dist/cli/commands/feedback.d.ts.map +1 -0
- package/dist/cli/commands/feedback.js +39 -0
- package/dist/cli/commands/finalize.d.ts +3 -0
- package/dist/cli/commands/finalize.d.ts.map +1 -0
- package/dist/cli/commands/finalize.js +41 -0
- package/dist/cli/commands/gate.d.ts +3 -0
- package/dist/cli/commands/gate.d.ts.map +1 -0
- package/dist/cli/commands/gate.js +89 -0
- package/dist/cli/commands/generate.d.ts +4 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +108 -0
- package/dist/cli/commands/heal.d.ts +3 -0
- package/dist/cli/commands/heal.d.ts.map +1 -0
- package/dist/cli/commands/heal.js +60 -0
- package/dist/cli/commands/impact.d.ts +4 -0
- package/dist/cli/commands/impact.d.ts.map +1 -0
- package/dist/cli/commands/impact.js +33 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +169 -0
- package/dist/cli/commands/llm_health.d.ts +2 -0
- package/dist/cli/commands/llm_health.d.ts.map +1 -0
- package/dist/cli/commands/llm_health.js +22 -0
- package/dist/cli/commands/plan.d.ts +4 -0
- package/dist/cli/commands/plan.d.ts.map +1 -0
- package/dist/cli/commands/plan.js +120 -0
- package/dist/cli/commands/plan_crew.d.ts +17 -0
- package/dist/cli/commands/plan_crew.d.ts.map +1 -0
- package/dist/cli/commands/plan_crew.js +316 -0
- package/dist/cli/commands/traceability.d.ts +4 -0
- package/dist/cli/commands/traceability.d.ts.map +1 -0
- package/dist/cli/commands/traceability.js +77 -0
- package/dist/cli/commands/train.d.ts +3 -0
- package/dist/cli/commands/train.d.ts.map +1 -0
- package/dist/cli/commands/train.js +391 -0
- package/dist/cli/defaults.d.ts +35 -0
- package/dist/cli/defaults.d.ts.map +1 -0
- package/dist/cli/defaults.js +172 -0
- package/dist/cli/errors.d.ts +27 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/errors.js +57 -0
- package/dist/cli/parse_args.d.ts +6 -0
- package/dist/cli/parse_args.d.ts.map +1 -0
- package/dist/cli/parse_args.js +257 -0
- package/dist/cli/types.d.ts +87 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli/usage.d.ts +2 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +109 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +194 -0
- package/dist/crew/context.d.ts +55 -0
- package/dist/crew/context.d.ts.map +1 -0
- package/dist/crew/context.js +36 -0
- package/dist/crew/orchestrator.d.ts +50 -0
- package/dist/crew/orchestrator.d.ts.map +1 -0
- package/dist/crew/orchestrator.js +329 -0
- package/dist/crew/protocol.d.ts +46 -0
- package/dist/crew/protocol.d.ts.map +1 -0
- package/dist/crew/protocol.js +4 -0
- package/dist/crew/provider.d.ts +17 -0
- package/dist/crew/provider.d.ts.map +1 -0
- package/dist/crew/provider.js +36 -0
- package/dist/crew/sanitize.d.ts +3 -0
- package/dist/crew/sanitize.d.ts.map +1 -0
- package/dist/crew/sanitize.js +31 -0
- package/dist/crew/types.d.ts +52 -0
- package/dist/crew/types.d.ts.map +1 -0
- package/dist/crew/types.js +4 -0
- package/dist/crew/workflows.d.ts +52 -0
- package/dist/crew/workflows.d.ts.map +1 -0
- package/dist/crew/workflows.js +36 -0
- package/dist/custom_provider.d.ts +20 -0
- package/dist/custom_provider.d.ts.map +1 -0
- package/dist/custom_provider.js +277 -0
- package/dist/engine/ai_enrichment.d.ts +44 -0
- package/dist/engine/ai_enrichment.d.ts.map +1 -0
- package/dist/engine/ai_enrichment.js +267 -0
- package/dist/engine/diff_loader.d.ts +11 -0
- package/dist/engine/diff_loader.d.ts.map +1 -0
- package/dist/engine/diff_loader.js +63 -0
- package/dist/engine/impact_engine.d.ts +72 -0
- package/dist/engine/impact_engine.d.ts.map +1 -0
- package/dist/engine/impact_engine.js +298 -0
- package/dist/engine/plan_builder.d.ts +11 -0
- package/dist/engine/plan_builder.d.ts.map +1 -0
- package/dist/engine/plan_builder.js +599 -0
- package/dist/esm/adapters/cypress.js +49 -0
- package/dist/esm/adapters/framework_adapter.js +114 -0
- package/dist/esm/adapters/playwright.js +49 -0
- package/dist/esm/adapters/pytest.js +59 -0
- package/dist/esm/adapters/supertest.js +48 -0
- package/dist/esm/agent/api_catalog.js +199 -0
- package/dist/esm/agent/config.js +872 -0
- package/dist/esm/agent/feedback.js +317 -0
- package/dist/esm/agent/git.js +252 -0
- package/dist/esm/agent/handoff.js +177 -0
- package/dist/esm/agent/llm_agents_flow.js +421 -0
- package/dist/esm/agent/native_flow.js +175 -0
- package/dist/esm/agent/pipeline.js +256 -0
- package/dist/esm/agent/pipeline_types.js +3 -0
- package/dist/esm/agent/pipeline_utils.js +146 -0
- package/dist/esm/agent/plan.js +83 -0
- package/dist/esm/agent/playwright_report.js +123 -0
- package/dist/esm/agent/process_runner.js +83 -0
- package/dist/esm/agent/spec_generator.js +249 -0
- package/dist/esm/agent/test_path.js +20 -0
- package/dist/esm/agent/traceability_capture.js +310 -0
- package/dist/esm/agent/traceability_ingest.js +234 -0
- package/dist/esm/agent/types.js +3 -0
- package/dist/esm/agent/utils.js +138 -0
- package/dist/esm/agent/validation_runner.js +73 -0
- package/dist/esm/agentic/fix_loop.js +91 -0
- package/dist/esm/agentic/playwright_runner.js +161 -0
- package/dist/esm/agentic/runner.js +207 -0
- package/dist/esm/agentic/types.js +3 -0
- package/dist/esm/agents/coverage-evaluator.js +37 -0
- package/dist/esm/agents/cross-impact.js +136 -0
- package/dist/esm/agents/executor.js +71 -0
- package/dist/esm/agents/explorer.js +39 -0
- package/dist/esm/agents/generator.js +73 -0
- package/dist/esm/agents/healer.js +27 -0
- package/dist/esm/agents/impact-analyst.js +34 -0
- package/dist/esm/agents/regression-advisor.js +112 -0
- package/dist/esm/agents/strategist.js +88 -0
- package/dist/esm/agents/test-designer.js +107 -0
- package/dist/esm/anthropic_provider.js +326 -0
- package/dist/esm/api.js +143 -0
- package/dist/esm/base_provider.js +198 -0
- package/dist/esm/budget_ledger.js +58 -0
- package/dist/esm/cache/cached_provider.js +85 -0
- package/dist/esm/cache/response_cache.js +140 -0
- package/dist/esm/cli/commands/analyze.js +74 -0
- package/dist/esm/cli/commands/bootstrap.js +106 -0
- package/dist/esm/cli/commands/cost_report.js +112 -0
- package/dist/esm/cli/commands/crew.js +252 -0
- package/dist/esm/cli/commands/feedback.js +36 -0
- package/dist/esm/cli/commands/finalize.js +38 -0
- package/dist/esm/cli/commands/gate.js +86 -0
- package/dist/esm/cli/commands/generate.js +105 -0
- package/dist/esm/cli/commands/heal.js +57 -0
- package/dist/esm/cli/commands/impact.js +30 -0
- package/dist/esm/cli/commands/init.js +133 -0
- package/dist/esm/cli/commands/llm_health.js +19 -0
- package/dist/esm/cli/commands/plan.js +117 -0
- package/dist/esm/cli/commands/plan_crew.js +309 -0
- package/dist/esm/cli/commands/traceability.js +73 -0
- package/dist/esm/cli/commands/train.js +355 -0
- package/dist/esm/cli/defaults.js +165 -0
- package/dist/esm/cli/errors.js +52 -0
- package/dist/esm/cli/parse_args.js +251 -0
- package/dist/esm/cli/types.js +3 -0
- package/dist/esm/cli/usage.js +106 -0
- package/dist/esm/cli.js +192 -0
- package/dist/esm/crew/context.js +32 -0
- package/dist/esm/crew/orchestrator.js +325 -0
- package/dist/esm/crew/protocol.js +3 -0
- package/dist/esm/crew/provider.js +33 -0
- package/dist/esm/crew/sanitize.js +27 -0
- package/dist/esm/crew/types.js +3 -0
- package/dist/esm/crew/workflows.js +33 -0
- package/dist/esm/custom_provider.js +273 -0
- package/dist/esm/engine/ai_enrichment.js +264 -0
- package/dist/esm/engine/diff_loader.js +59 -0
- package/dist/esm/engine/impact_engine.js +291 -0
- package/dist/esm/engine/plan_builder.js +593 -0
- package/dist/esm/index.js +72 -0
- package/dist/esm/knowledge/api_surface.js +408 -0
- package/dist/esm/knowledge/cluster_utils.js +60 -0
- package/dist/esm/knowledge/context_loader.js +85 -0
- package/dist/esm/knowledge/failure_history.js +121 -0
- package/dist/esm/knowledge/kg_bridge.js +381 -0
- package/dist/esm/knowledge/kg_types.js +3 -0
- package/dist/esm/knowledge/route_families.js +393 -0
- package/dist/esm/knowledge/spec_index.js +122 -0
- package/dist/esm/logger.js +115 -0
- package/dist/esm/mcp-server.js +621 -0
- package/dist/esm/metrics/prometheus.js +149 -0
- package/dist/esm/model_router.js +59 -0
- package/dist/esm/ollama_provider.js +301 -0
- package/dist/esm/openai_provider.js +243 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/pipeline/orchestrator.js +228 -0
- package/dist/esm/pipeline/spec_verifier.js +75 -0
- package/dist/esm/pipeline/stage0_preprocess.js +102 -0
- package/dist/esm/pipeline/stage1_impact.js +140 -0
- package/dist/esm/pipeline/stage2_coverage.js +153 -0
- package/dist/esm/pipeline/stage3_generation.js +284 -0
- package/dist/esm/pipeline/stage4_heal.js +288 -0
- package/dist/esm/progress.js +112 -0
- package/dist/esm/prompts/coverage.js +57 -0
- package/dist/esm/prompts/cross-impact.js +53 -0
- package/dist/esm/prompts/generation.js +297 -0
- package/dist/esm/prompts/generation_profile.js +147 -0
- package/dist/esm/prompts/heal.js +91 -0
- package/dist/esm/prompts/impact.js +63 -0
- package/dist/esm/prompts/json_extract.js +36 -0
- package/dist/esm/prompts/strategist.js +61 -0
- package/dist/esm/prompts/test-designer.js +92 -0
- package/dist/esm/provider_factory.js +366 -0
- package/dist/esm/provider_interface.js +23 -0
- package/dist/esm/provider_utils.js +96 -0
- package/dist/esm/qa-agent/cli.js +205 -0
- package/dist/esm/qa-agent/orchestrator.js +120 -0
- package/dist/esm/qa-agent/phase1/runner.js +139 -0
- package/dist/esm/qa-agent/phase1/scope.js +126 -0
- package/dist/esm/qa-agent/phase2/agent_browser.js +95 -0
- package/dist/esm/qa-agent/phase2/agent_loop.js +351 -0
- package/dist/esm/qa-agent/phase2/exploration_state.js +97 -0
- package/dist/esm/qa-agent/phase2/tools.js +386 -0
- package/dist/esm/qa-agent/phase2/vision.js +75 -0
- package/dist/esm/qa-agent/phase3/feedback.js +34 -0
- package/dist/esm/qa-agent/phase3/reporter.js +145 -0
- package/dist/esm/qa-agent/phase3/spec_generator.js +62 -0
- package/dist/esm/qa-agent/phase3/verdict.js +66 -0
- package/dist/esm/qa-agent/safe_env.js +23 -0
- package/dist/esm/qa-agent/types.js +3 -0
- package/dist/esm/reporters/junit.js +86 -0
- package/dist/esm/reporters/reporter.js +3 -0
- package/dist/esm/reporters/sarif.js +132 -0
- package/dist/esm/resilience/circuit_breaker.js +78 -0
- package/dist/esm/resilience/retry.js +56 -0
- package/dist/esm/sanitize.js +66 -0
- package/dist/esm/training/enricher.js +345 -0
- package/dist/esm/training/kg_scanner.js +115 -0
- package/dist/esm/training/merger.js +204 -0
- package/dist/esm/training/scanner.js +923 -0
- package/dist/esm/training/types.js +6 -0
- package/dist/esm/training/validator.js +254 -0
- package/dist/esm/validation/guardrails.js +101 -0
- package/dist/esm/validation/output_schema.js +80 -0
- package/dist/esm/version.js +33 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -0
- package/dist/knowledge/api_surface.d.ts +37 -0
- package/dist/knowledge/api_surface.d.ts.map +1 -0
- package/dist/knowledge/api_surface.js +418 -0
- package/dist/knowledge/cluster_utils.d.ts +28 -0
- package/dist/knowledge/cluster_utils.d.ts.map +1 -0
- package/dist/knowledge/cluster_utils.js +67 -0
- package/dist/knowledge/context_loader.d.ts +13 -0
- package/dist/knowledge/context_loader.d.ts.map +1 -0
- package/dist/knowledge/context_loader.js +90 -0
- package/dist/knowledge/failure_history.d.ts +39 -0
- package/dist/knowledge/failure_history.d.ts.map +1 -0
- package/dist/knowledge/failure_history.js +128 -0
- package/dist/knowledge/kg_bridge.d.ts +31 -0
- package/dist/knowledge/kg_bridge.d.ts.map +1 -0
- package/dist/knowledge/kg_bridge.js +388 -0
- package/dist/knowledge/kg_types.d.ts +75 -0
- package/dist/knowledge/kg_types.d.ts.map +1 -0
- package/dist/knowledge/kg_types.js +4 -0
- package/dist/knowledge/route_families.d.ts +98 -0
- package/dist/knowledge/route_families.d.ts.map +1 -0
- package/dist/knowledge/route_families.js +410 -0
- package/dist/knowledge/spec_index.d.ts +18 -0
- package/dist/knowledge/spec_index.d.ts.map +1 -0
- package/dist/knowledge/spec_index.js +128 -0
- package/dist/logger.d.ts +31 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +119 -0
- package/dist/mcp-server.d.ts +68 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +629 -0
- package/dist/metrics/prometheus.d.ts +37 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +153 -0
- package/dist/model_router.d.ts +28 -0
- package/dist/model_router.d.ts.map +1 -0
- package/dist/model_router.js +63 -0
- package/dist/ollama_provider.d.ts +65 -0
- package/dist/ollama_provider.d.ts.map +1 -0
- package/dist/ollama_provider.js +309 -0
- package/dist/openai_provider.d.ts +23 -0
- package/dist/openai_provider.d.ts.map +1 -0
- package/dist/openai_provider.js +251 -0
- package/dist/pipeline/orchestrator.d.ts +33 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/pipeline/orchestrator.js +231 -0
- package/dist/pipeline/spec_verifier.d.ts +20 -0
- package/dist/pipeline/spec_verifier.d.ts.map +1 -0
- package/dist/pipeline/spec_verifier.js +79 -0
- package/dist/pipeline/stage0_preprocess.d.ts +31 -0
- package/dist/pipeline/stage0_preprocess.d.ts.map +1 -0
- package/dist/pipeline/stage0_preprocess.js +105 -0
- package/dist/pipeline/stage1_impact.d.ts +19 -0
- package/dist/pipeline/stage1_impact.d.ts.map +1 -0
- package/dist/pipeline/stage1_impact.js +143 -0
- package/dist/pipeline/stage2_coverage.d.ts +19 -0
- package/dist/pipeline/stage2_coverage.d.ts.map +1 -0
- package/dist/pipeline/stage2_coverage.js +156 -0
- package/dist/pipeline/stage3_generation.d.ts +43 -0
- package/dist/pipeline/stage3_generation.d.ts.map +1 -0
- package/dist/pipeline/stage3_generation.js +287 -0
- package/dist/pipeline/stage4_heal.d.ts +62 -0
- package/dist/pipeline/stage4_heal.d.ts.map +1 -0
- package/dist/pipeline/stage4_heal.js +294 -0
- package/dist/progress.d.ts +22 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +116 -0
- package/dist/prompts/coverage.d.ts +39 -0
- package/dist/prompts/coverage.d.ts.map +1 -0
- package/dist/prompts/coverage.js +61 -0
- package/dist/prompts/cross-impact.d.ts +23 -0
- package/dist/prompts/cross-impact.d.ts.map +1 -0
- package/dist/prompts/cross-impact.js +57 -0
- package/dist/prompts/generation.d.ts +25 -0
- package/dist/prompts/generation.d.ts.map +1 -0
- package/dist/prompts/generation.js +302 -0
- package/dist/prompts/generation_profile.d.ts +29 -0
- package/dist/prompts/generation_profile.d.ts.map +1 -0
- package/dist/prompts/generation_profile.js +151 -0
- package/dist/prompts/heal.d.ts +23 -0
- package/dist/prompts/heal.d.ts.map +1 -0
- package/dist/prompts/heal.js +95 -0
- package/dist/prompts/impact.d.ts +31 -0
- package/dist/prompts/impact.d.ts.map +1 -0
- package/dist/prompts/impact.js +67 -0
- package/dist/prompts/json_extract.d.ts +14 -0
- package/dist/prompts/json_extract.d.ts.map +1 -0
- package/dist/prompts/json_extract.js +39 -0
- package/dist/prompts/strategist.d.ts +25 -0
- package/dist/prompts/strategist.d.ts.map +1 -0
- package/dist/prompts/strategist.js +65 -0
- package/dist/prompts/test-designer.d.ts +35 -0
- package/dist/prompts/test-designer.d.ts.map +1 -0
- package/dist/prompts/test-designer.js +96 -0
- package/dist/provider_factory.d.ts +104 -0
- package/dist/provider_factory.d.ts.map +1 -0
- package/dist/provider_factory.js +371 -0
- package/dist/provider_interface.d.ts +365 -0
- package/dist/provider_interface.d.ts.map +1 -0
- package/dist/provider_interface.js +28 -0
- package/dist/provider_utils.d.ts +39 -0
- package/dist/provider_utils.d.ts.map +1 -0
- package/dist/provider_utils.js +103 -0
- package/dist/qa-agent/cli.d.ts +3 -0
- package/dist/qa-agent/cli.d.ts.map +1 -0
- package/dist/qa-agent/cli.js +207 -0
- package/dist/qa-agent/orchestrator.d.ts +3 -0
- package/dist/qa-agent/orchestrator.d.ts.map +1 -0
- package/dist/qa-agent/orchestrator.js +123 -0
- package/dist/qa-agent/phase1/runner.d.ts +3 -0
- package/dist/qa-agent/phase1/runner.d.ts.map +1 -0
- package/dist/qa-agent/phase1/runner.js +142 -0
- package/dist/qa-agent/phase1/scope.d.ts +6 -0
- package/dist/qa-agent/phase1/scope.d.ts.map +1 -0
- package/dist/qa-agent/phase1/scope.js +129 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts +35 -0
- package/dist/qa-agent/phase2/agent_browser.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_browser.js +99 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts +3 -0
- package/dist/qa-agent/phase2/agent_loop.d.ts.map +1 -0
- package/dist/qa-agent/phase2/agent_loop.js +357 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts +12 -0
- package/dist/qa-agent/phase2/exploration_state.d.ts.map +1 -0
- package/dist/qa-agent/phase2/exploration_state.js +109 -0
- package/dist/qa-agent/phase2/tools.d.ts +28 -0
- package/dist/qa-agent/phase2/tools.d.ts.map +1 -0
- package/dist/qa-agent/phase2/tools.js +390 -0
- package/dist/qa-agent/phase2/vision.d.ts +3 -0
- package/dist/qa-agent/phase2/vision.d.ts.map +1 -0
- package/dist/qa-agent/phase2/vision.js +78 -0
- package/dist/qa-agent/phase3/feedback.d.ts +3 -0
- package/dist/qa-agent/phase3/feedback.d.ts.map +1 -0
- package/dist/qa-agent/phase3/feedback.js +37 -0
- package/dist/qa-agent/phase3/reporter.d.ts +3 -0
- package/dist/qa-agent/phase3/reporter.d.ts.map +1 -0
- package/dist/qa-agent/phase3/reporter.js +148 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts +3 -0
- package/dist/qa-agent/phase3/spec_generator.d.ts.map +1 -0
- package/dist/qa-agent/phase3/spec_generator.js +65 -0
- package/dist/qa-agent/phase3/verdict.d.ts +3 -0
- package/dist/qa-agent/phase3/verdict.d.ts.map +1 -0
- package/dist/qa-agent/phase3/verdict.js +69 -0
- package/dist/qa-agent/safe_env.d.ts +3 -0
- package/dist/qa-agent/safe_env.d.ts.map +1 -0
- package/dist/qa-agent/safe_env.js +26 -0
- package/dist/qa-agent/types.d.ts +130 -0
- package/dist/qa-agent/types.d.ts.map +1 -0
- package/dist/qa-agent/types.js +4 -0
- package/dist/reporters/junit.d.ts +6 -0
- package/dist/reporters/junit.d.ts.map +1 -0
- package/dist/reporters/junit.js +89 -0
- package/dist/reporters/reporter.d.ts +42 -0
- package/dist/reporters/reporter.d.ts.map +1 -0
- package/dist/reporters/reporter.js +4 -0
- package/dist/reporters/sarif.d.ts +7 -0
- package/dist/reporters/sarif.d.ts.map +1 -0
- package/dist/reporters/sarif.js +135 -0
- package/dist/resilience/circuit_breaker.d.ts +36 -0
- package/dist/resilience/circuit_breaker.d.ts.map +1 -0
- package/dist/resilience/circuit_breaker.js +82 -0
- package/dist/resilience/retry.d.ts +11 -0
- package/dist/resilience/retry.d.ts.map +1 -0
- package/dist/resilience/retry.js +59 -0
- package/dist/sanitize.d.ts +15 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +71 -0
- package/dist/training/enricher.d.ts +17 -0
- package/dist/training/enricher.d.ts.map +1 -0
- package/dist/training/enricher.js +350 -0
- package/dist/training/kg_scanner.d.ts +13 -0
- package/dist/training/kg_scanner.d.ts.map +1 -0
- package/dist/training/kg_scanner.js +118 -0
- package/dist/training/merger.d.ts +15 -0
- package/dist/training/merger.d.ts.map +1 -0
- package/dist/training/merger.js +208 -0
- package/dist/training/scanner.d.ts +36 -0
- package/dist/training/scanner.d.ts.map +1 -0
- package/dist/training/scanner.js +932 -0
- package/dist/training/types.d.ts +117 -0
- package/dist/training/types.d.ts.map +1 -0
- package/dist/training/types.js +9 -0
- package/dist/training/validator.d.ts +21 -0
- package/dist/training/validator.d.ts.map +1 -0
- package/dist/training/validator.js +262 -0
- package/dist/validation/guardrails.d.ts +31 -0
- package/dist/validation/guardrails.d.ts.map +1 -0
- package/dist/validation/guardrails.js +112 -0
- package/dist/validation/output_schema.d.ts +67 -0
- package/dist/validation/output_schema.d.ts.map +1 -0
- package/dist/validation/output_schema.js +84 -0
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +36 -0
- package/package.json +126 -0
- package/schemas/flow-decision.schema.json +83 -0
- package/schemas/gap.schema.json +18 -0
- package/schemas/impact.schema.json +455 -0
- package/schemas/plan.schema.json +491 -0
- package/schemas/route-families.schema.json +137 -0
- package/schemas/subsystem-risk-map.schema.json +62 -0
- package/schemas/traceability-input.schema.json +122 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.buildPlanFromImpact = buildPlanFromImpact;
|
|
6
|
+
exports.writePlanReport = writePlanReport;
|
|
7
|
+
exports.renderCiSummaryMarkdown = renderCiSummaryMarkdown;
|
|
8
|
+
exports.writeCiSummary = writeCiSummary;
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const minimatch_1 = require("minimatch");
|
|
12
|
+
const test_path_js_1 = require("../agent/test_path.js");
|
|
13
|
+
const impact_engine_js_1 = require("./impact_engine.js");
|
|
14
|
+
const route_families_js_1 = require("../knowledge/route_families.js");
|
|
15
|
+
const DEFAULT_POLICY = {
|
|
16
|
+
minConfidenceForTargeted: 60,
|
|
17
|
+
safeMergeMinConfidence: 85,
|
|
18
|
+
forceFullOnWarningsAtOrAbove: 2,
|
|
19
|
+
forceFullOnP0WithGaps: true,
|
|
20
|
+
forceFullOnRiskyFiles: true,
|
|
21
|
+
riskyFilePatterns: [
|
|
22
|
+
'**/auth/**',
|
|
23
|
+
'**/login/**',
|
|
24
|
+
'**/permissions/**',
|
|
25
|
+
'**/admin/**',
|
|
26
|
+
'**/security/**',
|
|
27
|
+
'**/migrations/**',
|
|
28
|
+
'**/schema/**',
|
|
29
|
+
'**/*.sql',
|
|
30
|
+
'**/webhook/**',
|
|
31
|
+
],
|
|
32
|
+
enforcementMode: 'advisory',
|
|
33
|
+
blockOnActions: ['must-add-tests'],
|
|
34
|
+
};
|
|
35
|
+
function featureLabel(f) {
|
|
36
|
+
return f.featureId || f.familyId;
|
|
37
|
+
}
|
|
38
|
+
function computeConfidence(impact) {
|
|
39
|
+
const gaps = (0, impact_engine_js_1.getGaps)(impact);
|
|
40
|
+
const totalFeatures = impact.impactedFeatures.length;
|
|
41
|
+
const boundRatio = totalFeatures > 0
|
|
42
|
+
? (totalFeatures / (totalFeatures + impact.unboundFiles.length))
|
|
43
|
+
: 1;
|
|
44
|
+
// Graph-resolved bindings start at 100
|
|
45
|
+
let confidence = 100;
|
|
46
|
+
// Deduct for unbound files (not mapped to any family)
|
|
47
|
+
if (impact.unboundFiles.length > 0) {
|
|
48
|
+
const unboundPenalty = Math.min(30, impact.unboundFiles.length * 3);
|
|
49
|
+
confidence -= unboundPenalty;
|
|
50
|
+
}
|
|
51
|
+
// Deduct for gaps
|
|
52
|
+
confidence -= Math.min(20, gaps.length * 5);
|
|
53
|
+
// Deduct for warnings
|
|
54
|
+
confidence -= Math.min(15, impact.warnings.length * 5);
|
|
55
|
+
// Bonus for high bound ratio
|
|
56
|
+
if (boundRatio >= 0.9) {
|
|
57
|
+
confidence = Math.min(100, confidence + 5);
|
|
58
|
+
}
|
|
59
|
+
return Math.max(0, Math.min(100, confidence));
|
|
60
|
+
}
|
|
61
|
+
function findRiskyFiles(changedFiles, patterns) {
|
|
62
|
+
return [...new Set(changedFiles.filter((file) => patterns.some((pattern) => (0, minimatch_1.minimatch)(file, pattern, { matchBase: true }))))];
|
|
63
|
+
}
|
|
64
|
+
function pickRunSet(impact, confidence, policy) {
|
|
65
|
+
const gaps = (0, impact_engine_js_1.getGaps)(impact);
|
|
66
|
+
const reasons = [];
|
|
67
|
+
const triggeredRules = [];
|
|
68
|
+
const riskyFiles = findRiskyFiles(impact.changedFiles, policy.riskyFilePatterns);
|
|
69
|
+
const hasP0 = impact.impactedFeatures.some((f) => f.priority === 'P0');
|
|
70
|
+
if (gaps.length > 0) {
|
|
71
|
+
reasons.push(`${gaps.length} uncovered P0/P1 feature(s) detected.`);
|
|
72
|
+
}
|
|
73
|
+
if (hasP0) {
|
|
74
|
+
reasons.push('P0 features are impacted by this change set.');
|
|
75
|
+
}
|
|
76
|
+
if (policy.forceFullOnRiskyFiles && riskyFiles.length > 0) {
|
|
77
|
+
triggeredRules.push('risky-files');
|
|
78
|
+
reasons.push(`Risky file patterns matched: ${riskyFiles.join(', ')}`);
|
|
79
|
+
}
|
|
80
|
+
if (confidence < policy.minConfidenceForTargeted) {
|
|
81
|
+
triggeredRules.push('low-confidence');
|
|
82
|
+
}
|
|
83
|
+
if (impact.warnings.length >= policy.forceFullOnWarningsAtOrAbove) {
|
|
84
|
+
triggeredRules.push('warning-threshold');
|
|
85
|
+
reasons.push('Warning threshold exceeded.');
|
|
86
|
+
}
|
|
87
|
+
if (policy.forceFullOnP0WithGaps && hasP0 && gaps.length > 0) {
|
|
88
|
+
triggeredRules.push('p0-with-gaps');
|
|
89
|
+
}
|
|
90
|
+
if (triggeredRules.length > 0) {
|
|
91
|
+
return {
|
|
92
|
+
runSet: 'full',
|
|
93
|
+
reasons: reasons.length > 0 ? reasons : ['Policy rules triggered full suite.'],
|
|
94
|
+
triggeredRules,
|
|
95
|
+
riskyFiles,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// If we have impacted features with specs, recommend targeted
|
|
99
|
+
const coveredFeatures = impact.impactedFeatures.filter((f) => f.coverageStatus !== 'uncovered');
|
|
100
|
+
if (coveredFeatures.length > 0) {
|
|
101
|
+
return {
|
|
102
|
+
runSet: 'targeted',
|
|
103
|
+
reasons: reasons.length > 0 ? reasons : ['Impacted features have test coverage.'],
|
|
104
|
+
triggeredRules,
|
|
105
|
+
riskyFiles,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
runSet: 'smoke',
|
|
110
|
+
reasons: reasons.length > 0 ? reasons : ['No targeted tests mapped from impacted features.'],
|
|
111
|
+
triggeredRules,
|
|
112
|
+
riskyFiles,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check which gaps have matching PR-included E2E spec files by binding
|
|
117
|
+
* spec files to families via the manifest. Returns familyIds that are covered.
|
|
118
|
+
*/
|
|
119
|
+
function matchPrSpecsToGaps(prTestFiles, gaps, testsRoot) {
|
|
120
|
+
const coveredFamilies = new Set();
|
|
121
|
+
const prE2ESpecs = prTestFiles.filter((t) => t.type === 'playwright' || t.type === 'cypress');
|
|
122
|
+
if (prE2ESpecs.length === 0 || !testsRoot) {
|
|
123
|
+
return coveredFamilies;
|
|
124
|
+
}
|
|
125
|
+
// Try to bind PR spec files to families via the manifest
|
|
126
|
+
const manifest = (0, route_families_js_1.loadRouteFamilyManifest)(testsRoot);
|
|
127
|
+
if (manifest) {
|
|
128
|
+
const specBindings = (0, route_families_js_1.bindFilesToFamilies)(prE2ESpecs.map((s) => s.file), manifest);
|
|
129
|
+
for (const sb of specBindings) {
|
|
130
|
+
for (const binding of sb.bindings) {
|
|
131
|
+
coveredFamilies.add(binding.family);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Fallback heuristic: if manifest binding didn't match (common for Cypress specs
|
|
136
|
+
// in directories not mapped in the manifest), check path-based keyword overlap.
|
|
137
|
+
if (coveredFamilies.size === 0) {
|
|
138
|
+
const gapFamilyIds = new Set(gaps.map((g) => g.familyId));
|
|
139
|
+
for (const spec of prE2ESpecs) {
|
|
140
|
+
const specLower = spec.file.toLowerCase().replace(/[_\-/\\]/g, ' ');
|
|
141
|
+
for (const familyId of gapFamilyIds) {
|
|
142
|
+
// Check if the spec path contains the family name or related terms
|
|
143
|
+
if (specLower.includes(familyId.toLowerCase())) {
|
|
144
|
+
coveredFamilies.add(familyId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return coveredFamilies;
|
|
150
|
+
}
|
|
151
|
+
function buildDecision(impact, runSet, confidence, policy) {
|
|
152
|
+
const gaps = (0, impact_engine_js_1.getGapsWithSuppressed)(impact).gaps;
|
|
153
|
+
if (gaps.length > 0) {
|
|
154
|
+
const prE2ESpecs = (impact.prIncludedTestFiles ?? [])
|
|
155
|
+
.filter((t) => t.type === 'playwright' || t.type === 'cypress');
|
|
156
|
+
if (prE2ESpecs.length > 0) {
|
|
157
|
+
// Bind PR specs to families โ only soften gaps that have matching specs
|
|
158
|
+
const coveredFamilies = matchPrSpecsToGaps(impact.prIncludedTestFiles ?? [], gaps);
|
|
159
|
+
const uncoveredGaps = gaps.filter((g) => !coveredFamilies.has(g.familyId));
|
|
160
|
+
if (uncoveredGaps.length === 0) {
|
|
161
|
+
// ALL gaps have matching PR specs
|
|
162
|
+
return {
|
|
163
|
+
action: 'run-now',
|
|
164
|
+
title: 'Run now',
|
|
165
|
+
summary: `Detected ${gaps.length} coverage gap(s), but the PR includes ${prE2ESpecs.length} E2E test file(s) covering them. Verify the new tests cover impacted flows.`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (uncoveredGaps.length < gaps.length) {
|
|
169
|
+
// SOME gaps covered by PR specs, others not
|
|
170
|
+
return {
|
|
171
|
+
action: 'must-add-tests',
|
|
172
|
+
title: 'Must add tests',
|
|
173
|
+
summary: `Detected ${gaps.length} coverage gap(s). PR includes E2E tests for ${gaps.length - uncoveredGaps.length}, but ${uncoveredGaps.length} flow(s) still need coverage.`,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// No gaps matched by PR specs โ but PR still has E2E files.
|
|
177
|
+
// Soften to run-now since the developer is actively writing tests.
|
|
178
|
+
return {
|
|
179
|
+
action: 'run-now',
|
|
180
|
+
title: 'Run now',
|
|
181
|
+
summary: `Detected ${gaps.length} coverage gap(s), but the PR includes ${prE2ESpecs.length} E2E test file(s). Verify the new tests cover impacted flows.`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
action: 'must-add-tests',
|
|
186
|
+
title: 'Must add tests',
|
|
187
|
+
summary: `Detected ${gaps.length} uncovered P0/P1 feature(s). Add or update tests before merge.`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (impact.changedFiles.length === 0 && impact.impactedFeatures.length === 0) {
|
|
191
|
+
return {
|
|
192
|
+
action: 'safe-to-merge',
|
|
193
|
+
title: 'Safe to merge',
|
|
194
|
+
summary: 'No app file changes detected โ no E2E coverage required for this change set.',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (runSet === 'smoke' && confidence >= policy.safeMergeMinConfidence && impact.warnings.length === 0) {
|
|
198
|
+
return {
|
|
199
|
+
action: 'safe-to-merge',
|
|
200
|
+
title: 'Safe to merge',
|
|
201
|
+
summary: 'No critical coverage gaps were detected and confidence is high.',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const coveredCount = impact.impactedFeatures.filter((f) => f.coverageStatus !== 'uncovered').length;
|
|
205
|
+
const coveredSuffix = coveredCount > 0
|
|
206
|
+
? ` All ${coveredCount} impacted feature(s) have test coverage.`
|
|
207
|
+
: '';
|
|
208
|
+
// When files changed but no flows were mapped, be transparent about the gap
|
|
209
|
+
if (impact.impactedFeatures.length === 0 && impact.changedFiles.length > 0) {
|
|
210
|
+
const unboundNote = impact.unboundFiles.length > 0
|
|
211
|
+
? ` ${impact.unboundFiles.length} file(s) could not be mapped to any known flow โ consider adding route-families bindings.`
|
|
212
|
+
: '';
|
|
213
|
+
return {
|
|
214
|
+
action: 'run-now',
|
|
215
|
+
title: 'Run now',
|
|
216
|
+
summary: `Changed files could not be mapped to E2E flows โ manual review recommended.${unboundNote} Verify with the E2E suite before merge.`,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
action: 'run-now',
|
|
221
|
+
title: 'Run now',
|
|
222
|
+
summary: `Impacted features are covered by existing tests.${coveredSuffix} Verify with the E2E suite before merge.`,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function evaluateEnforcement(decision, policy) {
|
|
226
|
+
const blockOnActions = (policy.blockOnActions && policy.blockOnActions.length > 0)
|
|
227
|
+
? [...policy.blockOnActions]
|
|
228
|
+
: ['must-add-tests'];
|
|
229
|
+
const matchedAction = blockOnActions.includes(decision.action);
|
|
230
|
+
if (policy.enforcementMode === 'block' && matchedAction) {
|
|
231
|
+
return {
|
|
232
|
+
mode: policy.enforcementMode,
|
|
233
|
+
blockOnActions,
|
|
234
|
+
matchedAction,
|
|
235
|
+
shouldFail: true,
|
|
236
|
+
summary: `Blocking mode active: decision "${decision.action}" is configured to fail CI.`,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (policy.enforcementMode === 'warn' && matchedAction) {
|
|
240
|
+
return {
|
|
241
|
+
mode: policy.enforcementMode,
|
|
242
|
+
blockOnActions,
|
|
243
|
+
matchedAction,
|
|
244
|
+
shouldFail: false,
|
|
245
|
+
summary: `Warning mode active: decision "${decision.action}" is advisory-only for CI.`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (policy.enforcementMode === 'block') {
|
|
249
|
+
return {
|
|
250
|
+
mode: policy.enforcementMode,
|
|
251
|
+
blockOnActions,
|
|
252
|
+
matchedAction,
|
|
253
|
+
shouldFail: false,
|
|
254
|
+
summary: `Blocking mode active, but decision "${decision.action}" is not configured for CI failure.`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
mode: policy.enforcementMode,
|
|
259
|
+
blockOnActions,
|
|
260
|
+
matchedAction,
|
|
261
|
+
shouldFail: false,
|
|
262
|
+
summary: 'Advisory mode active: recommendations do not fail CI by default.',
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Build recommended test list from impacted features' Playwright specs.
|
|
267
|
+
* When alwaysIncludeSubsystems is provided, specs from those subsystems are
|
|
268
|
+
* included regardless of their coverage status (blind-spot protection).
|
|
269
|
+
*/
|
|
270
|
+
function buildRecommendedTests(impact, alwaysIncludeSubsystems = []) {
|
|
271
|
+
const tests = new Set();
|
|
272
|
+
const alwaysSet = new Set(alwaysIncludeSubsystems);
|
|
273
|
+
for (const feature of impact.impactedFeatures) {
|
|
274
|
+
const shouldInclude = feature.coverageStatus !== 'uncovered' ||
|
|
275
|
+
feature.playwrightSpecs.some((spec) => alwaysSet.has((0, test_path_js_1.inferSubsystemFromTestPath)(spec)));
|
|
276
|
+
if (shouldInclude) {
|
|
277
|
+
for (const spec of feature.playwrightSpecs) {
|
|
278
|
+
tests.add(spec);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return [...tests];
|
|
283
|
+
}
|
|
284
|
+
function buildPlanFromImpact(impact, policyOverride, aiEnrichment, adaptiveThresholds) {
|
|
285
|
+
const policy = { ...DEFAULT_POLICY, ...(policyOverride || {}) };
|
|
286
|
+
// Apply adaptive calibration overrides (if available and not explicitly overridden)
|
|
287
|
+
if (adaptiveThresholds && policyOverride?.minConfidenceForTargeted === undefined) {
|
|
288
|
+
policy.minConfidenceForTargeted = adaptiveThresholds.minConfidenceForTargeted;
|
|
289
|
+
}
|
|
290
|
+
if (adaptiveThresholds && policyOverride?.safeMergeMinConfidence === undefined) {
|
|
291
|
+
policy.safeMergeMinConfidence = adaptiveThresholds.safeMergeMinConfidence;
|
|
292
|
+
}
|
|
293
|
+
// Apply alwaysIncludeSubsystems: force their tests into the recommended set
|
|
294
|
+
const alwaysIncludeSubsystems = adaptiveThresholds?.alwaysIncludeSubsystems ?? [];
|
|
295
|
+
const confidence = computeConfidence(impact);
|
|
296
|
+
const runSetResult = pickRunSet(impact, confidence, policy);
|
|
297
|
+
const decision = buildDecision(impact, runSetResult.runSet, confidence, policy);
|
|
298
|
+
const enforcement = evaluateEnforcement(decision, policy);
|
|
299
|
+
const { gaps, suppressedGaps } = (0, impact_engine_js_1.getGapsWithSuppressed)(impact);
|
|
300
|
+
const partialGaps = (0, impact_engine_js_1.getPartialGaps)(impact);
|
|
301
|
+
// Build two separate lookup maps from aiEnrichment: one by featureId, one by familyId.
|
|
302
|
+
// The familyId map stores only the FIRST feature encountered to avoid last-write-wins collisions.
|
|
303
|
+
const aiFeatureByFeatureId = new Map();
|
|
304
|
+
const aiFeatureByFamilyId = new Map();
|
|
305
|
+
if (aiEnrichment) {
|
|
306
|
+
for (const ef of aiEnrichment.enrichedFeatures) {
|
|
307
|
+
if (ef.featureId) {
|
|
308
|
+
aiFeatureByFeatureId.set(ef.featureId, ef);
|
|
309
|
+
}
|
|
310
|
+
if (ef.familyId && !aiFeatureByFamilyId.has(ef.familyId)) {
|
|
311
|
+
aiFeatureByFamilyId.set(ef.familyId, ef);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const gapDetails = gaps.map((f) => {
|
|
316
|
+
const label = featureLabel(f);
|
|
317
|
+
const aiFeature = f.featureId
|
|
318
|
+
? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
|
|
319
|
+
: aiFeatureByFamilyId.get(f.familyId);
|
|
320
|
+
const baseReasons = [`No E2E tests found for ${label}`];
|
|
321
|
+
let aiReasonsList = [];
|
|
322
|
+
if (aiFeature) {
|
|
323
|
+
if (aiFeature.aiReasons.length > 0) {
|
|
324
|
+
aiReasonsList = aiFeature.aiReasons.slice(0, 2);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
// Fallback: LLM returned scenarios but no reasons โ synthesize a description
|
|
328
|
+
const fileHint = f.changedFiles.slice(0, 3).map((p) => p.split('/').pop()).join(', ');
|
|
329
|
+
aiReasonsList = [`Changes to ${fileHint} affect the ${label} feature, which currently lacks E2E coverage.`];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const reasons = aiReasonsList.length > 0
|
|
333
|
+
? [...baseReasons, ...aiReasonsList]
|
|
334
|
+
: baseReasons;
|
|
335
|
+
const missingScenarios = aiFeature && aiFeature.aiMissingScenarios.length > 0
|
|
336
|
+
? aiFeature.aiMissingScenarios
|
|
337
|
+
: (f.userFlows.length > 0 ? f.userFlows.slice(0, 5) : undefined);
|
|
338
|
+
return {
|
|
339
|
+
id: label,
|
|
340
|
+
name: label,
|
|
341
|
+
priority: f.priority,
|
|
342
|
+
reasons,
|
|
343
|
+
files: f.changedFiles.slice(0, 6),
|
|
344
|
+
missingScenarios,
|
|
345
|
+
source: aiFeature ? 'ai+deterministic' : 'deterministic',
|
|
346
|
+
};
|
|
347
|
+
});
|
|
348
|
+
// Add partial gaps as advisory info (Cypress-only coverage โ Playwright migration recommended)
|
|
349
|
+
for (const f of partialGaps) {
|
|
350
|
+
const label = featureLabel(f);
|
|
351
|
+
const aiFeature = f.featureId
|
|
352
|
+
? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
|
|
353
|
+
: aiFeatureByFamilyId.get(f.familyId);
|
|
354
|
+
const baseReasons = [`${label} is covered by Cypress only โ consider adding Playwright tests`];
|
|
355
|
+
let partialAiReasons = [];
|
|
356
|
+
if (aiFeature) {
|
|
357
|
+
if (aiFeature.aiReasons.length > 0) {
|
|
358
|
+
partialAiReasons = aiFeature.aiReasons.slice(0, 2);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
const fileHint = f.changedFiles.slice(0, 3).map((p) => p.split('/').pop()).join(', ');
|
|
362
|
+
partialAiReasons = [`Changes to ${fileHint} affect the ${label} feature, which has Cypress but no Playwright coverage.`];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const reasons = partialAiReasons.length > 0
|
|
366
|
+
? [...baseReasons, ...partialAiReasons]
|
|
367
|
+
: baseReasons;
|
|
368
|
+
gapDetails.push({
|
|
369
|
+
id: label,
|
|
370
|
+
name: `${label} (partial)`,
|
|
371
|
+
priority: f.priority,
|
|
372
|
+
reasons,
|
|
373
|
+
files: f.changedFiles.slice(0, 6),
|
|
374
|
+
source: aiFeature ? 'ai+deterministic' : 'deterministic',
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
const coveredFlows = impact.impactedFeatures
|
|
378
|
+
.filter((f) => f.coverageStatus === 'covered')
|
|
379
|
+
.map((f) => {
|
|
380
|
+
const aiFeature = f.featureId
|
|
381
|
+
? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
|
|
382
|
+
: aiFeatureByFamilyId.get(f.familyId);
|
|
383
|
+
// Only surface advisory scenarios when AI found new behavior in this diff
|
|
384
|
+
let advisoryScenarios = aiFeature?.aiMissingScenarios?.length
|
|
385
|
+
? [...aiFeature.aiMissingScenarios]
|
|
386
|
+
: undefined;
|
|
387
|
+
// Promote suppressed gaps to advisory on covered flows that share changed files.
|
|
388
|
+
// When a family-level gap is suppressed (e.g. "post" because post.go is also in
|
|
389
|
+
// a covered feature like "channels/threads"), the behavioral change should appear
|
|
390
|
+
// here as "new behavior detected" instead of vanishing.
|
|
391
|
+
for (const sg of suppressedGaps) {
|
|
392
|
+
const sharedFiles = sg.changedFiles.filter((file) => f.changedFiles.includes(file));
|
|
393
|
+
if (sharedFiles.length > 0) {
|
|
394
|
+
const sgAi = sg.featureId
|
|
395
|
+
? (aiFeatureByFeatureId.get(sg.featureId) ?? aiFeatureByFamilyId.get(sg.familyId))
|
|
396
|
+
: aiFeatureByFamilyId.get(sg.familyId);
|
|
397
|
+
const sgScenarios = sgAi?.aiMissingScenarios?.length
|
|
398
|
+
? sgAi.aiMissingScenarios
|
|
399
|
+
: sg.userFlows.slice(0, 3);
|
|
400
|
+
if (sgScenarios.length > 0) {
|
|
401
|
+
advisoryScenarios = [...(advisoryScenarios || []), ...sgScenarios];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
id: featureLabel(f),
|
|
407
|
+
name: featureLabel(f),
|
|
408
|
+
priority: f.priority,
|
|
409
|
+
coveredBy: [
|
|
410
|
+
...(f.playwrightSpecs.length > 0 ? [`${f.playwrightSpecs.length} Playwright spec(s)`] : []),
|
|
411
|
+
...(f.cypressSpecs.length > 0 ? [`${f.cypressSpecs.length} Cypress spec(s)`] : []),
|
|
412
|
+
].slice(0, 3),
|
|
413
|
+
advisoryScenarios,
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
const recommendedTests = buildRecommendedTests(impact, alwaysIncludeSubsystems);
|
|
417
|
+
const requiredNewTests = gaps.map((f) => `${featureLabel(f)}: Add E2E tests`);
|
|
418
|
+
const p0 = impact.impactedFeatures.filter((f) => f.priority === 'P0').length;
|
|
419
|
+
const p1 = impact.impactedFeatures.filter((f) => f.priority === 'P1').length;
|
|
420
|
+
const p2 = impact.impactedFeatures.filter((f) => f.priority === 'P2').length;
|
|
421
|
+
const coveredCount = impact.impactedFeatures.filter((f) => f.coverageStatus === 'covered').length;
|
|
422
|
+
const partialCount = impact.impactedFeatures.filter((f) => f.coverageStatus === 'partial').length;
|
|
423
|
+
const runId = `plan-${Date.now().toString(36)}`;
|
|
424
|
+
const planSource = aiEnrichment ? 'ai+deterministic' : 'impact';
|
|
425
|
+
return {
|
|
426
|
+
schemaVersion: '1.0.0',
|
|
427
|
+
runId,
|
|
428
|
+
generatedAt: new Date().toISOString(),
|
|
429
|
+
source: planSource,
|
|
430
|
+
runSet: runSetResult.runSet,
|
|
431
|
+
confidence,
|
|
432
|
+
reasons: runSetResult.reasons,
|
|
433
|
+
recommendedTests,
|
|
434
|
+
requiredNewTests,
|
|
435
|
+
gapDetails,
|
|
436
|
+
coveredFlows,
|
|
437
|
+
policy: {
|
|
438
|
+
riskyFiles: runSetResult.riskyFiles,
|
|
439
|
+
triggeredRules: runSetResult.triggeredRules,
|
|
440
|
+
applied: policy,
|
|
441
|
+
},
|
|
442
|
+
decision,
|
|
443
|
+
enforcement,
|
|
444
|
+
metrics: {
|
|
445
|
+
changedFiles: impact.changedFiles.length,
|
|
446
|
+
impactedFlows: impact.impactedFeatures.length,
|
|
447
|
+
p0Flows: p0,
|
|
448
|
+
p1Flows: p1,
|
|
449
|
+
p2Flows: p2,
|
|
450
|
+
coveredFlows: coveredCount,
|
|
451
|
+
partialFlows: partialCount,
|
|
452
|
+
uncoveredP0P1Flows: gaps.length,
|
|
453
|
+
unboundFiles: impact.unboundFiles.length,
|
|
454
|
+
warnings: impact.warnings.length,
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function writePlanReport(appRoot, plan) {
|
|
459
|
+
const baseDir = (0, path_1.join)(appRoot, '.e2e-ai-agents');
|
|
460
|
+
(0, fs_1.mkdirSync)(baseDir, { recursive: true });
|
|
461
|
+
const planPath = (0, path_1.join)(baseDir, 'plan.json');
|
|
462
|
+
(0, fs_1.writeFileSync)(planPath, JSON.stringify(plan, null, 2), 'utf-8');
|
|
463
|
+
return planPath;
|
|
464
|
+
}
|
|
465
|
+
function renderCiSummaryMarkdown(plan) {
|
|
466
|
+
const lines = [];
|
|
467
|
+
const { uncoveredP0P1Flows, changedFiles, impactedFlows, coveredFlows: coveredCount, partialFlows: partialCount, unboundFiles: unboundCount } = plan.metrics;
|
|
468
|
+
const mustAddTests = plan.decision.action === 'must-add-tests';
|
|
469
|
+
const hasGapsButPrHasSpecs = !mustAddTests && plan.gapDetails.filter((g) => !g.name.includes('(partial)')).length > 0;
|
|
470
|
+
const flowsWithAdvisory = plan.coveredFlows.filter((f) => f.advisoryScenarios && f.advisoryScenarios.length > 0);
|
|
471
|
+
const cleanFlows = plan.coveredFlows.filter((f) => !f.advisoryScenarios || f.advisoryScenarios.length === 0);
|
|
472
|
+
const statusEmoji = mustAddTests ? '๐ด' : plan.decision.action === 'safe-to-merge' ? '๐ข' : '๐ก';
|
|
473
|
+
lines.push(`## ${statusEmoji} E2E Coverage: ${plan.decision.title}`);
|
|
474
|
+
lines.push('');
|
|
475
|
+
lines.push(`${plan.decision.summary}`);
|
|
476
|
+
lines.push('');
|
|
477
|
+
// Coverage breakdown: "3 covered ยท 2 new ยท 1 gap ยท 1 partial"
|
|
478
|
+
const parts = [];
|
|
479
|
+
if ((coveredCount ?? 0) > 0) {
|
|
480
|
+
parts.push(`${coveredCount} covered`);
|
|
481
|
+
}
|
|
482
|
+
if (flowsWithAdvisory.length > 0) {
|
|
483
|
+
parts.push(`${flowsWithAdvisory.length} new behavior`);
|
|
484
|
+
}
|
|
485
|
+
if (uncoveredP0P1Flows > 0) {
|
|
486
|
+
parts.push(`${uncoveredP0P1Flows} gap${uncoveredP0P1Flows !== 1 ? 's' : ''}`);
|
|
487
|
+
}
|
|
488
|
+
if ((partialCount ?? 0) > 0) {
|
|
489
|
+
parts.push(`${partialCount} partial`);
|
|
490
|
+
}
|
|
491
|
+
const breakdown = parts.length > 0 ? ` (${parts.join(' ยท ')})` : '';
|
|
492
|
+
lines.push(`**${changedFiles}** files changed โ **${impactedFlows}** features impacted${breakdown}`);
|
|
493
|
+
// โโ Blocking gaps โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
494
|
+
if (mustAddTests && plan.requiredNewTests.length > 0) {
|
|
495
|
+
lines.push('');
|
|
496
|
+
lines.push(`### โ ๏ธ Missing coverage for ${uncoveredP0P1Flows} P0/P1 flow(s)`);
|
|
497
|
+
lines.push('');
|
|
498
|
+
for (const gap of plan.gapDetails.filter((g) => !g.name.includes('(partial)'))) {
|
|
499
|
+
const aiLabel = gap.source === 'ai+deterministic' ? ' โฆ AI-enriched' : '';
|
|
500
|
+
// Warning box: name + priority + AI reason (always visible)
|
|
501
|
+
lines.push(`> [!WARNING]`);
|
|
502
|
+
lines.push(`> **${gap.name}** ยท ${gap.priority}${aiLabel}`);
|
|
503
|
+
const aiReasons = gap.reasons.slice(1);
|
|
504
|
+
if (aiReasons.length > 0) {
|
|
505
|
+
lines.push(`> ${aiReasons.join(' ')}`);
|
|
506
|
+
}
|
|
507
|
+
lines.push('');
|
|
508
|
+
// Scenarios: collapsible below the warning box
|
|
509
|
+
if (gap.missingScenarios && gap.missingScenarios.length > 0) {
|
|
510
|
+
lines.push(`<details><summary>๐ Suggested test scenarios (${gap.missingScenarios.length})</summary>`);
|
|
511
|
+
lines.push('');
|
|
512
|
+
for (const scenario of gap.missingScenarios) {
|
|
513
|
+
lines.push(`- [ ] ${scenario}`);
|
|
514
|
+
}
|
|
515
|
+
lines.push('');
|
|
516
|
+
lines.push('</details>');
|
|
517
|
+
lines.push('');
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// โโ Informational gaps (PR includes E2E specs) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
522
|
+
if (hasGapsButPrHasSpecs) {
|
|
523
|
+
const infoGaps = plan.gapDetails.filter((g) => !g.name.includes('(partial)'));
|
|
524
|
+
lines.push('');
|
|
525
|
+
lines.push(`### โน๏ธ Coverage gaps detected (PR includes E2E tests)`);
|
|
526
|
+
lines.push('');
|
|
527
|
+
lines.push('> The PR adds E2E test files. Verify they cover these flows:');
|
|
528
|
+
lines.push('');
|
|
529
|
+
for (const gap of infoGaps) {
|
|
530
|
+
const aiLabel = gap.source === 'ai+deterministic' ? ' โฆ AI-enriched' : '';
|
|
531
|
+
lines.push(`- **${gap.name}** ยท ${gap.priority}${aiLabel}`);
|
|
532
|
+
const aiReasons = gap.reasons.slice(1);
|
|
533
|
+
if (aiReasons.length > 0) {
|
|
534
|
+
lines.push(` ${aiReasons.join(' ')}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
lines.push('');
|
|
538
|
+
}
|
|
539
|
+
// โโ Advisory: covered flows with new behavior โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
540
|
+
if (flowsWithAdvisory.length > 0) {
|
|
541
|
+
lines.push('');
|
|
542
|
+
lines.push(`### ๐ก New behavior detected in ${flowsWithAdvisory.length} covered feature${flowsWithAdvisory.length !== 1 ? 's' : ''} โ consider adding tests`);
|
|
543
|
+
lines.push('');
|
|
544
|
+
for (const flow of flowsWithAdvisory) {
|
|
545
|
+
const specParts = [];
|
|
546
|
+
for (const s of flow.coveredBy) {
|
|
547
|
+
// Strip "N Playwright spec(s)" โ "N PW" and "N Cypress spec(s)" โ "N Cy"
|
|
548
|
+
specParts.push(s.replace(/ Playwright spec\(s\)/, ' PW').replace(/ Cypress spec\(s\)/, ' Cy'));
|
|
549
|
+
}
|
|
550
|
+
const specSummary = specParts.length > 0 ? ` โ ${specParts.join(' ยท ')}` : '';
|
|
551
|
+
const scenarioCount = flow.advisoryScenarios.length;
|
|
552
|
+
lines.push(`<details><summary>๐ก <strong>${flow.name}</strong> ยท ${flow.priority}${specSummary} ยท ${scenarioCount} scenario${scenarioCount !== 1 ? 's' : ''}</summary>`);
|
|
553
|
+
lines.push('');
|
|
554
|
+
for (const s of flow.advisoryScenarios) {
|
|
555
|
+
lines.push(`- [ ] ${s}`);
|
|
556
|
+
}
|
|
557
|
+
lines.push('');
|
|
558
|
+
lines.push('</details>');
|
|
559
|
+
lines.push('');
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
// โโ Clean covered flows (collapsed) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
563
|
+
if (cleanFlows.length > 0) {
|
|
564
|
+
lines.push('');
|
|
565
|
+
lines.push(`<details><summary>โ
Covered flows (${cleanFlows.length})</summary>`);
|
|
566
|
+
lines.push('');
|
|
567
|
+
for (const flow of cleanFlows) {
|
|
568
|
+
lines.push(`- **${flow.name}** [${flow.priority}] โ ${flow.coveredBy.join(', ')}`);
|
|
569
|
+
}
|
|
570
|
+
lines.push('');
|
|
571
|
+
lines.push('</details>');
|
|
572
|
+
}
|
|
573
|
+
// โโ Risky files detected โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
574
|
+
if (plan.policy.riskyFiles.length > 0) {
|
|
575
|
+
lines.push('');
|
|
576
|
+
lines.push(`### โ ๏ธ Risky file patterns matched`);
|
|
577
|
+
lines.push('');
|
|
578
|
+
for (const f of plan.policy.riskyFiles) {
|
|
579
|
+
lines.push(`- \`${f}\``);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
// โโ Unbound files warning โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
583
|
+
if ((unboundCount ?? 0) > 0) {
|
|
584
|
+
lines.push('');
|
|
585
|
+
lines.push(`> **${unboundCount}** changed file(s) could not be mapped to any E2E flow. Consider updating \`route-families.json\` to cover these files.`);
|
|
586
|
+
}
|
|
587
|
+
if (plan.confidence < 100) {
|
|
588
|
+
lines.push('');
|
|
589
|
+
lines.push(`**Confidence**: ${plan.confidence}%`);
|
|
590
|
+
}
|
|
591
|
+
return lines.join('\n');
|
|
592
|
+
}
|
|
593
|
+
function writeCiSummary(appRoot, markdown, relativePath = '.e2e-ai-agents/ci-summary.md') {
|
|
594
|
+
const fullPath = (0, path_1.join)(appRoot, relativePath);
|
|
595
|
+
const dir = (0, path_1.dirname)(fullPath);
|
|
596
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
597
|
+
(0, fs_1.writeFileSync)(fullPath, markdown, 'utf-8');
|
|
598
|
+
return fullPath;
|
|
599
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
/**
|
|
4
|
+
* Cypress Adapter โ FrameworkAdapter implementation for Cypress.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
export class CypressAdapter {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.name = 'cypress';
|
|
11
|
+
this.specGlob = '**/*.cy.{ts,js,tsx,jsx}';
|
|
12
|
+
this.extractTestPattern = /\b(?:it|describe|context)\s*\(/g;
|
|
13
|
+
this.configFileNames = ['cypress.config.ts', 'cypress.config.js'];
|
|
14
|
+
}
|
|
15
|
+
detect(projectRoot) {
|
|
16
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
17
|
+
if (!fs.existsSync(pkgPath)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const raw = fs.readFileSync(pkgPath, 'utf-8');
|
|
22
|
+
const pkg = JSON.parse(raw);
|
|
23
|
+
const allDeps = {
|
|
24
|
+
...pkg.dependencies,
|
|
25
|
+
...pkg.devDependencies,
|
|
26
|
+
};
|
|
27
|
+
return 'cypress' in allDeps;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
buildRunCommand(specPath, options) {
|
|
34
|
+
const args = ['cypress', 'run', '--spec', specPath];
|
|
35
|
+
if (options?.headed) {
|
|
36
|
+
args.push('--headed');
|
|
37
|
+
}
|
|
38
|
+
if (options?.browser) {
|
|
39
|
+
args.push('--browser', options.browser);
|
|
40
|
+
}
|
|
41
|
+
if (options?.project) {
|
|
42
|
+
args.push('--project', options.project);
|
|
43
|
+
}
|
|
44
|
+
if (options?.timeout != null) {
|
|
45
|
+
args.push('--config', `defaultCommandTimeout=${options.timeout}`);
|
|
46
|
+
}
|
|
47
|
+
return { executable: 'npx', args };
|
|
48
|
+
}
|
|
49
|
+
}
|