@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,243 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import OpenAI from 'openai';
|
|
4
|
+
import { LLMProviderError, UnsupportedCapabilityError } from './provider_interface.js';
|
|
5
|
+
import { API_KEY_PATTERNS, sanitizeErrorMessage, withTimeout, validateAndSanitizeUrl } from './provider_utils.js';
|
|
6
|
+
import { BaseProvider } from './base_provider.js';
|
|
7
|
+
import { logger } from './logger.js';
|
|
8
|
+
function inferVisionSupport(model) {
|
|
9
|
+
const lower = model.toLowerCase();
|
|
10
|
+
return lower.includes('vision') || lower.includes('4o') || lower.includes('omni');
|
|
11
|
+
}
|
|
12
|
+
export class OpenAIProvider extends BaseProvider {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
super();
|
|
15
|
+
this.name = 'openai';
|
|
16
|
+
if (!API_KEY_PATTERNS.openai.test(config.apiKey)) {
|
|
17
|
+
throw new Error('Invalid API key format. Expected sk-* format.');
|
|
18
|
+
}
|
|
19
|
+
if (config.baseUrl) {
|
|
20
|
+
const validation = validateAndSanitizeUrl(config.baseUrl);
|
|
21
|
+
if (!validation.valid) {
|
|
22
|
+
throw new Error(`Invalid base URL: ${validation.warning}`);
|
|
23
|
+
}
|
|
24
|
+
if (validation.warning) {
|
|
25
|
+
logger.warn(`HTTPS required for remote URLs: ${validation.warning}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
this.client = new OpenAI({
|
|
29
|
+
apiKey: config.apiKey,
|
|
30
|
+
baseURL: config.baseUrl,
|
|
31
|
+
organization: config.organizationId,
|
|
32
|
+
});
|
|
33
|
+
this.model = config.model || 'gpt-4';
|
|
34
|
+
const maxTokens = config.maxTokens || 128000;
|
|
35
|
+
const costPer1MInputTokens = config.costPer1MInputTokens ?? 0;
|
|
36
|
+
const costPer1MOutputTokens = config.costPer1MOutputTokens ?? 0;
|
|
37
|
+
this.capabilities = {
|
|
38
|
+
vision: inferVisionSupport(this.model),
|
|
39
|
+
streaming: true,
|
|
40
|
+
maxTokens,
|
|
41
|
+
costPer1MInputTokens,
|
|
42
|
+
costPer1MOutputTokens,
|
|
43
|
+
supportsTools: true,
|
|
44
|
+
supportsPromptCaching: false,
|
|
45
|
+
typicalResponseTimeMs: 1200,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async generateText(prompt, options) {
|
|
49
|
+
this.checkBudget();
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
try {
|
|
52
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
53
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
54
|
+
}
|
|
55
|
+
const messages = [];
|
|
56
|
+
if (options?.systemPrompt) {
|
|
57
|
+
messages.push({ role: 'system', content: options.systemPrompt });
|
|
58
|
+
}
|
|
59
|
+
messages.push({ role: 'user', content: prompt });
|
|
60
|
+
const response = await withTimeout(this.client.chat.completions.create({
|
|
61
|
+
model: this.model,
|
|
62
|
+
messages,
|
|
63
|
+
max_tokens: options?.maxTokens,
|
|
64
|
+
temperature: options?.temperature,
|
|
65
|
+
top_p: options?.topP,
|
|
66
|
+
stop: options?.stopSequences,
|
|
67
|
+
}), options?.timeout, 'generateText');
|
|
68
|
+
const responseTime = Date.now() - startTime;
|
|
69
|
+
const text = response.choices[0]?.message?.content || '';
|
|
70
|
+
const usage = this.extractUsage(response.usage);
|
|
71
|
+
const cost = this.calculateCost(usage, this.capabilities.costPer1MInputTokens, this.capabilities.costPer1MOutputTokens);
|
|
72
|
+
this.updateStats(usage, responseTime, cost);
|
|
73
|
+
return {
|
|
74
|
+
text,
|
|
75
|
+
usage,
|
|
76
|
+
cost,
|
|
77
|
+
metadata: {
|
|
78
|
+
model: this.model,
|
|
79
|
+
responseTimeMs: responseTime,
|
|
80
|
+
finishReason: response.choices[0]?.finish_reason,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
this.stats.failedRequests++;
|
|
86
|
+
throw new LLMProviderError(sanitizeErrorMessage(error, 'generateText'), this.name, this.extractStatusCode(error), error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async analyzeImage(images, prompt, options) {
|
|
90
|
+
if (!this.capabilities.vision) {
|
|
91
|
+
throw new UnsupportedCapabilityError(this.name, 'vision');
|
|
92
|
+
}
|
|
93
|
+
const startTime = Date.now();
|
|
94
|
+
try {
|
|
95
|
+
if (images.length === 0 || images.length > 20) {
|
|
96
|
+
throw new Error('Image count must be between 1 and 20');
|
|
97
|
+
}
|
|
98
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
99
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
100
|
+
}
|
|
101
|
+
const content = [{ type: 'text', text: prompt }];
|
|
102
|
+
for (const image of images) {
|
|
103
|
+
const mediaType = (image.mimeType || image.mediaType || 'image/png');
|
|
104
|
+
if (!['image/png', 'image/jpeg', 'image/webp'].includes(mediaType)) {
|
|
105
|
+
throw new Error(`Unsupported image type: ${mediaType}`);
|
|
106
|
+
}
|
|
107
|
+
const data = image.data || image.base64 || '';
|
|
108
|
+
if (data.length > 20 * 1024 * 1024) {
|
|
109
|
+
throw new Error('Image data exceeds maximum size (20MB)');
|
|
110
|
+
}
|
|
111
|
+
const url = `data:${mediaType};base64,${data}`;
|
|
112
|
+
content.push({ type: 'image_url', image_url: { url } });
|
|
113
|
+
if (image.description) {
|
|
114
|
+
content.push({ type: 'text', text: `[Image: ${image.description}]` });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const messages = [];
|
|
118
|
+
if (options?.systemPrompt) {
|
|
119
|
+
messages.push({ role: 'system', content: options.systemPrompt });
|
|
120
|
+
}
|
|
121
|
+
messages.push({ role: 'user', content });
|
|
122
|
+
const response = await withTimeout(this.client.chat.completions.create({
|
|
123
|
+
model: this.model,
|
|
124
|
+
messages,
|
|
125
|
+
max_tokens: options?.maxTokens,
|
|
126
|
+
temperature: options?.temperature,
|
|
127
|
+
top_p: options?.topP,
|
|
128
|
+
stop: options?.stopSequences,
|
|
129
|
+
}), options?.timeout, 'analyzeImage');
|
|
130
|
+
const responseTime = Date.now() - startTime;
|
|
131
|
+
const text = response.choices[0]?.message?.content || '';
|
|
132
|
+
const usage = this.extractUsage(response.usage);
|
|
133
|
+
const cost = this.calculateCost(usage, this.capabilities.costPer1MInputTokens, this.capabilities.costPer1MOutputTokens);
|
|
134
|
+
this.updateStats(usage, responseTime, cost);
|
|
135
|
+
return {
|
|
136
|
+
text,
|
|
137
|
+
usage,
|
|
138
|
+
cost,
|
|
139
|
+
metadata: {
|
|
140
|
+
model: this.model,
|
|
141
|
+
responseTimeMs: responseTime,
|
|
142
|
+
finishReason: response.choices[0]?.finish_reason,
|
|
143
|
+
imageCount: images.length,
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
this.stats.failedRequests++;
|
|
149
|
+
throw new LLMProviderError(sanitizeErrorMessage(error, 'analyzeImage'), this.name, this.extractStatusCode(error), error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async *streamText(prompt, options) {
|
|
153
|
+
try {
|
|
154
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
155
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
156
|
+
}
|
|
157
|
+
const messages = [];
|
|
158
|
+
if (options?.systemPrompt) {
|
|
159
|
+
messages.push({ role: 'system', content: options.systemPrompt });
|
|
160
|
+
}
|
|
161
|
+
messages.push({ role: 'user', content: prompt });
|
|
162
|
+
const stream = await withTimeout(this.client.chat.completions.create({
|
|
163
|
+
model: this.model,
|
|
164
|
+
messages,
|
|
165
|
+
max_tokens: options?.maxTokens,
|
|
166
|
+
temperature: options?.temperature,
|
|
167
|
+
top_p: options?.topP,
|
|
168
|
+
stop: options?.stopSequences,
|
|
169
|
+
stream: true,
|
|
170
|
+
}), options?.timeout, 'streamText');
|
|
171
|
+
for await (const chunk of stream) {
|
|
172
|
+
const content = chunk.choices[0]?.delta?.content;
|
|
173
|
+
if (content) {
|
|
174
|
+
yield content;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this.stats.requestCount++;
|
|
178
|
+
this.stats.lastUpdated = new Date();
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
this.stats.failedRequests++;
|
|
182
|
+
throw new LLMProviderError(sanitizeErrorMessage(error, 'streamText'), this.name, this.extractStatusCode(error), error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
extractUsage(usage) {
|
|
186
|
+
return {
|
|
187
|
+
inputTokens: usage?.prompt_tokens || 0,
|
|
188
|
+
outputTokens: usage?.completion_tokens || 0,
|
|
189
|
+
totalTokens: usage?.total_tokens || 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
extractStatusCode(error) {
|
|
193
|
+
if (error && typeof error === 'object') {
|
|
194
|
+
const err = error;
|
|
195
|
+
const status = err.status;
|
|
196
|
+
if (typeof status === 'number') {
|
|
197
|
+
return status;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
async checkHealth() {
|
|
203
|
+
try {
|
|
204
|
+
await withTimeout(this.client.chat.completions.create({
|
|
205
|
+
model: this.model,
|
|
206
|
+
max_tokens: 5,
|
|
207
|
+
messages: [{ role: 'user', content: 'Hi' }],
|
|
208
|
+
}), 5000, 'health check');
|
|
209
|
+
return {
|
|
210
|
+
healthy: true,
|
|
211
|
+
message: 'OpenAI API is accessible',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return {
|
|
216
|
+
healthy: false,
|
|
217
|
+
message: `OpenAI API error: ${sanitizeErrorMessage(error, 'health check')}`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export async function checkOpenAISetup(apiKey) {
|
|
223
|
+
if (!apiKey) {
|
|
224
|
+
return {
|
|
225
|
+
valid: false,
|
|
226
|
+
message: 'No API key provided',
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const provider = new OpenAIProvider({ apiKey });
|
|
231
|
+
const health = await provider.checkHealth();
|
|
232
|
+
return {
|
|
233
|
+
valid: health.healthy,
|
|
234
|
+
message: health.message,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
return {
|
|
239
|
+
valid: false,
|
|
240
|
+
message: `Setup check failed: ${sanitizeErrorMessage(error, 'setup check')}`,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { getChangedFiles, isTestFile } from '../agent/git.js';
|
|
6
|
+
import { logger } from '../logger.js';
|
|
7
|
+
import { preprocess } from './stage0_preprocess.js';
|
|
8
|
+
import { runImpactStage } from './stage1_impact.js';
|
|
9
|
+
import { runCoverageStage } from './stage2_coverage.js';
|
|
10
|
+
import { runGenerationStage } from './stage3_generation.js';
|
|
11
|
+
import { runHealStage, resolveHealTargets, renderHealMarkdown } from './stage4_heal.js';
|
|
12
|
+
import { buildSummary } from '../validation/output_schema.js';
|
|
13
|
+
import { computeCannotDetermineRatio } from '../validation/guardrails.js';
|
|
14
|
+
import { resolveGenerationProfile } from '../prompts/generation_profile.js';
|
|
15
|
+
function createRunId() {
|
|
16
|
+
const ciRunId = process.env.GITHUB_RUN_ID;
|
|
17
|
+
const entropy = Math.random().toString(36).slice(2, 8);
|
|
18
|
+
const ts = Date.now().toString(36);
|
|
19
|
+
if (ciRunId) {
|
|
20
|
+
return `pipeline-gh-${ciRunId}-${ts}-${entropy}`;
|
|
21
|
+
}
|
|
22
|
+
return `pipeline-local-${ts}-${entropy}`;
|
|
23
|
+
}
|
|
24
|
+
export async function runPipeline(config) {
|
|
25
|
+
const runId = createRunId();
|
|
26
|
+
const startedAt = new Date().toISOString();
|
|
27
|
+
const allWarnings = [];
|
|
28
|
+
const stages = config.stages || ['preprocess', 'impact', 'coverage'];
|
|
29
|
+
const profile = config.profile || resolveGenerationProfile();
|
|
30
|
+
let generatedSpecs;
|
|
31
|
+
let healResult;
|
|
32
|
+
// Step 1: Get changed files
|
|
33
|
+
const gitResult = getChangedFiles(config.appPath, config.gitSince, {
|
|
34
|
+
includeUncommitted: config.gitIncludeUncommitted,
|
|
35
|
+
});
|
|
36
|
+
if (gitResult.error) {
|
|
37
|
+
allWarnings.push(`Git diff warning: ${gitResult.error}`);
|
|
38
|
+
}
|
|
39
|
+
const changedFiles = gitResult.files
|
|
40
|
+
.map((f) => f.replace(/\\/g, '/'))
|
|
41
|
+
.filter((f) => !isTestFile(f));
|
|
42
|
+
if (changedFiles.length === 0) {
|
|
43
|
+
allWarnings.push('No changed application files detected.');
|
|
44
|
+
const emptyReport = {
|
|
45
|
+
runId,
|
|
46
|
+
timestamp: startedAt,
|
|
47
|
+
gitRef: config.gitSince,
|
|
48
|
+
summary: buildSummary([]),
|
|
49
|
+
decisions: [],
|
|
50
|
+
warnings: allWarnings,
|
|
51
|
+
model: {},
|
|
52
|
+
};
|
|
53
|
+
const reportPath = writeReport(config.testsRoot, emptyReport);
|
|
54
|
+
return { report: emptyReport, reportPath, warnings: allWarnings };
|
|
55
|
+
}
|
|
56
|
+
const timings = {};
|
|
57
|
+
// Step 2: Preprocess — deterministic file classification + route family binding
|
|
58
|
+
const preprocessTimer = logger.timer('preprocess');
|
|
59
|
+
const preprocessResult = preprocess(changedFiles, {
|
|
60
|
+
appPath: config.appPath,
|
|
61
|
+
testsRoot: config.testsRoot,
|
|
62
|
+
routeFamilies: config.routeFamilies,
|
|
63
|
+
apiSurface: config.apiSurface,
|
|
64
|
+
});
|
|
65
|
+
timings.preprocess = preprocessTimer.end();
|
|
66
|
+
allWarnings.push(...preprocessResult.warnings);
|
|
67
|
+
let decisions = [];
|
|
68
|
+
// Step 3: Impact stage — AI-powered flow identification per family
|
|
69
|
+
if (stages.includes('impact')) {
|
|
70
|
+
const impactTimer = logger.timer('impact');
|
|
71
|
+
const impactResult = await runImpactStage(preprocessResult.familyGroups, preprocessResult.manifest, preprocessResult.specIndex, preprocessResult.apiSurface, preprocessResult.context, config.impact || {});
|
|
72
|
+
decisions = impactResult.decisions;
|
|
73
|
+
allWarnings.push(...impactResult.warnings);
|
|
74
|
+
timings.impact = impactTimer.end();
|
|
75
|
+
// Check cannot_determine ratio
|
|
76
|
+
const cannotDetermineRatio = computeCannotDetermineRatio(decisions);
|
|
77
|
+
if (cannotDetermineRatio > 0.3) {
|
|
78
|
+
allWarnings.push(`High cannot_determine ratio (${(cannotDetermineRatio * 100).toFixed(0)}%). Consider updating route-families.json or running with MCP exploration.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Step 4: Coverage stage — AI-powered spec coverage evaluation
|
|
82
|
+
if (stages.includes('coverage') && decisions.length > 0) {
|
|
83
|
+
const coverageTimer = logger.timer('coverage');
|
|
84
|
+
const coverageResult = await runCoverageStage(decisions, preprocessResult.specIndex, preprocessResult.context, config.testsRoot, { ...(config.coverage || {}), profile });
|
|
85
|
+
decisions = coverageResult.decisions;
|
|
86
|
+
timings.coverage = coverageTimer.end();
|
|
87
|
+
allWarnings.push(...coverageResult.warnings);
|
|
88
|
+
}
|
|
89
|
+
// Step 5: Generation stage — AI-powered spec generation for create_spec / add_scenarios
|
|
90
|
+
if (stages.includes('generation') && decisions.length > 0) {
|
|
91
|
+
const generationTimer = logger.timer('generation');
|
|
92
|
+
const generationResult = await runGenerationStage(decisions, preprocessResult.apiSurface, config.testsRoot, { ...(config.generation || {}), profile });
|
|
93
|
+
generatedSpecs = generationResult.generated;
|
|
94
|
+
timings.generation = generationTimer.end();
|
|
95
|
+
allWarnings.push(...generationResult.warnings);
|
|
96
|
+
}
|
|
97
|
+
// Step 6: Heal stage — MCP-backed playwright-test-healer for failing/flaky specs
|
|
98
|
+
if (stages.includes('heal')) {
|
|
99
|
+
const healTimer = logger.timer('heal');
|
|
100
|
+
const healTargets = resolveHealTargets(config.testsRoot, {
|
|
101
|
+
playwrightReportPath: config.playwrightReportPath,
|
|
102
|
+
generatedSpecs,
|
|
103
|
+
}, decisions);
|
|
104
|
+
if (healTargets.length > 0) {
|
|
105
|
+
healResult = await runHealStage(config.testsRoot, healTargets, { ...(config.heal || { mcp: true }), profile });
|
|
106
|
+
allWarnings.push(...healResult.warnings);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
allWarnings.push('Heal stage: no targets found (no failing specs in report, no generated specs).');
|
|
110
|
+
}
|
|
111
|
+
timings.heal = healTimer.end();
|
|
112
|
+
}
|
|
113
|
+
// Build report
|
|
114
|
+
const report = {
|
|
115
|
+
runId,
|
|
116
|
+
timestamp: startedAt,
|
|
117
|
+
gitRef: config.gitSince,
|
|
118
|
+
summary: buildSummary(decisions),
|
|
119
|
+
decisions,
|
|
120
|
+
warnings: allWarnings,
|
|
121
|
+
model: {
|
|
122
|
+
impactAgent: config.impact?.provider || 'auto',
|
|
123
|
+
coverageAgent: config.coverage?.provider || 'auto',
|
|
124
|
+
generationAgent: stages.includes('generation') ? (config.generation?.provider || 'auto') : undefined,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
const reportPath = writeReport(config.testsRoot, report, healResult, timings);
|
|
128
|
+
return { report, reportPath, warnings: allWarnings, generated: generatedSpecs, healResult };
|
|
129
|
+
}
|
|
130
|
+
function writeReport(testsRoot, report, healResult, timings) {
|
|
131
|
+
const outputDir = join(testsRoot, '.e2e-ai-agents');
|
|
132
|
+
if (!existsSync(outputDir)) {
|
|
133
|
+
mkdirSync(outputDir, { recursive: true });
|
|
134
|
+
}
|
|
135
|
+
// Include timings in the JSON report if available
|
|
136
|
+
const reportWithTimings = timings ? { ...report, timings } : report;
|
|
137
|
+
const jsonPath = join(outputDir, 'pipeline-report.json');
|
|
138
|
+
writeFileSync(jsonPath, JSON.stringify(reportWithTimings, null, 2), 'utf-8');
|
|
139
|
+
const mdPath = join(outputDir, 'pipeline-report.md');
|
|
140
|
+
writeFileSync(mdPath, renderMarkdown(report, healResult), 'utf-8');
|
|
141
|
+
return jsonPath;
|
|
142
|
+
}
|
|
143
|
+
function renderMarkdown(report, healResult) {
|
|
144
|
+
const lines = [
|
|
145
|
+
`# Impact Analysis Pipeline Report`,
|
|
146
|
+
'',
|
|
147
|
+
`**Run ID:** ${report.runId}`,
|
|
148
|
+
`**Timestamp:** ${report.timestamp}`,
|
|
149
|
+
`**Git Ref:** ${report.gitRef}`,
|
|
150
|
+
'',
|
|
151
|
+
`## Summary`,
|
|
152
|
+
'',
|
|
153
|
+
`| Metric | Value |`,
|
|
154
|
+
`|--------|-------|`,
|
|
155
|
+
`| Changed Files | ${report.summary.changedFiles} |`,
|
|
156
|
+
`| Route Families Impacted | ${report.summary.routeFamiliesImpacted.join(', ') || 'none'} |`,
|
|
157
|
+
`| Flows Identified | ${report.summary.flowsIdentified} |`,
|
|
158
|
+
`| Covered | ${report.summary.flowsCovered} |`,
|
|
159
|
+
`| Partial | ${report.summary.flowsPartial} |`,
|
|
160
|
+
`| Uncovered | ${report.summary.flowsUncovered} |`,
|
|
161
|
+
`| Cannot Determine | ${report.summary.actionsRequired.cannot_determine} |`,
|
|
162
|
+
`| Overall Confidence | ${report.summary.overallConfidence} |`,
|
|
163
|
+
'',
|
|
164
|
+
];
|
|
165
|
+
if (report.decisions.length > 0) {
|
|
166
|
+
lines.push('## Decisions', '');
|
|
167
|
+
for (const d of report.decisions) {
|
|
168
|
+
lines.push(`### ${d.flowName} (${d.priority})`);
|
|
169
|
+
lines.push('');
|
|
170
|
+
lines.push(`- **Action:** ${d.action}`);
|
|
171
|
+
lines.push(`- **Route Family:** ${d.routeFamily}${d.featureId ? ` / ${d.featureId}` : ''}`);
|
|
172
|
+
if (d.specificRoute) {
|
|
173
|
+
lines.push(`- **Route:** ${d.specificRoute}`);
|
|
174
|
+
}
|
|
175
|
+
lines.push(`- **Confidence:** ${d.confidence}%`);
|
|
176
|
+
lines.push(`- **Evidence:** ${d.evidence}`);
|
|
177
|
+
lines.push(`- **Changed Files:** ${d.changedFiles.join(', ')}`);
|
|
178
|
+
if (d.userActions.length > 0) {
|
|
179
|
+
lines.push(`- **User Actions:** ${d.userActions.join('; ')}`);
|
|
180
|
+
}
|
|
181
|
+
if (d.existingSpecs.length > 0) {
|
|
182
|
+
lines.push('- **Existing Coverage:**');
|
|
183
|
+
for (const spec of d.existingSpecs) {
|
|
184
|
+
lines.push(` - ${spec.path} (${spec.coverageLevel})`);
|
|
185
|
+
if (spec.testTitles.length > 0) {
|
|
186
|
+
for (const title of spec.testTitles) {
|
|
187
|
+
lines.push(` - \`${title}\``);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (spec.missingScenarios && spec.missingScenarios.length > 0) {
|
|
191
|
+
lines.push(' - Missing:');
|
|
192
|
+
for (const scenario of spec.missingScenarios) {
|
|
193
|
+
lines.push(` - ${scenario}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (d.scenariosToAdd && d.scenariosToAdd.length > 0) {
|
|
199
|
+
lines.push('- **Scenarios to Add:**');
|
|
200
|
+
for (const s of d.scenariosToAdd) {
|
|
201
|
+
lines.push(` - ${s}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (d.targetSpec) {
|
|
205
|
+
lines.push(`- **Target Spec:** ${d.targetSpec}`);
|
|
206
|
+
}
|
|
207
|
+
if (d.newSpecPath) {
|
|
208
|
+
lines.push(`- **New Spec Path:** ${d.newSpecPath}`);
|
|
209
|
+
}
|
|
210
|
+
if (d.blockingReason) {
|
|
211
|
+
lines.push(`- **Blocking Reason:** ${d.blockingReason}`);
|
|
212
|
+
}
|
|
213
|
+
lines.push('');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (healResult) {
|
|
217
|
+
lines.push('');
|
|
218
|
+
lines.push(renderHealMarkdown(healResult));
|
|
219
|
+
}
|
|
220
|
+
if (report.warnings.length > 0) {
|
|
221
|
+
lines.push('## Warnings', '');
|
|
222
|
+
for (const w of report.warnings) {
|
|
223
|
+
lines.push(`- ${w}`);
|
|
224
|
+
}
|
|
225
|
+
lines.push('');
|
|
226
|
+
}
|
|
227
|
+
return lines.join('\n');
|
|
228
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import { runCommand } from '../agent/process_runner.js';
|
|
5
|
+
/** Env var prefixes/names stripped when running LLM-generated specs */
|
|
6
|
+
const SENSITIVE_ENV_PREFIXES = [
|
|
7
|
+
'AWS_', 'AZURE_', 'GCP_', 'GOOGLE_', 'ANTHROPIC_', 'OPENAI_',
|
|
8
|
+
'GITHUB_TOKEN', 'NPM_TOKEN', 'SSH_', 'SECRET_', 'PRIVATE_',
|
|
9
|
+
'DATABASE_URL', 'DB_', 'REDIS_', 'POSTGRES_', 'MYSQL_', 'MONGO_',
|
|
10
|
+
'API_KEY', 'API_SECRET', 'AUTH_', 'JWT_', 'STRIPE_', 'TWILIO_',
|
|
11
|
+
'SENDGRID_', 'SLACK_TOKEN', 'SLACK_BOT', 'MATTERMOST_',
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Build a restricted environment for running LLM-generated spec files.
|
|
15
|
+
* Strips credentials and secrets to limit damage from malicious generated code.
|
|
16
|
+
*/
|
|
17
|
+
function buildRestrictedEnv() {
|
|
18
|
+
const env = {};
|
|
19
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
20
|
+
const isSensitive = SENSITIVE_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
21
|
+
if (!isSensitive) {
|
|
22
|
+
env[key] = value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return env;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validate and normalize a spec path to prevent argument injection.
|
|
29
|
+
* Rejects raw input that starts with '-' (could be interpreted as flags by tsc/playwright).
|
|
30
|
+
*/
|
|
31
|
+
function sanitizeSpecPath(specPath) {
|
|
32
|
+
if (specPath.startsWith('-')) {
|
|
33
|
+
throw new Error(`Invalid spec path: "${specPath}" — path must not start with a dash`);
|
|
34
|
+
}
|
|
35
|
+
return resolve(specPath);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Compile-check a generated spec file using tsc --noEmit.
|
|
39
|
+
* Returns success: true if compilation succeeds, or errors array on failure.
|
|
40
|
+
*/
|
|
41
|
+
export function compileCheckSpec(specPath, testsRoot) {
|
|
42
|
+
const safeSpecPath = sanitizeSpecPath(specPath);
|
|
43
|
+
const result = runCommand('npx', ['tsc', '--noEmit', '--esModuleInterop', '--resolveJsonModule', '--moduleResolution', 'node', '--target', 'ES2020', safeSpecPath], testsRoot, 30000, buildRestrictedEnv());
|
|
44
|
+
if (result.status === 0) {
|
|
45
|
+
return { success: true, errors: [] };
|
|
46
|
+
}
|
|
47
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
|
|
48
|
+
const errorLines = output.split('\n')
|
|
49
|
+
.filter((l) => l.includes('error TS') || l.includes('Error:'))
|
|
50
|
+
.slice(0, 10);
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
errors: errorLines.length > 0 ? errorLines : [output.slice(0, 500) || 'Compilation failed'],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Smoke-run a generated spec against a running app.
|
|
58
|
+
* Runs in a restricted environment with sensitive env vars stripped.
|
|
59
|
+
* Returns success: true if the test passes with retries.
|
|
60
|
+
*/
|
|
61
|
+
export function smokeRunSpec(specPath, testsRoot, playwrightBinary) {
|
|
62
|
+
const safeSpecPath = sanitizeSpecPath(specPath);
|
|
63
|
+
const result = runCommand(playwrightBinary, ['test', safeSpecPath, '--retries', '2', '--reporter', 'list'], testsRoot, 120000, buildRestrictedEnv());
|
|
64
|
+
if (result.status === 0) {
|
|
65
|
+
return { success: true };
|
|
66
|
+
}
|
|
67
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join('\n');
|
|
68
|
+
const errorLines = output.split('\n')
|
|
69
|
+
.filter((l) => l.includes('Error') || l.includes('FAILED') || l.includes('Timeout'))
|
|
70
|
+
.slice(0, 5);
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: errorLines.join('\n') || result.error || 'Smoke run failed',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { bindFilesToFamilies, buildHeuristicFamilies, loadRouteFamilyManifest, } from '../knowledge/route_families.js';
|
|
6
|
+
import { loadOrBuildApiSurface } from '../knowledge/api_surface.js';
|
|
7
|
+
import { buildSpecIndex } from '../knowledge/spec_index.js';
|
|
8
|
+
import { loadContextDocuments } from '../knowledge/context_loader.js';
|
|
9
|
+
const MAX_SNIPPET_CHARS = 3000;
|
|
10
|
+
const MAX_FILES_PER_GROUP = 30;
|
|
11
|
+
function loadFileSnippet(appPath, filePath) {
|
|
12
|
+
const candidates = [
|
|
13
|
+
join(appPath, filePath),
|
|
14
|
+
filePath,
|
|
15
|
+
];
|
|
16
|
+
for (const candidate of candidates) {
|
|
17
|
+
if (existsSync(candidate)) {
|
|
18
|
+
try {
|
|
19
|
+
const content = readFileSync(candidate, 'utf-8');
|
|
20
|
+
if (content.length <= MAX_SNIPPET_CHARS) {
|
|
21
|
+
return content;
|
|
22
|
+
}
|
|
23
|
+
return content.slice(0, MAX_SNIPPET_CHARS) + '\n// ... truncated';
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
export function preprocess(changedFiles, config) {
|
|
33
|
+
const warnings = [];
|
|
34
|
+
// Load route family manifest, fall back to heuristic families
|
|
35
|
+
let manifest = loadRouteFamilyManifest(config.testsRoot, config.routeFamilies);
|
|
36
|
+
if (!manifest) {
|
|
37
|
+
manifest = buildHeuristicFamilies(changedFiles, config.testsRoot);
|
|
38
|
+
warnings.push('Route family manifest not found. Using directory-based heuristics (lower accuracy).', 'Tip: Run `impact-gate train` to generate a proper manifest.');
|
|
39
|
+
}
|
|
40
|
+
// Load API surface catalog
|
|
41
|
+
const apiSurface = loadOrBuildApiSurface(config.testsRoot, config.apiSurface);
|
|
42
|
+
if (apiSurface.pageObjects.length === 0) {
|
|
43
|
+
warnings.push('API surface catalog is empty. Generated test validation will be limited.');
|
|
44
|
+
}
|
|
45
|
+
// Build spec index
|
|
46
|
+
const specIndex = buildSpecIndex(config.testsRoot, undefined, manifest);
|
|
47
|
+
// Load context documents
|
|
48
|
+
const context = loadContextDocuments(config.testsRoot, config.appPath);
|
|
49
|
+
warnings.push(...context.warnings);
|
|
50
|
+
// Bind files to families (manifest is always non-null now — either real or heuristic)
|
|
51
|
+
const fileBindings = bindFilesToFamilies(changedFiles, manifest);
|
|
52
|
+
const unboundFiles = fileBindings
|
|
53
|
+
.filter((fb) => fb.bindings.length === 0)
|
|
54
|
+
.map((fb) => fb.file);
|
|
55
|
+
if (unboundFiles.length > 0) {
|
|
56
|
+
warnings.push(`${unboundFiles.length} changed file(s) did not match any route family: ${unboundFiles.slice(0, 5).join(', ')}${unboundFiles.length > 5 ? '...' : ''}`);
|
|
57
|
+
}
|
|
58
|
+
// Group files by family+feature
|
|
59
|
+
const groupMap = new Map();
|
|
60
|
+
for (const binding of fileBindings) {
|
|
61
|
+
if (binding.bindings.length === 0) {
|
|
62
|
+
// Unbound files go into a special "unbound" group
|
|
63
|
+
const key = '__unbound__';
|
|
64
|
+
if (!groupMap.has(key)) {
|
|
65
|
+
groupMap.set(key, { familyId: '__unbound__', files: [] });
|
|
66
|
+
}
|
|
67
|
+
const group = groupMap.get(key);
|
|
68
|
+
if (group.files.length < MAX_FILES_PER_GROUP) {
|
|
69
|
+
group.files.push({
|
|
70
|
+
path: binding.file,
|
|
71
|
+
snippet: loadFileSnippet(config.appPath, binding.file),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
for (const b of binding.bindings) {
|
|
77
|
+
const key = b.feature ? `${b.family}::${b.feature}` : b.family;
|
|
78
|
+
if (!groupMap.has(key)) {
|
|
79
|
+
groupMap.set(key, { familyId: b.family, featureId: b.feature, files: [] });
|
|
80
|
+
}
|
|
81
|
+
const group = groupMap.get(key);
|
|
82
|
+
if (group.files.length < MAX_FILES_PER_GROUP) {
|
|
83
|
+
group.files.push({
|
|
84
|
+
path: binding.file,
|
|
85
|
+
snippet: loadFileSnippet(config.appPath, binding.file),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const familyGroups = Array.from(groupMap.values()).filter((g) => g.familyId !== '__unbound__');
|
|
91
|
+
return {
|
|
92
|
+
changedFiles,
|
|
93
|
+
fileBindings,
|
|
94
|
+
unboundFiles,
|
|
95
|
+
familyGroups,
|
|
96
|
+
manifest,
|
|
97
|
+
apiSurface,
|
|
98
|
+
specIndex,
|
|
99
|
+
context,
|
|
100
|
+
warnings,
|
|
101
|
+
};
|
|
102
|
+
}
|