agentic-qe 3.3.0 → 3.3.1
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/README.md +6 -2
- package/package.json +6 -1
- package/v3/CHANGELOG.md +88 -0
- package/v3/dist/cli/bundle.js +18794 -15534
- package/v3/dist/cli/command-registry.d.ts +55 -0
- package/v3/dist/cli/command-registry.d.ts.map +1 -0
- package/v3/dist/cli/command-registry.js +103 -0
- package/v3/dist/cli/command-registry.js.map +1 -0
- package/v3/dist/cli/commands/code.d.ts +9 -0
- package/v3/dist/cli/commands/code.d.ts.map +1 -0
- package/v3/dist/cli/commands/code.js +254 -0
- package/v3/dist/cli/commands/code.js.map +1 -0
- package/v3/dist/cli/commands/completions.d.ts +8 -0
- package/v3/dist/cli/commands/completions.d.ts.map +1 -0
- package/v3/dist/cli/commands/completions.js +99 -0
- package/v3/dist/cli/commands/completions.js.map +1 -0
- package/v3/dist/cli/commands/coverage.d.ts +9 -0
- package/v3/dist/cli/commands/coverage.d.ts.map +1 -0
- package/v3/dist/cli/commands/coverage.js +208 -0
- package/v3/dist/cli/commands/coverage.js.map +1 -0
- package/v3/dist/cli/commands/fleet.d.ts +11 -0
- package/v3/dist/cli/commands/fleet.d.ts.map +1 -0
- package/v3/dist/cli/commands/fleet.js +338 -0
- package/v3/dist/cli/commands/fleet.js.map +1 -0
- package/v3/dist/cli/commands/migrate.d.ts +9 -0
- package/v3/dist/cli/commands/migrate.d.ts.map +1 -0
- package/v3/dist/cli/commands/migrate.js +566 -0
- package/v3/dist/cli/commands/migrate.js.map +1 -0
- package/v3/dist/cli/commands/quality.d.ts +9 -0
- package/v3/dist/cli/commands/quality.d.ts.map +1 -0
- package/v3/dist/cli/commands/quality.js +40 -0
- package/v3/dist/cli/commands/quality.js.map +1 -0
- package/v3/dist/cli/commands/security.d.ts +9 -0
- package/v3/dist/cli/commands/security.d.ts.map +1 -0
- package/v3/dist/cli/commands/security.js +124 -0
- package/v3/dist/cli/commands/security.js.map +1 -0
- package/v3/dist/cli/commands/sync.d.ts +19 -0
- package/v3/dist/cli/commands/sync.d.ts.map +1 -0
- package/v3/dist/cli/commands/sync.js +283 -0
- package/v3/dist/cli/commands/sync.js.map +1 -0
- package/v3/dist/cli/commands/test.d.ts +9 -0
- package/v3/dist/cli/commands/test.d.ts.map +1 -0
- package/v3/dist/cli/commands/test.js +166 -0
- package/v3/dist/cli/commands/test.js.map +1 -0
- package/v3/dist/cli/handlers/agent-handler.d.ts +20 -0
- package/v3/dist/cli/handlers/agent-handler.d.ts.map +1 -0
- package/v3/dist/cli/handlers/agent-handler.js +158 -0
- package/v3/dist/cli/handlers/agent-handler.js.map +1 -0
- package/v3/dist/cli/handlers/domain-handler.d.ts +20 -0
- package/v3/dist/cli/handlers/domain-handler.d.ts.map +1 -0
- package/v3/dist/cli/handlers/domain-handler.js +115 -0
- package/v3/dist/cli/handlers/domain-handler.js.map +1 -0
- package/v3/dist/cli/handlers/index.d.ts +13 -0
- package/v3/dist/cli/handlers/index.d.ts.map +1 -0
- package/v3/dist/cli/handlers/index.js +15 -0
- package/v3/dist/cli/handlers/index.js.map +1 -0
- package/v3/dist/cli/handlers/init-handler.d.ts +38 -0
- package/v3/dist/cli/handlers/init-handler.d.ts.map +1 -0
- package/v3/dist/cli/handlers/init-handler.js +288 -0
- package/v3/dist/cli/handlers/init-handler.js.map +1 -0
- package/v3/dist/cli/handlers/interfaces.d.ts +104 -0
- package/v3/dist/cli/handlers/interfaces.d.ts.map +1 -0
- package/v3/dist/cli/handlers/interfaces.js +109 -0
- package/v3/dist/cli/handlers/interfaces.js.map +1 -0
- package/v3/dist/cli/handlers/protocol-handler.d.ts +19 -0
- package/v3/dist/cli/handlers/protocol-handler.d.ts.map +1 -0
- package/v3/dist/cli/handlers/protocol-handler.js +79 -0
- package/v3/dist/cli/handlers/protocol-handler.js.map +1 -0
- package/v3/dist/cli/handlers/status-handler.d.ts +30 -0
- package/v3/dist/cli/handlers/status-handler.d.ts.map +1 -0
- package/v3/dist/cli/handlers/status-handler.js +218 -0
- package/v3/dist/cli/handlers/status-handler.js.map +1 -0
- package/v3/dist/cli/handlers/task-handler.d.ts +22 -0
- package/v3/dist/cli/handlers/task-handler.d.ts.map +1 -0
- package/v3/dist/cli/handlers/task-handler.js +271 -0
- package/v3/dist/cli/handlers/task-handler.js.map +1 -0
- package/v3/dist/cli/index.d.ts +4 -0
- package/v3/dist/cli/index.d.ts.map +1 -1
- package/v3/dist/cli/index.js +48 -2677
- package/v3/dist/cli/index.js.map +1 -1
- package/v3/dist/cli/wizards/core/index.d.ts +11 -0
- package/v3/dist/cli/wizards/core/index.d.ts.map +1 -0
- package/v3/dist/cli/wizards/core/index.js +15 -0
- package/v3/dist/cli/wizards/core/index.js.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-base.d.ts +87 -0
- package/v3/dist/cli/wizards/core/wizard-base.d.ts.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-base.js +120 -0
- package/v3/dist/cli/wizards/core/wizard-base.js.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-command.d.ts +182 -0
- package/v3/dist/cli/wizards/core/wizard-command.d.ts.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-command.js +45 -0
- package/v3/dist/cli/wizards/core/wizard-command.js.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-step.d.ts +109 -0
- package/v3/dist/cli/wizards/core/wizard-step.d.ts.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-step.js +384 -0
- package/v3/dist/cli/wizards/core/wizard-step.js.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-utils.d.ts +117 -0
- package/v3/dist/cli/wizards/core/wizard-utils.d.ts.map +1 -0
- package/v3/dist/cli/wizards/core/wizard-utils.js +291 -0
- package/v3/dist/cli/wizards/core/wizard-utils.js.map +1 -0
- package/v3/dist/cli/wizards/coverage-wizard.d.ts +13 -68
- package/v3/dist/cli/wizards/coverage-wizard.d.ts.map +1 -1
- package/v3/dist/cli/wizards/coverage-wizard.js +127 -391
- package/v3/dist/cli/wizards/coverage-wizard.js.map +1 -1
- package/v3/dist/cli/wizards/fleet-wizard.d.ts +13 -64
- package/v3/dist/cli/wizards/fleet-wizard.d.ts.map +1 -1
- package/v3/dist/cli/wizards/fleet-wizard.js +150 -363
- package/v3/dist/cli/wizards/fleet-wizard.js.map +1 -1
- package/v3/dist/cli/wizards/index.d.ts +2 -0
- package/v3/dist/cli/wizards/index.d.ts.map +1 -1
- package/v3/dist/cli/wizards/index.js +3 -0
- package/v3/dist/cli/wizards/index.js.map +1 -1
- package/v3/dist/cli/wizards/security-wizard.d.ts +13 -64
- package/v3/dist/cli/wizards/security-wizard.d.ts.map +1 -1
- package/v3/dist/cli/wizards/security-wizard.js +152 -395
- package/v3/dist/cli/wizards/security-wizard.js.map +1 -1
- package/v3/dist/cli/wizards/test-wizard.d.ts +13 -77
- package/v3/dist/cli/wizards/test-wizard.d.ts.map +1 -1
- package/v3/dist/cli/wizards/test-wizard.js +196 -328
- package/v3/dist/cli/wizards/test-wizard.js.map +1 -1
- package/v3/dist/coordination/mincut/mincut-health-monitor.d.ts +3 -0
- package/v3/dist/coordination/mincut/mincut-health-monitor.d.ts.map +1 -1
- package/v3/dist/coordination/mincut/mincut-health-monitor.js +6 -2
- package/v3/dist/coordination/mincut/mincut-health-monitor.js.map +1 -1
- package/v3/dist/coordination/mincut/queen-integration.d.ts +3 -0
- package/v3/dist/coordination/mincut/queen-integration.d.ts.map +1 -1
- package/v3/dist/coordination/mincut/queen-integration.js +6 -1
- package/v3/dist/coordination/mincut/queen-integration.js.map +1 -1
- package/v3/dist/coordination/task-executor.js +2 -2
- package/v3/dist/coordination/task-executor.js.map +1 -1
- package/v3/dist/domains/chaos-resilience/plugin.js +2 -2
- package/v3/dist/domains/chaos-resilience/plugin.js.map +1 -1
- package/v3/dist/domains/code-intelligence/plugin.js +2 -2
- package/v3/dist/domains/code-intelligence/plugin.js.map +1 -1
- package/v3/dist/domains/contract-testing/plugin.js +2 -2
- package/v3/dist/domains/contract-testing/plugin.js.map +1 -1
- package/v3/dist/domains/coverage-analysis/plugin.d.ts.map +1 -1
- package/v3/dist/domains/coverage-analysis/plugin.js +2 -1
- package/v3/dist/domains/coverage-analysis/plugin.js.map +1 -1
- package/v3/dist/domains/defect-intelligence/plugin.js +2 -2
- package/v3/dist/domains/defect-intelligence/plugin.js.map +1 -1
- package/v3/dist/domains/domain-interface.d.ts.map +1 -1
- package/v3/dist/domains/domain-interface.js +3 -1
- package/v3/dist/domains/domain-interface.js.map +1 -1
- package/v3/dist/domains/learning-optimization/plugin.js +2 -2
- package/v3/dist/domains/learning-optimization/plugin.js.map +1 -1
- package/v3/dist/domains/quality-assessment/plugin.js +2 -2
- package/v3/dist/domains/quality-assessment/plugin.js.map +1 -1
- package/v3/dist/domains/requirements-validation/plugin.js +2 -2
- package/v3/dist/domains/requirements-validation/plugin.js.map +1 -1
- package/v3/dist/domains/security-compliance/plugin.js +2 -2
- package/v3/dist/domains/security-compliance/plugin.js.map +1 -1
- package/v3/dist/domains/test-execution/index.d.ts +2 -1
- package/v3/dist/domains/test-execution/index.d.ts.map +1 -1
- package/v3/dist/domains/test-execution/index.js +0 -2
- package/v3/dist/domains/test-execution/index.js.map +1 -1
- package/v3/dist/domains/test-execution/interfaces.d.ts +222 -25
- package/v3/dist/domains/test-execution/interfaces.d.ts.map +1 -1
- package/v3/dist/domains/test-execution/interfaces.js +130 -3
- package/v3/dist/domains/test-execution/interfaces.js.map +1 -1
- package/v3/dist/domains/test-execution/plugin.d.ts.map +1 -1
- package/v3/dist/domains/test-execution/plugin.js +2 -1
- package/v3/dist/domains/test-execution/plugin.js.map +1 -1
- package/v3/dist/domains/test-execution/test-prioritization-types.d.ts +5 -172
- package/v3/dist/domains/test-execution/test-prioritization-types.d.ts.map +1 -1
- package/v3/dist/domains/test-execution/test-prioritization-types.js +6 -129
- package/v3/dist/domains/test-execution/test-prioritization-types.js.map +1 -1
- package/v3/dist/domains/test-execution/types/index.d.ts +7 -3
- package/v3/dist/domains/test-execution/types/index.d.ts.map +1 -1
- package/v3/dist/domains/test-execution/types/index.js +7 -17
- package/v3/dist/domains/test-execution/types/index.js.map +1 -1
- package/v3/dist/domains/test-generation/coordinator.d.ts +1 -1
- package/v3/dist/domains/test-generation/coordinator.d.ts.map +1 -1
- package/v3/dist/domains/test-generation/coordinator.js +3 -3
- package/v3/dist/domains/test-generation/coordinator.js.map +1 -1
- package/v3/dist/domains/test-generation/factories/index.d.ts +8 -0
- package/v3/dist/domains/test-generation/factories/index.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/factories/index.js +8 -0
- package/v3/dist/domains/test-generation/factories/index.js.map +1 -0
- package/v3/dist/domains/test-generation/factories/test-generator-factory.d.ts +108 -0
- package/v3/dist/domains/test-generation/factories/test-generator-factory.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/factories/test-generator-factory.js +158 -0
- package/v3/dist/domains/test-generation/factories/test-generator-factory.js.map +1 -0
- package/v3/dist/domains/test-generation/generators/base-test-generator.d.ts +79 -0
- package/v3/dist/domains/test-generation/generators/base-test-generator.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/generators/base-test-generator.js +252 -0
- package/v3/dist/domains/test-generation/generators/base-test-generator.js.map +1 -0
- package/v3/dist/domains/test-generation/generators/index.d.ts +11 -0
- package/v3/dist/domains/test-generation/generators/index.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/generators/index.js +13 -0
- package/v3/dist/domains/test-generation/generators/index.js.map +1 -0
- package/v3/dist/domains/test-generation/generators/jest-vitest-generator.d.ts +77 -0
- package/v3/dist/domains/test-generation/generators/jest-vitest-generator.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/generators/jest-vitest-generator.js +365 -0
- package/v3/dist/domains/test-generation/generators/jest-vitest-generator.js.map +1 -0
- package/v3/dist/domains/test-generation/generators/mocha-generator.d.ts +56 -0
- package/v3/dist/domains/test-generation/generators/mocha-generator.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/generators/mocha-generator.js +197 -0
- package/v3/dist/domains/test-generation/generators/mocha-generator.js.map +1 -0
- package/v3/dist/domains/test-generation/generators/pytest-generator.d.ts +66 -0
- package/v3/dist/domains/test-generation/generators/pytest-generator.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/generators/pytest-generator.js +240 -0
- package/v3/dist/domains/test-generation/generators/pytest-generator.js.map +1 -0
- package/v3/dist/domains/test-generation/index.d.ts +2 -2
- package/v3/dist/domains/test-generation/index.d.ts.map +1 -1
- package/v3/dist/domains/test-generation/index.js +3 -3
- package/v3/dist/domains/test-generation/index.js.map +1 -1
- package/v3/dist/domains/test-generation/interfaces/index.d.ts +9 -0
- package/v3/dist/domains/test-generation/interfaces/index.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/interfaces/index.js +9 -0
- package/v3/dist/domains/test-generation/interfaces/index.js.map +1 -0
- package/v3/dist/domains/test-generation/interfaces/test-generator.interface.d.ts +166 -0
- package/v3/dist/domains/test-generation/interfaces/test-generator.interface.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/interfaces/test-generator.interface.js +8 -0
- package/v3/dist/domains/test-generation/interfaces/test-generator.interface.js.map +1 -0
- package/v3/dist/domains/test-generation/interfaces.d.ts +163 -24
- package/v3/dist/domains/test-generation/interfaces.d.ts.map +1 -1
- package/v3/dist/domains/test-generation/interfaces.js +2 -2
- package/v3/dist/domains/test-generation/plugin.d.ts.map +1 -1
- package/v3/dist/domains/test-generation/plugin.js +6 -5
- package/v3/dist/domains/test-generation/plugin.js.map +1 -1
- package/v3/dist/domains/test-generation/{coherence-gate.d.ts → services/coherence-gate-service.d.ts} +4 -4
- package/v3/dist/domains/test-generation/services/coherence-gate-service.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/{coherence-gate.js → services/coherence-gate-service.js} +2 -2
- package/v3/dist/domains/test-generation/services/coherence-gate-service.js.map +1 -0
- package/v3/dist/domains/test-generation/services/index.d.ts +8 -2
- package/v3/dist/domains/test-generation/services/index.d.ts.map +1 -1
- package/v3/dist/domains/test-generation/services/index.js +10 -3
- package/v3/dist/domains/test-generation/services/index.js.map +1 -1
- package/v3/dist/domains/test-generation/services/property-test-generator.d.ts +34 -0
- package/v3/dist/domains/test-generation/services/property-test-generator.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/services/property-test-generator.js +306 -0
- package/v3/dist/domains/test-generation/services/property-test-generator.js.map +1 -0
- package/v3/dist/domains/test-generation/services/tdd-generator.d.ts +33 -0
- package/v3/dist/domains/test-generation/services/tdd-generator.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/services/tdd-generator.js +342 -0
- package/v3/dist/domains/test-generation/services/tdd-generator.js.map +1 -0
- package/v3/dist/domains/test-generation/services/test-data-generator.d.ts +34 -0
- package/v3/dist/domains/test-generation/services/test-data-generator.d.ts.map +1 -0
- package/v3/dist/domains/test-generation/services/test-data-generator.js +245 -0
- package/v3/dist/domains/test-generation/services/test-data-generator.js.map +1 -0
- package/v3/dist/domains/test-generation/services/test-generator.d.ts +51 -160
- package/v3/dist/domains/test-generation/services/test-generator.d.ts.map +1 -1
- package/v3/dist/domains/test-generation/services/test-generator.js +101 -1858
- package/v3/dist/domains/test-generation/services/test-generator.js.map +1 -1
- package/v3/dist/domains/visual-accessibility/plugin.js +2 -2
- package/v3/dist/domains/visual-accessibility/plugin.js.map +1 -1
- package/v3/dist/init/phases/12-verification.d.ts +23 -0
- package/v3/dist/init/phases/12-verification.d.ts.map +1 -1
- package/v3/dist/init/phases/12-verification.js +185 -2
- package/v3/dist/init/phases/12-verification.js.map +1 -1
- package/v3/dist/integrations/agentic-flow/model-router/complexity-analyzer.d.ts +24 -62
- package/v3/dist/integrations/agentic-flow/model-router/complexity-analyzer.d.ts.map +1 -1
- package/v3/dist/integrations/agentic-flow/model-router/complexity-analyzer.js +45 -497
- package/v3/dist/integrations/agentic-flow/model-router/complexity-analyzer.js.map +1 -1
- package/v3/dist/integrations/agentic-flow/model-router/router.js +2 -2
- package/v3/dist/integrations/agentic-flow/model-router/router.js.map +1 -1
- package/v3/dist/integrations/agentic-flow/model-router/score-calculator.d.ts +98 -0
- package/v3/dist/integrations/agentic-flow/model-router/score-calculator.d.ts.map +1 -0
- package/v3/dist/integrations/agentic-flow/model-router/score-calculator.js +197 -0
- package/v3/dist/integrations/agentic-flow/model-router/score-calculator.js.map +1 -0
- package/v3/dist/integrations/agentic-flow/model-router/signal-collector.d.ts +102 -0
- package/v3/dist/integrations/agentic-flow/model-router/signal-collector.d.ts.map +1 -0
- package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js +372 -0
- package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js.map +1 -0
- package/v3/dist/integrations/agentic-flow/model-router/tier-recommender.d.ts +64 -0
- package/v3/dist/integrations/agentic-flow/model-router/tier-recommender.d.ts.map +1 -0
- package/v3/dist/integrations/agentic-flow/model-router/tier-recommender.js +120 -0
- package/v3/dist/integrations/agentic-flow/model-router/tier-recommender.js.map +1 -0
- package/v3/dist/integrations/coherence/coherence-service.d.ts.map +1 -1
- package/v3/dist/integrations/coherence/coherence-service.js +87 -30
- package/v3/dist/integrations/coherence/coherence-service.js.map +1 -1
- package/v3/dist/integrations/coherence/engines/spectral-adapter.d.ts.map +1 -1
- package/v3/dist/integrations/coherence/engines/spectral-adapter.js +124 -35
- package/v3/dist/integrations/coherence/engines/spectral-adapter.js.map +1 -1
- package/v3/dist/learning/memory-auditor.d.ts.map +1 -1
- package/v3/dist/learning/memory-auditor.js +3 -1
- package/v3/dist/learning/memory-auditor.js.map +1 -1
- package/v3/dist/mcp/bundle.js +2475 -1463
- package/v3/dist/mcp/security/cve-prevention.d.ts +31 -134
- package/v3/dist/mcp/security/cve-prevention.d.ts.map +1 -1
- package/v3/dist/mcp/security/cve-prevention.js +37 -562
- package/v3/dist/mcp/security/cve-prevention.js.map +1 -1
- package/v3/dist/mcp/security/index.d.ts +5 -1
- package/v3/dist/mcp/security/index.d.ts.map +1 -1
- package/v3/dist/mcp/security/validators/command-validator.d.ts +41 -0
- package/v3/dist/mcp/security/validators/command-validator.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/command-validator.js +123 -0
- package/v3/dist/mcp/security/validators/command-validator.js.map +1 -0
- package/v3/dist/mcp/security/validators/crypto-validator.d.ts +40 -0
- package/v3/dist/mcp/security/validators/crypto-validator.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/crypto-validator.js +72 -0
- package/v3/dist/mcp/security/validators/crypto-validator.js.map +1 -0
- package/v3/dist/mcp/security/validators/index.d.ts +12 -0
- package/v3/dist/mcp/security/validators/index.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/index.js +22 -0
- package/v3/dist/mcp/security/validators/index.js.map +1 -0
- package/v3/dist/mcp/security/validators/input-sanitizer.d.ts +56 -0
- package/v3/dist/mcp/security/validators/input-sanitizer.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/input-sanitizer.js +157 -0
- package/v3/dist/mcp/security/validators/input-sanitizer.js.map +1 -0
- package/v3/dist/mcp/security/validators/interfaces.d.ts +164 -0
- package/v3/dist/mcp/security/validators/interfaces.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/interfaces.js +6 -0
- package/v3/dist/mcp/security/validators/interfaces.js.map +1 -0
- package/v3/dist/mcp/security/validators/path-traversal-validator.d.ts +50 -0
- package/v3/dist/mcp/security/validators/path-traversal-validator.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/path-traversal-validator.js +242 -0
- package/v3/dist/mcp/security/validators/path-traversal-validator.js.map +1 -0
- package/v3/dist/mcp/security/validators/regex-safety-validator.d.ts +50 -0
- package/v3/dist/mcp/security/validators/regex-safety-validator.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/regex-safety-validator.js +183 -0
- package/v3/dist/mcp/security/validators/regex-safety-validator.js.map +1 -0
- package/v3/dist/mcp/security/validators/validation-orchestrator.d.ts +66 -0
- package/v3/dist/mcp/security/validators/validation-orchestrator.d.ts.map +1 -0
- package/v3/dist/mcp/security/validators/validation-orchestrator.js +146 -0
- package/v3/dist/mcp/security/validators/validation-orchestrator.js.map +1 -0
- package/v3/dist/mcp/server.d.ts.map +1 -1
- package/v3/dist/mcp/server.js +1 -0
- package/v3/dist/mcp/server.js.map +1 -1
- package/v3/dist/mcp/tool-registry.d.ts +3 -1
- package/v3/dist/mcp/tool-registry.d.ts.map +1 -1
- package/v3/dist/mcp/tool-registry.js +155 -2
- package/v3/dist/mcp/tool-registry.js.map +1 -1
- package/v3/dist/mcp/tools/test-generation/generate.d.ts +1 -0
- package/v3/dist/mcp/tools/test-generation/generate.d.ts.map +1 -1
- package/v3/dist/mcp/tools/test-generation/generate.js +3 -2
- package/v3/dist/mcp/tools/test-generation/generate.js.map +1 -1
- package/v3/dist/sync/cloud/index.d.ts +8 -0
- package/v3/dist/sync/cloud/index.d.ts.map +1 -0
- package/v3/dist/sync/cloud/index.js +8 -0
- package/v3/dist/sync/cloud/index.js.map +1 -0
- package/v3/dist/sync/cloud/postgres-writer.d.ts +88 -0
- package/v3/dist/sync/cloud/postgres-writer.d.ts.map +1 -0
- package/v3/dist/sync/cloud/postgres-writer.js +319 -0
- package/v3/dist/sync/cloud/postgres-writer.js.map +1 -0
- package/v3/dist/sync/cloud/tunnel-manager.d.ts +75 -0
- package/v3/dist/sync/cloud/tunnel-manager.d.ts.map +1 -0
- package/v3/dist/sync/cloud/tunnel-manager.js +221 -0
- package/v3/dist/sync/cloud/tunnel-manager.js.map +1 -0
- package/v3/dist/sync/index.d.ts +35 -0
- package/v3/dist/sync/index.d.ts.map +1 -0
- package/v3/dist/sync/index.js +35 -0
- package/v3/dist/sync/index.js.map +1 -0
- package/v3/dist/sync/interfaces.d.ts +245 -0
- package/v3/dist/sync/interfaces.d.ts.map +1 -0
- package/v3/dist/sync/interfaces.js +160 -0
- package/v3/dist/sync/interfaces.js.map +1 -0
- package/v3/dist/sync/readers/index.d.ts +8 -0
- package/v3/dist/sync/readers/index.d.ts.map +1 -0
- package/v3/dist/sync/readers/index.js +8 -0
- package/v3/dist/sync/readers/index.js.map +1 -0
- package/v3/dist/sync/readers/json-reader.d.ts +95 -0
- package/v3/dist/sync/readers/json-reader.d.ts.map +1 -0
- package/v3/dist/sync/readers/json-reader.js +306 -0
- package/v3/dist/sync/readers/json-reader.js.map +1 -0
- package/v3/dist/sync/readers/sqlite-reader.d.ts +88 -0
- package/v3/dist/sync/readers/sqlite-reader.d.ts.map +1 -0
- package/v3/dist/sync/readers/sqlite-reader.js +255 -0
- package/v3/dist/sync/readers/sqlite-reader.js.map +1 -0
- package/v3/dist/sync/sync-agent.d.ts +116 -0
- package/v3/dist/sync/sync-agent.d.ts.map +1 -0
- package/v3/dist/sync/sync-agent.js +416 -0
- package/v3/dist/sync/sync-agent.js.map +1 -0
- package/v3/package.json +13 -2
- package/v3/dist/domains/test-generation/coherence-gate.d.ts.map +0 -1
- package/v3/dist/domains/test-generation/coherence-gate.js.map +0 -1
package/v3/dist/mcp/bundle.js
CHANGED
|
@@ -7279,12 +7279,11 @@ var init_types3 = __esm({
|
|
|
7279
7279
|
}
|
|
7280
7280
|
});
|
|
7281
7281
|
|
|
7282
|
-
// src/integrations/agentic-flow/model-router/
|
|
7283
|
-
var COMPLEXITY_KEYWORDS, SCOPE_PATTERNS,
|
|
7284
|
-
var
|
|
7285
|
-
"src/integrations/agentic-flow/model-router/
|
|
7282
|
+
// src/integrations/agentic-flow/model-router/signal-collector.ts
|
|
7283
|
+
var COMPLEXITY_KEYWORDS, SCOPE_PATTERNS, SignalCollector;
|
|
7284
|
+
var init_signal_collector = __esm({
|
|
7285
|
+
"src/integrations/agentic-flow/model-router/signal-collector.ts"() {
|
|
7286
7286
|
"use strict";
|
|
7287
|
-
init_types3();
|
|
7288
7287
|
init_types2();
|
|
7289
7288
|
COMPLEXITY_KEYWORDS = {
|
|
7290
7289
|
// Tier 0 - Mechanical transforms
|
|
@@ -7348,7 +7347,7 @@ var init_complexity_analyzer = __esm({
|
|
|
7348
7347
|
multiStep: /\b(orchestrate|coordinate|workflow|pipeline|multi[- ]step)\b/i,
|
|
7349
7348
|
crossDomain: /\b(cross[- ]domain|across (domains|modules)|integrate|coordination)\b/i
|
|
7350
7349
|
};
|
|
7351
|
-
|
|
7350
|
+
SignalCollector = class {
|
|
7352
7351
|
config;
|
|
7353
7352
|
agentBoosterAdapter;
|
|
7354
7353
|
constructor(config, agentBoosterAdapter) {
|
|
@@ -7356,46 +7355,44 @@ var init_complexity_analyzer = __esm({
|
|
|
7356
7355
|
this.agentBoosterAdapter = agentBoosterAdapter;
|
|
7357
7356
|
}
|
|
7358
7357
|
/**
|
|
7359
|
-
*
|
|
7358
|
+
* Collect all complexity signals from input
|
|
7360
7359
|
*/
|
|
7361
|
-
async
|
|
7362
|
-
const
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
);
|
|
7398
|
-
}
|
|
7360
|
+
async collectSignals(input) {
|
|
7361
|
+
const taskLower = input.task.toLowerCase();
|
|
7362
|
+
const agentBoosterCheck = await this.checkAgentBoosterEligibility(input);
|
|
7363
|
+
const keywordMatches = {
|
|
7364
|
+
simple: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.simple),
|
|
7365
|
+
moderate: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.moderate),
|
|
7366
|
+
complex: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.complex),
|
|
7367
|
+
critical: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.critical)
|
|
7368
|
+
};
|
|
7369
|
+
const linesOfCode = input.codeContext ? input.codeContext.split("\n").length : void 0;
|
|
7370
|
+
const fileCount = input.filePaths ? input.filePaths.length : void 0;
|
|
7371
|
+
const hasArchitectureScope = SCOPE_PATTERNS.architecture.test(taskLower);
|
|
7372
|
+
const hasSecurityScope = SCOPE_PATTERNS.security.test(taskLower);
|
|
7373
|
+
const requiresMultiStepReasoning = SCOPE_PATTERNS.multiStep.test(taskLower);
|
|
7374
|
+
const requiresCrossDomainCoordination = SCOPE_PATTERNS.crossDomain.test(taskLower);
|
|
7375
|
+
const requiresCreativity = this.detectCreativityRequirement(taskLower);
|
|
7376
|
+
const languageComplexity = this.estimateLanguageComplexity(
|
|
7377
|
+
input.codeContext,
|
|
7378
|
+
input.filePaths
|
|
7379
|
+
);
|
|
7380
|
+
const cyclomaticComplexity = input.codeContext ? this.estimateCyclomaticComplexity(input.codeContext) : void 0;
|
|
7381
|
+
return {
|
|
7382
|
+
linesOfCode,
|
|
7383
|
+
fileCount,
|
|
7384
|
+
hasArchitectureScope,
|
|
7385
|
+
hasSecurityScope,
|
|
7386
|
+
requiresMultiStepReasoning,
|
|
7387
|
+
requiresCrossDomainCoordination,
|
|
7388
|
+
isMechanicalTransform: agentBoosterCheck.eligible,
|
|
7389
|
+
languageComplexity,
|
|
7390
|
+
cyclomaticComplexity,
|
|
7391
|
+
dependencyCount: this.countDependencies(input.codeContext),
|
|
7392
|
+
requiresCreativity,
|
|
7393
|
+
detectedTransformType: agentBoosterCheck.transformType,
|
|
7394
|
+
keywordMatches
|
|
7395
|
+
};
|
|
7399
7396
|
}
|
|
7400
7397
|
/**
|
|
7401
7398
|
* Check if task is eligible for Agent Booster (Tier 0)
|
|
@@ -7416,7 +7413,12 @@ var init_complexity_analyzer = __esm({
|
|
|
7416
7413
|
let confidence = 0;
|
|
7417
7414
|
for (const keyword of keywords) {
|
|
7418
7415
|
if (taskLower.includes(keyword.toLowerCase())) {
|
|
7419
|
-
confidence
|
|
7416
|
+
confidence = confidence === 0 ? 0.5 : confidence + 0.25;
|
|
7417
|
+
}
|
|
7418
|
+
}
|
|
7419
|
+
for (const mechanicalKeyword of COMPLEXITY_KEYWORDS.mechanical) {
|
|
7420
|
+
if (taskLower.includes(mechanicalKeyword.toLowerCase())) {
|
|
7421
|
+
confidence = Math.max(confidence, 0.6);
|
|
7420
7422
|
}
|
|
7421
7423
|
}
|
|
7422
7424
|
if (confidence > maxConfidence) {
|
|
@@ -7451,143 +7453,8 @@ var init_complexity_analyzer = __esm({
|
|
|
7451
7453
|
reason: eligible ? `Detected ${detectedTransformType} transform pattern` : "No mechanical transform pattern detected"
|
|
7452
7454
|
};
|
|
7453
7455
|
}
|
|
7454
|
-
/**
|
|
7455
|
-
* Get recommended tier based on complexity score
|
|
7456
|
-
*/
|
|
7457
|
-
getRecommendedTier(complexity) {
|
|
7458
|
-
for (const tier of [0, 1, 2, 3, 4]) {
|
|
7459
|
-
const [min, max] = TIER_METADATA[tier].complexityRange;
|
|
7460
|
-
if (complexity >= min && complexity <= max) {
|
|
7461
|
-
return tier;
|
|
7462
|
-
}
|
|
7463
|
-
}
|
|
7464
|
-
return 2;
|
|
7465
|
-
}
|
|
7466
|
-
// ============================================================================
|
|
7467
|
-
// Private: Signal Collection
|
|
7468
|
-
// ============================================================================
|
|
7469
|
-
/**
|
|
7470
|
-
* Collect all complexity signals from input
|
|
7471
|
-
*/
|
|
7472
|
-
async collectSignals(input) {
|
|
7473
|
-
const taskLower = input.task.toLowerCase();
|
|
7474
|
-
const codeLower = input.codeContext?.toLowerCase() || "";
|
|
7475
|
-
const agentBoosterCheck = await this.checkAgentBoosterEligibility(input);
|
|
7476
|
-
const keywordMatches = {
|
|
7477
|
-
simple: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.simple),
|
|
7478
|
-
moderate: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.moderate),
|
|
7479
|
-
complex: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.complex),
|
|
7480
|
-
critical: this.findKeywordMatches(taskLower, COMPLEXITY_KEYWORDS.critical)
|
|
7481
|
-
};
|
|
7482
|
-
const linesOfCode = input.codeContext ? input.codeContext.split("\n").length : void 0;
|
|
7483
|
-
const fileCount = input.filePaths ? input.filePaths.length : void 0;
|
|
7484
|
-
const hasArchitectureScope = SCOPE_PATTERNS.architecture.test(taskLower);
|
|
7485
|
-
const hasSecurityScope = SCOPE_PATTERNS.security.test(taskLower);
|
|
7486
|
-
const requiresMultiStepReasoning = SCOPE_PATTERNS.multiStep.test(taskLower);
|
|
7487
|
-
const requiresCrossDomainCoordination = SCOPE_PATTERNS.crossDomain.test(taskLower);
|
|
7488
|
-
const requiresCreativity = taskLower.includes("design") || taskLower.includes("creative") || taskLower.includes("innovative") || taskLower.includes("novel");
|
|
7489
|
-
const languageComplexity = this.estimateLanguageComplexity(
|
|
7490
|
-
input.codeContext,
|
|
7491
|
-
input.filePaths
|
|
7492
|
-
);
|
|
7493
|
-
const cyclomaticComplexity = input.codeContext ? this.estimateCyclomaticComplexity(input.codeContext) : void 0;
|
|
7494
|
-
return {
|
|
7495
|
-
linesOfCode,
|
|
7496
|
-
fileCount,
|
|
7497
|
-
hasArchitectureScope,
|
|
7498
|
-
hasSecurityScope,
|
|
7499
|
-
requiresMultiStepReasoning,
|
|
7500
|
-
requiresCrossDomainCoordination,
|
|
7501
|
-
isMechanicalTransform: agentBoosterCheck.eligible,
|
|
7502
|
-
languageComplexity,
|
|
7503
|
-
cyclomaticComplexity,
|
|
7504
|
-
dependencyCount: this.countDependencies(input.codeContext),
|
|
7505
|
-
requiresCreativity,
|
|
7506
|
-
detectedTransformType: agentBoosterCheck.transformType,
|
|
7507
|
-
keywordMatches
|
|
7508
|
-
};
|
|
7509
|
-
}
|
|
7510
|
-
// ============================================================================
|
|
7511
|
-
// Private: Complexity Calculation
|
|
7512
|
-
// ============================================================================
|
|
7513
|
-
/**
|
|
7514
|
-
* Calculate code complexity component (0-100)
|
|
7515
|
-
*/
|
|
7516
|
-
calculateCodeComplexity(signals) {
|
|
7517
|
-
let score = 0;
|
|
7518
|
-
if (signals.linesOfCode !== void 0) {
|
|
7519
|
-
if (signals.linesOfCode < 10) score += 0;
|
|
7520
|
-
else if (signals.linesOfCode < 50) score += 10;
|
|
7521
|
-
else if (signals.linesOfCode < 200) score += 20;
|
|
7522
|
-
else score += 30;
|
|
7523
|
-
}
|
|
7524
|
-
if (signals.fileCount !== void 0) {
|
|
7525
|
-
if (signals.fileCount === 1) score += 0;
|
|
7526
|
-
else if (signals.fileCount < 5) score += 10;
|
|
7527
|
-
else score += 20;
|
|
7528
|
-
}
|
|
7529
|
-
if (signals.cyclomaticComplexity !== void 0) {
|
|
7530
|
-
if (signals.cyclomaticComplexity < 5) score += 0;
|
|
7531
|
-
else if (signals.cyclomaticComplexity < 10) score += 10;
|
|
7532
|
-
else if (signals.cyclomaticComplexity < 20) score += 20;
|
|
7533
|
-
else score += 30;
|
|
7534
|
-
}
|
|
7535
|
-
if (signals.languageComplexity === "low") score += 0;
|
|
7536
|
-
else if (signals.languageComplexity === "medium") score += 10;
|
|
7537
|
-
else if (signals.languageComplexity === "high") score += 20;
|
|
7538
|
-
return Math.min(score, 100);
|
|
7539
|
-
}
|
|
7540
|
-
/**
|
|
7541
|
-
* Calculate reasoning complexity component (0-100)
|
|
7542
|
-
*/
|
|
7543
|
-
calculateReasoningComplexity(signals) {
|
|
7544
|
-
let score = 0;
|
|
7545
|
-
const keywordScore = signals.keywordMatches.simple.length * 5 + signals.keywordMatches.moderate.length * 15 + signals.keywordMatches.complex.length * 25 + signals.keywordMatches.critical.length * 35;
|
|
7546
|
-
score += Math.min(keywordScore, 60);
|
|
7547
|
-
if (signals.requiresMultiStepReasoning) score += 20;
|
|
7548
|
-
if (signals.requiresCreativity) score += 20;
|
|
7549
|
-
return Math.min(score, 100);
|
|
7550
|
-
}
|
|
7551
|
-
/**
|
|
7552
|
-
* Calculate scope complexity component (0-100)
|
|
7553
|
-
*/
|
|
7554
|
-
calculateScopeComplexity(signals) {
|
|
7555
|
-
let score = 0;
|
|
7556
|
-
if (signals.hasArchitectureScope) score += 40;
|
|
7557
|
-
if (signals.hasSecurityScope) score += 30;
|
|
7558
|
-
if (signals.requiresCrossDomainCoordination) score += 20;
|
|
7559
|
-
if (signals.dependencyCount !== void 0) {
|
|
7560
|
-
if (signals.dependencyCount < 3) score += 0;
|
|
7561
|
-
else if (signals.dependencyCount < 10) score += 5;
|
|
7562
|
-
else score += 10;
|
|
7563
|
-
}
|
|
7564
|
-
return Math.min(score, 100);
|
|
7565
|
-
}
|
|
7566
|
-
/**
|
|
7567
|
-
* Calculate overall complexity score (0-100)
|
|
7568
|
-
*/
|
|
7569
|
-
calculateOverallComplexity(codeComplexity, reasoningComplexity, scopeComplexity, signals) {
|
|
7570
|
-
if (signals.isMechanicalTransform) {
|
|
7571
|
-
return 5;
|
|
7572
|
-
}
|
|
7573
|
-
const weighted = codeComplexity * 0.3 + reasoningComplexity * 0.4 + scopeComplexity * 0.3;
|
|
7574
|
-
return Math.min(Math.round(weighted), 100);
|
|
7575
|
-
}
|
|
7576
|
-
/**
|
|
7577
|
-
* Calculate confidence in complexity assessment (0-1)
|
|
7578
|
-
*/
|
|
7579
|
-
calculateConfidence(signals, input) {
|
|
7580
|
-
let confidence = 0.5;
|
|
7581
|
-
if (input.codeContext) confidence += 0.2;
|
|
7582
|
-
if (input.filePaths && input.filePaths.length > 0) confidence += 0.1;
|
|
7583
|
-
const totalKeywords = signals.keywordMatches.simple.length + signals.keywordMatches.moderate.length + signals.keywordMatches.complex.length + signals.keywordMatches.critical.length;
|
|
7584
|
-
if (totalKeywords >= 3) confidence += 0.1;
|
|
7585
|
-
else if (totalKeywords >= 1) confidence += 0.05;
|
|
7586
|
-
if (signals.isMechanicalTransform) confidence += 0.15;
|
|
7587
|
-
return Math.min(confidence, 1);
|
|
7588
|
-
}
|
|
7589
7456
|
// ============================================================================
|
|
7590
|
-
// Private
|
|
7457
|
+
// Private Helper Methods
|
|
7591
7458
|
// ============================================================================
|
|
7592
7459
|
/**
|
|
7593
7460
|
* Find matching keywords in text
|
|
@@ -7601,6 +7468,12 @@ var init_complexity_analyzer = __esm({
|
|
|
7601
7468
|
}
|
|
7602
7469
|
return matches;
|
|
7603
7470
|
}
|
|
7471
|
+
/**
|
|
7472
|
+
* Detect creativity requirements from task text
|
|
7473
|
+
*/
|
|
7474
|
+
detectCreativityRequirement(taskLower) {
|
|
7475
|
+
return taskLower.includes("design") || taskLower.includes("creative") || taskLower.includes("innovative") || taskLower.includes("novel");
|
|
7476
|
+
}
|
|
7604
7477
|
/**
|
|
7605
7478
|
* Estimate language complexity from code context
|
|
7606
7479
|
*/
|
|
@@ -7668,23 +7541,205 @@ var init_complexity_analyzer = __esm({
|
|
|
7668
7541
|
*/
|
|
7669
7542
|
getTransformKeywords(transformType) {
|
|
7670
7543
|
const keywordMap = {
|
|
7671
|
-
"var-to-const": [
|
|
7672
|
-
|
|
7673
|
-
|
|
7544
|
+
"var-to-const": [
|
|
7545
|
+
"var to const",
|
|
7546
|
+
"convert var",
|
|
7547
|
+
"var declaration",
|
|
7548
|
+
"var to let",
|
|
7549
|
+
"convert var to const",
|
|
7550
|
+
// Added explicit full phrase
|
|
7551
|
+
"change var to const"
|
|
7552
|
+
],
|
|
7553
|
+
"add-types": [
|
|
7554
|
+
"add types",
|
|
7555
|
+
"add type",
|
|
7556
|
+
"typescript types",
|
|
7557
|
+
"type annotations",
|
|
7558
|
+
"type annotation",
|
|
7559
|
+
"add type annotations"
|
|
7560
|
+
],
|
|
7561
|
+
"remove-console": [
|
|
7562
|
+
"remove console",
|
|
7563
|
+
"delete console",
|
|
7564
|
+
"console.log",
|
|
7565
|
+
"remove console.log",
|
|
7566
|
+
"strip console"
|
|
7567
|
+
],
|
|
7674
7568
|
"promise-to-async": [
|
|
7675
7569
|
"promise to async",
|
|
7676
7570
|
"async await",
|
|
7677
|
-
".then to async"
|
|
7571
|
+
".then to async",
|
|
7572
|
+
"convert to async",
|
|
7573
|
+
"convert function to async"
|
|
7678
7574
|
],
|
|
7679
7575
|
"cjs-to-esm": [
|
|
7680
7576
|
"commonjs to esm",
|
|
7681
7577
|
"require to import",
|
|
7682
|
-
"convert to esm"
|
|
7578
|
+
"convert to esm",
|
|
7579
|
+
"cjs to esm",
|
|
7580
|
+
"module conversion"
|
|
7683
7581
|
],
|
|
7684
|
-
"func-to-arrow": [
|
|
7582
|
+
"func-to-arrow": [
|
|
7583
|
+
"arrow function",
|
|
7584
|
+
"function to arrow",
|
|
7585
|
+
"convert to arrow",
|
|
7586
|
+
"convert function to arrow"
|
|
7587
|
+
]
|
|
7685
7588
|
};
|
|
7686
7589
|
return keywordMap[transformType] || [];
|
|
7687
7590
|
}
|
|
7591
|
+
};
|
|
7592
|
+
}
|
|
7593
|
+
});
|
|
7594
|
+
|
|
7595
|
+
// src/integrations/agentic-flow/model-router/score-calculator.ts
|
|
7596
|
+
var ScoreCalculator;
|
|
7597
|
+
var init_score_calculator = __esm({
|
|
7598
|
+
"src/integrations/agentic-flow/model-router/score-calculator.ts"() {
|
|
7599
|
+
"use strict";
|
|
7600
|
+
ScoreCalculator = class {
|
|
7601
|
+
/**
|
|
7602
|
+
* Calculate code complexity component (0-100)
|
|
7603
|
+
*/
|
|
7604
|
+
calculateCodeComplexity(signals) {
|
|
7605
|
+
let score = 0;
|
|
7606
|
+
score += this.calculateLinesOfCodeContribution(signals.linesOfCode);
|
|
7607
|
+
score += this.calculateFileCountContribution(signals.fileCount);
|
|
7608
|
+
score += this.calculateCyclomaticContribution(signals.cyclomaticComplexity);
|
|
7609
|
+
score += this.calculateLanguageContribution(signals.languageComplexity);
|
|
7610
|
+
return Math.min(score, 100);
|
|
7611
|
+
}
|
|
7612
|
+
/**
|
|
7613
|
+
* Calculate reasoning complexity component (0-100)
|
|
7614
|
+
*/
|
|
7615
|
+
calculateReasoningComplexity(signals) {
|
|
7616
|
+
let score = 0;
|
|
7617
|
+
score += this.calculateKeywordScore(signals.keywordMatches);
|
|
7618
|
+
if (signals.requiresMultiStepReasoning) score += 20;
|
|
7619
|
+
if (signals.requiresCreativity) score += 20;
|
|
7620
|
+
return Math.min(score, 100);
|
|
7621
|
+
}
|
|
7622
|
+
/**
|
|
7623
|
+
* Calculate scope complexity component (0-100)
|
|
7624
|
+
*/
|
|
7625
|
+
calculateScopeComplexity(signals) {
|
|
7626
|
+
let score = 0;
|
|
7627
|
+
if (signals.hasArchitectureScope) score += 40;
|
|
7628
|
+
if (signals.hasSecurityScope) score += 30;
|
|
7629
|
+
if (signals.requiresCrossDomainCoordination) score += 20;
|
|
7630
|
+
score += this.calculateDependencyContribution(signals.dependencyCount);
|
|
7631
|
+
return Math.min(score, 100);
|
|
7632
|
+
}
|
|
7633
|
+
/**
|
|
7634
|
+
* Calculate overall complexity score (0-100)
|
|
7635
|
+
*/
|
|
7636
|
+
calculateOverallComplexity(codeComplexity, reasoningComplexity, scopeComplexity, signals) {
|
|
7637
|
+
if (signals.isMechanicalTransform) {
|
|
7638
|
+
return 5;
|
|
7639
|
+
}
|
|
7640
|
+
const weighted = codeComplexity * 0.3 + reasoningComplexity * 0.4 + scopeComplexity * 0.3;
|
|
7641
|
+
return Math.min(Math.round(weighted), 100);
|
|
7642
|
+
}
|
|
7643
|
+
/**
|
|
7644
|
+
* Calculate confidence in complexity assessment (0-1)
|
|
7645
|
+
*/
|
|
7646
|
+
calculateConfidence(signals, input) {
|
|
7647
|
+
let confidence = 0.5;
|
|
7648
|
+
if (input.codeContext) confidence += 0.2;
|
|
7649
|
+
if (input.filePaths && input.filePaths.length > 0) confidence += 0.1;
|
|
7650
|
+
confidence += this.calculateKeywordConfidenceBoost(signals.keywordMatches);
|
|
7651
|
+
if (signals.isMechanicalTransform) confidence += 0.15;
|
|
7652
|
+
return Math.min(confidence, 1);
|
|
7653
|
+
}
|
|
7654
|
+
// ============================================================================
|
|
7655
|
+
// Private Helper Methods
|
|
7656
|
+
// ============================================================================
|
|
7657
|
+
/**
|
|
7658
|
+
* Calculate lines of code contribution (0-30 points)
|
|
7659
|
+
*/
|
|
7660
|
+
calculateLinesOfCodeContribution(linesOfCode) {
|
|
7661
|
+
if (linesOfCode === void 0) return 0;
|
|
7662
|
+
if (linesOfCode < 10) return 0;
|
|
7663
|
+
if (linesOfCode < 50) return 10;
|
|
7664
|
+
if (linesOfCode < 200) return 20;
|
|
7665
|
+
return 30;
|
|
7666
|
+
}
|
|
7667
|
+
/**
|
|
7668
|
+
* Calculate file count contribution (0-20 points)
|
|
7669
|
+
*/
|
|
7670
|
+
calculateFileCountContribution(fileCount) {
|
|
7671
|
+
if (fileCount === void 0) return 0;
|
|
7672
|
+
if (fileCount === 1) return 0;
|
|
7673
|
+
if (fileCount < 5) return 10;
|
|
7674
|
+
return 20;
|
|
7675
|
+
}
|
|
7676
|
+
/**
|
|
7677
|
+
* Calculate cyclomatic complexity contribution (0-30 points)
|
|
7678
|
+
*/
|
|
7679
|
+
calculateCyclomaticContribution(cyclomaticComplexity) {
|
|
7680
|
+
if (cyclomaticComplexity === void 0) return 0;
|
|
7681
|
+
if (cyclomaticComplexity < 5) return 0;
|
|
7682
|
+
if (cyclomaticComplexity < 10) return 10;
|
|
7683
|
+
if (cyclomaticComplexity < 20) return 20;
|
|
7684
|
+
return 30;
|
|
7685
|
+
}
|
|
7686
|
+
/**
|
|
7687
|
+
* Calculate language complexity contribution (0-20 points)
|
|
7688
|
+
*/
|
|
7689
|
+
calculateLanguageContribution(languageComplexity) {
|
|
7690
|
+
if (languageComplexity === "low") return 0;
|
|
7691
|
+
if (languageComplexity === "medium") return 10;
|
|
7692
|
+
if (languageComplexity === "high") return 20;
|
|
7693
|
+
return 0;
|
|
7694
|
+
}
|
|
7695
|
+
/**
|
|
7696
|
+
* Calculate keyword score (0-60 points)
|
|
7697
|
+
*/
|
|
7698
|
+
calculateKeywordScore(keywordMatches) {
|
|
7699
|
+
const keywordScore = keywordMatches.simple.length * 5 + keywordMatches.moderate.length * 15 + keywordMatches.complex.length * 25 + keywordMatches.critical.length * 35;
|
|
7700
|
+
return Math.min(keywordScore, 60);
|
|
7701
|
+
}
|
|
7702
|
+
/**
|
|
7703
|
+
* Calculate dependency count contribution (0-10 points)
|
|
7704
|
+
*/
|
|
7705
|
+
calculateDependencyContribution(dependencyCount) {
|
|
7706
|
+
if (dependencyCount === void 0) return 0;
|
|
7707
|
+
if (dependencyCount < 3) return 0;
|
|
7708
|
+
if (dependencyCount < 10) return 5;
|
|
7709
|
+
return 10;
|
|
7710
|
+
}
|
|
7711
|
+
/**
|
|
7712
|
+
* Calculate confidence boost from keyword matches (0-0.1)
|
|
7713
|
+
*/
|
|
7714
|
+
calculateKeywordConfidenceBoost(keywordMatches) {
|
|
7715
|
+
const totalKeywords = keywordMatches.simple.length + keywordMatches.moderate.length + keywordMatches.complex.length + keywordMatches.critical.length;
|
|
7716
|
+
if (totalKeywords >= 3) return 0.1;
|
|
7717
|
+
if (totalKeywords >= 1) return 0.05;
|
|
7718
|
+
return 0;
|
|
7719
|
+
}
|
|
7720
|
+
};
|
|
7721
|
+
}
|
|
7722
|
+
});
|
|
7723
|
+
|
|
7724
|
+
// src/integrations/agentic-flow/model-router/tier-recommender.ts
|
|
7725
|
+
var TierRecommender;
|
|
7726
|
+
var init_tier_recommender = __esm({
|
|
7727
|
+
"src/integrations/agentic-flow/model-router/tier-recommender.ts"() {
|
|
7728
|
+
"use strict";
|
|
7729
|
+
init_types3();
|
|
7730
|
+
TierRecommender = class {
|
|
7731
|
+
/**
|
|
7732
|
+
* Get recommended tier based on complexity score
|
|
7733
|
+
*/
|
|
7734
|
+
getRecommendedTier(complexity) {
|
|
7735
|
+
for (const tier of [0, 1, 2, 3, 4]) {
|
|
7736
|
+
const [min, max] = TIER_METADATA[tier].complexityRange;
|
|
7737
|
+
if (complexity >= min && complexity <= max) {
|
|
7738
|
+
return tier;
|
|
7739
|
+
}
|
|
7740
|
+
}
|
|
7741
|
+
return 2;
|
|
7742
|
+
}
|
|
7688
7743
|
/**
|
|
7689
7744
|
* Find alternative tiers that could handle this task
|
|
7690
7745
|
*/
|
|
@@ -7708,29 +7763,143 @@ var init_complexity_analyzer = __esm({
|
|
|
7708
7763
|
const parts = [];
|
|
7709
7764
|
parts.push(`Complexity score: ${overall}/100 (Tier ${tier})`);
|
|
7710
7765
|
if (signals.isMechanicalTransform) {
|
|
7711
|
-
parts.push(
|
|
7712
|
-
`Detected mechanical transform: ${signals.detectedTransformType}`
|
|
7713
|
-
);
|
|
7766
|
+
parts.push(this.formatMechanicalTransformInfo(signals.detectedTransformType));
|
|
7714
7767
|
}
|
|
7768
|
+
parts.push(...this.formatScopeExplanations(signals));
|
|
7769
|
+
parts.push(...this.formatCodeMetricsExplanations(signals));
|
|
7770
|
+
return parts.join(". ");
|
|
7771
|
+
}
|
|
7772
|
+
// ============================================================================
|
|
7773
|
+
// Private Helper Methods
|
|
7774
|
+
// ============================================================================
|
|
7775
|
+
/**
|
|
7776
|
+
* Format mechanical transform information
|
|
7777
|
+
*/
|
|
7778
|
+
formatMechanicalTransformInfo(transformType) {
|
|
7779
|
+
return `Detected mechanical transform: ${transformType}`;
|
|
7780
|
+
}
|
|
7781
|
+
/**
|
|
7782
|
+
* Format scope-related explanations
|
|
7783
|
+
*/
|
|
7784
|
+
formatScopeExplanations(signals) {
|
|
7785
|
+
const explanations = [];
|
|
7715
7786
|
if (signals.hasArchitectureScope) {
|
|
7716
|
-
|
|
7787
|
+
explanations.push("Architecture scope detected");
|
|
7717
7788
|
}
|
|
7718
7789
|
if (signals.hasSecurityScope) {
|
|
7719
|
-
|
|
7790
|
+
explanations.push("Security scope detected");
|
|
7720
7791
|
}
|
|
7721
7792
|
if (signals.requiresMultiStepReasoning) {
|
|
7722
|
-
|
|
7793
|
+
explanations.push("Multi-step reasoning required");
|
|
7723
7794
|
}
|
|
7724
7795
|
if (signals.requiresCrossDomainCoordination) {
|
|
7725
|
-
|
|
7796
|
+
explanations.push("Cross-domain coordination required");
|
|
7726
7797
|
}
|
|
7798
|
+
return explanations;
|
|
7799
|
+
}
|
|
7800
|
+
/**
|
|
7801
|
+
* Format code metrics explanations
|
|
7802
|
+
*/
|
|
7803
|
+
formatCodeMetricsExplanations(signals) {
|
|
7804
|
+
const explanations = [];
|
|
7727
7805
|
if (signals.linesOfCode !== void 0 && signals.linesOfCode > 100) {
|
|
7728
|
-
|
|
7806
|
+
explanations.push(`Large code change: ${signals.linesOfCode} lines`);
|
|
7729
7807
|
}
|
|
7730
7808
|
if (signals.fileCount !== void 0 && signals.fileCount > 3) {
|
|
7731
|
-
|
|
7809
|
+
explanations.push(`Multi-file change: ${signals.fileCount} files`);
|
|
7732
7810
|
}
|
|
7733
|
-
return
|
|
7811
|
+
return explanations;
|
|
7812
|
+
}
|
|
7813
|
+
};
|
|
7814
|
+
}
|
|
7815
|
+
});
|
|
7816
|
+
|
|
7817
|
+
// src/integrations/agentic-flow/model-router/complexity-analyzer.ts
|
|
7818
|
+
function createComplexityAnalyzer(config, agentBoosterAdapter) {
|
|
7819
|
+
const signalCollector = new SignalCollector(config, agentBoosterAdapter);
|
|
7820
|
+
const scoreCalculator = new ScoreCalculator();
|
|
7821
|
+
const tierRecommender = new TierRecommender();
|
|
7822
|
+
return new ComplexityAnalyzer(
|
|
7823
|
+
config,
|
|
7824
|
+
signalCollector,
|
|
7825
|
+
scoreCalculator,
|
|
7826
|
+
tierRecommender
|
|
7827
|
+
);
|
|
7828
|
+
}
|
|
7829
|
+
var ComplexityAnalyzer;
|
|
7830
|
+
var init_complexity_analyzer = __esm({
|
|
7831
|
+
"src/integrations/agentic-flow/model-router/complexity-analyzer.ts"() {
|
|
7832
|
+
"use strict";
|
|
7833
|
+
init_types3();
|
|
7834
|
+
init_signal_collector();
|
|
7835
|
+
init_score_calculator();
|
|
7836
|
+
init_tier_recommender();
|
|
7837
|
+
init_signal_collector();
|
|
7838
|
+
init_score_calculator();
|
|
7839
|
+
init_tier_recommender();
|
|
7840
|
+
ComplexityAnalyzer = class {
|
|
7841
|
+
config;
|
|
7842
|
+
signalCollector;
|
|
7843
|
+
scoreCalculator;
|
|
7844
|
+
tierRecommender;
|
|
7845
|
+
constructor(config, signalCollector, scoreCalculator, tierRecommender) {
|
|
7846
|
+
this.config = config;
|
|
7847
|
+
this.signalCollector = signalCollector;
|
|
7848
|
+
this.scoreCalculator = scoreCalculator;
|
|
7849
|
+
this.tierRecommender = tierRecommender;
|
|
7850
|
+
}
|
|
7851
|
+
/**
|
|
7852
|
+
* Analyze task complexity
|
|
7853
|
+
*/
|
|
7854
|
+
async analyze(input) {
|
|
7855
|
+
try {
|
|
7856
|
+
const signals = await this.signalCollector.collectSignals(input);
|
|
7857
|
+
const codeComplexity = this.scoreCalculator.calculateCodeComplexity(signals);
|
|
7858
|
+
const reasoningComplexity = this.scoreCalculator.calculateReasoningComplexity(signals);
|
|
7859
|
+
const scopeComplexity = this.scoreCalculator.calculateScopeComplexity(signals);
|
|
7860
|
+
const overall = this.scoreCalculator.calculateOverallComplexity(
|
|
7861
|
+
codeComplexity,
|
|
7862
|
+
reasoningComplexity,
|
|
7863
|
+
scopeComplexity,
|
|
7864
|
+
signals
|
|
7865
|
+
);
|
|
7866
|
+
const recommendedTier = this.tierRecommender.getRecommendedTier(overall);
|
|
7867
|
+
const alternateTiers = this.tierRecommender.findAlternateTiers(overall, recommendedTier);
|
|
7868
|
+
const confidence = this.scoreCalculator.calculateConfidence(signals, input);
|
|
7869
|
+
const explanation = this.tierRecommender.generateExplanation(
|
|
7870
|
+
overall,
|
|
7871
|
+
recommendedTier,
|
|
7872
|
+
signals
|
|
7873
|
+
);
|
|
7874
|
+
return {
|
|
7875
|
+
overall,
|
|
7876
|
+
codeComplexity,
|
|
7877
|
+
reasoningComplexity,
|
|
7878
|
+
scopeComplexity,
|
|
7879
|
+
confidence,
|
|
7880
|
+
signals,
|
|
7881
|
+
recommendedTier,
|
|
7882
|
+
alternateTiers,
|
|
7883
|
+
explanation
|
|
7884
|
+
};
|
|
7885
|
+
} catch (error) {
|
|
7886
|
+
throw new ComplexityAnalysisError(
|
|
7887
|
+
`Failed to analyze task complexity: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
7888
|
+
error instanceof Error ? error : void 0
|
|
7889
|
+
);
|
|
7890
|
+
}
|
|
7891
|
+
}
|
|
7892
|
+
/**
|
|
7893
|
+
* Check if task is eligible for Agent Booster (Tier 0)
|
|
7894
|
+
*/
|
|
7895
|
+
async checkAgentBoosterEligibility(input) {
|
|
7896
|
+
return this.signalCollector.checkAgentBoosterEligibility(input);
|
|
7897
|
+
}
|
|
7898
|
+
/**
|
|
7899
|
+
* Get recommended tier based on complexity score
|
|
7900
|
+
*/
|
|
7901
|
+
getRecommendedTier(complexity) {
|
|
7902
|
+
return this.tierRecommender.getRecommendedTier(complexity);
|
|
7734
7903
|
}
|
|
7735
7904
|
};
|
|
7736
7905
|
}
|
|
@@ -8303,7 +8472,7 @@ var init_router = __esm({
|
|
|
8303
8472
|
this.config = { ...DEFAULT_ROUTER_CONFIG, ...config };
|
|
8304
8473
|
this.agentBoosterAdapter = agentBoosterAdapter;
|
|
8305
8474
|
this.persistentMetricsTracker = persistentMetricsTracker;
|
|
8306
|
-
this.complexityAnalyzer =
|
|
8475
|
+
this.complexityAnalyzer = createComplexityAnalyzer(
|
|
8307
8476
|
this.config,
|
|
8308
8477
|
agentBoosterAdapter
|
|
8309
8478
|
);
|
|
@@ -10869,7 +11038,740 @@ function err(error) {
|
|
|
10869
11038
|
return { success: false, error };
|
|
10870
11039
|
}
|
|
10871
11040
|
|
|
11041
|
+
// src/mcp/security/validators/path-traversal-validator.ts
|
|
11042
|
+
var PATH_TRAVERSAL_PATTERNS = [
|
|
11043
|
+
/\.\./,
|
|
11044
|
+
// Basic traversal
|
|
11045
|
+
/%2e%2e/i,
|
|
11046
|
+
// URL encoded ..
|
|
11047
|
+
/%252e%252e/i,
|
|
11048
|
+
// Double URL encoded
|
|
11049
|
+
/\.\.%2f/i,
|
|
11050
|
+
// Mixed encoding
|
|
11051
|
+
/%2f\.\./i,
|
|
11052
|
+
// Forward slash + ..
|
|
11053
|
+
/\.\.%5c/i,
|
|
11054
|
+
// Backslash + ..
|
|
11055
|
+
/\.\.\\/,
|
|
11056
|
+
// Windows backslash traversal
|
|
11057
|
+
/%c0%ae/i,
|
|
11058
|
+
// UTF-8 overlong encoding
|
|
11059
|
+
/%c0%2f/i,
|
|
11060
|
+
// UTF-8 overlong /
|
|
11061
|
+
/%c1%9c/i,
|
|
11062
|
+
// UTF-8 overlong \
|
|
11063
|
+
/\0/,
|
|
11064
|
+
// Null byte injection
|
|
11065
|
+
/%00/i
|
|
11066
|
+
// URL encoded null
|
|
11067
|
+
];
|
|
11068
|
+
var DANGEROUS_PATH_COMPONENTS = [
|
|
11069
|
+
/^\/etc\//i,
|
|
11070
|
+
/^\/proc\//i,
|
|
11071
|
+
/^\/sys\//i,
|
|
11072
|
+
/^\/dev\//i,
|
|
11073
|
+
/^\/root\//i,
|
|
11074
|
+
/^\/home\/.+\/\./i,
|
|
11075
|
+
/^[A-Z]:\\Windows/i,
|
|
11076
|
+
/^[A-Z]:\\System/i,
|
|
11077
|
+
/^[A-Z]:\\Users\\.+\\AppData/i
|
|
11078
|
+
];
|
|
11079
|
+
var PathTraversalValidator = class {
|
|
11080
|
+
name = "path-traversal";
|
|
11081
|
+
/**
|
|
11082
|
+
* Get the primary risk level this validator addresses
|
|
11083
|
+
*/
|
|
11084
|
+
getRiskLevel() {
|
|
11085
|
+
return "critical";
|
|
11086
|
+
}
|
|
11087
|
+
/**
|
|
11088
|
+
* Validate a file path against traversal attacks
|
|
11089
|
+
*/
|
|
11090
|
+
validate(path16, options = {}) {
|
|
11091
|
+
const {
|
|
11092
|
+
basePath = "",
|
|
11093
|
+
allowAbsolute = false,
|
|
11094
|
+
allowedExtensions = [],
|
|
11095
|
+
deniedExtensions = [".exe", ".bat", ".cmd", ".sh", ".ps1", ".dll", ".so"],
|
|
11096
|
+
maxDepth = 10,
|
|
11097
|
+
maxLength = 4096
|
|
11098
|
+
} = options;
|
|
11099
|
+
if (path16.length > maxLength) {
|
|
11100
|
+
return {
|
|
11101
|
+
valid: false,
|
|
11102
|
+
error: `Path exceeds maximum length of ${maxLength}`,
|
|
11103
|
+
riskLevel: "medium"
|
|
11104
|
+
};
|
|
11105
|
+
}
|
|
11106
|
+
for (const pattern of PATH_TRAVERSAL_PATTERNS) {
|
|
11107
|
+
if (pattern.test(path16)) {
|
|
11108
|
+
return {
|
|
11109
|
+
valid: false,
|
|
11110
|
+
error: "Path traversal attempt detected",
|
|
11111
|
+
riskLevel: "critical"
|
|
11112
|
+
};
|
|
11113
|
+
}
|
|
11114
|
+
}
|
|
11115
|
+
if (!allowAbsolute && (path16.startsWith("/") || /^[A-Z]:/i.test(path16))) {
|
|
11116
|
+
return {
|
|
11117
|
+
valid: false,
|
|
11118
|
+
error: "Absolute paths are not allowed",
|
|
11119
|
+
riskLevel: "high"
|
|
11120
|
+
};
|
|
11121
|
+
}
|
|
11122
|
+
for (const pattern of DANGEROUS_PATH_COMPONENTS) {
|
|
11123
|
+
if (pattern.test(path16)) {
|
|
11124
|
+
return {
|
|
11125
|
+
valid: false,
|
|
11126
|
+
error: "Access to system paths is not allowed",
|
|
11127
|
+
riskLevel: "critical"
|
|
11128
|
+
};
|
|
11129
|
+
}
|
|
11130
|
+
}
|
|
11131
|
+
const normalizedPath = this.normalizePath(path16);
|
|
11132
|
+
if (normalizedPath.includes("..")) {
|
|
11133
|
+
return {
|
|
11134
|
+
valid: false,
|
|
11135
|
+
error: "Path traversal detected after normalization",
|
|
11136
|
+
riskLevel: "critical"
|
|
11137
|
+
};
|
|
11138
|
+
}
|
|
11139
|
+
const depth = normalizedPath.split("/").filter(Boolean).length;
|
|
11140
|
+
if (depth > maxDepth) {
|
|
11141
|
+
return {
|
|
11142
|
+
valid: false,
|
|
11143
|
+
error: `Path depth exceeds maximum of ${maxDepth}`,
|
|
11144
|
+
riskLevel: "low"
|
|
11145
|
+
};
|
|
11146
|
+
}
|
|
11147
|
+
const ext = this.getExtension(normalizedPath);
|
|
11148
|
+
if (ext) {
|
|
11149
|
+
const extWithDot = `.${ext.toLowerCase()}`;
|
|
11150
|
+
const extWithoutDot = ext.toLowerCase();
|
|
11151
|
+
if (deniedExtensions.length > 0) {
|
|
11152
|
+
const isDenied = deniedExtensions.some(
|
|
11153
|
+
(denied) => denied.toLowerCase() === extWithDot || denied.toLowerCase() === extWithoutDot
|
|
11154
|
+
);
|
|
11155
|
+
if (isDenied) {
|
|
11156
|
+
return {
|
|
11157
|
+
valid: false,
|
|
11158
|
+
error: `File extension '${ext}' is not allowed`,
|
|
11159
|
+
riskLevel: "high"
|
|
11160
|
+
};
|
|
11161
|
+
}
|
|
11162
|
+
}
|
|
11163
|
+
if (allowedExtensions.length > 0) {
|
|
11164
|
+
const isAllowed = allowedExtensions.some(
|
|
11165
|
+
(allowed) => allowed.toLowerCase() === extWithDot || allowed.toLowerCase() === extWithoutDot
|
|
11166
|
+
);
|
|
11167
|
+
if (!isAllowed) {
|
|
11168
|
+
return {
|
|
11169
|
+
valid: false,
|
|
11170
|
+
error: `File extension '${ext}' is not in allowed list`,
|
|
11171
|
+
riskLevel: "medium"
|
|
11172
|
+
};
|
|
11173
|
+
}
|
|
11174
|
+
}
|
|
11175
|
+
}
|
|
11176
|
+
const finalPath = basePath ? this.joinPathsAbsolute(basePath, normalizedPath) : normalizedPath;
|
|
11177
|
+
const normalizedBase = basePath.startsWith("/") ? `/${this.normalizePath(basePath)}` : this.normalizePath(basePath);
|
|
11178
|
+
if (basePath && !finalPath.startsWith(normalizedBase)) {
|
|
11179
|
+
return {
|
|
11180
|
+
valid: false,
|
|
11181
|
+
error: "Path escapes base directory",
|
|
11182
|
+
riskLevel: "critical"
|
|
11183
|
+
};
|
|
11184
|
+
}
|
|
11185
|
+
return {
|
|
11186
|
+
valid: true,
|
|
11187
|
+
normalizedPath: finalPath,
|
|
11188
|
+
riskLevel: "none"
|
|
11189
|
+
};
|
|
11190
|
+
}
|
|
11191
|
+
/**
|
|
11192
|
+
* Normalize a path by resolving . and .. components
|
|
11193
|
+
*/
|
|
11194
|
+
normalizePath(path16) {
|
|
11195
|
+
let normalized = path16.replace(/\\/g, "/");
|
|
11196
|
+
normalized = normalized.replace(/\/+/g, "/");
|
|
11197
|
+
const parts = normalized.split("/");
|
|
11198
|
+
const result = [];
|
|
11199
|
+
for (const part of parts) {
|
|
11200
|
+
if (part === "." || part === "") {
|
|
11201
|
+
continue;
|
|
11202
|
+
}
|
|
11203
|
+
if (part === "..") {
|
|
11204
|
+
if (result.length > 0 && result[result.length - 1] !== "..") {
|
|
11205
|
+
result.pop();
|
|
11206
|
+
}
|
|
11207
|
+
} else {
|
|
11208
|
+
result.push(part);
|
|
11209
|
+
}
|
|
11210
|
+
}
|
|
11211
|
+
return result.join("/");
|
|
11212
|
+
}
|
|
11213
|
+
/**
|
|
11214
|
+
* Safely join path components (strips leading/trailing slashes from all parts)
|
|
11215
|
+
*/
|
|
11216
|
+
joinPaths(...paths) {
|
|
11217
|
+
if (paths.length === 0) return "";
|
|
11218
|
+
return paths.map((p) => p.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
|
|
11219
|
+
}
|
|
11220
|
+
/**
|
|
11221
|
+
* Join paths preserving absolute path from first component
|
|
11222
|
+
*/
|
|
11223
|
+
joinPathsAbsolute(...paths) {
|
|
11224
|
+
if (paths.length === 0) return "";
|
|
11225
|
+
const isAbsolute4 = paths[0].startsWith("/");
|
|
11226
|
+
const result = paths.map((p) => {
|
|
11227
|
+
while (p.startsWith("/")) p = p.slice(1);
|
|
11228
|
+
while (p.endsWith("/")) p = p.slice(0, -1);
|
|
11229
|
+
return p;
|
|
11230
|
+
}).filter(Boolean).join("/");
|
|
11231
|
+
return isAbsolute4 ? `/${result}` : result;
|
|
11232
|
+
}
|
|
11233
|
+
/**
|
|
11234
|
+
* Get file extension from path
|
|
11235
|
+
*/
|
|
11236
|
+
getExtension(path16) {
|
|
11237
|
+
const match = path16.match(/\.([^./\\]+)$/);
|
|
11238
|
+
return match ? match[1] : null;
|
|
11239
|
+
}
|
|
11240
|
+
};
|
|
11241
|
+
var defaultValidator = new PathTraversalValidator();
|
|
11242
|
+
var validatePath = (path16, options) => defaultValidator.validate(path16, options);
|
|
11243
|
+
|
|
11244
|
+
// src/mcp/security/validators/regex-safety-validator.ts
|
|
11245
|
+
var REDOS_PATTERNS = [
|
|
11246
|
+
/\(\.\*\)\+/,
|
|
11247
|
+
// (.*)+
|
|
11248
|
+
/\(\.\+\)\+/,
|
|
11249
|
+
// (.+)+
|
|
11250
|
+
/\([^)]*\?\)\+/,
|
|
11251
|
+
// (...?)+
|
|
11252
|
+
/\([^)]*\*\)\+/,
|
|
11253
|
+
// (...*)+
|
|
11254
|
+
/\([^)]*\+\)\+/,
|
|
11255
|
+
// (...+)+
|
|
11256
|
+
/\(\[.*?\]\+\)\+/,
|
|
11257
|
+
// ([...]+)+
|
|
11258
|
+
/\(\[.*?\]\*\)\+/,
|
|
11259
|
+
// ([...]*)+
|
|
11260
|
+
/\(\[.*?\]\?\)\+/,
|
|
11261
|
+
// ([...]?)+
|
|
11262
|
+
/\(\[.*?\]\*\)\*/,
|
|
11263
|
+
// ([...]*)*
|
|
11264
|
+
/\.\*\.\*/,
|
|
11265
|
+
// .*.*
|
|
11266
|
+
/\.\+\.\+/,
|
|
11267
|
+
// .+.+
|
|
11268
|
+
/\(\.\|\.\)/
|
|
11269
|
+
// (.|.)
|
|
11270
|
+
];
|
|
11271
|
+
var MAX_REGEX_COMPLEXITY = 3;
|
|
11272
|
+
function countQuantifierNesting(pattern) {
|
|
11273
|
+
let maxDepth = 0;
|
|
11274
|
+
let currentDepth = 0;
|
|
11275
|
+
let inGroup = false;
|
|
11276
|
+
let escaped = false;
|
|
11277
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
11278
|
+
const char = pattern[i];
|
|
11279
|
+
if (escaped) {
|
|
11280
|
+
escaped = false;
|
|
11281
|
+
continue;
|
|
11282
|
+
}
|
|
11283
|
+
if (char === "\\") {
|
|
11284
|
+
escaped = true;
|
|
11285
|
+
continue;
|
|
11286
|
+
}
|
|
11287
|
+
if (char === "(") {
|
|
11288
|
+
inGroup = true;
|
|
11289
|
+
continue;
|
|
11290
|
+
}
|
|
11291
|
+
if (char === ")") {
|
|
11292
|
+
inGroup = false;
|
|
11293
|
+
const next = pattern[i + 1];
|
|
11294
|
+
if (next === "*" || next === "+" || next === "?" || next === "{") {
|
|
11295
|
+
currentDepth++;
|
|
11296
|
+
maxDepth = Math.max(maxDepth, currentDepth);
|
|
11297
|
+
}
|
|
11298
|
+
continue;
|
|
11299
|
+
}
|
|
11300
|
+
if ((char === "*" || char === "+" || char === "?") && !inGroup) {
|
|
11301
|
+
currentDepth = 1;
|
|
11302
|
+
maxDepth = Math.max(maxDepth, currentDepth);
|
|
11303
|
+
}
|
|
11304
|
+
}
|
|
11305
|
+
return maxDepth;
|
|
11306
|
+
}
|
|
11307
|
+
function hasExponentialBacktracking(pattern) {
|
|
11308
|
+
const dangerous = [
|
|
11309
|
+
/\(\[^\\]*\]\+\)\+/,
|
|
11310
|
+
// ([...]+)+
|
|
11311
|
+
/\(\[^\\]*\]\*\)\*/,
|
|
11312
|
+
// ([...]*)*
|
|
11313
|
+
/\([^)]+\|[^)]+\)\+/,
|
|
11314
|
+
// (a|b)+
|
|
11315
|
+
/\(\.\*\)[*+]/,
|
|
11316
|
+
// (.*)+, (.*)*
|
|
11317
|
+
/\(\.\+\)[*+]/
|
|
11318
|
+
// (.+)+, (.+)*
|
|
11319
|
+
];
|
|
11320
|
+
return dangerous.some((d) => d.test(pattern));
|
|
11321
|
+
}
|
|
11322
|
+
var RegexSafetyValidator = class {
|
|
11323
|
+
name = "regex-safety";
|
|
11324
|
+
maxComplexity;
|
|
11325
|
+
constructor(maxComplexity = MAX_REGEX_COMPLEXITY) {
|
|
11326
|
+
this.maxComplexity = maxComplexity;
|
|
11327
|
+
}
|
|
11328
|
+
/**
|
|
11329
|
+
* Get the primary risk level this validator addresses
|
|
11330
|
+
*/
|
|
11331
|
+
getRiskLevel() {
|
|
11332
|
+
return "high";
|
|
11333
|
+
}
|
|
11334
|
+
/**
|
|
11335
|
+
* Validate a regex pattern (IValidationStrategy interface)
|
|
11336
|
+
*/
|
|
11337
|
+
validate(pattern, options = {}) {
|
|
11338
|
+
const { maxLength = 1e4, maxComplexity = this.maxComplexity } = options;
|
|
11339
|
+
if (pattern.length > maxLength) {
|
|
11340
|
+
return {
|
|
11341
|
+
valid: false,
|
|
11342
|
+
error: `Pattern exceeds maximum length of ${maxLength}`,
|
|
11343
|
+
riskLevel: "medium"
|
|
11344
|
+
};
|
|
11345
|
+
}
|
|
11346
|
+
const result = this.isRegexSafe(pattern, maxComplexity);
|
|
11347
|
+
return {
|
|
11348
|
+
valid: result.safe,
|
|
11349
|
+
error: result.error,
|
|
11350
|
+
riskLevel: result.safe ? "none" : "high"
|
|
11351
|
+
};
|
|
11352
|
+
}
|
|
11353
|
+
/**
|
|
11354
|
+
* Check if a regex pattern is safe from ReDoS
|
|
11355
|
+
*/
|
|
11356
|
+
isRegexSafe(pattern, maxComplexity = this.maxComplexity) {
|
|
11357
|
+
const riskyPatterns = [];
|
|
11358
|
+
for (const redosPattern of REDOS_PATTERNS) {
|
|
11359
|
+
if (redosPattern.test(pattern)) {
|
|
11360
|
+
riskyPatterns.push(redosPattern.source);
|
|
11361
|
+
}
|
|
11362
|
+
}
|
|
11363
|
+
const quantifierDepth = countQuantifierNesting(pattern);
|
|
11364
|
+
if (quantifierDepth > maxComplexity) {
|
|
11365
|
+
riskyPatterns.push(`Quantifier nesting depth: ${quantifierDepth} (max: ${maxComplexity})`);
|
|
11366
|
+
}
|
|
11367
|
+
if (hasExponentialBacktracking(pattern)) {
|
|
11368
|
+
riskyPatterns.push("Exponential backtracking potential detected");
|
|
11369
|
+
}
|
|
11370
|
+
return {
|
|
11371
|
+
safe: riskyPatterns.length === 0,
|
|
11372
|
+
pattern,
|
|
11373
|
+
escapedPattern: this.escapeRegex(pattern),
|
|
11374
|
+
riskyPatterns,
|
|
11375
|
+
error: riskyPatterns.length > 0 ? "Pattern may cause ReDoS" : void 0
|
|
11376
|
+
};
|
|
11377
|
+
}
|
|
11378
|
+
/**
|
|
11379
|
+
* Escape special regex characters in a string
|
|
11380
|
+
*/
|
|
11381
|
+
escapeRegex(str) {
|
|
11382
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11383
|
+
}
|
|
11384
|
+
/**
|
|
11385
|
+
* Create a safe regex with validation
|
|
11386
|
+
*/
|
|
11387
|
+
createSafeRegex(pattern, flags, maxLength = 1e4) {
|
|
11388
|
+
const safety = this.isRegexSafe(pattern);
|
|
11389
|
+
if (!safety.safe) {
|
|
11390
|
+
return null;
|
|
11391
|
+
}
|
|
11392
|
+
if (pattern.length > maxLength) {
|
|
11393
|
+
return null;
|
|
11394
|
+
}
|
|
11395
|
+
try {
|
|
11396
|
+
return new RegExp(pattern, flags);
|
|
11397
|
+
} catch {
|
|
11398
|
+
return null;
|
|
11399
|
+
}
|
|
11400
|
+
}
|
|
11401
|
+
};
|
|
11402
|
+
var defaultValidator2 = new RegexSafetyValidator();
|
|
11403
|
+
|
|
11404
|
+
// src/mcp/security/validators/crypto-validator.ts
|
|
11405
|
+
import { createHash, timingSafeEqual, randomBytes } from "crypto";
|
|
11406
|
+
var CryptoValidator = class {
|
|
11407
|
+
name = "crypto-security";
|
|
11408
|
+
/**
|
|
11409
|
+
* Get the primary risk level this validator addresses
|
|
11410
|
+
*/
|
|
11411
|
+
getRiskLevel() {
|
|
11412
|
+
return "critical";
|
|
11413
|
+
}
|
|
11414
|
+
/**
|
|
11415
|
+
* Perform a timing-safe string comparison
|
|
11416
|
+
* Prevents timing attacks by ensuring constant-time comparison
|
|
11417
|
+
*/
|
|
11418
|
+
timingSafeCompare(a, b) {
|
|
11419
|
+
const maxLen = Math.max(a.length, b.length);
|
|
11420
|
+
const paddedA = a.padEnd(maxLen, "\0");
|
|
11421
|
+
const paddedB = b.padEnd(maxLen, "\0");
|
|
11422
|
+
try {
|
|
11423
|
+
return timingSafeEqual(Buffer.from(paddedA), Buffer.from(paddedB));
|
|
11424
|
+
} catch {
|
|
11425
|
+
return false;
|
|
11426
|
+
}
|
|
11427
|
+
}
|
|
11428
|
+
/**
|
|
11429
|
+
* Timing-safe comparison for hashed values
|
|
11430
|
+
* Hashes the input value and compares against expected hash
|
|
11431
|
+
*/
|
|
11432
|
+
timingSafeHashCompare(value, expectedHash) {
|
|
11433
|
+
const hash = createHash("sha256").update(value).digest("hex");
|
|
11434
|
+
return this.timingSafeCompare(hash, expectedHash);
|
|
11435
|
+
}
|
|
11436
|
+
/**
|
|
11437
|
+
* Generate a secure random token
|
|
11438
|
+
* Uses cryptographically secure random bytes
|
|
11439
|
+
*/
|
|
11440
|
+
generateSecureToken(length = 32) {
|
|
11441
|
+
return randomBytes(length).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
11442
|
+
}
|
|
11443
|
+
/**
|
|
11444
|
+
* Hash a value securely using SHA-256
|
|
11445
|
+
*/
|
|
11446
|
+
secureHash(value, salt) {
|
|
11447
|
+
const data = salt ? `${salt}:${value}` : value;
|
|
11448
|
+
return createHash("sha256").update(data).digest("hex");
|
|
11449
|
+
}
|
|
11450
|
+
};
|
|
11451
|
+
var defaultValidator3 = new CryptoValidator();
|
|
11452
|
+
|
|
11453
|
+
// src/mcp/security/validators/input-sanitizer.ts
|
|
11454
|
+
var HTML_ESCAPE_MAP = {
|
|
11455
|
+
"&": "&",
|
|
11456
|
+
"<": "<",
|
|
11457
|
+
">": ">",
|
|
11458
|
+
'"': """,
|
|
11459
|
+
"'": "'",
|
|
11460
|
+
"/": "/",
|
|
11461
|
+
"`": "`",
|
|
11462
|
+
"=": "="
|
|
11463
|
+
};
|
|
11464
|
+
var SQL_INJECTION_PATTERNS = [
|
|
11465
|
+
/('|")\s*;\s*--/i,
|
|
11466
|
+
/'\s*OR\s+'1'\s*=\s*'1/i,
|
|
11467
|
+
/"\s*OR\s+"1"\s*=\s*"1/i,
|
|
11468
|
+
/UNION\s+SELECT/i,
|
|
11469
|
+
/INSERT\s+INTO/i,
|
|
11470
|
+
/DROP\s+TABLE/i,
|
|
11471
|
+
/DELETE\s+FROM/i,
|
|
11472
|
+
/UPDATE\s+.*\s+SET/i,
|
|
11473
|
+
/EXEC(\s+|\()sp_/i,
|
|
11474
|
+
/xp_cmdshell/i
|
|
11475
|
+
];
|
|
11476
|
+
var SHELL_METACHARACTERS = /[|;&$`<>{}[\]!#*?~]/g;
|
|
11477
|
+
var DANGEROUS_CONTROL_CHARS = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
|
|
11478
|
+
var InputSanitizer = class {
|
|
11479
|
+
name = "input-sanitization";
|
|
11480
|
+
/**
|
|
11481
|
+
* Get the primary risk level this sanitizer addresses
|
|
11482
|
+
*/
|
|
11483
|
+
getRiskLevel() {
|
|
11484
|
+
return "high";
|
|
11485
|
+
}
|
|
11486
|
+
/**
|
|
11487
|
+
* Sanitize input string with configurable options
|
|
11488
|
+
*/
|
|
11489
|
+
sanitize(input, options = {}) {
|
|
11490
|
+
const {
|
|
11491
|
+
maxLength = 1e4,
|
|
11492
|
+
allowedChars,
|
|
11493
|
+
stripHtml = true,
|
|
11494
|
+
stripSql = true,
|
|
11495
|
+
escapeShell = true,
|
|
11496
|
+
trim = true,
|
|
11497
|
+
stripControlChars = true
|
|
11498
|
+
} = options;
|
|
11499
|
+
let result = input;
|
|
11500
|
+
if (stripControlChars) {
|
|
11501
|
+
result = result.replace(DANGEROUS_CONTROL_CHARS, "");
|
|
11502
|
+
}
|
|
11503
|
+
if (trim) {
|
|
11504
|
+
result = result.trim();
|
|
11505
|
+
}
|
|
11506
|
+
if (result.length > maxLength) {
|
|
11507
|
+
result = result.substring(0, maxLength);
|
|
11508
|
+
}
|
|
11509
|
+
if (stripHtml) {
|
|
11510
|
+
result = this.stripHtmlTags(result);
|
|
11511
|
+
}
|
|
11512
|
+
if (stripSql) {
|
|
11513
|
+
for (const pattern of SQL_INJECTION_PATTERNS) {
|
|
11514
|
+
result = result.replace(pattern, "");
|
|
11515
|
+
}
|
|
11516
|
+
}
|
|
11517
|
+
if (escapeShell) {
|
|
11518
|
+
result = result.replace(SHELL_METACHARACTERS, "");
|
|
11519
|
+
}
|
|
11520
|
+
if (allowedChars) {
|
|
11521
|
+
result = result.split("").filter((char) => allowedChars.test(char)).join("");
|
|
11522
|
+
}
|
|
11523
|
+
return result;
|
|
11524
|
+
}
|
|
11525
|
+
/**
|
|
11526
|
+
* Escape HTML special characters
|
|
11527
|
+
*/
|
|
11528
|
+
escapeHtml(str) {
|
|
11529
|
+
return str.replace(/[&<>"'`=/]/g, (char) => HTML_ESCAPE_MAP[char] || char);
|
|
11530
|
+
}
|
|
11531
|
+
/**
|
|
11532
|
+
* Strip HTML tags from a string
|
|
11533
|
+
* Handles both complete tags and incomplete/malformed tags to prevent XSS
|
|
11534
|
+
*/
|
|
11535
|
+
stripHtmlTags(str) {
|
|
11536
|
+
const MAX_LENGTH = 1e5;
|
|
11537
|
+
if (str.length > MAX_LENGTH) {
|
|
11538
|
+
str = str.slice(0, MAX_LENGTH);
|
|
11539
|
+
}
|
|
11540
|
+
let result = str;
|
|
11541
|
+
let prevLength;
|
|
11542
|
+
do {
|
|
11543
|
+
prevLength = result.length;
|
|
11544
|
+
let cleaned = "";
|
|
11545
|
+
let inTag = false;
|
|
11546
|
+
for (let i = 0; i < result.length; i++) {
|
|
11547
|
+
const char = result[i];
|
|
11548
|
+
if (char === "<") {
|
|
11549
|
+
inTag = true;
|
|
11550
|
+
} else if (char === ">" && inTag) {
|
|
11551
|
+
inTag = false;
|
|
11552
|
+
} else if (!inTag) {
|
|
11553
|
+
cleaned += char;
|
|
11554
|
+
}
|
|
11555
|
+
}
|
|
11556
|
+
result = cleaned;
|
|
11557
|
+
} while (result.length < prevLength && result.length > 0);
|
|
11558
|
+
result = result.replace(/</g, "<").replace(/>/g, ">");
|
|
11559
|
+
return result;
|
|
11560
|
+
}
|
|
11561
|
+
};
|
|
11562
|
+
var defaultSanitizer = new InputSanitizer();
|
|
11563
|
+
var sanitizeInput = (input, options) => defaultSanitizer.sanitize(input, options);
|
|
11564
|
+
|
|
11565
|
+
// src/mcp/security/validators/command-validator.ts
|
|
11566
|
+
var DEFAULT_ALLOWED_COMMANDS = [
|
|
11567
|
+
"ls",
|
|
11568
|
+
"cat",
|
|
11569
|
+
"echo",
|
|
11570
|
+
"grep",
|
|
11571
|
+
"find",
|
|
11572
|
+
"head",
|
|
11573
|
+
"tail",
|
|
11574
|
+
"wc",
|
|
11575
|
+
"npm",
|
|
11576
|
+
"node",
|
|
11577
|
+
"yarn",
|
|
11578
|
+
"pnpm",
|
|
11579
|
+
"git",
|
|
11580
|
+
"jest",
|
|
11581
|
+
"vitest",
|
|
11582
|
+
"playwright"
|
|
11583
|
+
];
|
|
11584
|
+
var BLOCKED_COMMAND_PATTERNS = [
|
|
11585
|
+
/;/,
|
|
11586
|
+
// Command chaining with semicolon
|
|
11587
|
+
/&&/,
|
|
11588
|
+
// Command chaining with AND
|
|
11589
|
+
/\|\|/,
|
|
11590
|
+
// Command chaining with OR
|
|
11591
|
+
/\|/,
|
|
11592
|
+
// Piping
|
|
11593
|
+
/`.*`/,
|
|
11594
|
+
// Backtick command substitution
|
|
11595
|
+
/\$\(.*\)/,
|
|
11596
|
+
// $() command substitution
|
|
11597
|
+
/>\s*\/dev\/sd/i,
|
|
11598
|
+
// Writing to block devices
|
|
11599
|
+
/>\s*\/etc\//i
|
|
11600
|
+
// Writing to /etc
|
|
11601
|
+
];
|
|
11602
|
+
var SHELL_METACHARACTERS2 = /[|;&$`<>{}[\]!#*?~]/g;
|
|
11603
|
+
var CommandValidator = class {
|
|
11604
|
+
name = "command-injection";
|
|
11605
|
+
defaultAllowedCommands;
|
|
11606
|
+
constructor(defaultAllowedCommands = DEFAULT_ALLOWED_COMMANDS) {
|
|
11607
|
+
this.defaultAllowedCommands = defaultAllowedCommands;
|
|
11608
|
+
}
|
|
11609
|
+
/**
|
|
11610
|
+
* Get the primary risk level this validator addresses
|
|
11611
|
+
*/
|
|
11612
|
+
getRiskLevel() {
|
|
11613
|
+
return "critical";
|
|
11614
|
+
}
|
|
11615
|
+
/**
|
|
11616
|
+
* Validate a command (IValidationStrategy interface)
|
|
11617
|
+
*/
|
|
11618
|
+
validate(command, options = {}) {
|
|
11619
|
+
const allowedCommands = options.allowedCommands ?? this.defaultAllowedCommands;
|
|
11620
|
+
return this.validateCommand(command, allowedCommands);
|
|
11621
|
+
}
|
|
11622
|
+
/**
|
|
11623
|
+
* Validate and sanitize a command
|
|
11624
|
+
*/
|
|
11625
|
+
validateCommand(command, allowedCommands = this.defaultAllowedCommands) {
|
|
11626
|
+
const blockedPatterns = [];
|
|
11627
|
+
for (const pattern of BLOCKED_COMMAND_PATTERNS) {
|
|
11628
|
+
if (pattern.test(command)) {
|
|
11629
|
+
blockedPatterns.push(pattern.source);
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
if (blockedPatterns.length > 0) {
|
|
11633
|
+
return {
|
|
11634
|
+
valid: false,
|
|
11635
|
+
error: "Command contains blocked patterns",
|
|
11636
|
+
blockedPatterns,
|
|
11637
|
+
riskLevel: "critical"
|
|
11638
|
+
};
|
|
11639
|
+
}
|
|
11640
|
+
const parts = command.trim().split(/\s+/);
|
|
11641
|
+
const baseCommand = parts[0].split("/").pop() || "";
|
|
11642
|
+
if (!allowedCommands.includes(baseCommand)) {
|
|
11643
|
+
return {
|
|
11644
|
+
valid: false,
|
|
11645
|
+
error: `Command '${baseCommand}' is not in the allowed list`,
|
|
11646
|
+
blockedPatterns: [],
|
|
11647
|
+
riskLevel: "high"
|
|
11648
|
+
};
|
|
11649
|
+
}
|
|
11650
|
+
const sanitizedParts = parts.map((part, i) => {
|
|
11651
|
+
if (i === 0) return part;
|
|
11652
|
+
return part.replace(SHELL_METACHARACTERS2, "");
|
|
11653
|
+
});
|
|
11654
|
+
return {
|
|
11655
|
+
valid: true,
|
|
11656
|
+
sanitizedCommand: sanitizedParts.join(" "),
|
|
11657
|
+
blockedPatterns: [],
|
|
11658
|
+
riskLevel: "none"
|
|
11659
|
+
};
|
|
11660
|
+
}
|
|
11661
|
+
/**
|
|
11662
|
+
* Escape a string for safe shell usage
|
|
11663
|
+
*/
|
|
11664
|
+
escapeShellArg(arg) {
|
|
11665
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
11666
|
+
}
|
|
11667
|
+
};
|
|
11668
|
+
var defaultValidator4 = new CommandValidator();
|
|
11669
|
+
var validateCommand = (command, allowedCommands) => {
|
|
11670
|
+
if (allowedCommands) {
|
|
11671
|
+
return defaultValidator4.validateCommand(command, allowedCommands);
|
|
11672
|
+
}
|
|
11673
|
+
return defaultValidator4.validate(command);
|
|
11674
|
+
};
|
|
11675
|
+
|
|
10872
11676
|
// src/mcp/tool-registry.ts
|
|
11677
|
+
var VALID_TOOL_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_:-]{0,127}$/;
|
|
11678
|
+
var MAX_PARAM_STRING_LENGTH = 1e6;
|
|
11679
|
+
function validateToolName(name) {
|
|
11680
|
+
if (typeof name !== "string") {
|
|
11681
|
+
return { valid: false, error: "Tool name must be a string" };
|
|
11682
|
+
}
|
|
11683
|
+
if (name.length === 0) {
|
|
11684
|
+
return { valid: false, error: "Tool name cannot be empty" };
|
|
11685
|
+
}
|
|
11686
|
+
if (name.length > 128) {
|
|
11687
|
+
return { valid: false, error: "Tool name exceeds maximum length (128)" };
|
|
11688
|
+
}
|
|
11689
|
+
if (!VALID_TOOL_NAME_PATTERN.test(name)) {
|
|
11690
|
+
return {
|
|
11691
|
+
valid: false,
|
|
11692
|
+
error: "Tool name contains invalid characters. Use only alphanumeric, underscore, hyphen, or colon"
|
|
11693
|
+
};
|
|
11694
|
+
}
|
|
11695
|
+
return { valid: true };
|
|
11696
|
+
}
|
|
11697
|
+
function validateParamValue(value, param) {
|
|
11698
|
+
if (value === void 0 || value === null) {
|
|
11699
|
+
if (param.required) {
|
|
11700
|
+
return { valid: false, error: `Required parameter '${param.name}' is missing` };
|
|
11701
|
+
}
|
|
11702
|
+
return { valid: true };
|
|
11703
|
+
}
|
|
11704
|
+
switch (param.type) {
|
|
11705
|
+
case "string":
|
|
11706
|
+
if (typeof value !== "string") {
|
|
11707
|
+
return { valid: false, error: `Parameter '${param.name}' must be a string` };
|
|
11708
|
+
}
|
|
11709
|
+
if (value.length > MAX_PARAM_STRING_LENGTH) {
|
|
11710
|
+
return { valid: false, error: `Parameter '${param.name}' exceeds maximum length` };
|
|
11711
|
+
}
|
|
11712
|
+
break;
|
|
11713
|
+
case "number":
|
|
11714
|
+
if (typeof value !== "number" || isNaN(value)) {
|
|
11715
|
+
return { valid: false, error: `Parameter '${param.name}' must be a number` };
|
|
11716
|
+
}
|
|
11717
|
+
break;
|
|
11718
|
+
case "boolean":
|
|
11719
|
+
if (typeof value !== "boolean") {
|
|
11720
|
+
return { valid: false, error: `Parameter '${param.name}' must be a boolean` };
|
|
11721
|
+
}
|
|
11722
|
+
break;
|
|
11723
|
+
case "object":
|
|
11724
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
11725
|
+
return { valid: false, error: `Parameter '${param.name}' must be an object` };
|
|
11726
|
+
}
|
|
11727
|
+
break;
|
|
11728
|
+
case "array":
|
|
11729
|
+
if (!Array.isArray(value)) {
|
|
11730
|
+
return { valid: false, error: `Parameter '${param.name}' must be an array` };
|
|
11731
|
+
}
|
|
11732
|
+
break;
|
|
11733
|
+
}
|
|
11734
|
+
if (param.enum && param.enum.length > 0) {
|
|
11735
|
+
if (!param.enum.includes(value)) {
|
|
11736
|
+
return {
|
|
11737
|
+
valid: false,
|
|
11738
|
+
error: `Parameter '${param.name}' must be one of: ${param.enum.join(", ")}`
|
|
11739
|
+
};
|
|
11740
|
+
}
|
|
11741
|
+
}
|
|
11742
|
+
return { valid: true };
|
|
11743
|
+
}
|
|
11744
|
+
function validateParams(params, definition) {
|
|
11745
|
+
const errors = [];
|
|
11746
|
+
const knownParams = new Set(definition.parameters.map((p) => p.name));
|
|
11747
|
+
for (const key of Object.keys(params)) {
|
|
11748
|
+
if (!knownParams.has(key)) {
|
|
11749
|
+
errors.push(`Unknown parameter: '${key}'`);
|
|
11750
|
+
}
|
|
11751
|
+
}
|
|
11752
|
+
for (const param of definition.parameters) {
|
|
11753
|
+
const result = validateParamValue(params[param.name], param);
|
|
11754
|
+
if (!result.valid && result.error) {
|
|
11755
|
+
errors.push(result.error);
|
|
11756
|
+
}
|
|
11757
|
+
}
|
|
11758
|
+
return { valid: errors.length === 0, errors };
|
|
11759
|
+
}
|
|
11760
|
+
function sanitizeParams(params) {
|
|
11761
|
+
const sanitized = { ...params };
|
|
11762
|
+
for (const [key, value] of Object.entries(sanitized)) {
|
|
11763
|
+
if (typeof value === "string") {
|
|
11764
|
+
sanitized[key] = sanitizeInput(value);
|
|
11765
|
+
} else if (Array.isArray(value)) {
|
|
11766
|
+
sanitized[key] = value.map(
|
|
11767
|
+
(item) => typeof item === "string" ? sanitizeInput(item) : item
|
|
11768
|
+
);
|
|
11769
|
+
} else if (typeof value === "object" && value !== null) {
|
|
11770
|
+
sanitized[key] = sanitizeParams(value);
|
|
11771
|
+
}
|
|
11772
|
+
}
|
|
11773
|
+
return sanitized;
|
|
11774
|
+
}
|
|
10873
11775
|
var DOMAIN_KEYWORDS = {
|
|
10874
11776
|
"test-generation": [
|
|
10875
11777
|
"test",
|
|
@@ -11137,11 +12039,20 @@ var ToolRegistry = class {
|
|
|
11137
12039
|
return Array.from(names).map((name) => this.tools.get(name)?.definition).filter((d) => d !== void 0);
|
|
11138
12040
|
}
|
|
11139
12041
|
/**
|
|
11140
|
-
* Invoke a tool
|
|
12042
|
+
* Invoke a tool with input validation and sanitization (SEC-001 fix)
|
|
11141
12043
|
*/
|
|
11142
12044
|
async invoke(name, params) {
|
|
11143
12045
|
const startTime = Date.now();
|
|
11144
12046
|
const requestId = uuidv4();
|
|
12047
|
+
const nameValidation = validateToolName(name);
|
|
12048
|
+
if (!nameValidation.valid) {
|
|
12049
|
+
this.stats.errors++;
|
|
12050
|
+
return {
|
|
12051
|
+
success: false,
|
|
12052
|
+
error: `Invalid tool name: ${nameValidation.error}`,
|
|
12053
|
+
metadata: this.createMetadata(startTime, requestId)
|
|
12054
|
+
};
|
|
12055
|
+
}
|
|
11145
12056
|
const tool = this.tools.get(name);
|
|
11146
12057
|
if (!tool) {
|
|
11147
12058
|
this.stats.errors++;
|
|
@@ -11151,6 +12062,16 @@ var ToolRegistry = class {
|
|
|
11151
12062
|
metadata: this.createMetadata(startTime, requestId)
|
|
11152
12063
|
};
|
|
11153
12064
|
}
|
|
12065
|
+
const paramValidation = validateParams(params, tool.definition);
|
|
12066
|
+
if (!paramValidation.valid) {
|
|
12067
|
+
this.stats.errors++;
|
|
12068
|
+
return {
|
|
12069
|
+
success: false,
|
|
12070
|
+
error: `Parameter validation failed: ${paramValidation.errors.join("; ")}`,
|
|
12071
|
+
metadata: this.createMetadata(startTime, requestId, tool.definition.domain)
|
|
12072
|
+
};
|
|
12073
|
+
}
|
|
12074
|
+
const sanitizedParams = sanitizeParams(params);
|
|
11154
12075
|
if (!tool.loaded) {
|
|
11155
12076
|
tool.loaded = true;
|
|
11156
12077
|
this.stats.loadedTools++;
|
|
@@ -11159,7 +12080,7 @@ var ToolRegistry = class {
|
|
|
11159
12080
|
tool.lastUsed = /* @__PURE__ */ new Date();
|
|
11160
12081
|
this.stats.invocations++;
|
|
11161
12082
|
try {
|
|
11162
|
-
const result = await tool.handler(
|
|
12083
|
+
const result = await tool.handler(sanitizedParams);
|
|
11163
12084
|
return {
|
|
11164
12085
|
...result,
|
|
11165
12086
|
metadata: {
|
|
@@ -11971,8 +12892,10 @@ var BaseDomainPlugin = class {
|
|
|
11971
12892
|
this.memory = memory;
|
|
11972
12893
|
}
|
|
11973
12894
|
_initialized = false;
|
|
12895
|
+
// Issue #205 fix: Default to 'idle' status for fresh installs (0 agents)
|
|
12896
|
+
// Domains transition to 'healthy' when they have active agents
|
|
11974
12897
|
_health = {
|
|
11975
|
-
status: "
|
|
12898
|
+
status: "idle",
|
|
11976
12899
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
11977
12900
|
errors: []
|
|
11978
12901
|
};
|
|
@@ -12094,403 +13017,207 @@ import { v4 as uuidv44 } from "uuid";
|
|
|
12094
13017
|
import * as fs2 from "fs";
|
|
12095
13018
|
import * as path2 from "path";
|
|
12096
13019
|
import * as ts from "typescript";
|
|
13020
|
+
|
|
13021
|
+
// src/domains/test-generation/generators/base-test-generator.ts
|
|
12097
13022
|
import { faker } from "@faker-js/faker";
|
|
12098
|
-
var
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
enableAIGeneration: true
|
|
12103
|
-
};
|
|
12104
|
-
var TestGeneratorService = class {
|
|
12105
|
-
constructor(memory, config = {}) {
|
|
12106
|
-
this.memory = memory;
|
|
12107
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
12108
|
-
}
|
|
12109
|
-
config;
|
|
12110
|
-
/**
|
|
12111
|
-
* Generate tests for given source files
|
|
12112
|
-
*/
|
|
12113
|
-
async generateTests(request) {
|
|
12114
|
-
try {
|
|
12115
|
-
const {
|
|
12116
|
-
sourceFiles,
|
|
12117
|
-
testType,
|
|
12118
|
-
framework,
|
|
12119
|
-
coverageTarget = this.config.coverageTargetDefault,
|
|
12120
|
-
patterns = []
|
|
12121
|
-
} = request;
|
|
12122
|
-
if (sourceFiles.length === 0) {
|
|
12123
|
-
return err(new Error("No source files provided"));
|
|
12124
|
-
}
|
|
12125
|
-
const tests = [];
|
|
12126
|
-
const patternsUsed = [];
|
|
12127
|
-
for (const sourceFile of sourceFiles) {
|
|
12128
|
-
const fileTests = await this.generateTestsForFile(
|
|
12129
|
-
sourceFile,
|
|
12130
|
-
testType,
|
|
12131
|
-
framework,
|
|
12132
|
-
patterns
|
|
12133
|
-
);
|
|
12134
|
-
if (fileTests.success) {
|
|
12135
|
-
tests.push(...fileTests.value.tests);
|
|
12136
|
-
patternsUsed.push(...fileTests.value.patternsUsed);
|
|
12137
|
-
}
|
|
12138
|
-
}
|
|
12139
|
-
const coverageEstimate = this.estimateCoverage(tests, coverageTarget);
|
|
12140
|
-
await this.storeGenerationMetadata(tests, patternsUsed);
|
|
12141
|
-
return ok({
|
|
12142
|
-
tests,
|
|
12143
|
-
coverageEstimate,
|
|
12144
|
-
patternsUsed: [...new Set(patternsUsed)]
|
|
12145
|
-
});
|
|
12146
|
-
} catch (error) {
|
|
12147
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
12148
|
-
}
|
|
12149
|
-
}
|
|
13023
|
+
var BaseTestGenerator = class {
|
|
13024
|
+
// ============================================================================
|
|
13025
|
+
// Shared Utilities - Available to all subclasses
|
|
13026
|
+
// ============================================================================
|
|
12150
13027
|
/**
|
|
12151
|
-
* Generate
|
|
13028
|
+
* Generate a test value for a parameter based on its type and name
|
|
13029
|
+
* Uses @faker-js/faker for realistic test data
|
|
13030
|
+
*
|
|
13031
|
+
* @param param - Parameter information
|
|
13032
|
+
* @returns Generated test value as a string
|
|
12152
13033
|
*/
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
return ok([]);
|
|
12157
|
-
}
|
|
12158
|
-
const tests = [];
|
|
12159
|
-
const lineGroups = this.groupConsecutiveLines(uncoveredLines);
|
|
12160
|
-
for (const group of lineGroups) {
|
|
12161
|
-
const test = await this.generateTestForLines(file, group, framework);
|
|
12162
|
-
if (test) {
|
|
12163
|
-
tests.push(test);
|
|
12164
|
-
}
|
|
12165
|
-
}
|
|
12166
|
-
return ok(tests);
|
|
12167
|
-
} catch (error) {
|
|
12168
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
13034
|
+
generateTestValue(param) {
|
|
13035
|
+
if (param.defaultValue) {
|
|
13036
|
+
return param.defaultValue;
|
|
12169
13037
|
}
|
|
13038
|
+
const type = param.type?.toLowerCase() || "unknown";
|
|
13039
|
+
const name = param.name.toLowerCase();
|
|
13040
|
+
if (name.includes("id")) return `'${faker.string.uuid()}'`;
|
|
13041
|
+
if (name.includes("email")) return `'${faker.internet.email()}'`;
|
|
13042
|
+
if (name.includes("name")) return `'${faker.person.fullName()}'`;
|
|
13043
|
+
if (name.includes("url")) return `'${faker.internet.url()}'`;
|
|
13044
|
+
if (name.includes("date")) return `new Date('${faker.date.recent().toISOString()}')`;
|
|
13045
|
+
if (name.includes("phone")) return `'${faker.phone.number()}'`;
|
|
13046
|
+
if (name.includes("address")) return `'${faker.location.streetAddress()}'`;
|
|
13047
|
+
if (type.includes("string")) return `'${faker.lorem.word()}'`;
|
|
13048
|
+
if (type.includes("number")) return String(faker.number.int({ min: 1, max: 100 }));
|
|
13049
|
+
if (type.includes("boolean")) return "true";
|
|
13050
|
+
if (type.includes("[]") || type.includes("array")) return "[]";
|
|
13051
|
+
if (type.includes("object") || type.includes("{")) return "{}";
|
|
13052
|
+
if (type.includes("function")) return "() => {}";
|
|
13053
|
+
if (type.includes("promise")) return "Promise.resolve()";
|
|
13054
|
+
if (type.includes("date")) return "new Date()";
|
|
13055
|
+
return `mock${param.name.charAt(0).toUpperCase() + param.name.slice(1)}`;
|
|
12170
13056
|
}
|
|
12171
13057
|
/**
|
|
12172
|
-
* Generate
|
|
13058
|
+
* Generate test cases for a function based on its signature
|
|
13059
|
+
* Creates happy-path, edge-case, error-handling, and boundary tests
|
|
13060
|
+
*
|
|
13061
|
+
* @param fn - Function information from AST
|
|
13062
|
+
* @returns Array of test cases
|
|
12173
13063
|
*/
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
13064
|
+
generateTestCasesForFunction(fn) {
|
|
13065
|
+
const testCases = [];
|
|
13066
|
+
const validParams = fn.parameters.map((p) => this.generateTestValue(p)).join(", ");
|
|
13067
|
+
const fnCall = fn.isAsync ? `await ${fn.name}(${validParams})` : `${fn.name}(${validParams})`;
|
|
13068
|
+
testCases.push({
|
|
13069
|
+
description: "should handle valid input correctly",
|
|
13070
|
+
type: "happy-path",
|
|
13071
|
+
action: `const result = ${fnCall};`,
|
|
13072
|
+
assertion: "expect(result).toBeDefined();"
|
|
13073
|
+
});
|
|
13074
|
+
for (const param of fn.parameters) {
|
|
13075
|
+
if (!param.optional) {
|
|
13076
|
+
const paramsWithUndefined = fn.parameters.map((p) => p.name === param.name ? "undefined" : this.generateTestValue(p)).join(", ");
|
|
13077
|
+
testCases.push({
|
|
13078
|
+
description: `should handle undefined ${param.name}`,
|
|
13079
|
+
type: "error-handling",
|
|
13080
|
+
action: fn.isAsync ? `const action = async () => await ${fn.name}(${paramsWithUndefined});` : `const action = () => ${fn.name}(${paramsWithUndefined});`,
|
|
13081
|
+
assertion: "expect(action).toThrow();"
|
|
13082
|
+
});
|
|
12186
13083
|
}
|
|
12187
|
-
|
|
12188
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
13084
|
+
testCases.push(...this.generateBoundaryTestCases(fn, param));
|
|
12189
13085
|
}
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
const { function: funcName, properties, constraints = {} } = request;
|
|
12197
|
-
const tests = properties.map((property) => ({
|
|
12198
|
-
property,
|
|
12199
|
-
testCode: this.generatePropertyTestCode(funcName, property, constraints),
|
|
12200
|
-
generators: this.inferGenerators(property, constraints)
|
|
12201
|
-
}));
|
|
12202
|
-
return ok({
|
|
12203
|
-
tests,
|
|
12204
|
-
arbitraries: this.collectArbitraries(tests)
|
|
13086
|
+
if (fn.isAsync) {
|
|
13087
|
+
testCases.push({
|
|
13088
|
+
description: "should handle async rejection gracefully",
|
|
13089
|
+
type: "error-handling",
|
|
13090
|
+
action: `// Mock or setup to cause rejection`,
|
|
13091
|
+
assertion: `// await expect(${fn.name}(invalidParams)).rejects.toThrow();`
|
|
12205
13092
|
});
|
|
12206
|
-
} catch (error) {
|
|
12207
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
12208
13093
|
}
|
|
13094
|
+
return testCases;
|
|
12209
13095
|
}
|
|
12210
13096
|
/**
|
|
12211
|
-
* Generate test
|
|
13097
|
+
* Generate boundary test cases for a parameter based on its type
|
|
12212
13098
|
*/
|
|
12213
|
-
|
|
12214
|
-
|
|
12215
|
-
|
|
12216
|
-
|
|
12217
|
-
const
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
}
|
|
12225
|
-
return ok({
|
|
12226
|
-
records,
|
|
12227
|
-
schema,
|
|
12228
|
-
seed
|
|
13099
|
+
generateBoundaryTestCases(fn, param) {
|
|
13100
|
+
const testCases = [];
|
|
13101
|
+
const type = param.type?.toLowerCase() || "";
|
|
13102
|
+
if (type.includes("string")) {
|
|
13103
|
+
const paramsWithEmpty = fn.parameters.map((p) => p.name === param.name ? "''" : this.generateTestValue(p)).join(", ");
|
|
13104
|
+
const emptyCall = fn.isAsync ? `await ${fn.name}(${paramsWithEmpty})` : `${fn.name}(${paramsWithEmpty})`;
|
|
13105
|
+
testCases.push({
|
|
13106
|
+
description: `should handle empty string for ${param.name}`,
|
|
13107
|
+
type: "boundary",
|
|
13108
|
+
action: `const result = ${emptyCall};`,
|
|
13109
|
+
assertion: "expect(result).toBeDefined();"
|
|
12229
13110
|
});
|
|
12230
|
-
} catch (error) {
|
|
12231
|
-
return err(error instanceof Error ? error : new Error(String(error)));
|
|
12232
13111
|
}
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
13112
|
+
if (type.includes("number")) {
|
|
13113
|
+
const paramsWithZero = fn.parameters.map((p) => p.name === param.name ? "0" : this.generateTestValue(p)).join(", ");
|
|
13114
|
+
const zeroCall = fn.isAsync ? `await ${fn.name}(${paramsWithZero})` : `${fn.name}(${paramsWithZero})`;
|
|
13115
|
+
testCases.push({
|
|
13116
|
+
description: `should handle zero for ${param.name}`,
|
|
13117
|
+
type: "boundary",
|
|
13118
|
+
action: `const result = ${zeroCall};`,
|
|
13119
|
+
assertion: "expect(result).toBeDefined();"
|
|
13120
|
+
});
|
|
13121
|
+
const paramsWithNegative = fn.parameters.map((p) => p.name === param.name ? "-1" : this.generateTestValue(p)).join(", ");
|
|
13122
|
+
const negativeCall = fn.isAsync ? `await ${fn.name}(${paramsWithNegative})` : `${fn.name}(${paramsWithNegative})`;
|
|
13123
|
+
testCases.push({
|
|
13124
|
+
description: `should handle negative value for ${param.name}`,
|
|
13125
|
+
type: "edge-case",
|
|
13126
|
+
action: `const result = ${negativeCall};`,
|
|
13127
|
+
assertion: "expect(result).toBeDefined();"
|
|
13128
|
+
});
|
|
12247
13129
|
}
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
);
|
|
12257
|
-
} else {
|
|
12258
|
-
testCode = this.generateStubTestCode(sourceFile, testType, framework, applicablePatterns);
|
|
13130
|
+
if (type.includes("[]") || type.includes("array")) {
|
|
13131
|
+
const paramsWithEmpty = fn.parameters.map((p) => p.name === param.name ? "[]" : this.generateTestValue(p)).join(", ");
|
|
13132
|
+
const emptyCall = fn.isAsync ? `await ${fn.name}(${paramsWithEmpty})` : `${fn.name}(${paramsWithEmpty})`;
|
|
13133
|
+
testCases.push({
|
|
13134
|
+
description: `should handle empty array for ${param.name}`,
|
|
13135
|
+
type: "boundary",
|
|
13136
|
+
action: `const result = ${emptyCall};`,
|
|
13137
|
+
assertion: "expect(result).toBeDefined();"
|
|
13138
|
+
});
|
|
12259
13139
|
}
|
|
12260
|
-
|
|
12261
|
-
id: uuidv44(),
|
|
12262
|
-
name: `${this.extractModuleName(sourceFile)} tests`,
|
|
12263
|
-
sourceFile,
|
|
12264
|
-
testFile,
|
|
12265
|
-
testCode,
|
|
12266
|
-
type: testType,
|
|
12267
|
-
assertions: this.countAssertions(testCode)
|
|
12268
|
-
};
|
|
12269
|
-
return ok({ tests: [test], patternsUsed });
|
|
13140
|
+
return testCases;
|
|
12270
13141
|
}
|
|
12271
13142
|
/**
|
|
12272
|
-
*
|
|
13143
|
+
* Extract exports from code analysis for import statements
|
|
12273
13144
|
*/
|
|
12274
|
-
|
|
12275
|
-
const
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
const classes = [];
|
|
12284
|
-
const visit = (node) => {
|
|
12285
|
-
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
12286
|
-
functions.push(this.extractFunctionInfo(node, sourceFile));
|
|
12287
|
-
} else if (ts.isVariableStatement(node)) {
|
|
12288
|
-
for (const declaration of node.declarationList.declarations) {
|
|
12289
|
-
if (ts.isVariableDeclaration(declaration) && declaration.initializer && (ts.isArrowFunction(declaration.initializer) || ts.isFunctionExpression(declaration.initializer))) {
|
|
12290
|
-
const name = declaration.name.getText(sourceFile);
|
|
12291
|
-
functions.push(
|
|
12292
|
-
this.extractArrowFunctionInfo(name, declaration.initializer, sourceFile, node)
|
|
12293
|
-
);
|
|
12294
|
-
}
|
|
12295
|
-
}
|
|
12296
|
-
} else if (ts.isClassDeclaration(node) && node.name) {
|
|
12297
|
-
classes.push(this.extractClassInfo(node, sourceFile));
|
|
12298
|
-
}
|
|
12299
|
-
ts.forEachChild(node, visit);
|
|
12300
|
-
};
|
|
12301
|
-
ts.forEachChild(sourceFile, visit);
|
|
12302
|
-
return { functions, classes };
|
|
13145
|
+
extractExports(functions, classes) {
|
|
13146
|
+
const exports = [];
|
|
13147
|
+
for (const fn of functions) {
|
|
13148
|
+
if (fn.isExported) exports.push(fn.name);
|
|
13149
|
+
}
|
|
13150
|
+
for (const cls of classes) {
|
|
13151
|
+
if (cls.isExported) exports.push(cls.name);
|
|
13152
|
+
}
|
|
13153
|
+
return exports;
|
|
12303
13154
|
}
|
|
12304
13155
|
/**
|
|
12305
|
-
*
|
|
13156
|
+
* Generate import statement based on exports
|
|
12306
13157
|
*/
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
12313
|
-
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
12314
|
-
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
12315
|
-
return {
|
|
12316
|
-
name,
|
|
12317
|
-
parameters,
|
|
12318
|
-
returnType,
|
|
12319
|
-
isAsync,
|
|
12320
|
-
isExported,
|
|
12321
|
-
complexity: this.calculateComplexity(node),
|
|
12322
|
-
startLine: startLine + 1,
|
|
12323
|
-
endLine: endLine + 1,
|
|
12324
|
-
body: node.body?.getText(sourceFile)
|
|
12325
|
-
};
|
|
13158
|
+
generateImportStatement(exports, importPath, moduleName) {
|
|
13159
|
+
if (exports.length > 0) {
|
|
13160
|
+
return `import { ${exports.join(", ")} } from '${importPath}';`;
|
|
13161
|
+
}
|
|
13162
|
+
return `import * as ${moduleName} from '${importPath}';`;
|
|
12326
13163
|
}
|
|
12327
13164
|
/**
|
|
12328
|
-
*
|
|
13165
|
+
* Generate pattern comment header
|
|
12329
13166
|
*/
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
const isExported = ts.isVariableStatement(parentNode) && (parentNode.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false);
|
|
12335
|
-
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
12336
|
-
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
12337
|
-
return {
|
|
12338
|
-
name,
|
|
12339
|
-
parameters,
|
|
12340
|
-
returnType,
|
|
12341
|
-
isAsync,
|
|
12342
|
-
isExported,
|
|
12343
|
-
complexity: this.calculateComplexity(node),
|
|
12344
|
-
startLine: startLine + 1,
|
|
12345
|
-
endLine: endLine + 1,
|
|
12346
|
-
body: node.body?.getText(sourceFile)
|
|
12347
|
-
};
|
|
13167
|
+
generatePatternComment(patterns) {
|
|
13168
|
+
if (patterns.length === 0) return "";
|
|
13169
|
+
return `// Applied patterns: ${patterns.map((p) => p.name).join(", ")}
|
|
13170
|
+
`;
|
|
12348
13171
|
}
|
|
12349
13172
|
/**
|
|
12350
|
-
*
|
|
13173
|
+
* Convert string to camelCase
|
|
12351
13174
|
*/
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
const methods = [];
|
|
12355
|
-
const properties = [];
|
|
12356
|
-
let hasConstructor = false;
|
|
12357
|
-
let constructorParams;
|
|
12358
|
-
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
12359
|
-
for (const member of node.members) {
|
|
12360
|
-
if (ts.isMethodDeclaration(member)) {
|
|
12361
|
-
const methodName = member.name.getText(sourceFile);
|
|
12362
|
-
const parameters = this.extractParameters(member.parameters, sourceFile);
|
|
12363
|
-
const returnType = member.type?.getText(sourceFile);
|
|
12364
|
-
const isAsync = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
12365
|
-
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(
|
|
12366
|
-
member.getStart(sourceFile)
|
|
12367
|
-
);
|
|
12368
|
-
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(member.getEnd());
|
|
12369
|
-
methods.push({
|
|
12370
|
-
name: methodName,
|
|
12371
|
-
parameters,
|
|
12372
|
-
returnType,
|
|
12373
|
-
isAsync,
|
|
12374
|
-
isExported: false,
|
|
12375
|
-
complexity: this.calculateComplexity(member),
|
|
12376
|
-
startLine: startLine + 1,
|
|
12377
|
-
endLine: endLine + 1,
|
|
12378
|
-
body: member.body?.getText(sourceFile)
|
|
12379
|
-
});
|
|
12380
|
-
} else if (ts.isConstructorDeclaration(member)) {
|
|
12381
|
-
hasConstructor = true;
|
|
12382
|
-
constructorParams = this.extractParameters(member.parameters, sourceFile);
|
|
12383
|
-
} else if (ts.isPropertyDeclaration(member)) {
|
|
12384
|
-
const propName = member.name.getText(sourceFile);
|
|
12385
|
-
const propType = member.type?.getText(sourceFile);
|
|
12386
|
-
const isPrivate = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword) ?? false;
|
|
12387
|
-
const isReadonly = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false;
|
|
12388
|
-
properties.push({
|
|
12389
|
-
name: propName,
|
|
12390
|
-
type: propType,
|
|
12391
|
-
isPrivate,
|
|
12392
|
-
isReadonly
|
|
12393
|
-
});
|
|
12394
|
-
}
|
|
12395
|
-
}
|
|
12396
|
-
return {
|
|
12397
|
-
name,
|
|
12398
|
-
methods,
|
|
12399
|
-
properties,
|
|
12400
|
-
isExported,
|
|
12401
|
-
hasConstructor,
|
|
12402
|
-
constructorParams
|
|
12403
|
-
};
|
|
13175
|
+
camelCase(str) {
|
|
13176
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase()).replace(/^./, (chr) => chr.toLowerCase());
|
|
12404
13177
|
}
|
|
12405
13178
|
/**
|
|
12406
|
-
*
|
|
13179
|
+
* Convert string to PascalCase
|
|
12407
13180
|
*/
|
|
12408
|
-
|
|
12409
|
-
return
|
|
12410
|
-
name: param.name.getText(sourceFile),
|
|
12411
|
-
type: param.type?.getText(sourceFile),
|
|
12412
|
-
optional: param.questionToken !== void 0,
|
|
12413
|
-
defaultValue: param.initializer?.getText(sourceFile)
|
|
12414
|
-
}));
|
|
13181
|
+
pascalCase(str) {
|
|
13182
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase()).replace(/^./, (chr) => chr.toUpperCase());
|
|
12415
13183
|
}
|
|
12416
13184
|
/**
|
|
12417
|
-
*
|
|
13185
|
+
* Format line range for display
|
|
12418
13186
|
*/
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
break;
|
|
12434
|
-
case ts.SyntaxKind.BinaryExpression: {
|
|
12435
|
-
const binary = n;
|
|
12436
|
-
if (binary.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || binary.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
12437
|
-
complexity++;
|
|
12438
|
-
}
|
|
12439
|
-
break;
|
|
12440
|
-
}
|
|
12441
|
-
}
|
|
12442
|
-
ts.forEachChild(n, visit);
|
|
12443
|
-
};
|
|
12444
|
-
ts.forEachChild(node, visit);
|
|
12445
|
-
return complexity;
|
|
13187
|
+
formatLineRange(lines) {
|
|
13188
|
+
if (lines.length === 1) {
|
|
13189
|
+
return `line ${lines[0]}`;
|
|
13190
|
+
}
|
|
13191
|
+
return `lines ${lines[0]}-${lines[lines.length - 1]}`;
|
|
13192
|
+
}
|
|
13193
|
+
};
|
|
13194
|
+
|
|
13195
|
+
// src/domains/test-generation/generators/jest-vitest-generator.ts
|
|
13196
|
+
var JestVitestGenerator = class extends BaseTestGenerator {
|
|
13197
|
+
framework;
|
|
13198
|
+
constructor(framework = "vitest") {
|
|
13199
|
+
super();
|
|
13200
|
+
this.framework = framework;
|
|
12446
13201
|
}
|
|
12447
13202
|
/**
|
|
12448
|
-
*
|
|
13203
|
+
* Get the mock utility name (vi for vitest, jest for jest)
|
|
12449
13204
|
*/
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
const importPath = this.getImportPath(sourceFile);
|
|
12453
|
-
switch (framework) {
|
|
12454
|
-
case "jest":
|
|
12455
|
-
case "vitest":
|
|
12456
|
-
return this.generateRealJestVitestTest(
|
|
12457
|
-
moduleName,
|
|
12458
|
-
importPath,
|
|
12459
|
-
testType,
|
|
12460
|
-
analysis,
|
|
12461
|
-
patterns,
|
|
12462
|
-
framework
|
|
12463
|
-
);
|
|
12464
|
-
case "mocha":
|
|
12465
|
-
return this.generateRealMochaTest(moduleName, importPath, testType, analysis, patterns);
|
|
12466
|
-
case "pytest":
|
|
12467
|
-
return this.generateRealPytestTest(moduleName, importPath, testType, analysis, patterns);
|
|
12468
|
-
default:
|
|
12469
|
-
return this.generateRealJestVitestTest(
|
|
12470
|
-
moduleName,
|
|
12471
|
-
importPath,
|
|
12472
|
-
testType,
|
|
12473
|
-
analysis,
|
|
12474
|
-
patterns,
|
|
12475
|
-
"vitest"
|
|
12476
|
-
);
|
|
12477
|
-
}
|
|
13205
|
+
get mockUtil() {
|
|
13206
|
+
return this.framework === "vitest" ? "vi" : "jest";
|
|
12478
13207
|
}
|
|
12479
13208
|
/**
|
|
12480
|
-
* Generate
|
|
13209
|
+
* Generate complete test file from analysis
|
|
12481
13210
|
*/
|
|
12482
|
-
|
|
12483
|
-
const
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
for (const fn of analysis.functions) {
|
|
12487
|
-
if (fn.isExported) exports.push(fn.name);
|
|
12488
|
-
}
|
|
12489
|
-
for (const cls of analysis.classes) {
|
|
12490
|
-
if (cls.isExported) exports.push(cls.name);
|
|
13211
|
+
generateTests(context) {
|
|
13212
|
+
const { moduleName, importPath, testType, patterns, analysis } = context;
|
|
13213
|
+
if (!analysis || analysis.functions.length === 0 && analysis.classes.length === 0) {
|
|
13214
|
+
return this.generateStubTests(context);
|
|
12491
13215
|
}
|
|
12492
|
-
const
|
|
12493
|
-
|
|
13216
|
+
const patternComment = this.generatePatternComment(patterns);
|
|
13217
|
+
const exports = this.extractExports(analysis.functions, analysis.classes);
|
|
13218
|
+
const importStatement = this.generateImportStatement(exports, importPath, moduleName);
|
|
13219
|
+
const mockImport = this.framework === "vitest" ? ", vi" : "";
|
|
13220
|
+
let testCode = `${patternComment}import { describe, it, expect, beforeEach${mockImport} } from '${this.framework}';
|
|
12494
13221
|
${importStatement}
|
|
12495
13222
|
|
|
12496
13223
|
`;
|
|
@@ -12503,7 +13230,7 @@ ${importStatement}
|
|
|
12503
13230
|
return testCode;
|
|
12504
13231
|
}
|
|
12505
13232
|
/**
|
|
12506
|
-
* Generate tests for a function
|
|
13233
|
+
* Generate tests for a standalone function
|
|
12507
13234
|
*/
|
|
12508
13235
|
generateFunctionTests(fn, _testType) {
|
|
12509
13236
|
const testCases = this.generateTestCasesForFunction(fn);
|
|
@@ -12531,78 +13258,6 @@ ${importStatement}
|
|
|
12531
13258
|
`;
|
|
12532
13259
|
return code;
|
|
12533
13260
|
}
|
|
12534
|
-
/**
|
|
12535
|
-
* Generate test cases for a function
|
|
12536
|
-
*/
|
|
12537
|
-
generateTestCasesForFunction(fn) {
|
|
12538
|
-
const testCases = [];
|
|
12539
|
-
const validParams = fn.parameters.map((p) => this.generateTestValue(p)).join(", ");
|
|
12540
|
-
const fnCall = fn.isAsync ? `await ${fn.name}(${validParams})` : `${fn.name}(${validParams})`;
|
|
12541
|
-
testCases.push({
|
|
12542
|
-
description: "should handle valid input correctly",
|
|
12543
|
-
type: "happy-path",
|
|
12544
|
-
action: `const result = ${fnCall};`,
|
|
12545
|
-
assertion: "expect(result).toBeDefined();"
|
|
12546
|
-
});
|
|
12547
|
-
for (const param of fn.parameters) {
|
|
12548
|
-
if (!param.optional) {
|
|
12549
|
-
const paramsWithUndefined = fn.parameters.map((p) => p.name === param.name ? "undefined" : this.generateTestValue(p)).join(", ");
|
|
12550
|
-
testCases.push({
|
|
12551
|
-
description: `should handle undefined ${param.name}`,
|
|
12552
|
-
type: "error-handling",
|
|
12553
|
-
action: fn.isAsync ? `const action = async () => await ${fn.name}(${paramsWithUndefined});` : `const action = () => ${fn.name}(${paramsWithUndefined});`,
|
|
12554
|
-
assertion: "expect(action).toThrow();"
|
|
12555
|
-
});
|
|
12556
|
-
}
|
|
12557
|
-
if (param.type?.includes("string")) {
|
|
12558
|
-
const paramsWithEmpty = fn.parameters.map((p) => p.name === param.name ? "''" : this.generateTestValue(p)).join(", ");
|
|
12559
|
-
const emptyCall = fn.isAsync ? `await ${fn.name}(${paramsWithEmpty})` : `${fn.name}(${paramsWithEmpty})`;
|
|
12560
|
-
testCases.push({
|
|
12561
|
-
description: `should handle empty string for ${param.name}`,
|
|
12562
|
-
type: "boundary",
|
|
12563
|
-
action: `const result = ${emptyCall};`,
|
|
12564
|
-
assertion: "expect(result).toBeDefined();"
|
|
12565
|
-
});
|
|
12566
|
-
}
|
|
12567
|
-
if (param.type?.includes("number")) {
|
|
12568
|
-
const paramsWithZero = fn.parameters.map((p) => p.name === param.name ? "0" : this.generateTestValue(p)).join(", ");
|
|
12569
|
-
const zeroCall = fn.isAsync ? `await ${fn.name}(${paramsWithZero})` : `${fn.name}(${paramsWithZero})`;
|
|
12570
|
-
testCases.push({
|
|
12571
|
-
description: `should handle zero for ${param.name}`,
|
|
12572
|
-
type: "boundary",
|
|
12573
|
-
action: `const result = ${zeroCall};`,
|
|
12574
|
-
assertion: "expect(result).toBeDefined();"
|
|
12575
|
-
});
|
|
12576
|
-
const paramsWithNegative = fn.parameters.map((p) => p.name === param.name ? "-1" : this.generateTestValue(p)).join(", ");
|
|
12577
|
-
const negativeCall = fn.isAsync ? `await ${fn.name}(${paramsWithNegative})` : `${fn.name}(${paramsWithNegative})`;
|
|
12578
|
-
testCases.push({
|
|
12579
|
-
description: `should handle negative value for ${param.name}`,
|
|
12580
|
-
type: "edge-case",
|
|
12581
|
-
action: `const result = ${negativeCall};`,
|
|
12582
|
-
assertion: "expect(result).toBeDefined();"
|
|
12583
|
-
});
|
|
12584
|
-
}
|
|
12585
|
-
if (param.type?.includes("[]") || param.type?.includes("Array")) {
|
|
12586
|
-
const paramsWithEmpty = fn.parameters.map((p) => p.name === param.name ? "[]" : this.generateTestValue(p)).join(", ");
|
|
12587
|
-
const emptyCall = fn.isAsync ? `await ${fn.name}(${paramsWithEmpty})` : `${fn.name}(${paramsWithEmpty})`;
|
|
12588
|
-
testCases.push({
|
|
12589
|
-
description: `should handle empty array for ${param.name}`,
|
|
12590
|
-
type: "boundary",
|
|
12591
|
-
action: `const result = ${emptyCall};`,
|
|
12592
|
-
assertion: "expect(result).toBeDefined();"
|
|
12593
|
-
});
|
|
12594
|
-
}
|
|
12595
|
-
}
|
|
12596
|
-
if (fn.isAsync) {
|
|
12597
|
-
testCases.push({
|
|
12598
|
-
description: "should handle async rejection gracefully",
|
|
12599
|
-
type: "error-handling",
|
|
12600
|
-
action: `// Mock or setup to cause rejection`,
|
|
12601
|
-
assertion: `// await expect(${fn.name}(invalidParams)).rejects.toThrow();`
|
|
12602
|
-
});
|
|
12603
|
-
}
|
|
12604
|
-
return testCases;
|
|
12605
|
-
}
|
|
12606
13261
|
/**
|
|
12607
13262
|
* Generate tests for a class
|
|
12608
13263
|
*/
|
|
@@ -12682,259 +13337,11 @@ ${importStatement}
|
|
|
12682
13337
|
return code;
|
|
12683
13338
|
}
|
|
12684
13339
|
/**
|
|
12685
|
-
* Generate
|
|
12686
|
-
*/
|
|
12687
|
-
generateTestValue(param) {
|
|
12688
|
-
if (param.defaultValue) {
|
|
12689
|
-
return param.defaultValue;
|
|
12690
|
-
}
|
|
12691
|
-
const type = param.type?.toLowerCase() || "unknown";
|
|
12692
|
-
const name = param.name.toLowerCase();
|
|
12693
|
-
if (name.includes("id")) return `'${faker.string.uuid()}'`;
|
|
12694
|
-
if (name.includes("email")) return `'${faker.internet.email()}'`;
|
|
12695
|
-
if (name.includes("name")) return `'${faker.person.fullName()}'`;
|
|
12696
|
-
if (name.includes("url")) return `'${faker.internet.url()}'`;
|
|
12697
|
-
if (name.includes("date")) return `new Date('${faker.date.recent().toISOString()}')`;
|
|
12698
|
-
if (type.includes("string")) return `'${faker.lorem.word()}'`;
|
|
12699
|
-
if (type.includes("number")) return String(faker.number.int({ min: 1, max: 100 }));
|
|
12700
|
-
if (type.includes("boolean")) return "true";
|
|
12701
|
-
if (type.includes("[]") || type.includes("array")) return "[]";
|
|
12702
|
-
if (type.includes("object") || type.includes("{")) return "{}";
|
|
12703
|
-
if (type.includes("function")) return "() => {}";
|
|
12704
|
-
if (type.includes("promise")) return "Promise.resolve()";
|
|
12705
|
-
if (type.includes("date")) return "new Date()";
|
|
12706
|
-
return `mock${param.name.charAt(0).toUpperCase() + param.name.slice(1)}`;
|
|
12707
|
-
}
|
|
12708
|
-
/**
|
|
12709
|
-
* Generate real Mocha test code
|
|
12710
|
-
*/
|
|
12711
|
-
generateRealMochaTest(moduleName, importPath, testType, analysis, patterns) {
|
|
12712
|
-
const patternComment = patterns.length > 0 ? `// Applied patterns: ${patterns.map((p) => p.name).join(", ")}
|
|
12713
|
-
` : "";
|
|
12714
|
-
const exports = [];
|
|
12715
|
-
for (const fn of analysis.functions) {
|
|
12716
|
-
if (fn.isExported) exports.push(fn.name);
|
|
12717
|
-
}
|
|
12718
|
-
for (const cls of analysis.classes) {
|
|
12719
|
-
if (cls.isExported) exports.push(cls.name);
|
|
12720
|
-
}
|
|
12721
|
-
const importStatement = exports.length > 0 ? `import { ${exports.join(", ")} } from '${importPath}';` : `import * as ${moduleName} from '${importPath}';`;
|
|
12722
|
-
let code = `${patternComment}import { expect } from 'chai';
|
|
12723
|
-
${importStatement}
|
|
12724
|
-
|
|
12725
|
-
describe('${moduleName} - ${testType} tests', function() {
|
|
12726
|
-
`;
|
|
12727
|
-
for (const fn of analysis.functions) {
|
|
12728
|
-
code += this.generateMochaFunctionTests(fn);
|
|
12729
|
-
}
|
|
12730
|
-
for (const cls of analysis.classes) {
|
|
12731
|
-
code += this.generateMochaClassTests(cls);
|
|
12732
|
-
}
|
|
12733
|
-
code += `});
|
|
12734
|
-
`;
|
|
12735
|
-
return code;
|
|
12736
|
-
}
|
|
12737
|
-
/**
|
|
12738
|
-
* Generate Mocha tests for a function
|
|
12739
|
-
*/
|
|
12740
|
-
generateMochaFunctionTests(fn) {
|
|
12741
|
-
const validParams = fn.parameters.map((p) => this.generateTestValue(p)).join(", ");
|
|
12742
|
-
const fnCall = fn.isAsync ? `await ${fn.name}(${validParams})` : `${fn.name}(${validParams})`;
|
|
12743
|
-
let code = ` describe('${fn.name}', function() {
|
|
12744
|
-
`;
|
|
12745
|
-
code += ` it('should handle valid input', ${fn.isAsync ? "async " : ""}function() {
|
|
12746
|
-
`;
|
|
12747
|
-
code += ` const result = ${fnCall};
|
|
12748
|
-
`;
|
|
12749
|
-
code += ` expect(result).to.not.be.undefined;
|
|
12750
|
-
`;
|
|
12751
|
-
code += ` });
|
|
12752
|
-
`;
|
|
12753
|
-
code += ` });
|
|
12754
|
-
|
|
12755
|
-
`;
|
|
12756
|
-
return code;
|
|
12757
|
-
}
|
|
12758
|
-
/**
|
|
12759
|
-
* Generate Mocha tests for a class
|
|
12760
|
-
*/
|
|
12761
|
-
generateMochaClassTests(cls) {
|
|
12762
|
-
const constructorArgs = cls.constructorParams?.map((p) => this.generateTestValue(p)).join(", ") || "";
|
|
12763
|
-
let code = ` describe('${cls.name}', function() {
|
|
12764
|
-
`;
|
|
12765
|
-
code += ` let instance;
|
|
12766
|
-
|
|
12767
|
-
`;
|
|
12768
|
-
code += ` beforeEach(function() {
|
|
12769
|
-
`;
|
|
12770
|
-
code += ` instance = new ${cls.name}(${constructorArgs});
|
|
12771
|
-
`;
|
|
12772
|
-
code += ` });
|
|
12773
|
-
|
|
12774
|
-
`;
|
|
12775
|
-
code += ` it('should instantiate correctly', function() {
|
|
12776
|
-
`;
|
|
12777
|
-
code += ` expect(instance).to.be.instanceOf(${cls.name});
|
|
12778
|
-
`;
|
|
12779
|
-
code += ` });
|
|
12780
|
-
`;
|
|
12781
|
-
for (const method of cls.methods) {
|
|
12782
|
-
if (!method.name.startsWith("_")) {
|
|
12783
|
-
const methodParams = method.parameters.map((p) => this.generateTestValue(p)).join(", ");
|
|
12784
|
-
code += `
|
|
12785
|
-
it('${method.name} should work', ${method.isAsync ? "async " : ""}function() {
|
|
12786
|
-
`;
|
|
12787
|
-
code += ` const result = ${method.isAsync ? "await " : ""}instance.${method.name}(${methodParams});
|
|
12788
|
-
`;
|
|
12789
|
-
code += ` expect(result).to.not.be.undefined;
|
|
12790
|
-
`;
|
|
12791
|
-
code += ` });
|
|
12792
|
-
`;
|
|
12793
|
-
}
|
|
12794
|
-
}
|
|
12795
|
-
code += ` });
|
|
12796
|
-
|
|
12797
|
-
`;
|
|
12798
|
-
return code;
|
|
12799
|
-
}
|
|
12800
|
-
/**
|
|
12801
|
-
* Generate real Pytest test code
|
|
12802
|
-
*/
|
|
12803
|
-
generateRealPytestTest(moduleName, importPath, testType, analysis, patterns) {
|
|
12804
|
-
const patternComment = patterns.length > 0 ? `# Applied patterns: ${patterns.map((p) => p.name).join(", ")}
|
|
12805
|
-
` : "";
|
|
12806
|
-
const exports = [];
|
|
12807
|
-
for (const fn of analysis.functions) {
|
|
12808
|
-
if (fn.isExported) exports.push(fn.name);
|
|
12809
|
-
}
|
|
12810
|
-
for (const cls of analysis.classes) {
|
|
12811
|
-
if (cls.isExported) exports.push(cls.name);
|
|
12812
|
-
}
|
|
12813
|
-
const pythonImport = importPath.replace(/\//g, ".").replace(/\.(ts|js)$/, "");
|
|
12814
|
-
const importStatement = exports.length > 0 ? `from ${pythonImport} import ${exports.join(", ")}` : `import ${pythonImport} as ${moduleName}`;
|
|
12815
|
-
let code = `${patternComment}import pytest
|
|
12816
|
-
${importStatement}
|
|
12817
|
-
|
|
12818
|
-
|
|
12819
|
-
class Test${moduleName.charAt(0).toUpperCase() + moduleName.slice(1)}:
|
|
12820
|
-
"""${testType} tests for ${moduleName}"""
|
|
12821
|
-
|
|
12822
|
-
`;
|
|
12823
|
-
for (const fn of analysis.functions) {
|
|
12824
|
-
code += this.generatePytestFunctionTests(fn);
|
|
12825
|
-
}
|
|
12826
|
-
for (const cls of analysis.classes) {
|
|
12827
|
-
code += this.generatePytestClassTests(cls);
|
|
12828
|
-
}
|
|
12829
|
-
return code;
|
|
12830
|
-
}
|
|
12831
|
-
/**
|
|
12832
|
-
* Generate Pytest tests for a function
|
|
12833
|
-
*/
|
|
12834
|
-
generatePytestFunctionTests(fn) {
|
|
12835
|
-
const validParams = fn.parameters.map((p) => this.generatePythonTestValue(p)).join(", ");
|
|
12836
|
-
let code = ` def test_${fn.name}_valid_input(self):
|
|
12837
|
-
`;
|
|
12838
|
-
code += ` """Test ${fn.name} with valid input"""
|
|
12839
|
-
`;
|
|
12840
|
-
code += ` result = ${fn.name}(${validParams})
|
|
12841
|
-
`;
|
|
12842
|
-
code += ` assert result is not None
|
|
12843
|
-
|
|
12844
|
-
`;
|
|
12845
|
-
return code;
|
|
12846
|
-
}
|
|
12847
|
-
/**
|
|
12848
|
-
* Generate Pytest tests for a class
|
|
12849
|
-
*/
|
|
12850
|
-
generatePytestClassTests(cls) {
|
|
12851
|
-
const constructorArgs = cls.constructorParams?.map((p) => this.generatePythonTestValue(p)).join(", ") || "";
|
|
12852
|
-
let code = `
|
|
12853
|
-
class Test${cls.name}:
|
|
12854
|
-
`;
|
|
12855
|
-
code += ` """Tests for ${cls.name}"""
|
|
12856
|
-
|
|
12857
|
-
`;
|
|
12858
|
-
code += ` @pytest.fixture
|
|
12859
|
-
`;
|
|
12860
|
-
code += ` def instance(self):
|
|
12861
|
-
`;
|
|
12862
|
-
code += ` return ${cls.name}(${constructorArgs})
|
|
12863
|
-
|
|
12864
|
-
`;
|
|
12865
|
-
code += ` def test_instantiation(self, instance):
|
|
12866
|
-
`;
|
|
12867
|
-
code += ` assert isinstance(instance, ${cls.name})
|
|
12868
|
-
|
|
12869
|
-
`;
|
|
12870
|
-
for (const method of cls.methods) {
|
|
12871
|
-
if (!method.name.startsWith("_")) {
|
|
12872
|
-
const methodParams = method.parameters.map((p) => this.generatePythonTestValue(p)).join(", ");
|
|
12873
|
-
code += ` def test_${method.name}(self, instance):
|
|
12874
|
-
`;
|
|
12875
|
-
code += ` result = instance.${method.name}(${methodParams})
|
|
12876
|
-
`;
|
|
12877
|
-
code += ` assert result is not None
|
|
12878
|
-
|
|
12879
|
-
`;
|
|
12880
|
-
}
|
|
12881
|
-
}
|
|
12882
|
-
return code;
|
|
12883
|
-
}
|
|
12884
|
-
/**
|
|
12885
|
-
* Generate a Python test value for a parameter
|
|
13340
|
+
* Generate stub tests when no AST analysis is available
|
|
12886
13341
|
*/
|
|
12887
|
-
|
|
12888
|
-
const
|
|
12889
|
-
const
|
|
12890
|
-
if (name.includes("id")) return `"${faker.string.uuid()}"`;
|
|
12891
|
-
if (name.includes("name")) return `"${faker.person.fullName()}"`;
|
|
12892
|
-
if (name.includes("email")) return `"${faker.internet.email()}"`;
|
|
12893
|
-
if (type.includes("str")) return `"${faker.lorem.word()}"`;
|
|
12894
|
-
if (type.includes("int") || type.includes("number")) {
|
|
12895
|
-
return String(faker.number.int({ min: 1, max: 100 }));
|
|
12896
|
-
}
|
|
12897
|
-
if (type.includes("bool")) return "True";
|
|
12898
|
-
if (type.includes("list") || type.includes("[]")) return "[]";
|
|
12899
|
-
if (type.includes("dict") || type.includes("{}")) return "{}";
|
|
12900
|
-
return "None";
|
|
12901
|
-
}
|
|
12902
|
-
async findApplicablePatterns(sourceFile, requestedPatterns) {
|
|
12903
|
-
const patterns = [];
|
|
12904
|
-
for (const patternName of requestedPatterns) {
|
|
12905
|
-
const stored = await this.memory.get(`pattern:${patternName}`);
|
|
12906
|
-
if (stored) {
|
|
12907
|
-
patterns.push(stored);
|
|
12908
|
-
}
|
|
12909
|
-
}
|
|
12910
|
-
const extension = sourceFile.split(".").pop() || "";
|
|
12911
|
-
const searchResults = await this.memory.search(`pattern:*:${extension}`, 5);
|
|
12912
|
-
for (const key of searchResults) {
|
|
12913
|
-
const pattern = await this.memory.get(key);
|
|
12914
|
-
if (pattern && !patterns.some((p) => p.id === pattern.id)) {
|
|
12915
|
-
patterns.push(pattern);
|
|
12916
|
-
}
|
|
12917
|
-
}
|
|
12918
|
-
return patterns;
|
|
12919
|
-
}
|
|
12920
|
-
generateStubTestCode(sourceFile, testType, framework, patterns) {
|
|
12921
|
-
const moduleName = this.extractModuleName(sourceFile);
|
|
12922
|
-
const importPath = this.getImportPath(sourceFile);
|
|
12923
|
-
switch (framework) {
|
|
12924
|
-
case "jest":
|
|
12925
|
-
case "vitest":
|
|
12926
|
-
return this.generateJestVitestTest(moduleName, importPath, testType, patterns);
|
|
12927
|
-
case "mocha":
|
|
12928
|
-
return this.generateMochaTest(moduleName, importPath, testType, patterns);
|
|
12929
|
-
case "pytest":
|
|
12930
|
-
return this.generatePytestTest(moduleName, importPath, testType, patterns);
|
|
12931
|
-
default:
|
|
12932
|
-
return this.generateJestVitestTest(moduleName, importPath, testType, patterns);
|
|
12933
|
-
}
|
|
12934
|
-
}
|
|
12935
|
-
generateJestVitestTest(moduleName, importPath, testType, patterns) {
|
|
12936
|
-
const patternComment = patterns.length > 0 ? `// Applied patterns: ${patterns.map((p) => p.name).join(", ")}
|
|
12937
|
-
` : "";
|
|
13342
|
+
generateStubTests(context) {
|
|
13343
|
+
const { moduleName, importPath, testType, patterns } = context;
|
|
13344
|
+
const patternComment = this.generatePatternComment(patterns);
|
|
12938
13345
|
const basicOpsTest = this.generateBasicOpsTest(moduleName, patterns);
|
|
12939
13346
|
const edgeCaseTest = this.generateEdgeCaseTest(moduleName, patterns);
|
|
12940
13347
|
const errorHandlingTest = this.generateErrorHandlingTest(moduleName, patterns);
|
|
@@ -12954,7 +13361,43 @@ ${errorHandlingTest}
|
|
|
12954
13361
|
`;
|
|
12955
13362
|
}
|
|
12956
13363
|
/**
|
|
12957
|
-
* Generate
|
|
13364
|
+
* Generate coverage-focused tests for specific lines
|
|
13365
|
+
*/
|
|
13366
|
+
generateCoverageTests(moduleName, importPath, lines) {
|
|
13367
|
+
const funcName = this.camelCase(moduleName);
|
|
13368
|
+
const lineRange = this.formatLineRange(lines);
|
|
13369
|
+
return `// Coverage test for ${lineRange} in ${moduleName}
|
|
13370
|
+
import { ${funcName} } from '${importPath}';
|
|
13371
|
+
|
|
13372
|
+
describe('${moduleName} coverage', () => {
|
|
13373
|
+
describe('${lineRange}', () => {
|
|
13374
|
+
it('should execute code path covering ${lineRange}', () => {
|
|
13375
|
+
// Arrange: Set up test inputs to reach uncovered lines
|
|
13376
|
+
const testInput = undefined; // Replace with appropriate input
|
|
13377
|
+
|
|
13378
|
+
// Act: Execute the code path
|
|
13379
|
+
const result = ${funcName}(testInput);
|
|
13380
|
+
|
|
13381
|
+
// Assert: Verify the code was reached and behaves correctly
|
|
13382
|
+
expect(result).toBeDefined();
|
|
13383
|
+
});
|
|
13384
|
+
|
|
13385
|
+
it('should handle edge case for ${lineRange}', () => {
|
|
13386
|
+
// Arrange: Set up edge case input
|
|
13387
|
+
const edgeCaseInput = null;
|
|
13388
|
+
|
|
13389
|
+
// Act & Assert: Verify edge case handling
|
|
13390
|
+
expect(() => ${funcName}(edgeCaseInput)).not.toThrow();
|
|
13391
|
+
});
|
|
13392
|
+
});
|
|
13393
|
+
});
|
|
13394
|
+
`;
|
|
13395
|
+
}
|
|
13396
|
+
// ============================================================================
|
|
13397
|
+
// Pattern-Aware Stub Test Generators
|
|
13398
|
+
// ============================================================================
|
|
13399
|
+
/**
|
|
13400
|
+
* Generate basic operations test based on detected patterns
|
|
12958
13401
|
*/
|
|
12959
13402
|
generateBasicOpsTest(moduleName, patterns) {
|
|
12960
13403
|
const isService = patterns.some(
|
|
@@ -13013,7 +13456,7 @@ ${errorHandlingTest}
|
|
|
13013
13456
|
});`;
|
|
13014
13457
|
}
|
|
13015
13458
|
/**
|
|
13016
|
-
* Generate edge case test based on patterns
|
|
13459
|
+
* Generate edge case test based on detected patterns
|
|
13017
13460
|
*/
|
|
13018
13461
|
generateEdgeCaseTest(moduleName, patterns) {
|
|
13019
13462
|
const hasValidation = patterns.some(
|
|
@@ -13060,7 +13503,7 @@ ${errorHandlingTest}
|
|
|
13060
13503
|
});`;
|
|
13061
13504
|
}
|
|
13062
13505
|
/**
|
|
13063
|
-
* Generate error handling test based on patterns
|
|
13506
|
+
* Generate error handling test based on detected patterns
|
|
13064
13507
|
*/
|
|
13065
13508
|
generateErrorHandlingTest(moduleName, patterns) {
|
|
13066
13509
|
const hasErrorPattern = patterns.some(
|
|
@@ -13111,9 +13554,118 @@ ${errorHandlingTest}
|
|
|
13111
13554
|
expect(() => Object.keys(${moduleName})).not.toThrow();
|
|
13112
13555
|
});`;
|
|
13113
13556
|
}
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13557
|
+
};
|
|
13558
|
+
|
|
13559
|
+
// src/domains/test-generation/generators/mocha-generator.ts
|
|
13560
|
+
var MochaGenerator = class extends BaseTestGenerator {
|
|
13561
|
+
framework = "mocha";
|
|
13562
|
+
/**
|
|
13563
|
+
* Generate complete test file from analysis
|
|
13564
|
+
*/
|
|
13565
|
+
generateTests(context) {
|
|
13566
|
+
const { moduleName, importPath, testType, patterns, analysis } = context;
|
|
13567
|
+
if (!analysis || analysis.functions.length === 0 && analysis.classes.length === 0) {
|
|
13568
|
+
return this.generateStubTests(context);
|
|
13569
|
+
}
|
|
13570
|
+
const patternComment = this.generatePatternComment(patterns);
|
|
13571
|
+
const exports = this.extractExports(analysis.functions, analysis.classes);
|
|
13572
|
+
const importStatement = this.generateImportStatement(exports, importPath, moduleName);
|
|
13573
|
+
let code = `${patternComment}import { expect } from 'chai';
|
|
13574
|
+
${importStatement}
|
|
13575
|
+
|
|
13576
|
+
describe('${moduleName} - ${testType} tests', function() {
|
|
13577
|
+
`;
|
|
13578
|
+
for (const fn of analysis.functions) {
|
|
13579
|
+
code += this.generateFunctionTests(fn, testType);
|
|
13580
|
+
}
|
|
13581
|
+
for (const cls of analysis.classes) {
|
|
13582
|
+
code += this.generateClassTests(cls, testType);
|
|
13583
|
+
}
|
|
13584
|
+
code += `});
|
|
13585
|
+
`;
|
|
13586
|
+
return code;
|
|
13587
|
+
}
|
|
13588
|
+
/**
|
|
13589
|
+
* Generate tests for a standalone function
|
|
13590
|
+
*/
|
|
13591
|
+
generateFunctionTests(fn, _testType) {
|
|
13592
|
+
const validParams = fn.parameters.map((p) => this.generateTestValue(p)).join(", ");
|
|
13593
|
+
const fnCall = fn.isAsync ? `await ${fn.name}(${validParams})` : `${fn.name}(${validParams})`;
|
|
13594
|
+
let code = ` describe('${fn.name}', function() {
|
|
13595
|
+
`;
|
|
13596
|
+
code += ` it('should handle valid input', ${fn.isAsync ? "async " : ""}function() {
|
|
13597
|
+
`;
|
|
13598
|
+
code += ` const result = ${fnCall};
|
|
13599
|
+
`;
|
|
13600
|
+
code += ` expect(result).to.not.be.undefined;
|
|
13601
|
+
`;
|
|
13602
|
+
code += ` });
|
|
13603
|
+
`;
|
|
13604
|
+
for (const param of fn.parameters) {
|
|
13605
|
+
if (!param.optional) {
|
|
13606
|
+
const paramsWithUndefined = fn.parameters.map((p) => p.name === param.name ? "undefined" : this.generateTestValue(p)).join(", ");
|
|
13607
|
+
code += `
|
|
13608
|
+
it('should handle undefined ${param.name}', function() {
|
|
13609
|
+
`;
|
|
13610
|
+
code += ` expect(function() { ${fn.name}(${paramsWithUndefined}); }).to.throw();
|
|
13611
|
+
`;
|
|
13612
|
+
code += ` });
|
|
13613
|
+
`;
|
|
13614
|
+
}
|
|
13615
|
+
}
|
|
13616
|
+
code += ` });
|
|
13617
|
+
|
|
13618
|
+
`;
|
|
13619
|
+
return code;
|
|
13620
|
+
}
|
|
13621
|
+
/**
|
|
13622
|
+
* Generate tests for a class
|
|
13623
|
+
*/
|
|
13624
|
+
generateClassTests(cls, _testType) {
|
|
13625
|
+
const constructorArgs = cls.constructorParams?.map((p) => this.generateTestValue(p)).join(", ") || "";
|
|
13626
|
+
let code = ` describe('${cls.name}', function() {
|
|
13627
|
+
`;
|
|
13628
|
+
code += ` let instance;
|
|
13629
|
+
|
|
13630
|
+
`;
|
|
13631
|
+
code += ` beforeEach(function() {
|
|
13632
|
+
`;
|
|
13633
|
+
code += ` instance = new ${cls.name}(${constructorArgs});
|
|
13634
|
+
`;
|
|
13635
|
+
code += ` });
|
|
13636
|
+
|
|
13637
|
+
`;
|
|
13638
|
+
code += ` it('should instantiate correctly', function() {
|
|
13639
|
+
`;
|
|
13640
|
+
code += ` expect(instance).to.be.instanceOf(${cls.name});
|
|
13641
|
+
`;
|
|
13642
|
+
code += ` });
|
|
13643
|
+
`;
|
|
13644
|
+
for (const method of cls.methods) {
|
|
13645
|
+
if (!method.name.startsWith("_")) {
|
|
13646
|
+
const methodParams = method.parameters.map((p) => this.generateTestValue(p)).join(", ");
|
|
13647
|
+
code += `
|
|
13648
|
+
it('${method.name} should work', ${method.isAsync ? "async " : ""}function() {
|
|
13649
|
+
`;
|
|
13650
|
+
code += ` const result = ${method.isAsync ? "await " : ""}instance.${method.name}(${methodParams});
|
|
13651
|
+
`;
|
|
13652
|
+
code += ` expect(result).to.not.be.undefined;
|
|
13653
|
+
`;
|
|
13654
|
+
code += ` });
|
|
13655
|
+
`;
|
|
13656
|
+
}
|
|
13657
|
+
}
|
|
13658
|
+
code += ` });
|
|
13659
|
+
|
|
13660
|
+
`;
|
|
13661
|
+
return code;
|
|
13662
|
+
}
|
|
13663
|
+
/**
|
|
13664
|
+
* Generate stub tests when no AST analysis is available
|
|
13665
|
+
*/
|
|
13666
|
+
generateStubTests(context) {
|
|
13667
|
+
const { moduleName, importPath, testType, patterns } = context;
|
|
13668
|
+
const patternComment = this.generatePatternComment(patterns);
|
|
13117
13669
|
const isAsync = patterns.some(
|
|
13118
13670
|
(p) => p.name.toLowerCase().includes("async") || p.name.toLowerCase().includes("promise")
|
|
13119
13671
|
);
|
|
@@ -13146,12 +13698,12 @@ describe('${moduleName}', function() {
|
|
|
13146
13698
|
? new ${moduleName}()
|
|
13147
13699
|
: ${moduleName};
|
|
13148
13700
|
expect(instance).to.exist;
|
|
13149
|
-
expect(()
|
|
13701
|
+
expect(function() { JSON.stringify(instance); }).to.not.throw();
|
|
13150
13702
|
});
|
|
13151
13703
|
|
|
13152
13704
|
it('should handle error conditions', function() {
|
|
13153
13705
|
// Verify error resilience
|
|
13154
|
-
expect(()
|
|
13706
|
+
expect(function() {
|
|
13155
13707
|
const instance = typeof ${moduleName} === 'function'
|
|
13156
13708
|
? new ${moduleName}()
|
|
13157
13709
|
: ${moduleName};
|
|
@@ -13162,9 +13714,147 @@ describe('${moduleName}', function() {
|
|
|
13162
13714
|
});
|
|
13163
13715
|
`;
|
|
13164
13716
|
}
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13717
|
+
/**
|
|
13718
|
+
* Generate coverage-focused tests for specific lines
|
|
13719
|
+
*/
|
|
13720
|
+
generateCoverageTests(moduleName, importPath, lines) {
|
|
13721
|
+
const funcName = this.camelCase(moduleName);
|
|
13722
|
+
const lineRange = this.formatLineRange(lines);
|
|
13723
|
+
return `// Coverage test for ${lineRange} in ${moduleName}
|
|
13724
|
+
import { expect } from 'chai';
|
|
13725
|
+
import { ${funcName} } from '${importPath}';
|
|
13726
|
+
|
|
13727
|
+
describe('${moduleName} coverage', function() {
|
|
13728
|
+
describe('${lineRange}', function() {
|
|
13729
|
+
it('should execute code path covering ${lineRange}', function() {
|
|
13730
|
+
// Arrange: Set up test inputs to reach uncovered lines
|
|
13731
|
+
const testInput = undefined; // Replace with appropriate input
|
|
13732
|
+
|
|
13733
|
+
// Act: Execute the code path
|
|
13734
|
+
const result = ${funcName}(testInput);
|
|
13735
|
+
|
|
13736
|
+
// Assert: Verify the code was reached and behaves correctly
|
|
13737
|
+
expect(result).to.not.be.undefined;
|
|
13738
|
+
});
|
|
13739
|
+
|
|
13740
|
+
it('should handle edge case for ${lineRange}', function() {
|
|
13741
|
+
// Arrange: Set up edge case input
|
|
13742
|
+
const edgeCaseInput = null;
|
|
13743
|
+
|
|
13744
|
+
// Act & Assert: Verify edge case handling
|
|
13745
|
+
expect(function() { ${funcName}(edgeCaseInput); }).to.not.throw();
|
|
13746
|
+
});
|
|
13747
|
+
});
|
|
13748
|
+
});
|
|
13749
|
+
`;
|
|
13750
|
+
}
|
|
13751
|
+
};
|
|
13752
|
+
|
|
13753
|
+
// src/domains/test-generation/generators/pytest-generator.ts
|
|
13754
|
+
import { faker as faker2 } from "@faker-js/faker";
|
|
13755
|
+
var PytestGenerator = class extends BaseTestGenerator {
|
|
13756
|
+
framework = "pytest";
|
|
13757
|
+
/**
|
|
13758
|
+
* Generate complete test file from analysis
|
|
13759
|
+
*/
|
|
13760
|
+
generateTests(context) {
|
|
13761
|
+
const { moduleName, importPath, testType, patterns, analysis } = context;
|
|
13762
|
+
if (!analysis || analysis.functions.length === 0 && analysis.classes.length === 0) {
|
|
13763
|
+
return this.generateStubTests(context);
|
|
13764
|
+
}
|
|
13765
|
+
const patternComment = this.generatePythonPatternComment(patterns);
|
|
13766
|
+
const exports = this.extractExports(analysis.functions, analysis.classes);
|
|
13767
|
+
const pythonImport = importPath.replace(/\//g, ".").replace(/\.(ts|js)$/, "");
|
|
13768
|
+
const importStatement = exports.length > 0 ? `from ${pythonImport} import ${exports.join(", ")}` : `import ${pythonImport} as ${moduleName}`;
|
|
13769
|
+
let code = `${patternComment}import pytest
|
|
13770
|
+
${importStatement}
|
|
13771
|
+
|
|
13772
|
+
|
|
13773
|
+
class Test${this.pascalCase(moduleName)}:
|
|
13774
|
+
"""${testType} tests for ${moduleName}"""
|
|
13775
|
+
|
|
13776
|
+
`;
|
|
13777
|
+
for (const fn of analysis.functions) {
|
|
13778
|
+
code += this.generateFunctionTests(fn, testType);
|
|
13779
|
+
}
|
|
13780
|
+
for (const cls of analysis.classes) {
|
|
13781
|
+
code += this.generateClassTests(cls, testType);
|
|
13782
|
+
}
|
|
13783
|
+
return code;
|
|
13784
|
+
}
|
|
13785
|
+
/**
|
|
13786
|
+
* Generate tests for a standalone function
|
|
13787
|
+
*/
|
|
13788
|
+
generateFunctionTests(fn, _testType) {
|
|
13789
|
+
const validParams = fn.parameters.map((p) => this.generatePythonTestValue(p)).join(", ");
|
|
13790
|
+
let code = ` def test_${fn.name}_valid_input(self):
|
|
13791
|
+
`;
|
|
13792
|
+
code += ` """Test ${fn.name} with valid input"""
|
|
13793
|
+
`;
|
|
13794
|
+
code += ` result = ${fn.name}(${validParams})
|
|
13795
|
+
`;
|
|
13796
|
+
code += ` assert result is not None
|
|
13797
|
+
|
|
13798
|
+
`;
|
|
13799
|
+
for (const param of fn.parameters) {
|
|
13800
|
+
if (!param.optional && param.type?.includes("str")) {
|
|
13801
|
+
code += ` def test_${fn.name}_empty_${param.name}(self):
|
|
13802
|
+
`;
|
|
13803
|
+
code += ` """Test ${fn.name} with empty ${param.name}"""
|
|
13804
|
+
`;
|
|
13805
|
+
const paramsWithEmpty = fn.parameters.map((p) => p.name === param.name ? '""' : this.generatePythonTestValue(p)).join(", ");
|
|
13806
|
+
code += ` result = ${fn.name}(${paramsWithEmpty})
|
|
13807
|
+
`;
|
|
13808
|
+
code += ` assert result is not None
|
|
13809
|
+
|
|
13810
|
+
`;
|
|
13811
|
+
}
|
|
13812
|
+
}
|
|
13813
|
+
return code;
|
|
13814
|
+
}
|
|
13815
|
+
/**
|
|
13816
|
+
* Generate tests for a class
|
|
13817
|
+
*/
|
|
13818
|
+
generateClassTests(cls, _testType) {
|
|
13819
|
+
const constructorArgs = cls.constructorParams?.map((p) => this.generatePythonTestValue(p)).join(", ") || "";
|
|
13820
|
+
let code = `
|
|
13821
|
+
class Test${cls.name}:
|
|
13822
|
+
`;
|
|
13823
|
+
code += ` """Tests for ${cls.name}"""
|
|
13824
|
+
|
|
13825
|
+
`;
|
|
13826
|
+
code += ` @pytest.fixture
|
|
13827
|
+
`;
|
|
13828
|
+
code += ` def instance(self):
|
|
13829
|
+
`;
|
|
13830
|
+
code += ` return ${cls.name}(${constructorArgs})
|
|
13831
|
+
|
|
13832
|
+
`;
|
|
13833
|
+
code += ` def test_instantiation(self, instance):
|
|
13834
|
+
`;
|
|
13835
|
+
code += ` assert isinstance(instance, ${cls.name})
|
|
13836
|
+
|
|
13837
|
+
`;
|
|
13838
|
+
for (const method of cls.methods) {
|
|
13839
|
+
if (!method.name.startsWith("_")) {
|
|
13840
|
+
const methodParams = method.parameters.map((p) => this.generatePythonTestValue(p)).join(", ");
|
|
13841
|
+
code += ` def test_${method.name}(self, instance):
|
|
13842
|
+
`;
|
|
13843
|
+
code += ` result = instance.${method.name}(${methodParams})
|
|
13844
|
+
`;
|
|
13845
|
+
code += ` assert result is not None
|
|
13846
|
+
|
|
13847
|
+
`;
|
|
13848
|
+
}
|
|
13849
|
+
}
|
|
13850
|
+
return code;
|
|
13851
|
+
}
|
|
13852
|
+
/**
|
|
13853
|
+
* Generate stub tests when no AST analysis is available
|
|
13854
|
+
*/
|
|
13855
|
+
generateStubTests(context) {
|
|
13856
|
+
const { moduleName, importPath, testType, patterns } = context;
|
|
13857
|
+
const patternComment = this.generatePythonPatternComment(patterns);
|
|
13168
13858
|
const isAsync = patterns.some(
|
|
13169
13859
|
(p) => p.name.toLowerCase().includes("async") || p.name.toLowerCase().includes("promise")
|
|
13170
13860
|
);
|
|
@@ -13174,7 +13864,7 @@ describe('${moduleName}', function() {
|
|
|
13174
13864
|
from ${importPath} import ${moduleName}
|
|
13175
13865
|
|
|
13176
13866
|
|
|
13177
|
-
class Test${moduleName}:
|
|
13867
|
+
class Test${this.pascalCase(moduleName)}:
|
|
13178
13868
|
"""${testType} tests for ${moduleName}"""
|
|
13179
13869
|
|
|
13180
13870
|
def test_is_defined(self):
|
|
@@ -13213,7 +13903,169 @@ class Test${moduleName}:
|
|
|
13213
13903
|
pass
|
|
13214
13904
|
`;
|
|
13215
13905
|
}
|
|
13216
|
-
|
|
13906
|
+
/**
|
|
13907
|
+
* Generate coverage-focused tests for specific lines
|
|
13908
|
+
*/
|
|
13909
|
+
generateCoverageTests(moduleName, importPath, lines) {
|
|
13910
|
+
const funcName = this.camelCase(moduleName);
|
|
13911
|
+
const lineRange = this.formatLineRange(lines);
|
|
13912
|
+
const pythonImport = importPath.replace(/\//g, ".");
|
|
13913
|
+
return `# Coverage test for ${lineRange} in ${moduleName}
|
|
13914
|
+
import pytest
|
|
13915
|
+
from ${pythonImport} import ${funcName}
|
|
13916
|
+
|
|
13917
|
+
class Test${this.pascalCase(moduleName)}Coverage:
|
|
13918
|
+
"""Tests to cover ${lineRange}"""
|
|
13919
|
+
|
|
13920
|
+
def test_cover_${lines[0]}_${lines[lines.length - 1]}(self):
|
|
13921
|
+
"""Exercise code path covering ${lineRange}"""
|
|
13922
|
+
# Arrange: Set up test inputs to reach uncovered lines
|
|
13923
|
+
test_input = None # Replace with appropriate input
|
|
13924
|
+
|
|
13925
|
+
# Act: Execute the code path
|
|
13926
|
+
try:
|
|
13927
|
+
result = ${funcName}(test_input)
|
|
13928
|
+
|
|
13929
|
+
# Assert: Verify expected behavior
|
|
13930
|
+
assert result is not None
|
|
13931
|
+
except Exception as e:
|
|
13932
|
+
# If exception is expected for this path, verify it
|
|
13933
|
+
pytest.fail(f"Unexpected exception: {e}")
|
|
13934
|
+
`;
|
|
13935
|
+
}
|
|
13936
|
+
// ============================================================================
|
|
13937
|
+
// Python-Specific Helpers
|
|
13938
|
+
// ============================================================================
|
|
13939
|
+
/**
|
|
13940
|
+
* Generate Python pattern comment
|
|
13941
|
+
*/
|
|
13942
|
+
generatePythonPatternComment(patterns) {
|
|
13943
|
+
if (patterns.length === 0) return "";
|
|
13944
|
+
return `# Applied patterns: ${patterns.map((p) => p.name).join(", ")}
|
|
13945
|
+
`;
|
|
13946
|
+
}
|
|
13947
|
+
/**
|
|
13948
|
+
* Generate a Python test value for a parameter
|
|
13949
|
+
*/
|
|
13950
|
+
generatePythonTestValue(param) {
|
|
13951
|
+
const type = param.type?.toLowerCase() || "unknown";
|
|
13952
|
+
const name = param.name.toLowerCase();
|
|
13953
|
+
if (name.includes("id")) return `"${faker2.string.uuid()}"`;
|
|
13954
|
+
if (name.includes("name")) return `"${faker2.person.fullName()}"`;
|
|
13955
|
+
if (name.includes("email")) return `"${faker2.internet.email()}"`;
|
|
13956
|
+
if (name.includes("url")) return `"${faker2.internet.url()}"`;
|
|
13957
|
+
if (type.includes("str")) return `"${faker2.lorem.word()}"`;
|
|
13958
|
+
if (type.includes("int") || type.includes("number")) {
|
|
13959
|
+
return String(faker2.number.int({ min: 1, max: 100 }));
|
|
13960
|
+
}
|
|
13961
|
+
if (type.includes("bool")) return "True";
|
|
13962
|
+
if (type.includes("list") || type.includes("[]")) return "[]";
|
|
13963
|
+
if (type.includes("dict") || type.includes("{}")) return "{}";
|
|
13964
|
+
if (type.includes("float")) return String(faker2.number.float({ min: 0, max: 100 }));
|
|
13965
|
+
return "None";
|
|
13966
|
+
}
|
|
13967
|
+
};
|
|
13968
|
+
|
|
13969
|
+
// src/domains/test-generation/factories/test-generator-factory.ts
|
|
13970
|
+
var SUPPORTED_FRAMEWORKS = [
|
|
13971
|
+
"jest",
|
|
13972
|
+
"vitest",
|
|
13973
|
+
"mocha",
|
|
13974
|
+
"pytest"
|
|
13975
|
+
];
|
|
13976
|
+
var DEFAULT_FRAMEWORK = "vitest";
|
|
13977
|
+
var TestGeneratorFactory = class {
|
|
13978
|
+
/**
|
|
13979
|
+
* Cache of created generators for reuse
|
|
13980
|
+
*/
|
|
13981
|
+
cache = /* @__PURE__ */ new Map();
|
|
13982
|
+
/**
|
|
13983
|
+
* Create a test generator for the specified framework
|
|
13984
|
+
*
|
|
13985
|
+
* @param framework - Target test framework
|
|
13986
|
+
* @returns Test generator instance
|
|
13987
|
+
* @throws Error if framework is not supported
|
|
13988
|
+
*/
|
|
13989
|
+
create(framework) {
|
|
13990
|
+
const cached = this.cache.get(framework);
|
|
13991
|
+
if (cached) {
|
|
13992
|
+
return cached;
|
|
13993
|
+
}
|
|
13994
|
+
const generator = this.createGenerator(framework);
|
|
13995
|
+
this.cache.set(framework, generator);
|
|
13996
|
+
return generator;
|
|
13997
|
+
}
|
|
13998
|
+
/**
|
|
13999
|
+
* Check if a framework is supported
|
|
14000
|
+
*
|
|
14001
|
+
* @param framework - Framework to check
|
|
14002
|
+
* @returns True if supported, with type narrowing
|
|
14003
|
+
*/
|
|
14004
|
+
supports(framework) {
|
|
14005
|
+
return SUPPORTED_FRAMEWORKS.includes(framework);
|
|
14006
|
+
}
|
|
14007
|
+
/**
|
|
14008
|
+
* Get the default framework
|
|
14009
|
+
*
|
|
14010
|
+
* @returns Default test framework (vitest)
|
|
14011
|
+
*/
|
|
14012
|
+
getDefault() {
|
|
14013
|
+
return DEFAULT_FRAMEWORK;
|
|
14014
|
+
}
|
|
14015
|
+
/**
|
|
14016
|
+
* Get all supported frameworks
|
|
14017
|
+
*
|
|
14018
|
+
* @returns Array of supported framework names
|
|
14019
|
+
*/
|
|
14020
|
+
getSupportedFrameworks() {
|
|
14021
|
+
return [...SUPPORTED_FRAMEWORKS];
|
|
14022
|
+
}
|
|
14023
|
+
/**
|
|
14024
|
+
* Create a generator instance for the framework
|
|
14025
|
+
*/
|
|
14026
|
+
createGenerator(framework) {
|
|
14027
|
+
switch (framework) {
|
|
14028
|
+
case "jest":
|
|
14029
|
+
return new JestVitestGenerator("jest");
|
|
14030
|
+
case "vitest":
|
|
14031
|
+
return new JestVitestGenerator("vitest");
|
|
14032
|
+
case "mocha":
|
|
14033
|
+
return new MochaGenerator();
|
|
14034
|
+
case "pytest":
|
|
14035
|
+
return new PytestGenerator();
|
|
14036
|
+
default:
|
|
14037
|
+
throw new Error(`Unsupported test framework: ${framework}`);
|
|
14038
|
+
}
|
|
14039
|
+
}
|
|
14040
|
+
/**
|
|
14041
|
+
* Clear the generator cache
|
|
14042
|
+
* Useful for testing or when memory needs to be freed
|
|
14043
|
+
*/
|
|
14044
|
+
clearCache() {
|
|
14045
|
+
this.cache.clear();
|
|
14046
|
+
}
|
|
14047
|
+
};
|
|
14048
|
+
var testGeneratorFactory = new TestGeneratorFactory();
|
|
14049
|
+
|
|
14050
|
+
// src/domains/test-generation/services/tdd-generator.ts
|
|
14051
|
+
var TDDGeneratorService = class {
|
|
14052
|
+
/**
|
|
14053
|
+
* Generate TDD artifacts based on the requested phase
|
|
14054
|
+
*/
|
|
14055
|
+
async generateTDDTests(request) {
|
|
14056
|
+
const { feature, behavior, framework, phase } = request;
|
|
14057
|
+
switch (phase) {
|
|
14058
|
+
case "red":
|
|
14059
|
+
return this.generateRedPhaseTest(feature, behavior, framework);
|
|
14060
|
+
case "green":
|
|
14061
|
+
return this.generateGreenPhaseCode(feature, behavior, framework);
|
|
14062
|
+
case "refactor":
|
|
14063
|
+
return this.generateRefactoringSuggestions(feature, behavior);
|
|
14064
|
+
default:
|
|
14065
|
+
throw new Error(`Unknown TDD phase: ${phase}`);
|
|
14066
|
+
}
|
|
14067
|
+
}
|
|
14068
|
+
generateRedPhaseTest(feature, behavior, _framework) {
|
|
13217
14069
|
const funcName = this.camelCase(feature);
|
|
13218
14070
|
const assertions = this.generateAssertionsFromBehavior(behavior, funcName);
|
|
13219
14071
|
const testCode = `describe('${feature}', () => {
|
|
@@ -13228,10 +14080,6 @@ ${assertions}
|
|
|
13228
14080
|
nextStep: "Write the minimal implementation to make this test pass"
|
|
13229
14081
|
};
|
|
13230
14082
|
}
|
|
13231
|
-
/**
|
|
13232
|
-
* Generate specific assertions from behavior description
|
|
13233
|
-
* Uses NLP-style extraction to infer test values and assertions
|
|
13234
|
-
*/
|
|
13235
14083
|
generateAssertionsFromBehavior(behavior, funcName) {
|
|
13236
14084
|
const behaviorLower = behavior.toLowerCase();
|
|
13237
14085
|
const assertions = [];
|
|
@@ -13272,7 +14120,7 @@ ${assertions}
|
|
|
13272
14120
|
} else if (context.extractedNumber !== void 0) {
|
|
13273
14121
|
assertions.push(` expect(result).toContain(${expectedValue});`);
|
|
13274
14122
|
} else {
|
|
13275
|
-
assertions.push(` expect(result).toContain(testInput)
|
|
14123
|
+
assertions.push(` expect(result).toContain(testInput);`);
|
|
13276
14124
|
}
|
|
13277
14125
|
} else if (behaviorLower.includes("length") || behaviorLower.includes("count")) {
|
|
13278
14126
|
assertions.push(` const result = ${funcCall};`);
|
|
@@ -13329,9 +14177,6 @@ ${assertions}
|
|
|
13329
14177
|
}
|
|
13330
14178
|
return assertions.join("\n");
|
|
13331
14179
|
}
|
|
13332
|
-
/**
|
|
13333
|
-
* Extract contextual information from behavior description
|
|
13334
|
-
*/
|
|
13335
14180
|
extractBehaviorContext(behavior) {
|
|
13336
14181
|
const context = {};
|
|
13337
14182
|
const stringMatch = behavior.match(/["']([^"']+)["']/);
|
|
@@ -13363,9 +14208,6 @@ ${assertions}
|
|
|
13363
14208
|
}
|
|
13364
14209
|
return context;
|
|
13365
14210
|
}
|
|
13366
|
-
/**
|
|
13367
|
-
* Build function call with appropriate arguments based on context
|
|
13368
|
-
*/
|
|
13369
14211
|
buildFunctionCall(funcName, context, behaviorLower) {
|
|
13370
14212
|
if (context.inputType) {
|
|
13371
14213
|
return `${funcName}(testInput)`;
|
|
@@ -13398,7 +14240,7 @@ ${assertions}
|
|
|
13398
14240
|
}
|
|
13399
14241
|
return `${funcName}(input)`;
|
|
13400
14242
|
}
|
|
13401
|
-
|
|
14243
|
+
generateGreenPhaseCode(feature, behavior, _framework) {
|
|
13402
14244
|
const behaviorLower = behavior.toLowerCase();
|
|
13403
14245
|
const funcName = this.camelCase(feature);
|
|
13404
14246
|
const { returnType, implementation, params } = this.inferImplementationFromBehavior(behaviorLower);
|
|
@@ -13415,10 +14257,6 @@ ${implementation}
|
|
|
13415
14257
|
nextStep: "Refactor the code while keeping tests green"
|
|
13416
14258
|
};
|
|
13417
14259
|
}
|
|
13418
|
-
/**
|
|
13419
|
-
* Infer implementation details from behavior description using heuristics
|
|
13420
|
-
* Analyzes the behavior text to determine return type, parameters, and minimal implementation
|
|
13421
|
-
*/
|
|
13422
14260
|
inferImplementationFromBehavior(behavior) {
|
|
13423
14261
|
let returnType = "unknown";
|
|
13424
14262
|
let implementation = " return undefined;";
|
|
@@ -13481,7 +14319,7 @@ ${implementation}`;
|
|
|
13481
14319
|
}
|
|
13482
14320
|
return { returnType, implementation, params };
|
|
13483
14321
|
}
|
|
13484
|
-
|
|
14322
|
+
generateRefactoringSuggestions(_feature, _behavior) {
|
|
13485
14323
|
return {
|
|
13486
14324
|
phase: "refactor",
|
|
13487
14325
|
refactoringChanges: [
|
|
@@ -13494,6 +14332,28 @@ ${implementation}`;
|
|
|
13494
14332
|
nextStep: "Apply refactoring changes and ensure all tests still pass"
|
|
13495
14333
|
};
|
|
13496
14334
|
}
|
|
14335
|
+
camelCase(str) {
|
|
14336
|
+
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase()).replace(/^./, (chr) => chr.toLowerCase());
|
|
14337
|
+
}
|
|
14338
|
+
};
|
|
14339
|
+
|
|
14340
|
+
// src/domains/test-generation/services/property-test-generator.ts
|
|
14341
|
+
var PropertyTestGeneratorService = class {
|
|
14342
|
+
/**
|
|
14343
|
+
* Generate property-based tests
|
|
14344
|
+
*/
|
|
14345
|
+
async generatePropertyTests(request) {
|
|
14346
|
+
const { function: funcName, properties, constraints = {} } = request;
|
|
14347
|
+
const tests = properties.map((property) => ({
|
|
14348
|
+
property,
|
|
14349
|
+
testCode: this.generatePropertyTestCode(funcName, property, constraints),
|
|
14350
|
+
generators: this.inferGenerators(property, constraints)
|
|
14351
|
+
}));
|
|
14352
|
+
return {
|
|
14353
|
+
tests,
|
|
14354
|
+
arbitraries: this.collectArbitraries(tests)
|
|
14355
|
+
};
|
|
14356
|
+
}
|
|
13497
14357
|
generatePropertyTestCode(funcName, property, constraints) {
|
|
13498
14358
|
const propertyLower = property.toLowerCase();
|
|
13499
14359
|
const { generators, assertion, setupCode } = this.analyzePropertyForTestGeneration(
|
|
@@ -13515,9 +14375,6 @@ ${setupCode}
|
|
|
13515
14375
|
});
|
|
13516
14376
|
});`;
|
|
13517
14377
|
}
|
|
13518
|
-
/**
|
|
13519
|
-
* Analyze property description to determine generators and assertions
|
|
13520
|
-
*/
|
|
13521
14378
|
analyzePropertyForTestGeneration(propertyLower, funcName, constraints) {
|
|
13522
14379
|
const generators = [];
|
|
13523
14380
|
let assertion = "return result !== undefined;";
|
|
@@ -13611,9 +14468,6 @@ ${setupCode}
|
|
|
13611
14468
|
}
|
|
13612
14469
|
return { generators, assertion, setupCode };
|
|
13613
14470
|
}
|
|
13614
|
-
/**
|
|
13615
|
-
* Infer the appropriate fast-check generator from constraints
|
|
13616
|
-
*/
|
|
13617
14471
|
inferGeneratorFromConstraints(constraints, hint) {
|
|
13618
14472
|
const type = constraints.type?.toLowerCase() || hint.toLowerCase();
|
|
13619
14473
|
if (type.includes("string") || type.includes("text")) {
|
|
@@ -13632,34 +14486,19 @@ ${setupCode}
|
|
|
13632
14486
|
}
|
|
13633
14487
|
return "fc.integer()";
|
|
13634
14488
|
}
|
|
13635
|
-
if (type.includes("float") || type.includes("decimal"))
|
|
13636
|
-
|
|
13637
|
-
}
|
|
13638
|
-
if (type.includes("boolean") || type.includes("bool")) {
|
|
13639
|
-
return "fc.boolean()";
|
|
13640
|
-
}
|
|
14489
|
+
if (type.includes("float") || type.includes("decimal")) return "fc.float()";
|
|
14490
|
+
if (type.includes("boolean") || type.includes("bool")) return "fc.boolean()";
|
|
13641
14491
|
if (type.includes("array") || type.includes("list")) {
|
|
13642
14492
|
const itemType = constraints.itemType || "anything";
|
|
13643
14493
|
const itemGen = this.getSimpleGenerator(itemType);
|
|
13644
14494
|
return `fc.array(${itemGen})`;
|
|
13645
14495
|
}
|
|
13646
|
-
if (type.includes("object") || type.includes("record"))
|
|
13647
|
-
|
|
13648
|
-
|
|
13649
|
-
if (type.includes("
|
|
13650
|
-
return "fc.date()";
|
|
13651
|
-
}
|
|
13652
|
-
if (type.includes("uuid") || type.includes("id")) {
|
|
13653
|
-
return "fc.uuid()";
|
|
13654
|
-
}
|
|
13655
|
-
if (type.includes("email")) {
|
|
13656
|
-
return "fc.emailAddress()";
|
|
13657
|
-
}
|
|
14496
|
+
if (type.includes("object") || type.includes("record")) return "fc.object()";
|
|
14497
|
+
if (type.includes("date")) return "fc.date()";
|
|
14498
|
+
if (type.includes("uuid") || type.includes("id")) return "fc.uuid()";
|
|
14499
|
+
if (type.includes("email")) return "fc.emailAddress()";
|
|
13658
14500
|
return "fc.anything()";
|
|
13659
14501
|
}
|
|
13660
|
-
/**
|
|
13661
|
-
* Get a simple generator for a type name
|
|
13662
|
-
*/
|
|
13663
14502
|
getSimpleGenerator(typeName) {
|
|
13664
14503
|
const typeMap = {
|
|
13665
14504
|
string: "fc.string()",
|
|
@@ -13673,22 +14512,12 @@ ${setupCode}
|
|
|
13673
14512
|
};
|
|
13674
14513
|
return typeMap[typeName.toLowerCase()] || "fc.anything()";
|
|
13675
14514
|
}
|
|
13676
|
-
/**
|
|
13677
|
-
* Generate parameter names from generator list
|
|
13678
|
-
*/
|
|
13679
14515
|
generatePropertyParams(generators) {
|
|
13680
|
-
if (generators.length === 1)
|
|
13681
|
-
return "input";
|
|
13682
|
-
}
|
|
14516
|
+
if (generators.length === 1) return "input";
|
|
13683
14517
|
return generators.map((_, i) => String.fromCharCode(97 + i)).join(", ");
|
|
13684
14518
|
}
|
|
13685
|
-
/**
|
|
13686
|
-
* Generate argument list for function call
|
|
13687
|
-
*/
|
|
13688
14519
|
generatePropertyArgs(generators) {
|
|
13689
|
-
if (generators.length === 1)
|
|
13690
|
-
return "input";
|
|
13691
|
-
}
|
|
14520
|
+
if (generators.length === 1) return "input";
|
|
13692
14521
|
return generators.map((_, i) => String.fromCharCode(97 + i)).join(", ");
|
|
13693
14522
|
}
|
|
13694
14523
|
inferGenerators(property, constraints) {
|
|
@@ -13771,8 +14600,33 @@ ${setupCode}
|
|
|
13771
14600
|
}
|
|
13772
14601
|
return Array.from(arbitraries);
|
|
13773
14602
|
}
|
|
14603
|
+
};
|
|
14604
|
+
|
|
14605
|
+
// src/domains/test-generation/services/test-data-generator.ts
|
|
14606
|
+
import { faker as faker3 } from "@faker-js/faker";
|
|
14607
|
+
var TestDataGeneratorService = class {
|
|
14608
|
+
/**
|
|
14609
|
+
* Generate test data based on schema
|
|
14610
|
+
*/
|
|
14611
|
+
async generateTestData(request) {
|
|
14612
|
+
const { schema, count, locale = "en", preserveRelationships = false } = request;
|
|
14613
|
+
const seed = Date.now();
|
|
14614
|
+
const records = [];
|
|
14615
|
+
for (let i = 0; i < count; i++) {
|
|
14616
|
+
const record = this.generateRecordFromSchema(schema, seed + i, locale);
|
|
14617
|
+
records.push(record);
|
|
14618
|
+
}
|
|
14619
|
+
if (preserveRelationships) {
|
|
14620
|
+
this.linkRelatedRecords(records, schema);
|
|
14621
|
+
}
|
|
14622
|
+
return {
|
|
14623
|
+
records,
|
|
14624
|
+
schema,
|
|
14625
|
+
seed
|
|
14626
|
+
};
|
|
14627
|
+
}
|
|
13774
14628
|
generateRecordFromSchema(schema, seed, locale) {
|
|
13775
|
-
|
|
14629
|
+
faker3.seed(seed);
|
|
13776
14630
|
if (locale && locale !== "en") {
|
|
13777
14631
|
}
|
|
13778
14632
|
const record = {};
|
|
@@ -13805,146 +14659,146 @@ ${setupCode}
|
|
|
13805
14659
|
return this.generateNumberValue(options);
|
|
13806
14660
|
case "float":
|
|
13807
14661
|
case "decimal":
|
|
13808
|
-
return
|
|
14662
|
+
return faker3.number.float({ min: options?.min ?? 0, max: options?.max ?? 1e3, fractionDigits: 2 });
|
|
13809
14663
|
case "boolean":
|
|
13810
14664
|
case "bool":
|
|
13811
|
-
return
|
|
14665
|
+
return faker3.datatype.boolean();
|
|
13812
14666
|
case "date":
|
|
13813
14667
|
case "datetime":
|
|
13814
|
-
return
|
|
14668
|
+
return faker3.date.recent().toISOString();
|
|
13815
14669
|
case "email":
|
|
13816
|
-
return
|
|
14670
|
+
return faker3.internet.email();
|
|
13817
14671
|
case "uuid":
|
|
13818
14672
|
case "id":
|
|
13819
|
-
return
|
|
14673
|
+
return faker3.string.uuid();
|
|
13820
14674
|
case "url":
|
|
13821
|
-
return
|
|
14675
|
+
return faker3.internet.url();
|
|
13822
14676
|
case "phone":
|
|
13823
|
-
return
|
|
14677
|
+
return faker3.phone.number();
|
|
13824
14678
|
case "address":
|
|
13825
14679
|
return this.generateAddress();
|
|
13826
14680
|
case "name":
|
|
13827
14681
|
case "fullname":
|
|
13828
|
-
return
|
|
14682
|
+
return faker3.person.fullName();
|
|
13829
14683
|
case "firstname":
|
|
13830
|
-
return
|
|
14684
|
+
return faker3.person.firstName();
|
|
13831
14685
|
case "lastname":
|
|
13832
|
-
return
|
|
14686
|
+
return faker3.person.lastName();
|
|
13833
14687
|
case "username":
|
|
13834
|
-
return
|
|
14688
|
+
return faker3.internet.username();
|
|
13835
14689
|
case "password":
|
|
13836
|
-
return
|
|
14690
|
+
return faker3.internet.password();
|
|
13837
14691
|
case "company":
|
|
13838
|
-
return
|
|
14692
|
+
return faker3.company.name();
|
|
13839
14693
|
case "jobtitle":
|
|
13840
|
-
return
|
|
14694
|
+
return faker3.person.jobTitle();
|
|
13841
14695
|
case "text":
|
|
13842
14696
|
case "paragraph":
|
|
13843
|
-
return
|
|
14697
|
+
return faker3.lorem.paragraph();
|
|
13844
14698
|
case "sentence":
|
|
13845
|
-
return
|
|
14699
|
+
return faker3.lorem.sentence();
|
|
13846
14700
|
case "word":
|
|
13847
14701
|
case "words":
|
|
13848
|
-
return
|
|
14702
|
+
return faker3.lorem.word();
|
|
13849
14703
|
case "avatar":
|
|
13850
14704
|
case "image":
|
|
13851
|
-
return
|
|
14705
|
+
return faker3.image.avatar();
|
|
13852
14706
|
case "color":
|
|
13853
|
-
return
|
|
14707
|
+
return faker3.color.rgb();
|
|
13854
14708
|
case "ipaddress":
|
|
13855
14709
|
case "ip":
|
|
13856
|
-
return
|
|
14710
|
+
return faker3.internet.ipv4();
|
|
13857
14711
|
case "mac":
|
|
13858
|
-
return
|
|
14712
|
+
return faker3.internet.mac();
|
|
13859
14713
|
case "latitude":
|
|
13860
|
-
return
|
|
14714
|
+
return faker3.location.latitude();
|
|
13861
14715
|
case "longitude":
|
|
13862
|
-
return
|
|
14716
|
+
return faker3.location.longitude();
|
|
13863
14717
|
case "country":
|
|
13864
|
-
return
|
|
14718
|
+
return faker3.location.country();
|
|
13865
14719
|
case "city":
|
|
13866
|
-
return
|
|
14720
|
+
return faker3.location.city();
|
|
13867
14721
|
case "zipcode":
|
|
13868
14722
|
case "postalcode":
|
|
13869
|
-
return
|
|
14723
|
+
return faker3.location.zipCode();
|
|
13870
14724
|
case "creditcard":
|
|
13871
|
-
return
|
|
14725
|
+
return faker3.finance.creditCardNumber();
|
|
13872
14726
|
case "currency":
|
|
13873
|
-
return
|
|
14727
|
+
return faker3.finance.currencyCode();
|
|
13874
14728
|
case "amount":
|
|
13875
14729
|
case "price":
|
|
13876
|
-
return
|
|
14730
|
+
return faker3.finance.amount();
|
|
13877
14731
|
case "json":
|
|
13878
14732
|
case "object":
|
|
13879
|
-
return { key:
|
|
14733
|
+
return { key: faker3.lorem.word(), value: faker3.lorem.sentence() };
|
|
13880
14734
|
case "array":
|
|
13881
|
-
return [
|
|
14735
|
+
return [faker3.lorem.word(), faker3.lorem.word(), faker3.lorem.word()];
|
|
13882
14736
|
case "enum":
|
|
13883
14737
|
if (options?.enum && options.enum.length > 0) {
|
|
13884
|
-
return
|
|
14738
|
+
return faker3.helpers.arrayElement(options.enum);
|
|
13885
14739
|
}
|
|
13886
|
-
return
|
|
14740
|
+
return faker3.lorem.word();
|
|
13887
14741
|
default:
|
|
13888
14742
|
return this.inferValueFromFieldName(fieldName);
|
|
13889
14743
|
}
|
|
13890
14744
|
}
|
|
13891
14745
|
generateStringValue(fieldName, options) {
|
|
13892
14746
|
const lowerName = fieldName.toLowerCase();
|
|
13893
|
-
if (lowerName.includes("email")) return
|
|
13894
|
-
if (lowerName.includes("name") && lowerName.includes("first")) return
|
|
13895
|
-
if (lowerName.includes("name") && lowerName.includes("last")) return
|
|
13896
|
-
if (lowerName.includes("name")) return
|
|
13897
|
-
if (lowerName.includes("phone")) return
|
|
13898
|
-
if (lowerName.includes("address")) return
|
|
13899
|
-
if (lowerName.includes("city")) return
|
|
13900
|
-
if (lowerName.includes("country")) return
|
|
13901
|
-
if (lowerName.includes("zip") || lowerName.includes("postal")) return
|
|
13902
|
-
if (lowerName.includes("url") || lowerName.includes("website")) return
|
|
13903
|
-
if (lowerName.includes("username") || lowerName.includes("user")) return
|
|
13904
|
-
if (lowerName.includes("password")) return
|
|
13905
|
-
if (lowerName.includes("description") || lowerName.includes("bio")) return
|
|
13906
|
-
if (lowerName.includes("title")) return
|
|
13907
|
-
if (lowerName.includes("company")) return
|
|
13908
|
-
if (lowerName.includes("job")) return
|
|
13909
|
-
if (lowerName.includes("avatar") || lowerName.includes("image")) return
|
|
14747
|
+
if (lowerName.includes("email")) return faker3.internet.email();
|
|
14748
|
+
if (lowerName.includes("name") && lowerName.includes("first")) return faker3.person.firstName();
|
|
14749
|
+
if (lowerName.includes("name") && lowerName.includes("last")) return faker3.person.lastName();
|
|
14750
|
+
if (lowerName.includes("name")) return faker3.person.fullName();
|
|
14751
|
+
if (lowerName.includes("phone")) return faker3.phone.number();
|
|
14752
|
+
if (lowerName.includes("address")) return faker3.location.streetAddress();
|
|
14753
|
+
if (lowerName.includes("city")) return faker3.location.city();
|
|
14754
|
+
if (lowerName.includes("country")) return faker3.location.country();
|
|
14755
|
+
if (lowerName.includes("zip") || lowerName.includes("postal")) return faker3.location.zipCode();
|
|
14756
|
+
if (lowerName.includes("url") || lowerName.includes("website")) return faker3.internet.url();
|
|
14757
|
+
if (lowerName.includes("username") || lowerName.includes("user")) return faker3.internet.username();
|
|
14758
|
+
if (lowerName.includes("password")) return faker3.internet.password();
|
|
14759
|
+
if (lowerName.includes("description") || lowerName.includes("bio")) return faker3.lorem.paragraph();
|
|
14760
|
+
if (lowerName.includes("title")) return faker3.lorem.sentence();
|
|
14761
|
+
if (lowerName.includes("company")) return faker3.company.name();
|
|
14762
|
+
if (lowerName.includes("job")) return faker3.person.jobTitle();
|
|
14763
|
+
if (lowerName.includes("avatar") || lowerName.includes("image")) return faker3.image.avatar();
|
|
13910
14764
|
if (options?.pattern) {
|
|
13911
|
-
return
|
|
14765
|
+
return faker3.helpers.fromRegExp(options.pattern);
|
|
13912
14766
|
}
|
|
13913
|
-
return
|
|
14767
|
+
return faker3.lorem.words(3);
|
|
13914
14768
|
}
|
|
13915
14769
|
generateNumberValue(options) {
|
|
13916
14770
|
const min = options?.min ?? 0;
|
|
13917
14771
|
const max = options?.max ?? 1e4;
|
|
13918
|
-
return
|
|
14772
|
+
return faker3.number.int({ min, max });
|
|
13919
14773
|
}
|
|
13920
14774
|
generateAddress() {
|
|
13921
14775
|
return {
|
|
13922
|
-
street:
|
|
13923
|
-
city:
|
|
13924
|
-
state:
|
|
13925
|
-
zipCode:
|
|
13926
|
-
country:
|
|
14776
|
+
street: faker3.location.streetAddress(),
|
|
14777
|
+
city: faker3.location.city(),
|
|
14778
|
+
state: faker3.location.state(),
|
|
14779
|
+
zipCode: faker3.location.zipCode(),
|
|
14780
|
+
country: faker3.location.country()
|
|
13927
14781
|
};
|
|
13928
14782
|
}
|
|
13929
14783
|
inferValueFromFieldName(fieldName) {
|
|
13930
14784
|
const lowerName = fieldName.toLowerCase();
|
|
13931
|
-
if (lowerName.includes("id")) return
|
|
13932
|
-
if (lowerName.includes("email")) return
|
|
13933
|
-
if (lowerName.includes("name")) return
|
|
13934
|
-
if (lowerName.includes("phone")) return
|
|
13935
|
-
if (lowerName.includes("date") || lowerName.includes("time")) return
|
|
13936
|
-
if (lowerName.includes("url")) return
|
|
13937
|
-
if (lowerName.includes("count") || lowerName.includes("amount")) return
|
|
13938
|
-
if (lowerName.includes("price")) return
|
|
14785
|
+
if (lowerName.includes("id")) return faker3.string.uuid();
|
|
14786
|
+
if (lowerName.includes("email")) return faker3.internet.email();
|
|
14787
|
+
if (lowerName.includes("name")) return faker3.person.fullName();
|
|
14788
|
+
if (lowerName.includes("phone")) return faker3.phone.number();
|
|
14789
|
+
if (lowerName.includes("date") || lowerName.includes("time")) return faker3.date.recent().toISOString();
|
|
14790
|
+
if (lowerName.includes("url")) return faker3.internet.url();
|
|
14791
|
+
if (lowerName.includes("count") || lowerName.includes("amount")) return faker3.number.int({ min: 0, max: 100 });
|
|
14792
|
+
if (lowerName.includes("price")) return faker3.finance.amount();
|
|
13939
14793
|
if (lowerName.includes("active") || lowerName.includes("enabled") || lowerName.includes("is")) {
|
|
13940
|
-
return
|
|
14794
|
+
return faker3.datatype.boolean();
|
|
13941
14795
|
}
|
|
13942
|
-
return
|
|
14796
|
+
return faker3.lorem.word();
|
|
13943
14797
|
}
|
|
13944
14798
|
callFakerMethod(methodPath) {
|
|
13945
14799
|
try {
|
|
13946
14800
|
const parts = methodPath.split(".");
|
|
13947
|
-
let result =
|
|
14801
|
+
let result = faker3;
|
|
13948
14802
|
for (const part of parts) {
|
|
13949
14803
|
if (result && typeof result === "object" && part in result) {
|
|
13950
14804
|
const next = result[part];
|
|
@@ -13954,12 +14808,12 @@ ${setupCode}
|
|
|
13954
14808
|
result = next;
|
|
13955
14809
|
}
|
|
13956
14810
|
} else {
|
|
13957
|
-
return
|
|
14811
|
+
return faker3.lorem.word();
|
|
13958
14812
|
}
|
|
13959
14813
|
}
|
|
13960
14814
|
return result;
|
|
13961
14815
|
} catch {
|
|
13962
|
-
return
|
|
14816
|
+
return faker3.lorem.word();
|
|
13963
14817
|
}
|
|
13964
14818
|
}
|
|
13965
14819
|
linkRelatedRecords(records, schema) {
|
|
@@ -13978,21 +14832,177 @@ ${setupCode}
|
|
|
13978
14832
|
for (const { field, reference } of referenceFields) {
|
|
13979
14833
|
if (i > 0 && reference === "id") {
|
|
13980
14834
|
const prevRecord = records[Math.floor(Math.random() * i)];
|
|
13981
|
-
record[field] = prevRecord["id"] ??
|
|
14835
|
+
record[field] = prevRecord["id"] ?? faker3.string.uuid();
|
|
13982
14836
|
} else {
|
|
13983
|
-
record[field] =
|
|
14837
|
+
record[field] = faker3.string.uuid();
|
|
13984
14838
|
}
|
|
13985
14839
|
}
|
|
13986
14840
|
}
|
|
13987
14841
|
}
|
|
13988
14842
|
}
|
|
14843
|
+
};
|
|
14844
|
+
|
|
14845
|
+
// src/domains/test-generation/services/test-generator.ts
|
|
14846
|
+
var DEFAULT_CONFIG = {
|
|
14847
|
+
defaultFramework: "vitest",
|
|
14848
|
+
maxTestsPerFile: 50,
|
|
14849
|
+
coverageTargetDefault: 80,
|
|
14850
|
+
enableAIGeneration: true
|
|
14851
|
+
};
|
|
14852
|
+
var TestGeneratorService = class {
|
|
14853
|
+
config;
|
|
14854
|
+
memory;
|
|
14855
|
+
generatorFactory;
|
|
14856
|
+
tddGenerator;
|
|
14857
|
+
propertyTestGenerator;
|
|
14858
|
+
testDataGenerator;
|
|
14859
|
+
constructor(dependencies, config = {}) {
|
|
14860
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
14861
|
+
this.memory = dependencies.memory;
|
|
14862
|
+
this.generatorFactory = dependencies.generatorFactory || new TestGeneratorFactory();
|
|
14863
|
+
this.tddGenerator = dependencies.tddGenerator || new TDDGeneratorService();
|
|
14864
|
+
this.propertyTestGenerator = dependencies.propertyTestGenerator || new PropertyTestGeneratorService();
|
|
14865
|
+
this.testDataGenerator = dependencies.testDataGenerator || new TestDataGeneratorService();
|
|
14866
|
+
}
|
|
14867
|
+
/**
|
|
14868
|
+
* Generate tests for given source files
|
|
14869
|
+
*/
|
|
14870
|
+
async generateTests(request) {
|
|
14871
|
+
try {
|
|
14872
|
+
const {
|
|
14873
|
+
sourceFiles,
|
|
14874
|
+
testType,
|
|
14875
|
+
framework,
|
|
14876
|
+
coverageTarget = this.config.coverageTargetDefault,
|
|
14877
|
+
patterns = []
|
|
14878
|
+
} = request;
|
|
14879
|
+
if (sourceFiles.length === 0) {
|
|
14880
|
+
return err(new Error("No source files provided"));
|
|
14881
|
+
}
|
|
14882
|
+
const tests = [];
|
|
14883
|
+
const patternsUsed = [];
|
|
14884
|
+
for (const sourceFile of sourceFiles) {
|
|
14885
|
+
const fileTests = await this.generateTestsForFile(
|
|
14886
|
+
sourceFile,
|
|
14887
|
+
testType,
|
|
14888
|
+
framework,
|
|
14889
|
+
patterns
|
|
14890
|
+
);
|
|
14891
|
+
if (fileTests.success) {
|
|
14892
|
+
tests.push(...fileTests.value.tests);
|
|
14893
|
+
patternsUsed.push(...fileTests.value.patternsUsed);
|
|
14894
|
+
}
|
|
14895
|
+
}
|
|
14896
|
+
const coverageEstimate = this.estimateCoverage(tests, coverageTarget);
|
|
14897
|
+
await this.storeGenerationMetadata(tests, patternsUsed);
|
|
14898
|
+
return ok({
|
|
14899
|
+
tests,
|
|
14900
|
+
coverageEstimate,
|
|
14901
|
+
patternsUsed: Array.from(new Set(patternsUsed))
|
|
14902
|
+
});
|
|
14903
|
+
} catch (error) {
|
|
14904
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
14905
|
+
}
|
|
14906
|
+
}
|
|
14907
|
+
/**
|
|
14908
|
+
* Generate tests specifically targeting coverage gaps
|
|
14909
|
+
*/
|
|
14910
|
+
async generateForCoverageGap(file, uncoveredLines, framework) {
|
|
14911
|
+
try {
|
|
14912
|
+
if (uncoveredLines.length === 0) {
|
|
14913
|
+
return ok([]);
|
|
14914
|
+
}
|
|
14915
|
+
const tests = [];
|
|
14916
|
+
const lineGroups = this.groupConsecutiveLines(uncoveredLines);
|
|
14917
|
+
const frameworkType = this.generatorFactory.supports(framework) ? framework : this.config.defaultFramework;
|
|
14918
|
+
for (const group of lineGroups) {
|
|
14919
|
+
const test = await this.generateTestForLines(file, group, frameworkType);
|
|
14920
|
+
if (test) {
|
|
14921
|
+
tests.push(test);
|
|
14922
|
+
}
|
|
14923
|
+
}
|
|
14924
|
+
return ok(tests);
|
|
14925
|
+
} catch (error) {
|
|
14926
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
14927
|
+
}
|
|
14928
|
+
}
|
|
14929
|
+
/**
|
|
14930
|
+
* Generate tests following TDD workflow - delegates to TDDGeneratorService
|
|
14931
|
+
*/
|
|
14932
|
+
async generateTDDTests(request) {
|
|
14933
|
+
try {
|
|
14934
|
+
const result = await this.tddGenerator.generateTDDTests(request);
|
|
14935
|
+
return ok(result);
|
|
14936
|
+
} catch (error) {
|
|
14937
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
14938
|
+
}
|
|
14939
|
+
}
|
|
14940
|
+
/**
|
|
14941
|
+
* Generate property-based tests - delegates to PropertyTestGeneratorService
|
|
14942
|
+
*/
|
|
14943
|
+
async generatePropertyTests(request) {
|
|
14944
|
+
try {
|
|
14945
|
+
const result = await this.propertyTestGenerator.generatePropertyTests(request);
|
|
14946
|
+
return ok(result);
|
|
14947
|
+
} catch (error) {
|
|
14948
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
14949
|
+
}
|
|
14950
|
+
}
|
|
14951
|
+
/**
|
|
14952
|
+
* Generate test data based on schema - delegates to TestDataGeneratorService
|
|
14953
|
+
*/
|
|
14954
|
+
async generateTestData(request) {
|
|
14955
|
+
try {
|
|
14956
|
+
const result = await this.testDataGenerator.generateTestData(request);
|
|
14957
|
+
return ok(result);
|
|
14958
|
+
} catch (error) {
|
|
14959
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
14960
|
+
}
|
|
14961
|
+
}
|
|
14962
|
+
// ============================================================================
|
|
14963
|
+
// Private Helper Methods - Core Test Generation
|
|
14964
|
+
// ============================================================================
|
|
14965
|
+
async generateTestsForFile(sourceFile, testType, framework, patterns) {
|
|
14966
|
+
const testFile = this.getTestFilePath(sourceFile, framework);
|
|
14967
|
+
const patternsUsed = [];
|
|
14968
|
+
const applicablePatterns = await this.findApplicablePatterns(sourceFile, patterns);
|
|
14969
|
+
patternsUsed.push(...applicablePatterns.map((p) => p.name));
|
|
14970
|
+
let codeAnalysis = null;
|
|
14971
|
+
try {
|
|
14972
|
+
const content = fs2.readFileSync(sourceFile, "utf-8");
|
|
14973
|
+
codeAnalysis = this.analyzeSourceCode(content, sourceFile);
|
|
14974
|
+
} catch {
|
|
14975
|
+
}
|
|
14976
|
+
const generator = this.generatorFactory.create(framework);
|
|
14977
|
+
const moduleName = this.extractModuleName(sourceFile);
|
|
14978
|
+
const importPath = this.getImportPath(sourceFile);
|
|
14979
|
+
const context = {
|
|
14980
|
+
moduleName,
|
|
14981
|
+
importPath,
|
|
14982
|
+
testType,
|
|
14983
|
+
patterns: applicablePatterns,
|
|
14984
|
+
analysis: codeAnalysis ?? void 0
|
|
14985
|
+
};
|
|
14986
|
+
const testCode = generator.generateTests(context);
|
|
14987
|
+
const test = {
|
|
14988
|
+
id: uuidv44(),
|
|
14989
|
+
name: `${moduleName} tests`,
|
|
14990
|
+
sourceFile,
|
|
14991
|
+
testFile,
|
|
14992
|
+
testCode,
|
|
14993
|
+
type: testType,
|
|
14994
|
+
assertions: this.countAssertions(testCode)
|
|
14995
|
+
};
|
|
14996
|
+
return ok({ tests: [test], patternsUsed });
|
|
14997
|
+
}
|
|
13989
14998
|
async generateTestForLines(file, lines, framework) {
|
|
13990
14999
|
if (lines.length === 0) return null;
|
|
13991
15000
|
const testId = uuidv44();
|
|
13992
15001
|
const testFile = this.getTestFilePath(file, framework);
|
|
13993
15002
|
const moduleName = this.extractModuleName(file);
|
|
13994
15003
|
const importPath = this.getImportPath(file);
|
|
13995
|
-
const
|
|
15004
|
+
const generator = this.generatorFactory.create(framework);
|
|
15005
|
+
const testCode = generator.generateCoverageTests(moduleName, importPath, lines);
|
|
13996
15006
|
return {
|
|
13997
15007
|
id: testId,
|
|
13998
15008
|
name: `Coverage test for lines ${lines[0]}-${lines[lines.length - 1]}`,
|
|
@@ -14003,62 +15013,187 @@ ${setupCode}
|
|
|
14003
15013
|
assertions: this.countAssertions(testCode)
|
|
14004
15014
|
};
|
|
14005
15015
|
}
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14010
|
-
const
|
|
14011
|
-
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
|
|
14019
|
-
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
|
|
14026
|
-
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
15016
|
+
// ============================================================================
|
|
15017
|
+
// Private Helper Methods - AST Analysis
|
|
15018
|
+
// ============================================================================
|
|
15019
|
+
analyzeSourceCode(content, fileName) {
|
|
15020
|
+
const sourceFile = ts.createSourceFile(
|
|
15021
|
+
path2.basename(fileName),
|
|
15022
|
+
content,
|
|
15023
|
+
ts.ScriptTarget.Latest,
|
|
15024
|
+
true,
|
|
15025
|
+
ts.ScriptKind.TS
|
|
15026
|
+
);
|
|
15027
|
+
const functions = [];
|
|
15028
|
+
const classes = [];
|
|
15029
|
+
const visit = (node) => {
|
|
15030
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
15031
|
+
functions.push(this.extractFunctionInfo(node, sourceFile));
|
|
15032
|
+
} else if (ts.isVariableStatement(node)) {
|
|
15033
|
+
for (const declaration of node.declarationList.declarations) {
|
|
15034
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer && (ts.isArrowFunction(declaration.initializer) || ts.isFunctionExpression(declaration.initializer))) {
|
|
15035
|
+
const name = declaration.name.getText(sourceFile);
|
|
15036
|
+
functions.push(
|
|
15037
|
+
this.extractArrowFunctionInfo(name, declaration.initializer, sourceFile, node)
|
|
15038
|
+
);
|
|
15039
|
+
}
|
|
15040
|
+
}
|
|
15041
|
+
} else if (ts.isClassDeclaration(node) && node.name) {
|
|
15042
|
+
classes.push(this.extractClassInfo(node, sourceFile));
|
|
15043
|
+
}
|
|
15044
|
+
ts.forEachChild(node, visit);
|
|
15045
|
+
};
|
|
15046
|
+
ts.forEachChild(sourceFile, visit);
|
|
15047
|
+
return { functions, classes };
|
|
15048
|
+
}
|
|
15049
|
+
extractFunctionInfo(node, sourceFile) {
|
|
15050
|
+
const name = node.name?.getText(sourceFile) || "anonymous";
|
|
15051
|
+
const parameters = this.extractParameters(node.parameters, sourceFile);
|
|
15052
|
+
const returnType = node.type?.getText(sourceFile);
|
|
15053
|
+
const isAsync = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
15054
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
15055
|
+
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
15056
|
+
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
15057
|
+
return {
|
|
15058
|
+
name,
|
|
15059
|
+
parameters,
|
|
15060
|
+
returnType,
|
|
15061
|
+
isAsync,
|
|
15062
|
+
isExported,
|
|
15063
|
+
complexity: this.calculateComplexity(node),
|
|
15064
|
+
startLine: startLine + 1,
|
|
15065
|
+
endLine: endLine + 1,
|
|
15066
|
+
body: node.body?.getText(sourceFile)
|
|
15067
|
+
};
|
|
15068
|
+
}
|
|
15069
|
+
extractArrowFunctionInfo(name, node, sourceFile, parentNode) {
|
|
15070
|
+
const parameters = this.extractParameters(node.parameters, sourceFile);
|
|
15071
|
+
const returnType = node.type?.getText(sourceFile);
|
|
15072
|
+
const isAsync = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
15073
|
+
const isExported = ts.isVariableStatement(parentNode) && (parentNode.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false);
|
|
15074
|
+
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
15075
|
+
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
15076
|
+
return {
|
|
15077
|
+
name,
|
|
15078
|
+
parameters,
|
|
15079
|
+
returnType,
|
|
15080
|
+
isAsync,
|
|
15081
|
+
isExported,
|
|
15082
|
+
complexity: this.calculateComplexity(node),
|
|
15083
|
+
startLine: startLine + 1,
|
|
15084
|
+
endLine: endLine + 1,
|
|
15085
|
+
body: node.body?.getText(sourceFile)
|
|
15086
|
+
};
|
|
15087
|
+
}
|
|
15088
|
+
extractClassInfo(node, sourceFile) {
|
|
15089
|
+
const name = node.name?.getText(sourceFile) || "AnonymousClass";
|
|
15090
|
+
const methods = [];
|
|
15091
|
+
const properties = [];
|
|
15092
|
+
let hasConstructor = false;
|
|
15093
|
+
let constructorParams;
|
|
15094
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
15095
|
+
for (const member of node.members) {
|
|
15096
|
+
if (ts.isMethodDeclaration(member)) {
|
|
15097
|
+
const methodName = member.name.getText(sourceFile);
|
|
15098
|
+
const parameters = this.extractParameters(member.parameters, sourceFile);
|
|
15099
|
+
const returnType = member.type?.getText(sourceFile);
|
|
15100
|
+
const isAsync = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
15101
|
+
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(
|
|
15102
|
+
member.getStart(sourceFile)
|
|
15103
|
+
);
|
|
15104
|
+
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(member.getEnd());
|
|
15105
|
+
methods.push({
|
|
15106
|
+
name: methodName,
|
|
15107
|
+
parameters,
|
|
15108
|
+
returnType,
|
|
15109
|
+
isAsync,
|
|
15110
|
+
isExported: false,
|
|
15111
|
+
complexity: this.calculateComplexity(member),
|
|
15112
|
+
startLine: startLine + 1,
|
|
15113
|
+
endLine: endLine + 1,
|
|
15114
|
+
body: member.body?.getText(sourceFile)
|
|
15115
|
+
});
|
|
15116
|
+
} else if (ts.isConstructorDeclaration(member)) {
|
|
15117
|
+
hasConstructor = true;
|
|
15118
|
+
constructorParams = this.extractParameters(member.parameters, sourceFile);
|
|
15119
|
+
} else if (ts.isPropertyDeclaration(member)) {
|
|
15120
|
+
const propName = member.name.getText(sourceFile);
|
|
15121
|
+
const propType = member.type?.getText(sourceFile);
|
|
15122
|
+
const isPrivate = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword) ?? false;
|
|
15123
|
+
const isReadonly = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false;
|
|
15124
|
+
properties.push({
|
|
15125
|
+
name: propName,
|
|
15126
|
+
type: propType,
|
|
15127
|
+
isPrivate,
|
|
15128
|
+
isReadonly
|
|
15129
|
+
});
|
|
15130
|
+
}
|
|
14035
15131
|
}
|
|
14036
|
-
return
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14040
|
-
|
|
14041
|
-
|
|
14042
|
-
|
|
14043
|
-
|
|
14044
|
-
|
|
14045
|
-
|
|
14046
|
-
|
|
14047
|
-
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14060
|
-
|
|
14061
|
-
|
|
15132
|
+
return {
|
|
15133
|
+
name,
|
|
15134
|
+
methods,
|
|
15135
|
+
properties,
|
|
15136
|
+
isExported,
|
|
15137
|
+
hasConstructor,
|
|
15138
|
+
constructorParams
|
|
15139
|
+
};
|
|
15140
|
+
}
|
|
15141
|
+
extractParameters(params, sourceFile) {
|
|
15142
|
+
return params.map((param) => ({
|
|
15143
|
+
name: param.name.getText(sourceFile),
|
|
15144
|
+
type: param.type?.getText(sourceFile),
|
|
15145
|
+
optional: param.questionToken !== void 0,
|
|
15146
|
+
defaultValue: param.initializer?.getText(sourceFile)
|
|
15147
|
+
}));
|
|
15148
|
+
}
|
|
15149
|
+
calculateComplexity(node) {
|
|
15150
|
+
let complexity = 1;
|
|
15151
|
+
const visit = (n) => {
|
|
15152
|
+
switch (n.kind) {
|
|
15153
|
+
case ts.SyntaxKind.IfStatement:
|
|
15154
|
+
case ts.SyntaxKind.ForStatement:
|
|
15155
|
+
case ts.SyntaxKind.ForInStatement:
|
|
15156
|
+
case ts.SyntaxKind.ForOfStatement:
|
|
15157
|
+
case ts.SyntaxKind.WhileStatement:
|
|
15158
|
+
case ts.SyntaxKind.DoStatement:
|
|
15159
|
+
case ts.SyntaxKind.CaseClause:
|
|
15160
|
+
case ts.SyntaxKind.CatchClause:
|
|
15161
|
+
case ts.SyntaxKind.ConditionalExpression:
|
|
15162
|
+
complexity++;
|
|
15163
|
+
break;
|
|
15164
|
+
case ts.SyntaxKind.BinaryExpression: {
|
|
15165
|
+
const binary = n;
|
|
15166
|
+
if (binary.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || binary.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
15167
|
+
complexity++;
|
|
15168
|
+
}
|
|
15169
|
+
break;
|
|
15170
|
+
}
|
|
15171
|
+
}
|
|
15172
|
+
ts.forEachChild(n, visit);
|
|
15173
|
+
};
|
|
15174
|
+
ts.forEachChild(node, visit);
|
|
15175
|
+
return complexity;
|
|
15176
|
+
}
|
|
15177
|
+
// ============================================================================
|
|
15178
|
+
// Private Helper Methods - Utility Functions
|
|
15179
|
+
// ============================================================================
|
|
15180
|
+
async findApplicablePatterns(sourceFile, requestedPatterns) {
|
|
15181
|
+
const patterns = [];
|
|
15182
|
+
for (const patternName of requestedPatterns) {
|
|
15183
|
+
const stored = await this.memory.get(`pattern:${patternName}`);
|
|
15184
|
+
if (stored) {
|
|
15185
|
+
patterns.push(stored);
|
|
15186
|
+
}
|
|
15187
|
+
}
|
|
15188
|
+
const extension = sourceFile.split(".").pop() || "";
|
|
15189
|
+
const searchResults = await this.memory.search(`pattern:*:${extension}`, 5);
|
|
15190
|
+
for (const key of searchResults) {
|
|
15191
|
+
const pattern = await this.memory.get(key);
|
|
15192
|
+
if (pattern && !patterns.some((p) => p.id === pattern.id)) {
|
|
15193
|
+
patterns.push(pattern);
|
|
15194
|
+
}
|
|
15195
|
+
}
|
|
15196
|
+
return patterns;
|
|
14062
15197
|
}
|
|
14063
15198
|
groupConsecutiveLines(lines) {
|
|
14064
15199
|
if (lines.length === 0) return [];
|
|
@@ -14119,12 +15254,6 @@ describe('${moduleName} coverage', () => {
|
|
|
14119
15254
|
const estimatedCoverage = Math.min(target, Math.max(0, diminishedEstimate));
|
|
14120
15255
|
return Math.round(estimatedCoverage * 10) / 10;
|
|
14121
15256
|
}
|
|
14122
|
-
camelCase(str) {
|
|
14123
|
-
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase()).replace(/^./, (chr) => chr.toLowerCase());
|
|
14124
|
-
}
|
|
14125
|
-
pascalCase(str) {
|
|
14126
|
-
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase()).replace(/^./, (chr) => chr.toUpperCase());
|
|
14127
|
-
}
|
|
14128
15257
|
async storeGenerationMetadata(tests, patterns) {
|
|
14129
15258
|
const metadata = {
|
|
14130
15259
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14136,10 +15265,12 @@ describe('${moduleName} coverage', () => {
|
|
|
14136
15265
|
`test-generation:metadata:${Date.now()}`,
|
|
14137
15266
|
metadata,
|
|
14138
15267
|
{ namespace: "test-generation", ttl: 86400 * 7 }
|
|
14139
|
-
// 7 days
|
|
14140
15268
|
);
|
|
14141
15269
|
}
|
|
14142
15270
|
};
|
|
15271
|
+
function createTestGeneratorService(memory, config = {}) {
|
|
15272
|
+
return new TestGeneratorService({ memory }, config);
|
|
15273
|
+
}
|
|
14143
15274
|
|
|
14144
15275
|
// src/domains/test-generation/services/pattern-matcher.ts
|
|
14145
15276
|
import { v4 as uuidv45 } from "uuid";
|
|
@@ -14310,7 +15441,7 @@ var OllamaClient = class {
|
|
|
14310
15441
|
};
|
|
14311
15442
|
|
|
14312
15443
|
// src/shared/embeddings/embedding-cache.ts
|
|
14313
|
-
import { createHash } from "crypto";
|
|
15444
|
+
import { createHash as createHash2 } from "crypto";
|
|
14314
15445
|
var EmbeddingCache = class {
|
|
14315
15446
|
cache;
|
|
14316
15447
|
maxSize;
|
|
@@ -14326,7 +15457,7 @@ var EmbeddingCache = class {
|
|
|
14326
15457
|
* Generate SHA-256 hash of content for cache key
|
|
14327
15458
|
*/
|
|
14328
15459
|
hashContent(content, model) {
|
|
14329
|
-
return
|
|
15460
|
+
return createHash2("sha256").update(`${model}:${content}`).digest("hex");
|
|
14330
15461
|
}
|
|
14331
15462
|
/**
|
|
14332
15463
|
* Get cached embedding if available
|
|
@@ -17889,7 +19020,7 @@ var DecisionTransformerAlgorithm = class extends BaseRLAlgorithm {
|
|
|
17889
19020
|
}
|
|
17890
19021
|
};
|
|
17891
19022
|
|
|
17892
|
-
// src/domains/test-generation/coherence-gate.ts
|
|
19023
|
+
// src/domains/test-generation/services/coherence-gate-service.ts
|
|
17893
19024
|
var DEFAULT_COHERENCE_GATE_CONFIG = {
|
|
17894
19025
|
enabled: true,
|
|
17895
19026
|
coherenceThreshold: 0.1,
|
|
@@ -18297,7 +19428,7 @@ var TestGenerationCoordinator = class {
|
|
|
18297
19428
|
this.agentCoordinator = agentCoordinator;
|
|
18298
19429
|
this.coherenceService = coherenceService;
|
|
18299
19430
|
this.config = { ...DEFAULT_CONFIG3, ...config };
|
|
18300
|
-
this.testGenerator =
|
|
19431
|
+
this.testGenerator = createTestGeneratorService(memory);
|
|
18301
19432
|
this.patternMatcher = new PatternMatcherService(memory);
|
|
18302
19433
|
if (this.config.enableCoherenceGate && coherenceService) {
|
|
18303
19434
|
this.coherenceGate = createTestGenerationCoherenceGate(
|
|
@@ -19159,7 +20290,7 @@ var TestGenerationPlugin = class extends BaseDomainPlugin {
|
|
|
19159
20290
|
// Lifecycle Methods
|
|
19160
20291
|
// ============================================================================
|
|
19161
20292
|
async onInitialize() {
|
|
19162
|
-
this.testGenerator =
|
|
20293
|
+
this.testGenerator = createTestGeneratorService(
|
|
19163
20294
|
this.memory,
|
|
19164
20295
|
this.pluginConfig.testGenerator
|
|
19165
20296
|
);
|
|
@@ -19175,7 +20306,7 @@ var TestGenerationPlugin = class extends BaseDomainPlugin {
|
|
|
19175
20306
|
);
|
|
19176
20307
|
await this.coordinator.initialize();
|
|
19177
20308
|
this.updateHealth({
|
|
19178
|
-
status: "
|
|
20309
|
+
status: "idle",
|
|
19179
20310
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
19180
20311
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
19181
20312
|
errors: []
|
|
@@ -21332,7 +22463,46 @@ var RetryHandlerService = class {
|
|
|
21332
22463
|
}
|
|
21333
22464
|
};
|
|
21334
22465
|
|
|
21335
|
-
// src/domains/test-execution/
|
|
22466
|
+
// src/domains/test-execution/types/e2e-step.types.ts
|
|
22467
|
+
var E2EStepType = {
|
|
22468
|
+
/** Navigate to a URL */
|
|
22469
|
+
NAVIGATE: "navigate",
|
|
22470
|
+
/** Click an element */
|
|
22471
|
+
CLICK: "click",
|
|
22472
|
+
/** Type text into an element */
|
|
22473
|
+
TYPE: "type",
|
|
22474
|
+
/** Wait for a condition */
|
|
22475
|
+
WAIT: "wait",
|
|
22476
|
+
/** Make an assertion */
|
|
22477
|
+
ASSERT: "assert",
|
|
22478
|
+
/** Take a screenshot */
|
|
22479
|
+
SCREENSHOT: "screenshot",
|
|
22480
|
+
/** Perform accessibility check */
|
|
22481
|
+
A11Y_CHECK: "a11y-check"
|
|
22482
|
+
};
|
|
22483
|
+
function isNavigateStep(step) {
|
|
22484
|
+
return step.type === E2EStepType.NAVIGATE;
|
|
22485
|
+
}
|
|
22486
|
+
function isClickStep(step) {
|
|
22487
|
+
return step.type === E2EStepType.CLICK;
|
|
22488
|
+
}
|
|
22489
|
+
function isTypeStep(step) {
|
|
22490
|
+
return step.type === E2EStepType.TYPE;
|
|
22491
|
+
}
|
|
22492
|
+
function isWaitStep(step) {
|
|
22493
|
+
return step.type === E2EStepType.WAIT;
|
|
22494
|
+
}
|
|
22495
|
+
function isAssertStep(step) {
|
|
22496
|
+
return step.type === E2EStepType.ASSERT;
|
|
22497
|
+
}
|
|
22498
|
+
function isScreenshotStep(step) {
|
|
22499
|
+
return step.type === E2EStepType.SCREENSHOT;
|
|
22500
|
+
}
|
|
22501
|
+
function isA11yCheckStep(step) {
|
|
22502
|
+
return step.type === E2EStepType.A11Y_CHECK;
|
|
22503
|
+
}
|
|
22504
|
+
|
|
22505
|
+
// src/domains/test-execution/interfaces.ts
|
|
21336
22506
|
function mapToFeatures(test) {
|
|
21337
22507
|
const failureProbability = Math.min(1, test.failureRate ?? 0);
|
|
21338
22508
|
const flakiness = Math.min(1, test.flakinessScore ?? 0);
|
|
@@ -23975,45 +25145,6 @@ async function getBrowserClientForUseCase(useCase) {
|
|
|
23975
25145
|
return createBrowserClient({ useCase });
|
|
23976
25146
|
}
|
|
23977
25147
|
|
|
23978
|
-
// src/domains/test-execution/types/e2e-step.types.ts
|
|
23979
|
-
var E2EStepType = {
|
|
23980
|
-
/** Navigate to a URL */
|
|
23981
|
-
NAVIGATE: "navigate",
|
|
23982
|
-
/** Click an element */
|
|
23983
|
-
CLICK: "click",
|
|
23984
|
-
/** Type text into an element */
|
|
23985
|
-
TYPE: "type",
|
|
23986
|
-
/** Wait for a condition */
|
|
23987
|
-
WAIT: "wait",
|
|
23988
|
-
/** Make an assertion */
|
|
23989
|
-
ASSERT: "assert",
|
|
23990
|
-
/** Take a screenshot */
|
|
23991
|
-
SCREENSHOT: "screenshot",
|
|
23992
|
-
/** Perform accessibility check */
|
|
23993
|
-
A11Y_CHECK: "a11y-check"
|
|
23994
|
-
};
|
|
23995
|
-
function isNavigateStep(step) {
|
|
23996
|
-
return step.type === E2EStepType.NAVIGATE;
|
|
23997
|
-
}
|
|
23998
|
-
function isClickStep(step) {
|
|
23999
|
-
return step.type === E2EStepType.CLICK;
|
|
24000
|
-
}
|
|
24001
|
-
function isTypeStep(step) {
|
|
24002
|
-
return step.type === E2EStepType.TYPE;
|
|
24003
|
-
}
|
|
24004
|
-
function isWaitStep(step) {
|
|
24005
|
-
return step.type === E2EStepType.WAIT;
|
|
24006
|
-
}
|
|
24007
|
-
function isAssertStep(step) {
|
|
24008
|
-
return step.type === E2EStepType.ASSERT;
|
|
24009
|
-
}
|
|
24010
|
-
function isScreenshotStep(step) {
|
|
24011
|
-
return step.type === E2EStepType.SCREENSHOT;
|
|
24012
|
-
}
|
|
24013
|
-
function isA11yCheckStep(step) {
|
|
24014
|
-
return step.type === E2EStepType.A11Y_CHECK;
|
|
24015
|
-
}
|
|
24016
|
-
|
|
24017
25148
|
// src/domains/test-execution/services/e2e-runner.ts
|
|
24018
25149
|
var DEFAULT_E2E_RUNNER_CONFIG = {
|
|
24019
25150
|
defaultStepTimeout: 3e4,
|
|
@@ -26197,7 +27328,7 @@ var TestExecutionPlugin = class extends BaseDomainPlugin {
|
|
|
26197
27328
|
);
|
|
26198
27329
|
await this.coordinator.initialize();
|
|
26199
27330
|
this.updateHealth({
|
|
26200
|
-
status: "
|
|
27331
|
+
status: "idle",
|
|
26201
27332
|
lastActivity: /* @__PURE__ */ new Date()
|
|
26202
27333
|
});
|
|
26203
27334
|
}
|
|
@@ -28743,7 +29874,7 @@ var CoverageAnalysisPlugin = class _CoverageAnalysisPlugin extends BaseDomainPlu
|
|
|
28743
29874
|
async onInitialize() {
|
|
28744
29875
|
await this.coordinator.initialize();
|
|
28745
29876
|
this.updateHealth({
|
|
28746
|
-
status: "
|
|
29877
|
+
status: "idle",
|
|
28747
29878
|
lastActivity: /* @__PURE__ */ new Date()
|
|
28748
29879
|
});
|
|
28749
29880
|
}
|
|
@@ -29590,251 +30721,6 @@ var typescriptParser = new TypeScriptParser();
|
|
|
29590
30721
|
// src/shared/io/file-reader.ts
|
|
29591
30722
|
import * as fs5 from "node:fs/promises";
|
|
29592
30723
|
import * as path5 from "node:path";
|
|
29593
|
-
|
|
29594
|
-
// src/mcp/security/cve-prevention.ts
|
|
29595
|
-
var PATH_TRAVERSAL_PATTERNS = [
|
|
29596
|
-
/\.\./,
|
|
29597
|
-
// Basic traversal
|
|
29598
|
-
/%2e%2e/i,
|
|
29599
|
-
// URL encoded ..
|
|
29600
|
-
/%252e%252e/i,
|
|
29601
|
-
// Double URL encoded
|
|
29602
|
-
/\.\.%2f/i,
|
|
29603
|
-
// Mixed encoding
|
|
29604
|
-
/%2f\.\./i,
|
|
29605
|
-
// Forward slash + ..
|
|
29606
|
-
/\.\.%5c/i,
|
|
29607
|
-
// Backslash + ..
|
|
29608
|
-
/\.\.\\/,
|
|
29609
|
-
// Windows backslash traversal
|
|
29610
|
-
/%c0%ae/i,
|
|
29611
|
-
// UTF-8 overlong encoding
|
|
29612
|
-
/%c0%2f/i,
|
|
29613
|
-
// UTF-8 overlong /
|
|
29614
|
-
/%c1%9c/i,
|
|
29615
|
-
// UTF-8 overlong \
|
|
29616
|
-
/\0/,
|
|
29617
|
-
// Null byte injection
|
|
29618
|
-
/%00/i
|
|
29619
|
-
// URL encoded null
|
|
29620
|
-
];
|
|
29621
|
-
var DANGEROUS_PATH_COMPONENTS = [
|
|
29622
|
-
/^\/etc\//i,
|
|
29623
|
-
/^\/proc\//i,
|
|
29624
|
-
/^\/sys\//i,
|
|
29625
|
-
/^\/dev\//i,
|
|
29626
|
-
/^\/root\//i,
|
|
29627
|
-
/^\/home\/.+\/\./i,
|
|
29628
|
-
/^[A-Z]:\\Windows/i,
|
|
29629
|
-
/^[A-Z]:\\System/i,
|
|
29630
|
-
/^[A-Z]:\\Users\\.+\\AppData/i
|
|
29631
|
-
];
|
|
29632
|
-
function validatePath(path16, options = {}) {
|
|
29633
|
-
const {
|
|
29634
|
-
basePath = "",
|
|
29635
|
-
allowAbsolute = false,
|
|
29636
|
-
allowedExtensions = [],
|
|
29637
|
-
deniedExtensions = [".exe", ".bat", ".cmd", ".sh", ".ps1", ".dll", ".so"],
|
|
29638
|
-
maxDepth = 10,
|
|
29639
|
-
maxLength = 4096
|
|
29640
|
-
} = options;
|
|
29641
|
-
if (path16.length > maxLength) {
|
|
29642
|
-
return {
|
|
29643
|
-
valid: false,
|
|
29644
|
-
error: `Path exceeds maximum length of ${maxLength}`,
|
|
29645
|
-
riskLevel: "medium"
|
|
29646
|
-
};
|
|
29647
|
-
}
|
|
29648
|
-
for (const pattern of PATH_TRAVERSAL_PATTERNS) {
|
|
29649
|
-
if (pattern.test(path16)) {
|
|
29650
|
-
return {
|
|
29651
|
-
valid: false,
|
|
29652
|
-
error: "Path traversal attempt detected",
|
|
29653
|
-
riskLevel: "critical"
|
|
29654
|
-
};
|
|
29655
|
-
}
|
|
29656
|
-
}
|
|
29657
|
-
if (!allowAbsolute && (path16.startsWith("/") || /^[A-Z]:/i.test(path16))) {
|
|
29658
|
-
return {
|
|
29659
|
-
valid: false,
|
|
29660
|
-
error: "Absolute paths are not allowed",
|
|
29661
|
-
riskLevel: "high"
|
|
29662
|
-
};
|
|
29663
|
-
}
|
|
29664
|
-
for (const pattern of DANGEROUS_PATH_COMPONENTS) {
|
|
29665
|
-
if (pattern.test(path16)) {
|
|
29666
|
-
return {
|
|
29667
|
-
valid: false,
|
|
29668
|
-
error: "Access to system paths is not allowed",
|
|
29669
|
-
riskLevel: "critical"
|
|
29670
|
-
};
|
|
29671
|
-
}
|
|
29672
|
-
}
|
|
29673
|
-
const normalizedPath = normalizePath(path16);
|
|
29674
|
-
if (normalizedPath.includes("..")) {
|
|
29675
|
-
return {
|
|
29676
|
-
valid: false,
|
|
29677
|
-
error: "Path traversal detected after normalization",
|
|
29678
|
-
riskLevel: "critical"
|
|
29679
|
-
};
|
|
29680
|
-
}
|
|
29681
|
-
const depth = normalizedPath.split("/").filter(Boolean).length;
|
|
29682
|
-
if (depth > maxDepth) {
|
|
29683
|
-
return {
|
|
29684
|
-
valid: false,
|
|
29685
|
-
error: `Path depth exceeds maximum of ${maxDepth}`,
|
|
29686
|
-
riskLevel: "low"
|
|
29687
|
-
};
|
|
29688
|
-
}
|
|
29689
|
-
const ext = getExtension(normalizedPath);
|
|
29690
|
-
if (ext) {
|
|
29691
|
-
const extWithDot = `.${ext.toLowerCase()}`;
|
|
29692
|
-
const extWithoutDot = ext.toLowerCase();
|
|
29693
|
-
if (deniedExtensions.length > 0) {
|
|
29694
|
-
const isDenied = deniedExtensions.some(
|
|
29695
|
-
(denied) => denied.toLowerCase() === extWithDot || denied.toLowerCase() === extWithoutDot
|
|
29696
|
-
);
|
|
29697
|
-
if (isDenied) {
|
|
29698
|
-
return {
|
|
29699
|
-
valid: false,
|
|
29700
|
-
error: `File extension '${ext}' is not allowed`,
|
|
29701
|
-
riskLevel: "high"
|
|
29702
|
-
};
|
|
29703
|
-
}
|
|
29704
|
-
}
|
|
29705
|
-
if (allowedExtensions.length > 0) {
|
|
29706
|
-
const isAllowed = allowedExtensions.some(
|
|
29707
|
-
(allowed) => allowed.toLowerCase() === extWithDot || allowed.toLowerCase() === extWithoutDot
|
|
29708
|
-
);
|
|
29709
|
-
if (!isAllowed) {
|
|
29710
|
-
return {
|
|
29711
|
-
valid: false,
|
|
29712
|
-
error: `File extension '${ext}' is not in allowed list`,
|
|
29713
|
-
riskLevel: "medium"
|
|
29714
|
-
};
|
|
29715
|
-
}
|
|
29716
|
-
}
|
|
29717
|
-
}
|
|
29718
|
-
const finalPath = basePath ? joinPathsAbsolute(basePath, normalizedPath) : normalizedPath;
|
|
29719
|
-
const normalizedBase = basePath.startsWith("/") ? `/${normalizePath(basePath)}` : normalizePath(basePath);
|
|
29720
|
-
if (basePath && !finalPath.startsWith(normalizedBase)) {
|
|
29721
|
-
return {
|
|
29722
|
-
valid: false,
|
|
29723
|
-
error: "Path escapes base directory",
|
|
29724
|
-
riskLevel: "critical"
|
|
29725
|
-
};
|
|
29726
|
-
}
|
|
29727
|
-
return {
|
|
29728
|
-
valid: true,
|
|
29729
|
-
normalizedPath: finalPath,
|
|
29730
|
-
riskLevel: "none"
|
|
29731
|
-
};
|
|
29732
|
-
}
|
|
29733
|
-
function normalizePath(path16) {
|
|
29734
|
-
let normalized = path16.replace(/\\/g, "/");
|
|
29735
|
-
normalized = normalized.replace(/\/+/g, "/");
|
|
29736
|
-
const parts = normalized.split("/");
|
|
29737
|
-
const result = [];
|
|
29738
|
-
for (const part of parts) {
|
|
29739
|
-
if (part === "." || part === "") {
|
|
29740
|
-
continue;
|
|
29741
|
-
}
|
|
29742
|
-
if (part === "..") {
|
|
29743
|
-
if (result.length > 0 && result[result.length - 1] !== "..") {
|
|
29744
|
-
result.pop();
|
|
29745
|
-
}
|
|
29746
|
-
} else {
|
|
29747
|
-
result.push(part);
|
|
29748
|
-
}
|
|
29749
|
-
}
|
|
29750
|
-
return result.join("/");
|
|
29751
|
-
}
|
|
29752
|
-
function joinPathsAbsolute(...paths) {
|
|
29753
|
-
if (paths.length === 0) return "";
|
|
29754
|
-
const isAbsolute4 = paths[0].startsWith("/");
|
|
29755
|
-
const result = paths.map((p) => {
|
|
29756
|
-
while (p.startsWith("/")) p = p.slice(1);
|
|
29757
|
-
while (p.endsWith("/")) p = p.slice(0, -1);
|
|
29758
|
-
return p;
|
|
29759
|
-
}).filter(Boolean).join("/");
|
|
29760
|
-
return isAbsolute4 ? `/${result}` : result;
|
|
29761
|
-
}
|
|
29762
|
-
function getExtension(path16) {
|
|
29763
|
-
const match = path16.match(/\.([^./\\]+)$/);
|
|
29764
|
-
return match ? match[1] : null;
|
|
29765
|
-
}
|
|
29766
|
-
var SHELL_METACHARACTERS = /[|;&$`<>{}[\]!#*?~]/g;
|
|
29767
|
-
var DEFAULT_ALLOWED_COMMANDS = [
|
|
29768
|
-
"ls",
|
|
29769
|
-
"cat",
|
|
29770
|
-
"echo",
|
|
29771
|
-
"grep",
|
|
29772
|
-
"find",
|
|
29773
|
-
"head",
|
|
29774
|
-
"tail",
|
|
29775
|
-
"wc",
|
|
29776
|
-
"npm",
|
|
29777
|
-
"node",
|
|
29778
|
-
"yarn",
|
|
29779
|
-
"pnpm",
|
|
29780
|
-
"git",
|
|
29781
|
-
"jest",
|
|
29782
|
-
"vitest",
|
|
29783
|
-
"playwright"
|
|
29784
|
-
];
|
|
29785
|
-
var BLOCKED_COMMAND_PATTERNS = [
|
|
29786
|
-
/;/,
|
|
29787
|
-
// Command chaining with semicolon
|
|
29788
|
-
/&&/,
|
|
29789
|
-
// Command chaining with AND
|
|
29790
|
-
/\|\|/,
|
|
29791
|
-
// Command chaining with OR
|
|
29792
|
-
/\|/,
|
|
29793
|
-
// Piping
|
|
29794
|
-
/`.*`/,
|
|
29795
|
-
// Backtick command substitution
|
|
29796
|
-
/\$\(.*\)/,
|
|
29797
|
-
// $() command substitution
|
|
29798
|
-
/>\s*\/dev\/sd/i,
|
|
29799
|
-
// Writing to block devices
|
|
29800
|
-
/>\s*\/etc\//i
|
|
29801
|
-
// Writing to /etc
|
|
29802
|
-
];
|
|
29803
|
-
function validateCommand(command, allowedCommands = DEFAULT_ALLOWED_COMMANDS) {
|
|
29804
|
-
const blockedPatterns = [];
|
|
29805
|
-
for (const pattern of BLOCKED_COMMAND_PATTERNS) {
|
|
29806
|
-
if (pattern.test(command)) {
|
|
29807
|
-
blockedPatterns.push(pattern.source);
|
|
29808
|
-
}
|
|
29809
|
-
}
|
|
29810
|
-
if (blockedPatterns.length > 0) {
|
|
29811
|
-
return {
|
|
29812
|
-
valid: false,
|
|
29813
|
-
error: "Command contains blocked patterns",
|
|
29814
|
-
blockedPatterns
|
|
29815
|
-
};
|
|
29816
|
-
}
|
|
29817
|
-
const parts = command.trim().split(/\s+/);
|
|
29818
|
-
const baseCommand = parts[0].split("/").pop() || "";
|
|
29819
|
-
if (!allowedCommands.includes(baseCommand)) {
|
|
29820
|
-
return {
|
|
29821
|
-
valid: false,
|
|
29822
|
-
error: `Command '${baseCommand}' is not in the allowed list`,
|
|
29823
|
-
blockedPatterns: []
|
|
29824
|
-
};
|
|
29825
|
-
}
|
|
29826
|
-
const sanitizedParts = parts.map((part, i) => {
|
|
29827
|
-
if (i === 0) return part;
|
|
29828
|
-
return part.replace(SHELL_METACHARACTERS, "");
|
|
29829
|
-
});
|
|
29830
|
-
return {
|
|
29831
|
-
valid: true,
|
|
29832
|
-
sanitizedCommand: sanitizedParts.join(" "),
|
|
29833
|
-
blockedPatterns: []
|
|
29834
|
-
};
|
|
29835
|
-
}
|
|
29836
|
-
|
|
29837
|
-
// src/shared/io/file-reader.ts
|
|
29838
30724
|
var FileReadError = class extends Error {
|
|
29839
30725
|
constructor(message, filePath, code, cause) {
|
|
29840
30726
|
super(message);
|
|
@@ -34313,7 +35199,7 @@ var QualityAssessmentPlugin = class extends BaseDomainPlugin {
|
|
|
34313
35199
|
);
|
|
34314
35200
|
await this.coordinator.initialize();
|
|
34315
35201
|
this.updateHealth({
|
|
34316
|
-
status: "
|
|
35202
|
+
status: "idle",
|
|
34317
35203
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
34318
35204
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
34319
35205
|
errors: []
|
|
@@ -37213,7 +38099,7 @@ var DefectIntelligencePlugin = class extends BaseDomainPlugin {
|
|
|
37213
38099
|
);
|
|
37214
38100
|
await this.coordinator.initialize();
|
|
37215
38101
|
this.updateHealth({
|
|
37216
|
-
status: "
|
|
38102
|
+
status: "idle",
|
|
37217
38103
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
37218
38104
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
37219
38105
|
errors: []
|
|
@@ -39927,7 +40813,7 @@ var RequirementsValidationPlugin = class extends BaseDomainPlugin {
|
|
|
39927
40813
|
);
|
|
39928
40814
|
await this.coordinator.initialize();
|
|
39929
40815
|
this.updateHealth({
|
|
39930
|
-
status: "
|
|
40816
|
+
status: "idle",
|
|
39931
40817
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
39932
40818
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
39933
40819
|
errors: []
|
|
@@ -45803,7 +46689,7 @@ var CodeIntelligencePlugin = class extends BaseDomainPlugin {
|
|
|
45803
46689
|
);
|
|
45804
46690
|
await this.coordinator.initialize();
|
|
45805
46691
|
this.updateHealth({
|
|
45806
|
-
status: "
|
|
46692
|
+
status: "idle",
|
|
45807
46693
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
45808
46694
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
45809
46695
|
errors: []
|
|
@@ -47182,7 +48068,7 @@ var DEFAULT_CONFIG25 = {
|
|
|
47182
48068
|
dastMaxDepth: 5,
|
|
47183
48069
|
dastActiveScanning: false
|
|
47184
48070
|
};
|
|
47185
|
-
var
|
|
48071
|
+
var SQL_INJECTION_PATTERNS2 = [
|
|
47186
48072
|
{
|
|
47187
48073
|
id: "sqli-string-concat",
|
|
47188
48074
|
pattern: /query\s*\(\s*['"`].*\+.*['"`]\s*\)/g,
|
|
@@ -47590,7 +48476,7 @@ var AUTH_PATTERNS = [
|
|
|
47590
48476
|
}
|
|
47591
48477
|
];
|
|
47592
48478
|
var ALL_SECURITY_PATTERNS = [
|
|
47593
|
-
...
|
|
48479
|
+
...SQL_INJECTION_PATTERNS2,
|
|
47594
48480
|
...XSS_PATTERNS,
|
|
47595
48481
|
...SECRET_PATTERNS,
|
|
47596
48482
|
...PATH_TRAVERSAL_PATTERNS2,
|
|
@@ -53300,7 +54186,7 @@ var SecurityCompliancePlugin = class extends BaseDomainPlugin {
|
|
|
53300
54186
|
);
|
|
53301
54187
|
await this.coordinator.initialize();
|
|
53302
54188
|
this.updateHealth({
|
|
53303
|
-
status: "
|
|
54189
|
+
status: "idle",
|
|
53304
54190
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
53305
54191
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
53306
54192
|
errors: []
|
|
@@ -57429,7 +58315,7 @@ var ContractTestingPlugin = class extends BaseDomainPlugin {
|
|
|
57429
58315
|
);
|
|
57430
58316
|
await this.coordinator.initialize();
|
|
57431
58317
|
this.updateHealth({
|
|
57432
|
-
status: "
|
|
58318
|
+
status: "idle",
|
|
57433
58319
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
57434
58320
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
57435
58321
|
errors: []
|
|
@@ -62503,7 +63389,7 @@ var VisualAccessibilityPlugin = class extends BaseDomainPlugin {
|
|
|
62503
63389
|
);
|
|
62504
63390
|
await this.coordinator.initialize();
|
|
62505
63391
|
this.updateHealth({
|
|
62506
|
-
status: "
|
|
63392
|
+
status: "idle",
|
|
62507
63393
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
62508
63394
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
62509
63395
|
errors: []
|
|
@@ -66313,7 +67199,7 @@ var ChaosResiliencePlugin = class extends BaseDomainPlugin {
|
|
|
66313
67199
|
);
|
|
66314
67200
|
await this.coordinator.initialize();
|
|
66315
67201
|
this.updateHealth({
|
|
66316
|
-
status: "
|
|
67202
|
+
status: "idle",
|
|
66317
67203
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
66318
67204
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
66319
67205
|
errors: []
|
|
@@ -70530,7 +71416,7 @@ var LearningOptimizationPlugin = class extends BaseDomainPlugin {
|
|
|
70530
71416
|
);
|
|
70531
71417
|
await this.coordinator.initialize();
|
|
70532
71418
|
this.updateHealth({
|
|
70533
|
-
status: "
|
|
71419
|
+
status: "idle",
|
|
70534
71420
|
agents: { total: 0, active: 0, idle: 0, failed: 0 },
|
|
70535
71421
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
70536
71422
|
errors: []
|
|
@@ -73527,9 +74413,13 @@ var MinCutHealthMonitor = class {
|
|
|
73527
74413
|
/**
|
|
73528
74414
|
* Issue #205 fix: Check if topology is empty/fresh (no agents spawned yet)
|
|
73529
74415
|
* An empty topology is normal for a fresh install - not a critical issue
|
|
74416
|
+
*
|
|
74417
|
+
* Note: Domain coordinator vertices and workflow edges are always created,
|
|
74418
|
+
* so we check for actual agent vertices instead of raw counts.
|
|
73530
74419
|
*/
|
|
73531
74420
|
isEmptyTopology() {
|
|
73532
|
-
|
|
74421
|
+
const agentVertices = this.graph.getVerticesByType("agent");
|
|
74422
|
+
return agentVertices.length === 0;
|
|
73533
74423
|
}
|
|
73534
74424
|
/**
|
|
73535
74425
|
* Perform health check
|
|
@@ -74655,9 +75545,13 @@ var QueenMinCutBridge = class {
|
|
|
74655
75545
|
}
|
|
74656
75546
|
/**
|
|
74657
75547
|
* Issue #205 fix: Check if topology is empty/fresh (no agents spawned yet)
|
|
75548
|
+
*
|
|
75549
|
+
* Note: Domain coordinator vertices and workflow edges are always created,
|
|
75550
|
+
* so we check for actual agent vertices instead of raw counts.
|
|
74658
75551
|
*/
|
|
74659
75552
|
isEmptyTopology() {
|
|
74660
|
-
|
|
75553
|
+
const agentVertices = this.graph.getVerticesByType("agent");
|
|
75554
|
+
return agentVertices.length === 0;
|
|
74661
75555
|
}
|
|
74662
75556
|
/**
|
|
74663
75557
|
* Convert MinCut alerts to Queen health issues
|
|
@@ -76979,7 +77873,7 @@ import * as path11 from "path";
|
|
|
76979
77873
|
// src/coordination/result-saver.ts
|
|
76980
77874
|
import * as fs9 from "fs/promises";
|
|
76981
77875
|
import * as path10 from "path";
|
|
76982
|
-
import { createHash as
|
|
77876
|
+
import { createHash as createHash3 } from "crypto";
|
|
76983
77877
|
var TEST_FILE_PATTERNS = {
|
|
76984
77878
|
typescript: {
|
|
76985
77879
|
jest: ".test.ts",
|
|
@@ -77406,7 +78300,7 @@ ${data.recommendations.length === 0 ? "No recommendations - all quality gates pa
|
|
|
77406
78300
|
async createFileEntry(filePath, format) {
|
|
77407
78301
|
const content = await fs9.readFile(filePath);
|
|
77408
78302
|
const stats = await fs9.stat(filePath);
|
|
77409
|
-
const checksum =
|
|
78303
|
+
const checksum = createHash3("sha256").update(content).digest("hex").slice(0, 16);
|
|
77410
78304
|
return {
|
|
77411
78305
|
path: filePath,
|
|
77412
78306
|
format,
|
|
@@ -77501,7 +78395,7 @@ function getSecurityScanner(memory) {
|
|
|
77501
78395
|
}
|
|
77502
78396
|
function getTestGenerator(memory) {
|
|
77503
78397
|
if (!testGenerator) {
|
|
77504
|
-
testGenerator =
|
|
78398
|
+
testGenerator = createTestGeneratorService(memory);
|
|
77505
78399
|
}
|
|
77506
78400
|
return testGenerator;
|
|
77507
78401
|
}
|
|
@@ -83911,11 +84805,12 @@ var TestGenerateTool = class extends MCPToolBase {
|
|
|
83911
84805
|
testGeneratorService = null;
|
|
83912
84806
|
/**
|
|
83913
84807
|
* Initialize or get the test generator service with persistent storage
|
|
84808
|
+
* Uses factory function for proper dependency injection
|
|
83914
84809
|
*/
|
|
83915
84810
|
async getService() {
|
|
83916
84811
|
if (!this.testGeneratorService) {
|
|
83917
84812
|
const memory = await getSharedMemoryBackend();
|
|
83918
|
-
this.testGeneratorService =
|
|
84813
|
+
this.testGeneratorService = createTestGeneratorService(
|
|
83919
84814
|
memory,
|
|
83920
84815
|
{
|
|
83921
84816
|
defaultFramework: "vitest",
|
|
@@ -94449,19 +95344,16 @@ function createSpectralEngineWrapper(rawEngine) {
|
|
|
94449
95344
|
return indexToString.get(idx) ?? `unknown-${idx}`;
|
|
94450
95345
|
};
|
|
94451
95346
|
const buildGraph = () => ({
|
|
94452
|
-
|
|
94453
|
-
|
|
94454
|
-
|
|
94455
|
-
|
|
94456
|
-
//
|
|
94457
|
-
|
|
94458
|
-
|
|
94459
|
-
|
|
94460
|
-
//
|
|
94461
|
-
|
|
94462
|
-
// Convert to numeric
|
|
94463
|
-
weight: e.weight
|
|
94464
|
-
}))
|
|
95347
|
+
n: nodes.size,
|
|
95348
|
+
// Number of nodes
|
|
95349
|
+
edges: edges.map((e) => [
|
|
95350
|
+
getOrCreateIndex(e.source),
|
|
95351
|
+
// source as numeric index
|
|
95352
|
+
getOrCreateIndex(e.target),
|
|
95353
|
+
// target as numeric index
|
|
95354
|
+
e.weight
|
|
95355
|
+
// weight
|
|
95356
|
+
])
|
|
94465
95357
|
});
|
|
94466
95358
|
return {
|
|
94467
95359
|
add_node(id) {
|
|
@@ -94482,19 +95374,54 @@ function createSpectralEngineWrapper(rawEngine) {
|
|
|
94482
95374
|
}
|
|
94483
95375
|
},
|
|
94484
95376
|
compute_fiedler_value() {
|
|
94485
|
-
|
|
94486
|
-
|
|
95377
|
+
if (nodes.size < 2) {
|
|
95378
|
+
return 0;
|
|
95379
|
+
}
|
|
95380
|
+
if (edges.length === 0) {
|
|
95381
|
+
return 0;
|
|
95382
|
+
}
|
|
95383
|
+
try {
|
|
95384
|
+
const graph = buildGraph();
|
|
95385
|
+
const fiedler = rawEngine.algebraicConnectivity(graph);
|
|
95386
|
+
return Number.isFinite(fiedler) && fiedler >= 0 ? fiedler : 0;
|
|
95387
|
+
} catch (error) {
|
|
95388
|
+
console.warn("[SpectralAdapter] algebraicConnectivity failed:", error);
|
|
95389
|
+
return 0;
|
|
95390
|
+
}
|
|
94487
95391
|
},
|
|
94488
95392
|
predict_collapse_risk() {
|
|
94489
|
-
|
|
94490
|
-
|
|
94491
|
-
|
|
95393
|
+
if (nodes.size < 2) {
|
|
95394
|
+
return 0;
|
|
95395
|
+
}
|
|
95396
|
+
if (edges.length === 0) {
|
|
95397
|
+
return 1;
|
|
95398
|
+
}
|
|
95399
|
+
try {
|
|
95400
|
+
const graph = buildGraph();
|
|
95401
|
+
const fiedler = rawEngine.algebraicConnectivity(graph);
|
|
95402
|
+
const validFiedler = Number.isFinite(fiedler) && fiedler >= 0 ? fiedler : 0;
|
|
95403
|
+
return Math.max(0, Math.min(1, 1 - validFiedler));
|
|
95404
|
+
} catch (error) {
|
|
95405
|
+
console.warn("[SpectralAdapter] predict_collapse_risk failed:", error);
|
|
95406
|
+
return 0.8;
|
|
95407
|
+
}
|
|
94492
95408
|
},
|
|
94493
95409
|
get_weak_vertices(count) {
|
|
94494
|
-
|
|
94495
|
-
|
|
94496
|
-
|
|
94497
|
-
|
|
95410
|
+
if (nodes.size === 0) {
|
|
95411
|
+
return [];
|
|
95412
|
+
}
|
|
95413
|
+
if (edges.length === 0) {
|
|
95414
|
+
return Array.from(nodes).slice(0, count);
|
|
95415
|
+
}
|
|
95416
|
+
try {
|
|
95417
|
+
const graph = buildGraph();
|
|
95418
|
+
const minCut = rawEngine.predictMinCut(graph);
|
|
95419
|
+
if (!minCut?.vertices) return Array.from(nodes).slice(0, count);
|
|
95420
|
+
return minCut.vertices.slice(0, count).map((idx) => getStringId(idx));
|
|
95421
|
+
} catch (error) {
|
|
95422
|
+
console.warn("[SpectralAdapter] predictMinCut failed:", error);
|
|
95423
|
+
return Array.from(nodes).slice(0, count);
|
|
95424
|
+
}
|
|
94498
95425
|
},
|
|
94499
95426
|
clear() {
|
|
94500
95427
|
nodes.clear();
|
|
@@ -94620,9 +95547,21 @@ var SpectralAdapter = class {
|
|
|
94620
95547
|
if (this.nodes.size < 2) {
|
|
94621
95548
|
return 0;
|
|
94622
95549
|
}
|
|
94623
|
-
|
|
94624
|
-
|
|
94625
|
-
|
|
95550
|
+
if (this.edges.length === 0) {
|
|
95551
|
+
return 0;
|
|
95552
|
+
}
|
|
95553
|
+
try {
|
|
95554
|
+
const fiedlerValue = this.engine.compute_fiedler_value();
|
|
95555
|
+
this.logger.debug("Computed Fiedler value", { fiedlerValue });
|
|
95556
|
+
return fiedlerValue;
|
|
95557
|
+
} catch (error) {
|
|
95558
|
+
this.logger.warn("Failed to compute Fiedler value", {
|
|
95559
|
+
error: error instanceof Error ? error.message : String(error),
|
|
95560
|
+
nodeCount: this.nodes.size,
|
|
95561
|
+
edgeCount: this.edges.length
|
|
95562
|
+
});
|
|
95563
|
+
return 0;
|
|
95564
|
+
}
|
|
94626
95565
|
}
|
|
94627
95566
|
/**
|
|
94628
95567
|
* Predict the risk of swarm collapse
|
|
@@ -94636,9 +95575,21 @@ var SpectralAdapter = class {
|
|
|
94636
95575
|
if (this.nodes.size < 2) {
|
|
94637
95576
|
return 0;
|
|
94638
95577
|
}
|
|
94639
|
-
|
|
94640
|
-
|
|
94641
|
-
|
|
95578
|
+
if (this.edges.length === 0) {
|
|
95579
|
+
return 1;
|
|
95580
|
+
}
|
|
95581
|
+
try {
|
|
95582
|
+
const risk = this.engine.predict_collapse_risk();
|
|
95583
|
+
this.logger.debug("Predicted collapse risk", { risk });
|
|
95584
|
+
return risk;
|
|
95585
|
+
} catch (error) {
|
|
95586
|
+
this.logger.warn("Failed to predict collapse risk", {
|
|
95587
|
+
error: error instanceof Error ? error.message : String(error),
|
|
95588
|
+
nodeCount: this.nodes.size,
|
|
95589
|
+
edgeCount: this.edges.length
|
|
95590
|
+
});
|
|
95591
|
+
return 0.8;
|
|
95592
|
+
}
|
|
94642
95593
|
}
|
|
94643
95594
|
/**
|
|
94644
95595
|
* Get the nodes most at risk of causing collapse
|
|
@@ -94654,13 +95605,25 @@ var SpectralAdapter = class {
|
|
|
94654
95605
|
if (this.nodes.size === 0) {
|
|
94655
95606
|
return [];
|
|
94656
95607
|
}
|
|
95608
|
+
if (this.edges.length === 0) {
|
|
95609
|
+
return Array.from(this.nodes).slice(0, count);
|
|
95610
|
+
}
|
|
94657
95611
|
const safeCount = Math.min(count, this.nodes.size);
|
|
94658
|
-
|
|
94659
|
-
|
|
94660
|
-
|
|
94661
|
-
|
|
94662
|
-
|
|
94663
|
-
|
|
95612
|
+
try {
|
|
95613
|
+
const weakVertices = this.engine.get_weak_vertices(safeCount);
|
|
95614
|
+
this.logger.debug("Retrieved weak vertices", {
|
|
95615
|
+
requested: count,
|
|
95616
|
+
returned: weakVertices.length
|
|
95617
|
+
});
|
|
95618
|
+
return weakVertices;
|
|
95619
|
+
} catch (error) {
|
|
95620
|
+
this.logger.warn("Failed to get weak vertices", {
|
|
95621
|
+
error: error instanceof Error ? error.message : String(error),
|
|
95622
|
+
nodeCount: this.nodes.size,
|
|
95623
|
+
edgeCount: this.edges.length
|
|
95624
|
+
});
|
|
95625
|
+
return Array.from(this.nodes).slice(0, count);
|
|
95626
|
+
}
|
|
94664
95627
|
}
|
|
94665
95628
|
/**
|
|
94666
95629
|
* Analyze a swarm state for collapse risk
|
|
@@ -94719,8 +95682,10 @@ var SpectralAdapter = class {
|
|
|
94719
95682
|
const successSim = 1 - Math.abs(agent1.successRate - agent2.successRate);
|
|
94720
95683
|
const typeBonus = agent1.agentType === agent2.agentType ? 0.2 : 0;
|
|
94721
95684
|
let beliefSim = 0;
|
|
94722
|
-
|
|
94723
|
-
|
|
95685
|
+
const beliefs1 = agent1.beliefs ?? [];
|
|
95686
|
+
const beliefs2 = agent2.beliefs ?? [];
|
|
95687
|
+
if (beliefs1.length > 0 && beliefs2.length > 0) {
|
|
95688
|
+
beliefSim = this.computeBeliefOverlap(beliefs1, beliefs2);
|
|
94724
95689
|
}
|
|
94725
95690
|
return healthSim * 0.3 + successSim * 0.3 + typeBonus + beliefSim * 0.2;
|
|
94726
95691
|
}
|
|
@@ -96224,8 +97189,27 @@ var CoherenceService = class {
|
|
|
96224
97189
|
*/
|
|
96225
97190
|
async predictCollapse(state2) {
|
|
96226
97191
|
this.ensureInitialized();
|
|
97192
|
+
if (!state2.agents || state2.agents.length === 0) {
|
|
97193
|
+
return {
|
|
97194
|
+
risk: 0,
|
|
97195
|
+
fiedlerValue: 0,
|
|
97196
|
+
collapseImminent: false,
|
|
97197
|
+
weakVertices: [],
|
|
97198
|
+
recommendations: ["No agents to analyze"],
|
|
97199
|
+
durationMs: 0,
|
|
97200
|
+
usedFallback: true
|
|
97201
|
+
};
|
|
97202
|
+
}
|
|
96227
97203
|
if (this.spectralAdapter?.isInitialized()) {
|
|
96228
|
-
|
|
97204
|
+
try {
|
|
97205
|
+
return this.spectralAdapter.analyzeSwarmState(state2);
|
|
97206
|
+
} catch (error) {
|
|
97207
|
+
this.logger.warn("Spectral collapse prediction failed, using fallback", {
|
|
97208
|
+
error: error instanceof Error ? error.message : String(error),
|
|
97209
|
+
agentCount: state2.agents.length
|
|
97210
|
+
});
|
|
97211
|
+
return this.predictCollapseWithFallback(state2);
|
|
97212
|
+
}
|
|
96229
97213
|
}
|
|
96230
97214
|
return this.predictCollapseWithFallback(state2);
|
|
96231
97215
|
}
|
|
@@ -96392,34 +97376,60 @@ var CoherenceService = class {
|
|
|
96392
97376
|
async verifyConsensus(votes) {
|
|
96393
97377
|
this.ensureInitialized();
|
|
96394
97378
|
const startTime = Date.now();
|
|
97379
|
+
if (votes.length < 2) {
|
|
97380
|
+
return {
|
|
97381
|
+
isValid: votes.length === 1,
|
|
97382
|
+
confidence: votes.length === 1 ? votes[0].confidence : 0,
|
|
97383
|
+
isFalseConsensus: false,
|
|
97384
|
+
fiedlerValue: votes.length === 1 ? 1 : 0,
|
|
97385
|
+
collapseRisk: 0,
|
|
97386
|
+
recommendation: votes.length === 0 ? "No votes to analyze" : "Single vote - consensus trivially achieved",
|
|
97387
|
+
durationMs: Date.now() - startTime,
|
|
97388
|
+
usedFallback: true
|
|
97389
|
+
};
|
|
97390
|
+
}
|
|
96395
97391
|
if (this.spectralAdapter?.isInitialized()) {
|
|
96396
|
-
|
|
96397
|
-
|
|
96398
|
-
|
|
96399
|
-
|
|
96400
|
-
|
|
96401
|
-
|
|
96402
|
-
|
|
96403
|
-
|
|
96404
|
-
|
|
96405
|
-
|
|
96406
|
-
|
|
96407
|
-
|
|
97392
|
+
try {
|
|
97393
|
+
this.spectralAdapter.clear();
|
|
97394
|
+
for (const vote of votes) {
|
|
97395
|
+
this.spectralAdapter.addNode(vote.agentId);
|
|
97396
|
+
}
|
|
97397
|
+
let edgeCount = 0;
|
|
97398
|
+
for (let i = 0; i < votes.length; i++) {
|
|
97399
|
+
for (let j = i + 1; j < votes.length; j++) {
|
|
97400
|
+
if (votes[i].verdict === votes[j].verdict) {
|
|
97401
|
+
this.spectralAdapter.addEdge(
|
|
97402
|
+
votes[i].agentId,
|
|
97403
|
+
votes[j].agentId,
|
|
97404
|
+
Math.min(votes[i].confidence, votes[j].confidence)
|
|
97405
|
+
);
|
|
97406
|
+
edgeCount++;
|
|
97407
|
+
}
|
|
96408
97408
|
}
|
|
96409
97409
|
}
|
|
97410
|
+
if (edgeCount === 0) {
|
|
97411
|
+
this.logger.debug("No agreement edges, using fallback consensus");
|
|
97412
|
+
return this.verifyConsensusWithFallback(votes, startTime);
|
|
97413
|
+
}
|
|
97414
|
+
const collapseRisk = this.spectralAdapter.predictCollapseRisk();
|
|
97415
|
+
const fiedlerValue = this.spectralAdapter.computeFiedlerValue();
|
|
97416
|
+
return {
|
|
97417
|
+
isValid: collapseRisk < 0.3 && fiedlerValue > 0.1,
|
|
97418
|
+
confidence: 1 - collapseRisk,
|
|
97419
|
+
isFalseConsensus: fiedlerValue < 0.05,
|
|
97420
|
+
fiedlerValue,
|
|
97421
|
+
collapseRisk,
|
|
97422
|
+
recommendation: collapseRisk > 0.3 ? "Spawn independent reviewer" : "Consensus verified",
|
|
97423
|
+
durationMs: Date.now() - startTime,
|
|
97424
|
+
usedFallback: false
|
|
97425
|
+
};
|
|
97426
|
+
} catch (error) {
|
|
97427
|
+
this.logger.warn("Spectral consensus verification failed, using fallback", {
|
|
97428
|
+
error: error instanceof Error ? error.message : String(error),
|
|
97429
|
+
voteCount: votes.length
|
|
97430
|
+
});
|
|
97431
|
+
return this.verifyConsensusWithFallback(votes, startTime);
|
|
96410
97432
|
}
|
|
96411
|
-
const collapseRisk = this.spectralAdapter.predictCollapseRisk();
|
|
96412
|
-
const fiedlerValue = this.spectralAdapter.computeFiedlerValue();
|
|
96413
|
-
return {
|
|
96414
|
-
isValid: collapseRisk < 0.3 && fiedlerValue > 0.1,
|
|
96415
|
-
confidence: 1 - collapseRisk,
|
|
96416
|
-
isFalseConsensus: fiedlerValue < 0.05,
|
|
96417
|
-
fiedlerValue,
|
|
96418
|
-
collapseRisk,
|
|
96419
|
-
recommendation: collapseRisk > 0.3 ? "Spawn independent reviewer" : "Consensus verified",
|
|
96420
|
-
durationMs: Date.now() - startTime,
|
|
96421
|
-
usedFallback: false
|
|
96422
|
-
};
|
|
96423
97433
|
}
|
|
96424
97434
|
return this.verifyConsensusWithFallback(votes, startTime);
|
|
96425
97435
|
}
|
|
@@ -96561,16 +97571,17 @@ var CoherenceService = class {
|
|
|
96561
97571
|
* Convert agent health to a numerical embedding
|
|
96562
97572
|
*/
|
|
96563
97573
|
agentHealthToEmbedding(health) {
|
|
97574
|
+
const beliefs = health.beliefs ?? [];
|
|
96564
97575
|
return [
|
|
96565
97576
|
health.health,
|
|
96566
97577
|
health.successRate,
|
|
96567
97578
|
Math.min(1, health.errorCount / 10),
|
|
96568
97579
|
this.agentTypeToNumber(health.agentType),
|
|
96569
|
-
|
|
97580
|
+
beliefs.length / 10,
|
|
96570
97581
|
// Add belief embeddings (first 3 beliefs, padded)
|
|
96571
|
-
...
|
|
96572
|
-
...
|
|
96573
|
-
...
|
|
97582
|
+
...beliefs[0]?.embedding.slice(0, 5) || [0, 0, 0, 0, 0],
|
|
97583
|
+
...beliefs[1]?.embedding.slice(0, 5) || [0, 0, 0, 0, 0],
|
|
97584
|
+
...beliefs[2]?.embedding.slice(0, 5) || [0, 0, 0, 0, 0]
|
|
96574
97585
|
];
|
|
96575
97586
|
}
|
|
96576
97587
|
/**
|
|
@@ -97777,7 +98788,8 @@ var MemoryCoherenceAuditor = class {
|
|
|
97777
98788
|
findBroadPatterns(patterns) {
|
|
97778
98789
|
return patterns.filter((p) => {
|
|
97779
98790
|
const hasGenericName = /generic|general|common|basic/i.test(p.name);
|
|
97780
|
-
const
|
|
98791
|
+
const tags = p.context?.tags;
|
|
98792
|
+
const hasLowSpecificity = !tags || tags.length < 2;
|
|
97781
98793
|
return hasGenericName || hasLowSpecificity;
|
|
97782
98794
|
}).map((p) => p.id);
|
|
97783
98795
|
}
|