@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,297 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { formatApiSurfaceForPrompt } from '../knowledge/api_surface.js';
|
|
4
|
+
import { sanitizeForPrompt } from '../crew/sanitize.js';
|
|
5
|
+
import { isMattermostProfile } from './generation_profile.js';
|
|
6
|
+
function resolveRelevantPageObjects(apiSurface, decision) {
|
|
7
|
+
const relevant = [];
|
|
8
|
+
const familyHints = [
|
|
9
|
+
decision.routeFamily,
|
|
10
|
+
decision.featureId,
|
|
11
|
+
...decision.userActions.join(' ').toLowerCase().split(/\s+/),
|
|
12
|
+
]
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.map((s) => s.toLowerCase().replace(/[^a-z]/g, ''));
|
|
15
|
+
for (const po of apiSurface.pageObjects) {
|
|
16
|
+
const nameLower = po.className.toLowerCase();
|
|
17
|
+
if (nameLower.includes('channels') ||
|
|
18
|
+
nameLower.includes('page') ||
|
|
19
|
+
familyHints.some((hint) => hint.length > 3 && nameLower.includes(hint))) {
|
|
20
|
+
relevant.push(po.className);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return [...new Set(relevant)].slice(0, 10);
|
|
24
|
+
}
|
|
25
|
+
function buildAssertionPatternsBlock(patterns) {
|
|
26
|
+
if (!patterns || patterns.length === 0) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
return [
|
|
30
|
+
'REQUIRED ASSERTION PATTERNS:',
|
|
31
|
+
'Your tests MUST include assertions that verify each of these behaviors.',
|
|
32
|
+
'Do NOT just check element visibility — verify the actual business outcome.',
|
|
33
|
+
...patterns.map((p) => ` - [${p.type}] ${p.pattern}`),
|
|
34
|
+
'',
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
export function buildGenerationPrompt(ctx) {
|
|
38
|
+
const profile = ctx.profile;
|
|
39
|
+
const isMM = profile ? isMattermostProfile(profile) : false;
|
|
40
|
+
const relevantClasses = resolveRelevantPageObjects(ctx.apiSurface, ctx.decision);
|
|
41
|
+
const apiBlock = relevantClasses.length > 0
|
|
42
|
+
? formatApiSurfaceForPrompt(ctx.apiSurface, relevantClasses)
|
|
43
|
+
: 'No page objects available. Use raw Playwright selectors via page.getByRole/getByTestId.';
|
|
44
|
+
const scenariosBlock = (ctx.decision.scenariosToAdd || [])
|
|
45
|
+
.map((s, i) => ` ${i + 1}. ${s}`)
|
|
46
|
+
.join('\n');
|
|
47
|
+
const existingBlock = ctx.existingSpecContent
|
|
48
|
+
? `\nEXISTING SPEC (extend this file):\n\`\`\`typescript\n${ctx.existingSpecContent}\n\`\`\``
|
|
49
|
+
: '';
|
|
50
|
+
const modeInstruction = ctx.mode === 'create_spec'
|
|
51
|
+
? `Create a NEW spec file at: ${ctx.specPath}`
|
|
52
|
+
: `ADD scenarios to the EXISTING spec at: ${ctx.specPath}`;
|
|
53
|
+
const routeFamilyTag = ctx.decision.routeFamily;
|
|
54
|
+
// Build prompt based on profile
|
|
55
|
+
const projectName = profile?.projectName || 'Project';
|
|
56
|
+
const testFramework = profile?.testFramework || 'Playwright';
|
|
57
|
+
const importStatement = profile?.importStatement || '@playwright/test';
|
|
58
|
+
// API test mode prompt
|
|
59
|
+
if (profile?.testMode === 'api') {
|
|
60
|
+
return buildApiTestPrompt(ctx, profile, scenariosBlock, routeFamilyTag);
|
|
61
|
+
}
|
|
62
|
+
// Build rules from profile conventions or use Mattermost defaults
|
|
63
|
+
const rules = isMM
|
|
64
|
+
? [
|
|
65
|
+
`1. Import ONLY from "${importStatement}" — no other test framework imports.`,
|
|
66
|
+
'2. Every test must call `await pw.initSetup()` first.',
|
|
67
|
+
'3. Use `await pw.testBrowser.login(user)` to log in — never hardcode credentials.',
|
|
68
|
+
'4. Use ONLY page object methods listed above. Do NOT invent methods that are not listed.',
|
|
69
|
+
'5. If a method is not available, use `page.getByRole()` or `page.getByTestId()`.',
|
|
70
|
+
`6. Tag every test: {tag: '@${routeFamilyTag}'}`,
|
|
71
|
+
'7. Write one test per scenario with a descriptive name of what the user does and what is verified.',
|
|
72
|
+
`8. Use \`expect\` from "${importStatement}" — do NOT import from "@playwright/test".`,
|
|
73
|
+
'9. Include the copyright header for new files.',
|
|
74
|
+
'10. NEVER fabricate test IDs (MM-TXXXX). Use descriptive names only.',
|
|
75
|
+
]
|
|
76
|
+
: [
|
|
77
|
+
...(profile?.conventions || []).map((c, i) => `${i + 1}. ${c}`),
|
|
78
|
+
`${(profile?.conventions?.length || 0) + 1}. Use ONLY page object methods listed above. Do NOT invent methods that are not listed.`,
|
|
79
|
+
`${(profile?.conventions?.length || 0) + 2}. If a method is not available, use \`page.getByRole()\` or \`page.getByTestId()\`.`,
|
|
80
|
+
`${(profile?.conventions?.length || 0) + 3}. Tag every test: {tag: '@${routeFamilyTag}'}`,
|
|
81
|
+
];
|
|
82
|
+
// Build example block
|
|
83
|
+
const exampleBlock = isMM
|
|
84
|
+
? [
|
|
85
|
+
'EXAMPLE SPEC STRUCTURE:',
|
|
86
|
+
'```typescript',
|
|
87
|
+
'// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.',
|
|
88
|
+
'// See LICENSE.txt for license information.',
|
|
89
|
+
'',
|
|
90
|
+
`import {expect, test} from '${importStatement}';`,
|
|
91
|
+
'',
|
|
92
|
+
'test(',
|
|
93
|
+
" 'descriptive name of what is tested',",
|
|
94
|
+
` {tag: '@${routeFamilyTag}'},`,
|
|
95
|
+
' async ({pw}) => {',
|
|
96
|
+
' const {user} = await pw.initSetup();',
|
|
97
|
+
' const {channelsPage} = await pw.testBrowser.login(user);',
|
|
98
|
+
' await channelsPage.goto();',
|
|
99
|
+
' await channelsPage.toBeVisible();',
|
|
100
|
+
' // test steps...',
|
|
101
|
+
' },',
|
|
102
|
+
');',
|
|
103
|
+
'```',
|
|
104
|
+
]
|
|
105
|
+
: [
|
|
106
|
+
'EXAMPLE SPEC STRUCTURE:',
|
|
107
|
+
'```typescript',
|
|
108
|
+
...(profile?.copyrightHeader ? [profile.copyrightHeader, ''] : []),
|
|
109
|
+
`import {test, expect} from '${importStatement}';`,
|
|
110
|
+
'',
|
|
111
|
+
'test(',
|
|
112
|
+
" 'descriptive name of what is tested',",
|
|
113
|
+
` {tag: '@${routeFamilyTag}'},`,
|
|
114
|
+
' async ({page}) => {',
|
|
115
|
+
' // test steps...',
|
|
116
|
+
' },',
|
|
117
|
+
');',
|
|
118
|
+
'```',
|
|
119
|
+
];
|
|
120
|
+
return [
|
|
121
|
+
`You are generating ${projectName} ${testFramework} E2E test code.`,
|
|
122
|
+
'',
|
|
123
|
+
`TASK: ${modeInstruction}`,
|
|
124
|
+
'',
|
|
125
|
+
`FLOW: ${sanitizeForPrompt(ctx.decision.flowName)}`,
|
|
126
|
+
`Route Family: ${ctx.decision.routeFamily}${ctx.decision.featureId ? ` / ${ctx.decision.featureId}` : ''}`,
|
|
127
|
+
`Route: ${ctx.decision.specificRoute || '(not specified)'}`,
|
|
128
|
+
`Priority: ${ctx.decision.priority}`,
|
|
129
|
+
`Evidence: ${sanitizeForPrompt(ctx.decision.evidence)}`,
|
|
130
|
+
'',
|
|
131
|
+
'SCENARIOS TO IMPLEMENT:',
|
|
132
|
+
scenariosBlock || ' (implement core user actions for this flow)',
|
|
133
|
+
'',
|
|
134
|
+
'USER ACTIONS:',
|
|
135
|
+
ctx.decision.userActions.map((a) => ` - ${sanitizeForPrompt(a)}`).join('\n') || ' (none specified)',
|
|
136
|
+
'',
|
|
137
|
+
...buildAssertionPatternsBlock(ctx.decision.assertionPatterns),
|
|
138
|
+
'AVAILABLE PAGE OBJECTS AND METHODS:',
|
|
139
|
+
apiBlock,
|
|
140
|
+
existingBlock,
|
|
141
|
+
'',
|
|
142
|
+
'MANDATORY RULES:',
|
|
143
|
+
...rules,
|
|
144
|
+
'',
|
|
145
|
+
...exampleBlock,
|
|
146
|
+
'',
|
|
147
|
+
'Return ONLY the TypeScript code. No explanations, no markdown fences.',
|
|
148
|
+
].join('\n');
|
|
149
|
+
}
|
|
150
|
+
function buildApiTestPrompt(ctx, profile, scenariosBlock, routeFamilyTag) {
|
|
151
|
+
const modeInstruction = ctx.mode === 'create_spec'
|
|
152
|
+
? `Create a NEW test file at: ${ctx.specPath}`
|
|
153
|
+
: `ADD test cases to the EXISTING file at: ${ctx.specPath}`;
|
|
154
|
+
const existingBlock = ctx.existingSpecContent
|
|
155
|
+
? `\nEXISTING FILE (extend this):\n\`\`\`typescript\n${ctx.existingSpecContent}\n\`\`\``
|
|
156
|
+
: '';
|
|
157
|
+
return [
|
|
158
|
+
`You are generating ${profile.projectName} API test code using ${profile.testFramework}.`,
|
|
159
|
+
'',
|
|
160
|
+
`TASK: ${modeInstruction}`,
|
|
161
|
+
'',
|
|
162
|
+
`FLOW: ${sanitizeForPrompt(ctx.decision.flowName)}`,
|
|
163
|
+
`Route Family: ${ctx.decision.routeFamily}${ctx.decision.featureId ? ` / ${ctx.decision.featureId}` : ''}`,
|
|
164
|
+
`Endpoint: ${ctx.decision.specificRoute || '(not specified)'}`,
|
|
165
|
+
`Priority: ${ctx.decision.priority}`,
|
|
166
|
+
`Evidence: ${sanitizeForPrompt(ctx.decision.evidence)}`,
|
|
167
|
+
'',
|
|
168
|
+
'SCENARIOS TO IMPLEMENT:',
|
|
169
|
+
scenariosBlock || ' (implement core API endpoint tests)',
|
|
170
|
+
'',
|
|
171
|
+
'USER ACTIONS:',
|
|
172
|
+
ctx.decision.userActions.map((a) => ` - ${sanitizeForPrompt(a)}`).join('\n') || ' (none specified)',
|
|
173
|
+
'',
|
|
174
|
+
...buildAssertionPatternsBlock(ctx.decision.assertionPatterns),
|
|
175
|
+
existingBlock,
|
|
176
|
+
'',
|
|
177
|
+
'MANDATORY RULES:',
|
|
178
|
+
...profile.conventions.map((c, i) => `${i + 1}. ${c}`),
|
|
179
|
+
`${profile.conventions.length + 1}. Tag every test: {tag: '@${routeFamilyTag}'}`,
|
|
180
|
+
'',
|
|
181
|
+
'EXAMPLE TEST STRUCTURE:',
|
|
182
|
+
...(profile.testFramework.toLowerCase().includes('pytest')
|
|
183
|
+
? [
|
|
184
|
+
'```python',
|
|
185
|
+
...(profile.copyrightHeader ? [profile.copyrightHeader, ''] : []),
|
|
186
|
+
'import pytest',
|
|
187
|
+
'import requests',
|
|
188
|
+
'',
|
|
189
|
+
`BASE_URL = 'http://localhost:3000'`,
|
|
190
|
+
'',
|
|
191
|
+
'',
|
|
192
|
+
`class Test${ctx.decision.routeFamily.replace(/[^a-zA-Z0-9]/g, '')}:`,
|
|
193
|
+
" def test_should_return_200_for_valid_request(self):",
|
|
194
|
+
` res = requests.get(f'{BASE_URL}${ctx.decision.specificRoute || '/api/endpoint'}')`,
|
|
195
|
+
' assert res.status_code == 200',
|
|
196
|
+
'```',
|
|
197
|
+
]
|
|
198
|
+
: [
|
|
199
|
+
'```typescript',
|
|
200
|
+
...(profile.copyrightHeader ? [profile.copyrightHeader, ''] : []),
|
|
201
|
+
`import {describe, it, expect} from '${profile.importStatement}';`,
|
|
202
|
+
"import supertest from 'supertest';",
|
|
203
|
+
'',
|
|
204
|
+
"const request = supertest('http://localhost:3000');",
|
|
205
|
+
'',
|
|
206
|
+
`describe('${ctx.decision.routeFamily}', () => {`,
|
|
207
|
+
" it('should return 200 for valid request', async () => {",
|
|
208
|
+
` const res = await request.get('${ctx.decision.specificRoute || '/api/endpoint'}');`,
|
|
209
|
+
' expect(res.status).toBe(200);',
|
|
210
|
+
' });',
|
|
211
|
+
'});',
|
|
212
|
+
'```',
|
|
213
|
+
]),
|
|
214
|
+
'',
|
|
215
|
+
...(profile.testFramework.toLowerCase().includes('pytest')
|
|
216
|
+
? ['Return ONLY the Python code. No explanations, no markdown fences.']
|
|
217
|
+
: ['Return ONLY the TypeScript code. No explanations, no markdown fences.']),
|
|
218
|
+
].join('\n');
|
|
219
|
+
}
|
|
220
|
+
export function parseGenerationResponse(text, expectedPath, mode, flowId, profile) {
|
|
221
|
+
let code = text.trim();
|
|
222
|
+
const fenced = code.match(/^```(?:typescript|ts)?\s*([\s\S]*?)```\s*$/i);
|
|
223
|
+
if (fenced) {
|
|
224
|
+
code = fenced[1].trim();
|
|
225
|
+
}
|
|
226
|
+
if (!code.includes('test(') && !code.includes('it(') && !code.includes('describe(')) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
const importStatement = profile?.importStatement || '@playwright/test';
|
|
230
|
+
// Auto-add import if missing
|
|
231
|
+
if (!code.includes(importStatement)) {
|
|
232
|
+
if (profile?.testMode === 'api') {
|
|
233
|
+
code = `import {describe, it, expect} from '${importStatement}';\n\n${code}`;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
code = `import {expect, test} from '${importStatement}';\n\n${code}`;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return { specPath: expectedPath, code, mode, flowId };
|
|
240
|
+
}
|
|
241
|
+
const BUILT_IN_METHODS = new Set([
|
|
242
|
+
'click', 'fill', 'focus', 'hover', 'press', 'type', 'check', 'uncheck',
|
|
243
|
+
'selectOption', 'waitFor', 'waitForLoadState', 'evaluate', 'dispatchEvent',
|
|
244
|
+
'getAttribute', 'textContent', 'innerText', 'innerHTML',
|
|
245
|
+
'isVisible', 'isEnabled', 'isChecked', 'isHidden',
|
|
246
|
+
'toBeVisible', 'toBeEnabled', 'toBeChecked', 'toBeHidden',
|
|
247
|
+
'toContainText', 'toHaveText', 'toHaveURL', 'toHaveValue',
|
|
248
|
+
'not', 'nth', 'first', 'last', 'all',
|
|
249
|
+
'getByRole', 'getByText', 'getByLabel', 'getByPlaceholder',
|
|
250
|
+
'getByTestId', 'getByTitle', 'getByAltText',
|
|
251
|
+
'locator', 'frame', 'page', 'expect',
|
|
252
|
+
'goBack', 'goForward', 'reload', 'goto',
|
|
253
|
+
'keyboard', 'mouse', 'touchscreen', 'close', 'bringToFront',
|
|
254
|
+
'initSetup', 'login', 'waitUntil', 'skipIfNoLicense', 'ensureLicense',
|
|
255
|
+
'random', 'duration', 'isOutsideRemoteUserHour', 'setTimeout',
|
|
256
|
+
'skip', 'fixme', 'slow', 'fail',
|
|
257
|
+
// API testing built-ins
|
|
258
|
+
'get', 'post', 'put', 'patch', 'delete', 'send', 'set', 'query',
|
|
259
|
+
'toBe', 'toEqual', 'toBeDefined', 'toContain', 'toHaveProperty',
|
|
260
|
+
'toMatchObject', 'toHaveLength', 'toBeTruthy', 'toBeFalsy',
|
|
261
|
+
]);
|
|
262
|
+
/**
|
|
263
|
+
* Returns method names that appear in generated code but do not exist in the API surface.
|
|
264
|
+
* Detects all call patterns: await X.Y(), X.Y(), const z = X.Y(), chained calls.
|
|
265
|
+
*/
|
|
266
|
+
export function detectHallucinatedMethods(code, apiSurface) {
|
|
267
|
+
const allMethods = new Set(apiSurface.pageObjects.flatMap((po) => po.methods.map((m) => m.name)));
|
|
268
|
+
const suspected = new Set();
|
|
269
|
+
const callPatterns = [
|
|
270
|
+
/\bawait\s+\w+\.([a-zA-Z_]\w*)\s*\(/g,
|
|
271
|
+
/\b(?:const|let|var)\s+\w+\s*=\s*\w+\.([a-zA-Z_]\w*)\s*\(/g,
|
|
272
|
+
/\b\w+Page\.([a-zA-Z_]\w*)\s*\(/g, // any *Page object (channelsPage, settingsPage, etc.)
|
|
273
|
+
/\b(?:pw|page|this)\.\w*\.?([a-zA-Z_]\w*)\s*\(/g,
|
|
274
|
+
];
|
|
275
|
+
const generalCallRe = /\b[a-zA-Z_]\w*\.([a-zA-Z_]\w*)\s*\(/g;
|
|
276
|
+
for (const re of callPatterns) {
|
|
277
|
+
let match;
|
|
278
|
+
while ((match = re.exec(code)) !== null) {
|
|
279
|
+
const methodName = match[1];
|
|
280
|
+
if (!BUILT_IN_METHODS.has(methodName) && !allMethods.has(methodName) && methodName.length > 3) {
|
|
281
|
+
suspected.add(methodName);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
let match;
|
|
286
|
+
while ((match = generalCallRe.exec(code)) !== null) {
|
|
287
|
+
const methodName = match[1];
|
|
288
|
+
if (!BUILT_IN_METHODS.has(methodName) &&
|
|
289
|
+
!allMethods.has(methodName) &&
|
|
290
|
+
methodName.length > 3 &&
|
|
291
|
+
!methodName.startsWith('to') &&
|
|
292
|
+
!methodName.startsWith('get')) {
|
|
293
|
+
suspected.add(methodName);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return [...suspected];
|
|
297
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { UI_FRAMEWORKS, API_FRAMEWORKS } from '../adapters/framework_adapter.js';
|
|
4
|
+
const MATTERMOST_PROFILE = {
|
|
5
|
+
projectName: 'Mattermost',
|
|
6
|
+
testFramework: 'Playwright',
|
|
7
|
+
importStatement: '@mattermost/playwright-lib',
|
|
8
|
+
conventions: [
|
|
9
|
+
'Import ONLY from "@mattermost/playwright-lib" — no other test framework imports.',
|
|
10
|
+
'Every test must call `await pw.initSetup()` first.',
|
|
11
|
+
'Use `await pw.testBrowser.login(user)` to log in — never hardcode credentials.',
|
|
12
|
+
'Use `expect` from "@mattermost/playwright-lib" — do NOT import from "@playwright/test".',
|
|
13
|
+
'Include the copyright header for new files.',
|
|
14
|
+
],
|
|
15
|
+
copyrightHeader: [
|
|
16
|
+
'// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.',
|
|
17
|
+
'// See LICENSE.txt for license information.',
|
|
18
|
+
].join('\n'),
|
|
19
|
+
testMode: 'ui',
|
|
20
|
+
};
|
|
21
|
+
const DEFAULT_PLAYWRIGHT_PROFILE = {
|
|
22
|
+
projectName: 'Project',
|
|
23
|
+
testFramework: 'Playwright',
|
|
24
|
+
importStatement: '@playwright/test',
|
|
25
|
+
conventions: [
|
|
26
|
+
'Import from "@playwright/test" for test and expect.',
|
|
27
|
+
'Use page fixtures provided by Playwright.',
|
|
28
|
+
'Prefer ARIA roles and data-testid attributes for selectors.',
|
|
29
|
+
'Write one test per scenario with a descriptive name.',
|
|
30
|
+
],
|
|
31
|
+
testMode: 'ui',
|
|
32
|
+
};
|
|
33
|
+
const DEFAULT_API_PROFILE = {
|
|
34
|
+
projectName: 'Project',
|
|
35
|
+
testFramework: 'vitest + supertest',
|
|
36
|
+
importStatement: 'vitest',
|
|
37
|
+
conventions: [
|
|
38
|
+
'Import from "vitest" for test and expect.',
|
|
39
|
+
'Use supertest for HTTP request assertions.',
|
|
40
|
+
'Validate response status codes, headers, and body structure.',
|
|
41
|
+
'Test both success and error paths for each endpoint.',
|
|
42
|
+
],
|
|
43
|
+
testMode: 'api',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Resolves the generation profile from config and optional KG metadata.
|
|
47
|
+
* - If profile='mattermost' or Mattermost is detected, returns Mattermost profile.
|
|
48
|
+
* - If KG is present, derives project-specific profile from it.
|
|
49
|
+
* - Otherwise, returns generic Playwright profile.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveGenerationProfile(config, kg) {
|
|
52
|
+
// Explicit Mattermost profile
|
|
53
|
+
if (config?.profile === 'mattermost') {
|
|
54
|
+
return { ...MATTERMOST_PROFILE };
|
|
55
|
+
}
|
|
56
|
+
// KG-based profile derivation
|
|
57
|
+
if (kg) {
|
|
58
|
+
const frameworks = kg.project.frameworks.map((f) => f.toLowerCase());
|
|
59
|
+
const isMattermost = kg.project.name.toLowerCase().includes('mattermost') ||
|
|
60
|
+
frameworks.includes('@mattermost/playwright-lib');
|
|
61
|
+
if (isMattermost) {
|
|
62
|
+
return { ...MATTERMOST_PROFILE };
|
|
63
|
+
}
|
|
64
|
+
const testMode = config?.testMode || deriveTestMode(frameworks);
|
|
65
|
+
const testFramework = deriveTestFramework(frameworks, testMode);
|
|
66
|
+
const importStatement = deriveImportStatement(frameworks, testMode);
|
|
67
|
+
return {
|
|
68
|
+
projectName: kg.project.name || 'Project',
|
|
69
|
+
testFramework,
|
|
70
|
+
importStatement,
|
|
71
|
+
conventions: buildConventions(testFramework, importStatement, testMode),
|
|
72
|
+
testMode,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Default profiles based on test mode
|
|
76
|
+
if (config?.testMode === 'api') {
|
|
77
|
+
return { ...DEFAULT_API_PROFILE };
|
|
78
|
+
}
|
|
79
|
+
return { ...DEFAULT_PLAYWRIGHT_PROFILE };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checks if a profile is the Mattermost profile (for backward compatibility checks).
|
|
83
|
+
*/
|
|
84
|
+
export function isMattermostProfile(profile) {
|
|
85
|
+
return profile.importStatement === '@mattermost/playwright-lib';
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Internal helpers
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
function deriveTestMode(frameworks) {
|
|
91
|
+
const uiSet = new Set(UI_FRAMEWORKS);
|
|
92
|
+
const apiSet = new Set(API_FRAMEWORKS);
|
|
93
|
+
const hasUiFramework = frameworks.some((f) => uiSet.has(f));
|
|
94
|
+
const hasApiFramework = frameworks.some((f) => apiSet.has(f));
|
|
95
|
+
if (hasUiFramework && hasApiFramework)
|
|
96
|
+
return 'both';
|
|
97
|
+
if (hasApiFramework && !hasUiFramework)
|
|
98
|
+
return 'api';
|
|
99
|
+
return 'ui';
|
|
100
|
+
}
|
|
101
|
+
function deriveTestFramework(frameworks, testMode) {
|
|
102
|
+
if (testMode === 'api') {
|
|
103
|
+
if (frameworks.includes('pytest'))
|
|
104
|
+
return 'pytest';
|
|
105
|
+
if (frameworks.includes('jest'))
|
|
106
|
+
return 'jest + supertest';
|
|
107
|
+
return 'vitest + supertest';
|
|
108
|
+
}
|
|
109
|
+
if (frameworks.includes('cypress'))
|
|
110
|
+
return 'Cypress';
|
|
111
|
+
if (frameworks.includes('selenium'))
|
|
112
|
+
return 'Selenium';
|
|
113
|
+
return 'Playwright';
|
|
114
|
+
}
|
|
115
|
+
function deriveImportStatement(frameworks, testMode) {
|
|
116
|
+
if (testMode === 'api') {
|
|
117
|
+
if (frameworks.includes('pytest'))
|
|
118
|
+
return 'pytest';
|
|
119
|
+
if (frameworks.includes('jest'))
|
|
120
|
+
return 'jest';
|
|
121
|
+
return 'vitest';
|
|
122
|
+
}
|
|
123
|
+
if (frameworks.includes('cypress'))
|
|
124
|
+
return 'cypress';
|
|
125
|
+
return '@playwright/test';
|
|
126
|
+
}
|
|
127
|
+
function buildConventions(testFramework, importStatement, testMode) {
|
|
128
|
+
const conventions = [];
|
|
129
|
+
if (testMode === 'api' || testMode === 'both') {
|
|
130
|
+
conventions.push(`Import from "${importStatement}" for test and expect.`);
|
|
131
|
+
conventions.push('Validate response status codes, headers, and body structure.');
|
|
132
|
+
conventions.push('Test both success and error paths for each endpoint.');
|
|
133
|
+
}
|
|
134
|
+
if (testMode === 'ui' || testMode === 'both') {
|
|
135
|
+
if (testFramework.includes('Playwright')) {
|
|
136
|
+
conventions.push('Import from "@playwright/test" for test and expect.');
|
|
137
|
+
conventions.push('Use page fixtures provided by Playwright.');
|
|
138
|
+
}
|
|
139
|
+
else if (testFramework.includes('Cypress')) {
|
|
140
|
+
conventions.push('Use cy.* commands for browser interaction.');
|
|
141
|
+
}
|
|
142
|
+
conventions.push('Prefer ARIA roles and data-testid attributes for selectors.');
|
|
143
|
+
}
|
|
144
|
+
conventions.push('Write one test per scenario with a descriptive name of what the user does and what is verified.');
|
|
145
|
+
conventions.push('NEVER fabricate test IDs. Use descriptive names only.');
|
|
146
|
+
return conventions;
|
|
147
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { sanitizeForPrompt } from '../crew/sanitize.js';
|
|
4
|
+
import { isMattermostProfile } from './generation_profile.js';
|
|
5
|
+
/**
|
|
6
|
+
* Builds a route-family-aware heal prompt for the playwright-test-healer agent.
|
|
7
|
+
* Enriches the base healer constraints with flow context so the agent understands
|
|
8
|
+
* what the test is supposed to verify, reducing hallucination during selector repair.
|
|
9
|
+
*/
|
|
10
|
+
export function buildHealPrompt(ctx) {
|
|
11
|
+
const flowBlock = ctx.decision
|
|
12
|
+
? [
|
|
13
|
+
'',
|
|
14
|
+
'FLOW CONTEXT (use to understand test intent — do not change test objectives):',
|
|
15
|
+
` Flow: ${sanitizeForPrompt(ctx.decision.flowName)}`,
|
|
16
|
+
` Route Family: ${ctx.decision.routeFamily}${ctx.decision.featureId ? ` / ${ctx.decision.featureId}` : ''}`,
|
|
17
|
+
` Route: ${ctx.decision.specificRoute || '(family-level)'}`,
|
|
18
|
+
` User Actions: ${sanitizeForPrompt(ctx.decision.userActions.join('; ')) || 'not specified'}`,
|
|
19
|
+
` Evidence: ${sanitizeForPrompt(ctx.decision.evidence)}`,
|
|
20
|
+
].join('\n')
|
|
21
|
+
: '';
|
|
22
|
+
const statusNote = ctx.status === 'flaky'
|
|
23
|
+
? 'This test is FLAKY (passes sometimes, fails other times). Look for race conditions, missing waits, or order-dependent state.'
|
|
24
|
+
: 'This test is FAILING consistently. The selector, URL, or API call is likely broken.';
|
|
25
|
+
const failureBlock = ctx.failureDetail
|
|
26
|
+
? `\nFailure detail:\n${sanitizeForPrompt(ctx.failureDetail)}`
|
|
27
|
+
: '';
|
|
28
|
+
const consoleBlock = ctx.consoleErrors && ctx.consoleErrors.length > 0
|
|
29
|
+
? `\nRecent console errors from test run:\n${ctx.consoleErrors.slice(-3).map((e) => ` - ${sanitizeForPrompt(e)}`).join('\n')}`
|
|
30
|
+
: '';
|
|
31
|
+
const importLib = ctx.profile?.importStatement || '@playwright/test';
|
|
32
|
+
const isMM = ctx.profile ? isMattermostProfile(ctx.profile) : false;
|
|
33
|
+
const projectLabel = ctx.profile?.projectName || 'Project';
|
|
34
|
+
const frameworkLabel = ctx.profile?.testFramework || 'Playwright';
|
|
35
|
+
const constraints = isMM
|
|
36
|
+
? [
|
|
37
|
+
`- Import ONLY from "${importLib}". Do not use "@playwright/test" directly.`,
|
|
38
|
+
'- Do not use test.describe or test.only.',
|
|
39
|
+
'- Keep a single tag string matching the route family (e.g. "@channels", "@scheduled_posts").',
|
|
40
|
+
`- Use only existing ${projectLabel} ${frameworkLabel} fixture and page-object APIs.`,
|
|
41
|
+
'- Do NOT invent new pw.* clients or page object methods that do not exist.',
|
|
42
|
+
'- Avoid brittle class selectors (.backstage-navbar, .admin-console__wrapper, .left-panel, .panel-card).',
|
|
43
|
+
]
|
|
44
|
+
: [
|
|
45
|
+
`- Import from "${importLib}".`,
|
|
46
|
+
'- Keep a single tag string matching the route family.',
|
|
47
|
+
`- Use only existing ${projectLabel} ${frameworkLabel} page-object APIs.`,
|
|
48
|
+
'- Do NOT invent page object methods that do not exist.',
|
|
49
|
+
];
|
|
50
|
+
return [
|
|
51
|
+
`Heal this specific ${frameworkLabel} test file and keep edits minimal.`,
|
|
52
|
+
'',
|
|
53
|
+
`Target test file: ${ctx.specPath}`,
|
|
54
|
+
`Status: ${ctx.status.toUpperCase()} — ${statusNote}`,
|
|
55
|
+
failureBlock,
|
|
56
|
+
consoleBlock,
|
|
57
|
+
flowBlock,
|
|
58
|
+
'',
|
|
59
|
+
'Healing constraints (must follow):',
|
|
60
|
+
...constraints,
|
|
61
|
+
'- Prefer stable assertions using URL patterns, data-testid attributes, ARIA roles, and page-object methods.',
|
|
62
|
+
'- For flaky tests: add explicit waits (waitFor, expect().toBeVisible()) before interactions.',
|
|
63
|
+
'- Keep the test intent and scenario unchanged — only fix what is broken.',
|
|
64
|
+
'- If behavior is genuinely broken server-side, mark test.fixme with a clear comment explaining why.',
|
|
65
|
+
'',
|
|
66
|
+
'Run and fix this test until it compiles and passes, or mark test.fixme when the behavior is truly broken.',
|
|
67
|
+
].filter((line) => line !== null).join('\n');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Builds a minimal quality-fix prompt for spec files that fail content validation
|
|
71
|
+
* (e.g. contain test.describe, test.only, wrong imports).
|
|
72
|
+
*/
|
|
73
|
+
export function buildQualityFixPrompt(specPath, qualityIssues, profile) {
|
|
74
|
+
const importLib = profile?.importStatement || '@playwright/test';
|
|
75
|
+
const frameworkLabel = profile?.testFramework || 'Playwright';
|
|
76
|
+
return [
|
|
77
|
+
`Fix quality issues in this ${frameworkLabel} spec file. Make minimal edits only.`,
|
|
78
|
+
'',
|
|
79
|
+
`Target file: ${specPath}`,
|
|
80
|
+
'',
|
|
81
|
+
'Issues to fix:',
|
|
82
|
+
...qualityIssues.map((issue) => ` - ${issue}`),
|
|
83
|
+
'',
|
|
84
|
+
'Rules:',
|
|
85
|
+
`- Import only from "${importLib}".`,
|
|
86
|
+
'- Remove test.describe wrappers (flatten to top-level test() calls).',
|
|
87
|
+
'- Remove test.only calls.',
|
|
88
|
+
'- Ensure each test has exactly one tag string.',
|
|
89
|
+
'- Do not change test logic — only fix structural quality issues.',
|
|
90
|
+
].join('\n');
|
|
91
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
import { extractJsonFromResponse } from './json_extract.js';
|
|
4
|
+
import { formatSpecsForPrompt } from '../knowledge/spec_index.js';
|
|
5
|
+
import { formatApiSurfaceForPrompt } from '../knowledge/api_surface.js';
|
|
6
|
+
export function buildImpactPrompt(ctx) {
|
|
7
|
+
const familyRoutes = ctx.family.routes.join(', ');
|
|
8
|
+
const featureNote = ctx.featureId ? `\nFEATURE: ${ctx.featureId}` : '';
|
|
9
|
+
const pageObjectNames = ctx.family.pageObjects || [];
|
|
10
|
+
const componentNames = ctx.family.components || [];
|
|
11
|
+
const allClassNames = [...pageObjectNames, ...componentNames];
|
|
12
|
+
const apiSurfaceBlock = allClassNames.length > 0
|
|
13
|
+
? formatApiSurfaceForPrompt(ctx.apiSurface, allClassNames)
|
|
14
|
+
: 'No page objects or components mapped for this family.';
|
|
15
|
+
const specsBlock = ctx.existingSpecs.length > 0
|
|
16
|
+
? formatSpecsForPrompt(ctx.existingSpecs)
|
|
17
|
+
: 'No existing specs found for this route family.';
|
|
18
|
+
const changedFilesBlock = ctx.changedFiles
|
|
19
|
+
.map((f) => {
|
|
20
|
+
if (f.snippet) {
|
|
21
|
+
return `${f.path}:\n\`\`\`\n${f.snippet}\n\`\`\``;
|
|
22
|
+
}
|
|
23
|
+
return f.path;
|
|
24
|
+
})
|
|
25
|
+
.join('\n\n');
|
|
26
|
+
return [
|
|
27
|
+
`You are analyzing code changes in ${ctx.projectName || 'the project'} to identify impacted user-facing flows.`,
|
|
28
|
+
'',
|
|
29
|
+
`ROUTE FAMILY: ${ctx.family.id}`,
|
|
30
|
+
`ROUTES: ${familyRoutes}`,
|
|
31
|
+
featureNote,
|
|
32
|
+
'',
|
|
33
|
+
`PAGE OBJECTS AND COMPONENTS:`,
|
|
34
|
+
apiSurfaceBlock,
|
|
35
|
+
'',
|
|
36
|
+
`EXISTING SPECS FOR THIS FAMILY (${ctx.existingSpecs.length}):`,
|
|
37
|
+
specsBlock,
|
|
38
|
+
'',
|
|
39
|
+
`CHANGED FILES (${ctx.changedFiles.length}):`,
|
|
40
|
+
changedFilesBlock,
|
|
41
|
+
'',
|
|
42
|
+
ctx.contextBlock,
|
|
43
|
+
'',
|
|
44
|
+
'For each changed file, identify impacted user-facing flows.',
|
|
45
|
+
'',
|
|
46
|
+
'Return strict JSON only with this shape:',
|
|
47
|
+
'{"flows":[{"id":"<flow_id>","name":"<human readable name>","route":"<specific route from ROUTES>","userActions":["<what the user does>"],"priority":"P0|P1|P2","confidence":0-100,"evidence":"<why this flow is impacted>","pageObjects":["<page object used>"],"changedFiles":["<files>"]}]}',
|
|
48
|
+
'',
|
|
49
|
+
'Rules:',
|
|
50
|
+
'- ONLY use routes listed in ROUTES above.',
|
|
51
|
+
'- ONLY reference page objects and components listed above.',
|
|
52
|
+
'- Each flow must describe a specific user action, not a generic category.',
|
|
53
|
+
'- If you cannot determine the impacted flow with high confidence, return:',
|
|
54
|
+
' {"id":"unknown","name":"cannot determine","confidence":0,"evidence":"<reason>","userActions":[],"changedFiles":["<files>"]}',
|
|
55
|
+
'- Do NOT default to /admin_console/reporting/system_analytics unless the changed files are literally analytics code.',
|
|
56
|
+
'- Do NOT invent routes, page objects, or methods that are not listed above.',
|
|
57
|
+
'- Keep at most 8 flows.',
|
|
58
|
+
'- Prioritize true user-impacting flows; avoid low-value internal buckets.',
|
|
59
|
+
].filter(Boolean).join('\n');
|
|
60
|
+
}
|
|
61
|
+
export function parseImpactResponse(text) {
|
|
62
|
+
return extractJsonFromResponse(text, (obj) => obj != null && typeof obj === 'object' && Array.isArray(obj.flows));
|
|
63
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
/**
|
|
4
|
+
* Shared JSON extraction from LLM text responses.
|
|
5
|
+
* Handles fenced code blocks, bare JSON, and partial text.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Extract and parse JSON from LLM response text.
|
|
9
|
+
* Tries fenced code blocks first, then raw text.
|
|
10
|
+
* Returns null if no valid JSON found.
|
|
11
|
+
*
|
|
12
|
+
* @param text - Raw LLM response text
|
|
13
|
+
* @param validate - Predicate to check if parsed object has the expected shape
|
|
14
|
+
*/
|
|
15
|
+
export function extractJsonFromResponse(text, validate) {
|
|
16
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
17
|
+
const candidates = fenced ? [fenced[1], text] : [text];
|
|
18
|
+
for (const candidate of candidates) {
|
|
19
|
+
const start = candidate.indexOf('{');
|
|
20
|
+
const end = candidate.lastIndexOf('}');
|
|
21
|
+
if (start < 0 || end <= start) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const raw = candidate.slice(start, end + 1);
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(raw);
|
|
27
|
+
if (validate(parsed)) {
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|