@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,273 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { LLMProviderError, UnsupportedCapabilityError } from './provider_interface.js';
|
|
4
|
+
import { sanitizeErrorMessage, validateAndSanitizeUrl } from './provider_utils.js';
|
|
5
|
+
import { BaseProvider } from './base_provider.js';
|
|
6
|
+
import { logger } from './logger.js';
|
|
7
|
+
function normalizeUrl(baseUrl, pathSuffix) {
|
|
8
|
+
const trimmed = baseUrl.replace(/\/+$/, '');
|
|
9
|
+
if (trimmed.endsWith(pathSuffix)) {
|
|
10
|
+
return trimmed;
|
|
11
|
+
}
|
|
12
|
+
return `${trimmed}${pathSuffix}`;
|
|
13
|
+
}
|
|
14
|
+
async function postJson(url, headers, body, timeoutMs, context) {
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetch(url, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: {
|
|
21
|
+
'content-type': 'application/json',
|
|
22
|
+
...headers,
|
|
23
|
+
},
|
|
24
|
+
body: JSON.stringify(body),
|
|
25
|
+
signal: controller.signal,
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new Error(`HTTP ${response.status}`);
|
|
29
|
+
}
|
|
30
|
+
return (await response.json());
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
34
|
+
throw new Error(`Request timeout (${context})`);
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
if (timer) {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class CustomProvider extends BaseProvider {
|
|
45
|
+
constructor(config) {
|
|
46
|
+
super();
|
|
47
|
+
this.name = 'custom';
|
|
48
|
+
const validation = validateAndSanitizeUrl(config.baseUrl);
|
|
49
|
+
if (!validation.valid) {
|
|
50
|
+
throw new Error(`Invalid base URL: ${validation.warning}`);
|
|
51
|
+
}
|
|
52
|
+
if (validation.warning) {
|
|
53
|
+
logger.warn(`HTTPS required for remote URLs: ${validation.warning}`);
|
|
54
|
+
}
|
|
55
|
+
this.config = config;
|
|
56
|
+
this.capabilities = {
|
|
57
|
+
vision: config.requestFormat !== 'custom',
|
|
58
|
+
streaming: false,
|
|
59
|
+
maxTokens: 0,
|
|
60
|
+
costPer1MInputTokens: 0,
|
|
61
|
+
costPer1MOutputTokens: 0,
|
|
62
|
+
supportsTools: false,
|
|
63
|
+
supportsPromptCaching: false,
|
|
64
|
+
typicalResponseTimeMs: 0,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async generateText(prompt, options) {
|
|
68
|
+
this.checkBudget();
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
try {
|
|
71
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
72
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
73
|
+
}
|
|
74
|
+
const response = await this.dispatchRequest(prompt, options);
|
|
75
|
+
const responseTime = Date.now() - startTime;
|
|
76
|
+
const text = response.text;
|
|
77
|
+
const usage = response.usage;
|
|
78
|
+
const cost = response.cost;
|
|
79
|
+
this.updateStats(usage, responseTime, cost);
|
|
80
|
+
return {
|
|
81
|
+
text,
|
|
82
|
+
usage,
|
|
83
|
+
cost,
|
|
84
|
+
metadata: response.metadata,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
this.stats.failedRequests++;
|
|
89
|
+
throw new LLMProviderError(sanitizeErrorMessage(error, 'generateText'), this.name, undefined, error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async analyzeImage(images, prompt, options) {
|
|
93
|
+
if (this.config.requestFormat === 'custom') {
|
|
94
|
+
throw new UnsupportedCapabilityError(this.name, 'vision');
|
|
95
|
+
}
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
try {
|
|
98
|
+
if (images.length === 0 || images.length > 20) {
|
|
99
|
+
throw new Error('Image count must be between 1 and 20');
|
|
100
|
+
}
|
|
101
|
+
if (prompt.length > 10 * 1024 * 1024) {
|
|
102
|
+
throw new Error('Prompt exceeds maximum size (10MB)');
|
|
103
|
+
}
|
|
104
|
+
const response = await this.dispatchRequest(prompt, options, images);
|
|
105
|
+
const responseTime = Date.now() - startTime;
|
|
106
|
+
const text = response.text;
|
|
107
|
+
const usage = response.usage;
|
|
108
|
+
const cost = response.cost;
|
|
109
|
+
this.updateStats(usage, responseTime, cost);
|
|
110
|
+
return {
|
|
111
|
+
text,
|
|
112
|
+
usage,
|
|
113
|
+
cost,
|
|
114
|
+
metadata: response.metadata,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
this.stats.failedRequests++;
|
|
119
|
+
throw new LLMProviderError(sanitizeErrorMessage(error, 'analyzeImage'), this.name, undefined, error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async dispatchRequest(prompt, options, images) {
|
|
123
|
+
switch (this.config.requestFormat) {
|
|
124
|
+
case 'openai':
|
|
125
|
+
return this.requestOpenAI(prompt, options, images);
|
|
126
|
+
case 'anthropic':
|
|
127
|
+
return this.requestAnthropic(prompt, options, images);
|
|
128
|
+
case 'custom':
|
|
129
|
+
return this.requestCustom(prompt, options);
|
|
130
|
+
default:
|
|
131
|
+
throw new Error(`Unsupported request format: ${this.config.requestFormat}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async requestOpenAI(prompt, options, images) {
|
|
135
|
+
const url = normalizeUrl(this.config.baseUrl, '/chat/completions');
|
|
136
|
+
const messages = [];
|
|
137
|
+
if (options?.systemPrompt) {
|
|
138
|
+
messages.push({ role: 'system', content: options.systemPrompt });
|
|
139
|
+
}
|
|
140
|
+
if (images && images.length > 0) {
|
|
141
|
+
const content = [
|
|
142
|
+
{ type: 'text', text: prompt },
|
|
143
|
+
];
|
|
144
|
+
for (const image of images) {
|
|
145
|
+
const mediaType = (image.mimeType || image.mediaType || 'image/png');
|
|
146
|
+
if (!['image/png', 'image/jpeg', 'image/webp'].includes(mediaType)) {
|
|
147
|
+
throw new Error(`Unsupported image type: ${mediaType}`);
|
|
148
|
+
}
|
|
149
|
+
const data = image.data || image.base64 || '';
|
|
150
|
+
if (data.length > 20 * 1024 * 1024) {
|
|
151
|
+
throw new Error('Image data exceeds maximum size (20MB)');
|
|
152
|
+
}
|
|
153
|
+
content.push({
|
|
154
|
+
type: 'image_url',
|
|
155
|
+
image_url: { url: `data:${mediaType};base64,${data}` },
|
|
156
|
+
});
|
|
157
|
+
if (image.description) {
|
|
158
|
+
content.push({ type: 'text', text: `[Image: ${image.description}]` });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
messages.push({ role: 'user', content });
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
messages.push({ role: 'user', content: prompt });
|
|
165
|
+
}
|
|
166
|
+
const body = {
|
|
167
|
+
model: this.config.model,
|
|
168
|
+
messages,
|
|
169
|
+
max_tokens: options?.maxTokens,
|
|
170
|
+
temperature: options?.temperature,
|
|
171
|
+
top_p: options?.topP,
|
|
172
|
+
stop: options?.stopSequences,
|
|
173
|
+
};
|
|
174
|
+
const response = await postJson(url, this.config.auth, body, options?.timeout, 'openai');
|
|
175
|
+
const text = response.choices?.[0]?.message?.content || '';
|
|
176
|
+
const usage = {
|
|
177
|
+
inputTokens: response.usage?.prompt_tokens || 0,
|
|
178
|
+
outputTokens: response.usage?.completion_tokens || 0,
|
|
179
|
+
totalTokens: response.usage?.total_tokens || 0,
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
text,
|
|
183
|
+
usage,
|
|
184
|
+
cost: 0,
|
|
185
|
+
metadata: {
|
|
186
|
+
finishReason: response.choices?.[0]?.finish_reason,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async requestAnthropic(prompt, options, images) {
|
|
191
|
+
const url = normalizeUrl(this.config.baseUrl, '/messages');
|
|
192
|
+
const content = [
|
|
193
|
+
{ type: 'text', text: prompt },
|
|
194
|
+
];
|
|
195
|
+
if (images && images.length > 0) {
|
|
196
|
+
for (const image of images) {
|
|
197
|
+
const mediaType = (image.mimeType || image.mediaType || 'image/png');
|
|
198
|
+
if (!['image/png', 'image/jpeg', 'image/webp', 'image/gif'].includes(mediaType)) {
|
|
199
|
+
throw new Error(`Unsupported image type: ${mediaType}`);
|
|
200
|
+
}
|
|
201
|
+
const data = image.data || image.base64 || '';
|
|
202
|
+
if (data.length > 20 * 1024 * 1024) {
|
|
203
|
+
throw new Error('Image data exceeds maximum size (20MB)');
|
|
204
|
+
}
|
|
205
|
+
content.push({
|
|
206
|
+
type: 'image',
|
|
207
|
+
source: {
|
|
208
|
+
type: 'base64',
|
|
209
|
+
media_type: mediaType,
|
|
210
|
+
data,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
if (image.description) {
|
|
214
|
+
content.push({ type: 'text', text: `[Image: ${image.description}]` });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const body = {
|
|
219
|
+
model: this.config.model,
|
|
220
|
+
max_tokens: options?.maxTokens || 4000,
|
|
221
|
+
temperature: options?.temperature,
|
|
222
|
+
top_p: options?.topP,
|
|
223
|
+
stop_sequences: options?.stopSequences,
|
|
224
|
+
system: options?.systemPrompt,
|
|
225
|
+
messages: [
|
|
226
|
+
{
|
|
227
|
+
role: 'user',
|
|
228
|
+
content,
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
};
|
|
232
|
+
const response = await postJson(url, this.config.auth, body, options?.timeout, 'anthropic');
|
|
233
|
+
const text = (response.content || []).map((block) => block.text).join('\n');
|
|
234
|
+
const usage = {
|
|
235
|
+
inputTokens: response.usage?.input_tokens || 0,
|
|
236
|
+
outputTokens: response.usage?.output_tokens || 0,
|
|
237
|
+
totalTokens: (response.usage?.input_tokens || 0) + (response.usage?.output_tokens || 0),
|
|
238
|
+
};
|
|
239
|
+
return {
|
|
240
|
+
text,
|
|
241
|
+
usage,
|
|
242
|
+
cost: 0,
|
|
243
|
+
metadata: {
|
|
244
|
+
stopReason: response.stop_reason,
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
async requestCustom(prompt, options) {
|
|
249
|
+
if (!this.config.transformRequest || !this.config.transformResponse) {
|
|
250
|
+
throw new Error('Custom providers require transformRequest and transformResponse');
|
|
251
|
+
}
|
|
252
|
+
const body = this.config.transformRequest(prompt, options);
|
|
253
|
+
const response = await postJson(this.config.baseUrl, this.config.auth, body, options?.timeout, 'custom');
|
|
254
|
+
const transformed = this.config.transformResponse(response);
|
|
255
|
+
return {
|
|
256
|
+
text: transformed.text,
|
|
257
|
+
usage: transformed.usage,
|
|
258
|
+
cost: transformed.cost,
|
|
259
|
+
metadata: transformed.metadata,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
// CustomProvider doesn't support streaming
|
|
263
|
+
async *streamText() {
|
|
264
|
+
throw new Error('Streaming not supported for custom providers');
|
|
265
|
+
}
|
|
266
|
+
// CustomProvider doesn't have built-in health checks
|
|
267
|
+
async checkHealth() {
|
|
268
|
+
return {
|
|
269
|
+
healthy: true,
|
|
270
|
+
message: 'Custom provider configured',
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { formatDiffsForPrompt } from './diff_loader.js';
|
|
4
|
+
const MAX_SPEC_LIST = 50;
|
|
5
|
+
function normalizePriority(value) {
|
|
6
|
+
if (value === 'P0' || value === 'P1' || value === 'P2') {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
return 'P2';
|
|
10
|
+
}
|
|
11
|
+
const MAX_SCENARIOS_PER_SPEC = 20;
|
|
12
|
+
function buildPrompt(options) {
|
|
13
|
+
const { deterministicImpact, diffs, specList, specDetails, manifestSummary } = options;
|
|
14
|
+
const { changedFiles, impactedFeatures, unboundFiles } = deterministicImpact;
|
|
15
|
+
const lines = [];
|
|
16
|
+
// Optional manifest summary
|
|
17
|
+
if (manifestSummary) {
|
|
18
|
+
lines.push('## Application Overview');
|
|
19
|
+
lines.push(manifestSummary);
|
|
20
|
+
lines.push('');
|
|
21
|
+
}
|
|
22
|
+
// Changed files section
|
|
23
|
+
lines.push(`## Changed Files (${changedFiles.length} total)`);
|
|
24
|
+
for (const f of changedFiles) {
|
|
25
|
+
lines.push(`- ${f}`);
|
|
26
|
+
}
|
|
27
|
+
lines.push('');
|
|
28
|
+
// Diffs section
|
|
29
|
+
lines.push('## Code Diffs');
|
|
30
|
+
lines.push(formatDiffsForPrompt(diffs));
|
|
31
|
+
lines.push('');
|
|
32
|
+
// Deterministic features summary
|
|
33
|
+
lines.push('## Deterministic Impact Analysis');
|
|
34
|
+
lines.push('The following features/flows have been deterministically identified as impacted:');
|
|
35
|
+
lines.push('');
|
|
36
|
+
for (const feature of impactedFeatures) {
|
|
37
|
+
const featureIdPart = feature.featureId ? `featureId=${feature.featureId}` : 'featureId=undefined';
|
|
38
|
+
const specCount = feature.playwrightSpecs.length + feature.cypressSpecs.length;
|
|
39
|
+
const specList2 = [...feature.playwrightSpecs, ...feature.cypressSpecs];
|
|
40
|
+
const specsDisplay = specList2.length > 0 ? specList2.join(', ') : 'none';
|
|
41
|
+
const userFlowsDisplay = feature.userFlows.length > 0 ? ` userFlows=[${feature.userFlows.slice(0, 5).join(', ')}]` : '';
|
|
42
|
+
lines.push(`- familyId=${feature.familyId} ${featureIdPart} (${feature.priority}): ${specCount} specs, coverage=${feature.coverageStatus}, specs=[${specsDisplay}]${userFlowsDisplay}`);
|
|
43
|
+
}
|
|
44
|
+
lines.push('');
|
|
45
|
+
// Unbound files
|
|
46
|
+
if (unboundFiles.length > 0) {
|
|
47
|
+
lines.push('## Unbound Files (not mapped to any feature)');
|
|
48
|
+
for (const f of unboundFiles) {
|
|
49
|
+
lines.push(`- ${f}`);
|
|
50
|
+
}
|
|
51
|
+
lines.push('');
|
|
52
|
+
}
|
|
53
|
+
// Spec coverage with scenario titles (when available) or bare file paths
|
|
54
|
+
if (specDetails && specDetails.length > 0) {
|
|
55
|
+
const cappedDetails = specDetails.slice(0, MAX_SPEC_LIST);
|
|
56
|
+
const totalScenarios = cappedDetails.reduce((sum, s) => sum + s.scenarios.length, 0);
|
|
57
|
+
lines.push(`## Existing Test Coverage (${cappedDetails.length} specs, ${totalScenarios} scenarios)`);
|
|
58
|
+
lines.push('Use this to avoid suggesting scenarios that already exist.');
|
|
59
|
+
lines.push('');
|
|
60
|
+
for (const spec of cappedDetails) {
|
|
61
|
+
lines.push(`- ${spec.file}`);
|
|
62
|
+
const cappedScenarios = spec.scenarios.slice(0, MAX_SCENARIOS_PER_SPEC);
|
|
63
|
+
for (const scenario of cappedScenarios) {
|
|
64
|
+
lines.push(` • "${scenario}"`);
|
|
65
|
+
}
|
|
66
|
+
if (spec.scenarios.length > MAX_SCENARIOS_PER_SPEC) {
|
|
67
|
+
lines.push(` • ... and ${spec.scenarios.length - MAX_SCENARIOS_PER_SPEC} more`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
lines.push('');
|
|
71
|
+
}
|
|
72
|
+
else if (specList.length > 0) {
|
|
73
|
+
const cappedSpecs = specList.slice(0, MAX_SPEC_LIST);
|
|
74
|
+
lines.push(`## Available Test Specs (showing ${cappedSpecs.length} of ${specList.length})`);
|
|
75
|
+
for (const s of cappedSpecs) {
|
|
76
|
+
lines.push(`- ${s}`);
|
|
77
|
+
}
|
|
78
|
+
lines.push('');
|
|
79
|
+
}
|
|
80
|
+
// Instructions
|
|
81
|
+
lines.push('## Instructions');
|
|
82
|
+
lines.push('Return ONLY valid JSON (no markdown fences, no explanation) in this exact shape:');
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push('Rules for coveredBy:');
|
|
85
|
+
lines.push('- Reference SPECIFIC scenario titles from the Existing Test Coverage section when possible.');
|
|
86
|
+
lines.push('- Format: "file.spec.ts → scenario title"');
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push('Rules for missingScenarios:');
|
|
89
|
+
lines.push('- Cross-reference the scenario titles in Existing Test Coverage. If a scenario already exists that covers the behavior, do NOT suggest it — instead list it in coveredBy.');
|
|
90
|
+
lines.push('- For coverage=uncovered: list ONLY scenarios that test the SPECIFIC behavior change in this diff — NOT generic scenarios for the entire feature. A permission check fix needs permission-denial test scenarios, not general CRUD tests.');
|
|
91
|
+
lines.push('- For coverage=covered or coverage=partial: ONLY list scenarios introduced by THIS diff that have NO matching scenario in existing coverage. If the diff adds no new user-visible behavior, return []. Do not pad with generic scenarios.');
|
|
92
|
+
lines.push('- If the source code diff is trivial (single-line fix, type change, field rename, or the PR is primarily adding tests), return missingScenarios=[] and explain in reasons that no new user-visible behavior was introduced.');
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push(JSON.stringify({
|
|
95
|
+
impactedFlows: [
|
|
96
|
+
{
|
|
97
|
+
id: '<featureId or familyId from the deterministic list above>',
|
|
98
|
+
name: '<human-readable flow name>',
|
|
99
|
+
priority: 'P0|P1|P2',
|
|
100
|
+
reasons: [
|
|
101
|
+
'<EXACTLY 1-2 sentences explaining what SPECIFICALLY changed for users in THIS diff. Reference the actual behavior modification — NOT a general description of what the feature does. BAD: "Users can create and view posts." GOOD: "Users editing posts now require create_post permission, which may block edits for restricted roles.">',
|
|
102
|
+
],
|
|
103
|
+
coveredBy: ['<spec file paths that cover this flow>'],
|
|
104
|
+
missingScenarios: ['<concrete scenario title for a NEW or CHANGED behavior in THIS diff. Must be SPECIFIC to the code change — never generic feature tests. BAD: "User can create a new post". GOOD: "User without create_post permission sees error when editing a post". If the diff is trivial (typo, field rename, test-only) return [].>'],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
unboundFileAnalysis: [
|
|
108
|
+
{
|
|
109
|
+
file: '<path to unbound file>',
|
|
110
|
+
likelyFeature: '<best guess at which feature/family this affects>',
|
|
111
|
+
reason: '<why you think this file belongs to that feature>',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
}, null, 2));
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
}
|
|
117
|
+
function extractJSON(text) {
|
|
118
|
+
// Try markdown fenced block first
|
|
119
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
120
|
+
const candidates = fenced ? [fenced[1], text] : [text];
|
|
121
|
+
for (const candidate of candidates) {
|
|
122
|
+
const start = candidate.indexOf('{');
|
|
123
|
+
const end = candidate.lastIndexOf('}');
|
|
124
|
+
if (start >= 0 && end > start) {
|
|
125
|
+
return candidate.slice(start, end + 1).trim();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Fallback: return trimmed text
|
|
129
|
+
return text.trim();
|
|
130
|
+
}
|
|
131
|
+
function toEnrichedFeature(det, aiFlow) {
|
|
132
|
+
return {
|
|
133
|
+
familyId: det.familyId,
|
|
134
|
+
featureId: det.featureId,
|
|
135
|
+
priority: normalizePriority(det.priority),
|
|
136
|
+
changedFiles: det.changedFiles,
|
|
137
|
+
coverageStatus: det.coverageStatus,
|
|
138
|
+
playwrightSpecs: det.playwrightSpecs,
|
|
139
|
+
cypressSpecs: det.cypressSpecs,
|
|
140
|
+
userFlows: det.userFlows,
|
|
141
|
+
aiReasons: aiFlow?.reasons ?? [],
|
|
142
|
+
aiMissingScenarios: aiFlow?.missingScenarios ?? [],
|
|
143
|
+
aiCoveredBy: aiFlow?.coveredBy ?? [],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Enriches a deterministic impact result with AI-generated reasons,
|
|
148
|
+
* missing test scenarios, and coverage insights.
|
|
149
|
+
*/
|
|
150
|
+
export async function enrichImpactWithAI(options) {
|
|
151
|
+
const { deterministicImpact, provider } = options;
|
|
152
|
+
const warnings = [];
|
|
153
|
+
let tokenUsage = { input: 0, output: 0 };
|
|
154
|
+
const prompt = buildPrompt(options);
|
|
155
|
+
let aiResponse = null;
|
|
156
|
+
let unboundFileInsights = [];
|
|
157
|
+
try {
|
|
158
|
+
const response = await provider.generateText(prompt, {
|
|
159
|
+
maxTokens: 4000,
|
|
160
|
+
temperature: 0,
|
|
161
|
+
timeout: 45000,
|
|
162
|
+
systemPrompt: 'You are an expert E2E test analyst. Return only valid JSON.',
|
|
163
|
+
});
|
|
164
|
+
tokenUsage = {
|
|
165
|
+
input: response.usage?.inputTokens ?? 0,
|
|
166
|
+
output: response.usage?.outputTokens ?? 0,
|
|
167
|
+
};
|
|
168
|
+
const rawJSON = extractJSON(response.text);
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(rawJSON);
|
|
171
|
+
// Validate that impactedFlows is an array
|
|
172
|
+
if (!Array.isArray(parsed.impactedFlows)) {
|
|
173
|
+
warnings.push('AI response parsed but impactedFlows is not an array; returning empty enrichedFeatures');
|
|
174
|
+
return {
|
|
175
|
+
enrichedFeatures: [],
|
|
176
|
+
unboundFileInsights: [],
|
|
177
|
+
warnings,
|
|
178
|
+
providerName: provider.name,
|
|
179
|
+
tokenUsage,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
aiResponse = parsed;
|
|
183
|
+
unboundFileInsights = (parsed.unboundFileAnalysis ?? []).map((item) => ({
|
|
184
|
+
file: item.file,
|
|
185
|
+
likelyFeature: item.likelyFeature,
|
|
186
|
+
reason: item.reason,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
catch (parseErr) {
|
|
190
|
+
warnings.push(`Failed to parse AI response as JSON: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`);
|
|
191
|
+
return {
|
|
192
|
+
enrichedFeatures: [],
|
|
193
|
+
unboundFileInsights: [],
|
|
194
|
+
warnings,
|
|
195
|
+
providerName: provider.name,
|
|
196
|
+
tokenUsage,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
warnings.push(`AI provider error: ${err instanceof Error ? err.message : String(err)}`);
|
|
202
|
+
return {
|
|
203
|
+
enrichedFeatures: [],
|
|
204
|
+
unboundFileInsights: [],
|
|
205
|
+
warnings,
|
|
206
|
+
providerName: provider.name,
|
|
207
|
+
tokenUsage,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// Build a map of AI flows by id (featureId or familyId)
|
|
211
|
+
const aiFlowMap = new Map();
|
|
212
|
+
if (aiResponse?.impactedFlows) {
|
|
213
|
+
for (const flow of aiResponse.impactedFlows) {
|
|
214
|
+
aiFlowMap.set(flow.id, flow);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Build a set of all deterministic ids for unmatched-flow detection
|
|
218
|
+
const deterministicIds = new Set();
|
|
219
|
+
for (const det of deterministicImpact.impactedFeatures) {
|
|
220
|
+
if (det.featureId) {
|
|
221
|
+
deterministicIds.add(det.featureId);
|
|
222
|
+
}
|
|
223
|
+
deterministicIds.add(det.familyId);
|
|
224
|
+
}
|
|
225
|
+
// Warn on AI flows that don't match any deterministic feature
|
|
226
|
+
for (const flow of aiFlowMap.values()) {
|
|
227
|
+
if (!deterministicIds.has(flow.id)) {
|
|
228
|
+
warnings.push(`AI returned flow '${flow.id}' with no matching deterministic feature (using as-is)`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Merge deterministic features with AI data
|
|
232
|
+
const enrichedFeatures = deterministicImpact.impactedFeatures.map((det) => {
|
|
233
|
+
// Match by featureId first, then by familyId
|
|
234
|
+
const aiFlow = det.featureId
|
|
235
|
+
? (aiFlowMap.get(det.featureId) ?? aiFlowMap.get(det.familyId))
|
|
236
|
+
: aiFlowMap.get(det.familyId);
|
|
237
|
+
return toEnrichedFeature(det, aiFlow);
|
|
238
|
+
});
|
|
239
|
+
// Include AI flows that had no deterministic match (as-is, with empty deterministic fields)
|
|
240
|
+
for (const flow of aiFlowMap.values()) {
|
|
241
|
+
if (!deterministicIds.has(flow.id)) {
|
|
242
|
+
enrichedFeatures.push({
|
|
243
|
+
familyId: flow.id,
|
|
244
|
+
featureId: undefined,
|
|
245
|
+
priority: normalizePriority(flow.priority),
|
|
246
|
+
changedFiles: [],
|
|
247
|
+
coverageStatus: 'uncovered',
|
|
248
|
+
playwrightSpecs: [],
|
|
249
|
+
cypressSpecs: [],
|
|
250
|
+
userFlows: [],
|
|
251
|
+
aiReasons: flow.reasons ?? [],
|
|
252
|
+
aiMissingScenarios: flow.missingScenarios ?? [],
|
|
253
|
+
aiCoveredBy: flow.coveredBy ?? [],
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
enrichedFeatures,
|
|
259
|
+
unboundFileInsights,
|
|
260
|
+
warnings,
|
|
261
|
+
providerName: provider.name,
|
|
262
|
+
tokenUsage,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { runGitRaw } from '../agent/git.js';
|
|
4
|
+
const MAX_DIFF_CHARS = 8000;
|
|
5
|
+
const MAX_TOTAL_CHARS = 60000;
|
|
6
|
+
const TRUNCATION_NOTICE = '\n... (diff truncated)';
|
|
7
|
+
/**
|
|
8
|
+
* Loads git diffs for the given changed files relative to the given since ref.
|
|
9
|
+
* Uses `git merge-base` to find the accurate base ref first.
|
|
10
|
+
* Individual diffs are truncated at 8000 chars and total output is capped at 60000 chars.
|
|
11
|
+
*/
|
|
12
|
+
export function loadDiffs(appRoot, since, changedFiles) {
|
|
13
|
+
const result = new Map();
|
|
14
|
+
if (changedFiles.length === 0) {
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
// Try to get accurate merge base
|
|
18
|
+
let baseRef = since;
|
|
19
|
+
const mergeBaseOutput = runGitRaw(['merge-base', since, 'HEAD'], appRoot);
|
|
20
|
+
if (mergeBaseOutput) {
|
|
21
|
+
const candidate = mergeBaseOutput
|
|
22
|
+
.split('\n')
|
|
23
|
+
.map((line) => line.trim())
|
|
24
|
+
.find(Boolean);
|
|
25
|
+
if (candidate) {
|
|
26
|
+
baseRef = candidate;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
let totalChars = 0;
|
|
30
|
+
for (const file of changedFiles) {
|
|
31
|
+
if (totalChars >= MAX_TOTAL_CHARS) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
const diffOutput = runGitRaw(['diff', `${baseRef}..HEAD`, '--', file], appRoot);
|
|
35
|
+
if (diffOutput === null) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
let diff = diffOutput;
|
|
39
|
+
if (diff.length > MAX_DIFF_CHARS) {
|
|
40
|
+
diff = diff.slice(0, MAX_DIFF_CHARS) + TRUNCATION_NOTICE;
|
|
41
|
+
}
|
|
42
|
+
result.set(file, diff);
|
|
43
|
+
totalChars += diff.length;
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Formats a diffs map into a human-readable string suitable for an AI prompt.
|
|
49
|
+
*/
|
|
50
|
+
export function formatDiffsForPrompt(diffs) {
|
|
51
|
+
if (diffs.size === 0) {
|
|
52
|
+
return 'No diffs available.';
|
|
53
|
+
}
|
|
54
|
+
const sections = [];
|
|
55
|
+
for (const [file, diff] of diffs) {
|
|
56
|
+
sections.push(`--- ${file} ---\n${diff}`);
|
|
57
|
+
}
|
|
58
|
+
return sections.join('\n\n');
|
|
59
|
+
}
|