synapsexcoder 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/opencode.jsonc +102 -0
- package/README.md +353 -0
- package/dist/agents/agent-config-manager.d.ts +58 -0
- package/dist/agents/agent-config-manager.d.ts.map +1 -0
- package/dist/agents/agent-config-manager.js +313 -0
- package/dist/agents/agent-config-manager.js.map +1 -0
- package/dist/agents/base-agents.d.ts +352 -0
- package/dist/agents/base-agents.d.ts.map +1 -0
- package/dist/agents/base-agents.js +3961 -0
- package/dist/agents/base-agents.js.map +1 -0
- package/dist/agents/gated-subagent.d.ts +126 -0
- package/dist/agents/gated-subagent.d.ts.map +1 -0
- package/dist/agents/gated-subagent.js +591 -0
- package/dist/agents/gated-subagent.js.map +1 -0
- package/dist/agents/gated-subagents.d.ts +130 -0
- package/dist/agents/gated-subagents.d.ts.map +1 -0
- package/dist/agents/gated-subagents.js +2014 -0
- package/dist/agents/gated-subagents.js.map +1 -0
- package/dist/agents/internal-gatekeeper.d.ts +167 -0
- package/dist/agents/internal-gatekeeper.d.ts.map +1 -0
- package/dist/agents/internal-gatekeeper.js +1130 -0
- package/dist/agents/internal-gatekeeper.js.map +1 -0
- package/dist/agents/verification-agent.d.ts +86 -0
- package/dist/agents/verification-agent.d.ts.map +1 -0
- package/dist/agents/verification-agent.js +211 -0
- package/dist/agents/verification-agent.js.map +1 -0
- package/dist/analytics/analytics-types.d.ts +113 -0
- package/dist/analytics/analytics-types.d.ts.map +1 -0
- package/dist/analytics/analytics-types.js +8 -0
- package/dist/analytics/analytics-types.js.map +1 -0
- package/dist/analytics/dashboard-generator.d.ts +35 -0
- package/dist/analytics/dashboard-generator.d.ts.map +1 -0
- package/dist/analytics/dashboard-generator.js +365 -0
- package/dist/analytics/dashboard-generator.js.map +1 -0
- package/dist/analytics/index.d.ts +12 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +12 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/metrics-aggregator.d.ts +88 -0
- package/dist/analytics/metrics-aggregator.d.ts.map +1 -0
- package/dist/analytics/metrics-aggregator.js +280 -0
- package/dist/analytics/metrics-aggregator.js.map +1 -0
- package/dist/cli/index.d.ts +36 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2677 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/normalize_patch.d.ts +3 -0
- package/dist/cli/normalize_patch.d.ts.map +1 -0
- package/dist/cli/normalize_patch.js +34 -0
- package/dist/cli/normalize_patch.js.map +1 -0
- package/dist/commands/command-processor.d.ts +58 -0
- package/dist/commands/command-processor.d.ts.map +1 -0
- package/dist/commands/command-processor.js +796 -0
- package/dist/commands/command-processor.js.map +1 -0
- package/dist/config/compliance-checker.d.ts +93 -0
- package/dist/config/compliance-checker.d.ts.map +1 -0
- package/dist/config/compliance-checker.js +424 -0
- package/dist/config/compliance-checker.js.map +1 -0
- package/dist/config/enterprise-config.d.ts +173 -0
- package/dist/config/enterprise-config.d.ts.map +1 -0
- package/dist/config/enterprise-config.js +190 -0
- package/dist/config/enterprise-config.js.map +1 -0
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +11 -0
- package/dist/config/index.js.map +1 -0
- package/dist/context/context-system.d.ts +97 -0
- package/dist/context/context-system.d.ts.map +1 -0
- package/dist/context/context-system.js +880 -0
- package/dist/context/context-system.js.map +1 -0
- package/dist/context/store.d.ts +123 -0
- package/dist/context/store.d.ts.map +1 -0
- package/dist/context/store.js +281 -0
- package/dist/context/store.js.map +1 -0
- package/dist/dasp/dasp-controller.d.ts +83 -0
- package/dist/dasp/dasp-controller.d.ts.map +1 -0
- package/dist/dasp/dasp-controller.js +190 -0
- package/dist/dasp/dasp-controller.js.map +1 -0
- package/dist/dasp/feedback-adapter.d.ts +56 -0
- package/dist/dasp/feedback-adapter.d.ts.map +1 -0
- package/dist/dasp/feedback-adapter.js +158 -0
- package/dist/dasp/feedback-adapter.js.map +1 -0
- package/dist/dasp/index.d.ts +14 -0
- package/dist/dasp/index.d.ts.map +1 -0
- package/dist/dasp/index.js +10 -0
- package/dist/dasp/index.js.map +1 -0
- package/dist/dasp/prompt-templates.d.ts +38 -0
- package/dist/dasp/prompt-templates.d.ts.map +1 -0
- package/dist/dasp/prompt-templates.js +406 -0
- package/dist/dasp/prompt-templates.js.map +1 -0
- package/dist/dasp/vault-rag-provider.d.ts +51 -0
- package/dist/dasp/vault-rag-provider.d.ts.map +1 -0
- package/dist/dasp/vault-rag-provider.js +125 -0
- package/dist/dasp/vault-rag-provider.js.map +1 -0
- package/dist/distribution/cli-distribution.d.ts +68 -0
- package/dist/distribution/cli-distribution.d.ts.map +1 -0
- package/dist/distribution/cli-distribution.js +941 -0
- package/dist/distribution/cli-distribution.js.map +1 -0
- package/dist/docs/doc-generator.d.ts +78 -0
- package/dist/docs/doc-generator.d.ts.map +1 -0
- package/dist/docs/doc-generator.js +297 -0
- package/dist/docs/doc-generator.js.map +1 -0
- package/dist/docs/index.d.ts +13 -0
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +11 -0
- package/dist/docs/index.js.map +1 -0
- package/dist/docs/site-builder.d.ts +58 -0
- package/dist/docs/site-builder.d.ts.map +1 -0
- package/dist/docs/site-builder.js +229 -0
- package/dist/docs/site-builder.js.map +1 -0
- package/dist/ecosystem/adapters/claude-adapter.d.ts +29 -0
- package/dist/ecosystem/adapters/claude-adapter.d.ts.map +1 -0
- package/dist/ecosystem/adapters/claude-adapter.js +116 -0
- package/dist/ecosystem/adapters/claude-adapter.js.map +1 -0
- package/dist/ecosystem/adapters/cursor-adapter.d.ts +27 -0
- package/dist/ecosystem/adapters/cursor-adapter.d.ts.map +1 -0
- package/dist/ecosystem/adapters/cursor-adapter.js +93 -0
- package/dist/ecosystem/adapters/cursor-adapter.js.map +1 -0
- package/dist/ecosystem/adapters/v0-adapter.d.ts +30 -0
- package/dist/ecosystem/adapters/v0-adapter.d.ts.map +1 -0
- package/dist/ecosystem/adapters/v0-adapter.js +112 -0
- package/dist/ecosystem/adapters/v0-adapter.js.map +1 -0
- package/dist/ecosystem/ecosystem-router.d.ts +80 -0
- package/dist/ecosystem/ecosystem-router.d.ts.map +1 -0
- package/dist/ecosystem/ecosystem-router.js +241 -0
- package/dist/ecosystem/ecosystem-router.js.map +1 -0
- package/dist/ecosystem/ecosystem-types.d.ts +94 -0
- package/dist/ecosystem/ecosystem-types.d.ts.map +1 -0
- package/dist/ecosystem/ecosystem-types.js +27 -0
- package/dist/ecosystem/ecosystem-types.js.map +1 -0
- package/dist/ecosystem/index.d.ts +10 -0
- package/dist/ecosystem/index.d.ts.map +1 -0
- package/dist/ecosystem/index.js +9 -0
- package/dist/ecosystem/index.js.map +1 -0
- package/dist/integration/agentic-integration.d.ts +73 -0
- package/dist/integration/agentic-integration.d.ts.map +1 -0
- package/dist/integration/agentic-integration.js +253 -0
- package/dist/integration/agentic-integration.js.map +1 -0
- package/dist/integration/background-agents-integration.d.ts +54 -0
- package/dist/integration/background-agents-integration.d.ts.map +1 -0
- package/dist/integration/background-agents-integration.js +225 -0
- package/dist/integration/background-agents-integration.js.map +1 -0
- package/dist/integration/dcp-integration.d.ts +81 -0
- package/dist/integration/dcp-integration.d.ts.map +1 -0
- package/dist/integration/dcp-integration.js +189 -0
- package/dist/integration/dcp-integration.js.map +1 -0
- package/dist/integration/firecrawl-integration.d.ts +61 -0
- package/dist/integration/firecrawl-integration.d.ts.map +1 -0
- package/dist/integration/firecrawl-integration.js +246 -0
- package/dist/integration/firecrawl-integration.js.map +1 -0
- package/dist/integration/index.d.ts +40 -0
- package/dist/integration/index.d.ts.map +1 -0
- package/dist/integration/index.js +43 -0
- package/dist/integration/index.js.map +1 -0
- package/dist/integration/integration-hub.d.ts +43 -0
- package/dist/integration/integration-hub.d.ts.map +1 -0
- package/dist/integration/integration-hub.js +507 -0
- package/dist/integration/integration-hub.js.map +1 -0
- package/dist/integration/integration-loader.d.ts +42 -0
- package/dist/integration/integration-loader.d.ts.map +1 -0
- package/dist/integration/integration-loader.js +240 -0
- package/dist/integration/integration-loader.js.map +1 -0
- package/dist/integration/md-table-formatter-integration.d.ts +41 -0
- package/dist/integration/md-table-formatter-integration.d.ts.map +1 -0
- package/dist/integration/md-table-formatter-integration.js +183 -0
- package/dist/integration/md-table-formatter-integration.js.map +1 -0
- package/dist/integration/native/agentic/agentic-engine.d.ts +52 -0
- package/dist/integration/native/agentic/agentic-engine.d.ts.map +1 -0
- package/dist/integration/native/agentic/agentic-engine.js +267 -0
- package/dist/integration/native/agentic/agentic-engine.js.map +1 -0
- package/dist/integration/native/background/background-engine.d.ts +62 -0
- package/dist/integration/native/background/background-engine.d.ts.map +1 -0
- package/dist/integration/native/background/background-engine.js +167 -0
- package/dist/integration/native/background/background-engine.js.map +1 -0
- package/dist/integration/native/dcp/dcp-engine.d.ts +55 -0
- package/dist/integration/native/dcp/dcp-engine.d.ts.map +1 -0
- package/dist/integration/native/dcp/dcp-engine.js +168 -0
- package/dist/integration/native/dcp/dcp-engine.js.map +1 -0
- package/dist/integration/native/firecrawl/firecrawl-engine.d.ts +66 -0
- package/dist/integration/native/firecrawl/firecrawl-engine.d.ts.map +1 -0
- package/dist/integration/native/firecrawl/firecrawl-engine.js +221 -0
- package/dist/integration/native/firecrawl/firecrawl-engine.js.map +1 -0
- package/dist/integration/native/formatter/formatter-engine.d.ts +47 -0
- package/dist/integration/native/formatter/formatter-engine.d.ts.map +1 -0
- package/dist/integration/native/formatter/formatter-engine.js +130 -0
- package/dist/integration/native/formatter/formatter-engine.js.map +1 -0
- package/dist/integration/native/index.d.ts +41 -0
- package/dist/integration/native/index.d.ts.map +1 -0
- package/dist/integration/native/index.js +80 -0
- package/dist/integration/native/index.js.map +1 -0
- package/dist/integration/native/orchestration/orchestration-engine.d.ts +62 -0
- package/dist/integration/native/orchestration/orchestration-engine.d.ts.map +1 -0
- package/dist/integration/native/orchestration/orchestration-engine.js +177 -0
- package/dist/integration/native/orchestration/orchestration-engine.js.map +1 -0
- package/dist/integration/native/pty/pty-engine.d.ts +45 -0
- package/dist/integration/native/pty/pty-engine.d.ts.map +1 -0
- package/dist/integration/native/pty/pty-engine.js +103 -0
- package/dist/integration/native/pty/pty-engine.js.map +1 -0
- package/dist/integration/native/shell-strategy.d.ts +60 -0
- package/dist/integration/native/shell-strategy.d.ts.map +1 -0
- package/dist/integration/native/shell-strategy.js +131 -0
- package/dist/integration/native/shell-strategy.js.map +1 -0
- package/dist/integration/native/skillful/skillful-engine.d.ts +53 -0
- package/dist/integration/native/skillful/skillful-engine.d.ts.map +1 -0
- package/dist/integration/native/skillful/skillful-engine.js +127 -0
- package/dist/integration/native/skillful/skillful-engine.js.map +1 -0
- package/dist/integration/native/subtask2/subtask2-engine.d.ts +50 -0
- package/dist/integration/native/subtask2/subtask2-engine.d.ts.map +1 -0
- package/dist/integration/native/subtask2/subtask2-engine.js +158 -0
- package/dist/integration/native/subtask2/subtask2-engine.js.map +1 -0
- package/dist/integration/native/supermemory/supermemory-engine.d.ts +63 -0
- package/dist/integration/native/supermemory/supermemory-engine.d.ts.map +1 -0
- package/dist/integration/native/supermemory/supermemory-engine.js +127 -0
- package/dist/integration/native/supermemory/supermemory-engine.js.map +1 -0
- package/dist/integration/native/workspace/workspace-engine.d.ts +75 -0
- package/dist/integration/native/workspace/workspace-engine.d.ts.map +1 -0
- package/dist/integration/native/workspace/workspace-engine.js +216 -0
- package/dist/integration/native/workspace/workspace-engine.js.map +1 -0
- package/dist/integration/oh-my-opencode-integration.d.ts +59 -0
- package/dist/integration/oh-my-opencode-integration.d.ts.map +1 -0
- package/dist/integration/oh-my-opencode-integration.js +180 -0
- package/dist/integration/oh-my-opencode-integration.js.map +1 -0
- package/dist/integration/openagents-control-integration.d.ts +81 -0
- package/dist/integration/openagents-control-integration.d.ts.map +1 -0
- package/dist/integration/openagents-control-integration.js +273 -0
- package/dist/integration/openagents-control-integration.js.map +1 -0
- package/dist/integration/pty-integration.d.ts +64 -0
- package/dist/integration/pty-integration.d.ts.map +1 -0
- package/dist/integration/pty-integration.js +180 -0
- package/dist/integration/pty-integration.js.map +1 -0
- package/dist/integration/shell-strategy-integration.d.ts +26 -0
- package/dist/integration/shell-strategy-integration.d.ts.map +1 -0
- package/dist/integration/shell-strategy-integration.js +110 -0
- package/dist/integration/shell-strategy-integration.js.map +1 -0
- package/dist/integration/skillful-integration.d.ts +74 -0
- package/dist/integration/skillful-integration.d.ts.map +1 -0
- package/dist/integration/skillful-integration.js +317 -0
- package/dist/integration/skillful-integration.js.map +1 -0
- package/dist/integration/subtask2-integration.d.ts +71 -0
- package/dist/integration/subtask2-integration.d.ts.map +1 -0
- package/dist/integration/subtask2-integration.js +240 -0
- package/dist/integration/subtask2-integration.js.map +1 -0
- package/dist/integration/supermemory-integration.d.ts +82 -0
- package/dist/integration/supermemory-integration.d.ts.map +1 -0
- package/dist/integration/supermemory-integration.js +252 -0
- package/dist/integration/supermemory-integration.js.map +1 -0
- package/dist/integration/types.d.ts +218 -0
- package/dist/integration/types.d.ts.map +1 -0
- package/dist/integration/types.js +5 -0
- package/dist/integration/types.js.map +1 -0
- package/dist/integration/workspace-integration.d.ts +46 -0
- package/dist/integration/workspace-integration.d.ts.map +1 -0
- package/dist/integration/workspace-integration.js +181 -0
- package/dist/integration/workspace-integration.js.map +1 -0
- package/dist/knowledge-vault/deepwiki-sync.d.ts +99 -0
- package/dist/knowledge-vault/deepwiki-sync.d.ts.map +1 -0
- package/dist/knowledge-vault/deepwiki-sync.js +381 -0
- package/dist/knowledge-vault/deepwiki-sync.js.map +1 -0
- package/dist/knowledge-vault/index.d.ts +11 -0
- package/dist/knowledge-vault/index.d.ts.map +1 -0
- package/dist/knowledge-vault/index.js +8 -0
- package/dist/knowledge-vault/index.js.map +1 -0
- package/dist/knowledge-vault/knowledge-vault.d.ts +38 -0
- package/dist/knowledge-vault/knowledge-vault.d.ts.map +1 -0
- package/dist/knowledge-vault/knowledge-vault.js +284 -0
- package/dist/knowledge-vault/knowledge-vault.js.map +1 -0
- package/dist/knowledge-vault/opencode-doc-ingest.d.ts +23 -0
- package/dist/knowledge-vault/opencode-doc-ingest.d.ts.map +1 -0
- package/dist/knowledge-vault/opencode-doc-ingest.js +48 -0
- package/dist/knowledge-vault/opencode-doc-ingest.js.map +1 -0
- package/dist/knowledge-vault/types.d.ts +61 -0
- package/dist/knowledge-vault/types.d.ts.map +1 -0
- package/dist/knowledge-vault/types.js +5 -0
- package/dist/knowledge-vault/types.js.map +1 -0
- package/dist/knowledge-vault/vault-config.d.ts +12 -0
- package/dist/knowledge-vault/vault-config.d.ts.map +1 -0
- package/dist/knowledge-vault/vault-config.js +24 -0
- package/dist/knowledge-vault/vault-config.js.map +1 -0
- package/dist/mcp/index.d.ts +28 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +29 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcp-manager.d.ts +181 -0
- package/dist/mcp/mcp-manager.d.ts.map +1 -0
- package/dist/mcp/mcp-manager.js +621 -0
- package/dist/mcp/mcp-manager.js.map +1 -0
- package/dist/mcp/mcp-types.d.ts +199 -0
- package/dist/mcp/mcp-types.d.ts.map +1 -0
- package/dist/mcp/mcp-types.js +152 -0
- package/dist/mcp/mcp-types.js.map +1 -0
- package/dist/mcp/servers/mcp-server-bridge.d.ts +64 -0
- package/dist/mcp/servers/mcp-server-bridge.d.ts.map +1 -0
- package/dist/mcp/servers/mcp-server-bridge.js +189 -0
- package/dist/mcp/servers/mcp-server-bridge.js.map +1 -0
- package/dist/messages/message-bus.d.ts +148 -0
- package/dist/messages/message-bus.d.ts.map +1 -0
- package/dist/messages/message-bus.js +432 -0
- package/dist/messages/message-bus.js.map +1 -0
- package/dist/modes/custom-mode-registry.d.ts +121 -0
- package/dist/modes/custom-mode-registry.d.ts.map +1 -0
- package/dist/modes/custom-mode-registry.js +306 -0
- package/dist/modes/custom-mode-registry.js.map +1 -0
- package/dist/modes/index.d.ts +10 -0
- package/dist/modes/index.d.ts.map +1 -0
- package/dist/modes/index.js +8 -0
- package/dist/modes/index.js.map +1 -0
- package/dist/modes/mode-designer.d.ts +89 -0
- package/dist/modes/mode-designer.d.ts.map +1 -0
- package/dist/modes/mode-designer.js +485 -0
- package/dist/modes/mode-designer.js.map +1 -0
- package/dist/modes/mode-manager.d.ts +27 -0
- package/dist/modes/mode-manager.d.ts.map +1 -0
- package/dist/modes/mode-manager.js +68 -0
- package/dist/modes/mode-manager.js.map +1 -0
- package/dist/modes/synapse-modes.d.ts +26 -0
- package/dist/modes/synapse-modes.d.ts.map +1 -0
- package/dist/modes/synapse-modes.js +69 -0
- package/dist/modes/synapse-modes.js.map +1 -0
- package/dist/opencode/agent-delegate.d.ts +3 -0
- package/dist/opencode/agent-delegate.d.ts.map +1 -0
- package/dist/opencode/agent-delegate.js +3 -0
- package/dist/opencode/agent-delegate.js.map +1 -0
- package/dist/opencode/tool-bridge.d.ts +3 -0
- package/dist/opencode/tool-bridge.d.ts.map +1 -0
- package/dist/opencode/tool-bridge.js +3 -0
- package/dist/opencode/tool-bridge.js.map +1 -0
- package/dist/optimization/token-optimizer-v2.d.ts +18 -0
- package/dist/optimization/token-optimizer-v2.d.ts.map +1 -0
- package/dist/optimization/token-optimizer-v2.js +23 -0
- package/dist/optimization/token-optimizer-v2.js.map +1 -0
- package/dist/optimization/token-optimizer.d.ts +90 -0
- package/dist/optimization/token-optimizer.d.ts.map +1 -0
- package/dist/optimization/token-optimizer.js +399 -0
- package/dist/optimization/token-optimizer.js.map +1 -0
- package/dist/parallel/agent-farm.d.ts +123 -0
- package/dist/parallel/agent-farm.d.ts.map +1 -0
- package/dist/parallel/agent-farm.js +501 -0
- package/dist/parallel/agent-farm.js.map +1 -0
- package/dist/parallel/farm-scheduler.d.ts +115 -0
- package/dist/parallel/farm-scheduler.d.ts.map +1 -0
- package/dist/parallel/farm-scheduler.js +356 -0
- package/dist/parallel/farm-scheduler.js.map +1 -0
- package/dist/parallel/farm-types.d.ts +104 -0
- package/dist/parallel/farm-types.d.ts.map +1 -0
- package/dist/parallel/farm-types.js +9 -0
- package/dist/parallel/farm-types.js.map +1 -0
- package/dist/parallel/farm-worker.d.ts +62 -0
- package/dist/parallel/farm-worker.d.ts.map +1 -0
- package/dist/parallel/farm-worker.js +268 -0
- package/dist/parallel/farm-worker.js.map +1 -0
- package/dist/parallel/index.d.ts +14 -0
- package/dist/parallel/index.d.ts.map +1 -0
- package/dist/parallel/index.js +14 -0
- package/dist/parallel/index.js.map +1 -0
- package/dist/plugin/native-tools.d.ts +8 -0
- package/dist/plugin/native-tools.d.ts.map +1 -0
- package/dist/plugin/native-tools.js +147 -0
- package/dist/plugin/native-tools.js.map +1 -0
- package/dist/plugin/opencode-plugin.d.ts +32 -0
- package/dist/plugin/opencode-plugin.d.ts.map +1 -0
- package/dist/plugin/opencode-plugin.js +119 -0
- package/dist/plugin/opencode-plugin.js.map +1 -0
- package/dist/plugins/plugin-system.d.ts +108 -0
- package/dist/plugins/plugin-system.d.ts.map +1 -0
- package/dist/plugins/plugin-system.js +707 -0
- package/dist/plugins/plugin-system.js.map +1 -0
- package/dist/profiler/cpu-profiler.d.ts +53 -0
- package/dist/profiler/cpu-profiler.d.ts.map +1 -0
- package/dist/profiler/cpu-profiler.js +233 -0
- package/dist/profiler/cpu-profiler.js.map +1 -0
- package/dist/profiler/index.d.ts +36 -0
- package/dist/profiler/index.d.ts.map +1 -0
- package/dist/profiler/index.js +122 -0
- package/dist/profiler/index.js.map +1 -0
- package/dist/profiler/memory-profiler.d.ts +45 -0
- package/dist/profiler/memory-profiler.d.ts.map +1 -0
- package/dist/profiler/memory-profiler.js +211 -0
- package/dist/profiler/memory-profiler.js.map +1 -0
- package/dist/profiler/profiler-types.d.ts +234 -0
- package/dist/profiler/profiler-types.d.ts.map +1 -0
- package/dist/profiler/profiler-types.js +89 -0
- package/dist/profiler/profiler-types.js.map +1 -0
- package/dist/profiler/query-profiler.d.ts +48 -0
- package/dist/profiler/query-profiler.d.ts.map +1 -0
- package/dist/profiler/query-profiler.js +210 -0
- package/dist/profiler/query-profiler.js.map +1 -0
- package/dist/profiler/report-generator.d.ts +17 -0
- package/dist/profiler/report-generator.d.ts.map +1 -0
- package/dist/profiler/report-generator.js +329 -0
- package/dist/profiler/report-generator.js.map +1 -0
- package/dist/sdk/api.d.ts +85 -0
- package/dist/sdk/api.d.ts.map +1 -0
- package/dist/sdk/api.js +155 -0
- package/dist/sdk/api.js.map +1 -0
- package/dist/sdk/hooks.d.ts +58 -0
- package/dist/sdk/hooks.d.ts.map +1 -0
- package/dist/sdk/hooks.js +115 -0
- package/dist/sdk/hooks.js.map +1 -0
- package/dist/sdk/index.d.ts +17 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +14 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/plugin-base.d.ts +198 -0
- package/dist/sdk/plugin-base.d.ts.map +1 -0
- package/dist/sdk/plugin-base.js +227 -0
- package/dist/sdk/plugin-base.js.map +1 -0
- package/dist/security/audit-rules.d.ts +12 -0
- package/dist/security/audit-rules.d.ts.map +1 -0
- package/dist/security/audit-rules.js +214 -0
- package/dist/security/audit-rules.js.map +1 -0
- package/dist/security/index.d.ts +13 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +13 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/sarif-exporter.d.ts +36 -0
- package/dist/security/sarif-exporter.d.ts.map +1 -0
- package/dist/security/sarif-exporter.js +216 -0
- package/dist/security/sarif-exporter.js.map +1 -0
- package/dist/security/security-auditor.d.ts +30 -0
- package/dist/security/security-auditor.d.ts.map +1 -0
- package/dist/security/security-auditor.js +295 -0
- package/dist/security/security-auditor.js.map +1 -0
- package/dist/security/security-types.d.ts +132 -0
- package/dist/security/security-types.d.ts.map +1 -0
- package/dist/security/security-types.js +7 -0
- package/dist/security/security-types.js.map +1 -0
- package/dist/synapsexcoder/agent-delegate.d.ts +128 -0
- package/dist/synapsexcoder/agent-delegate.d.ts.map +1 -0
- package/dist/synapsexcoder/agent-delegate.js +837 -0
- package/dist/synapsexcoder/agent-delegate.js.map +1 -0
- package/dist/synapsexcoder/agent-mailbox.d.ts +26 -0
- package/dist/synapsexcoder/agent-mailbox.d.ts.map +1 -0
- package/dist/synapsexcoder/agent-mailbox.js +64 -0
- package/dist/synapsexcoder/agent-mailbox.js.map +1 -0
- package/dist/synapsexcoder/ast-grep/ast-grep-api.d.ts +62 -0
- package/dist/synapsexcoder/ast-grep/ast-grep-api.d.ts.map +1 -0
- package/dist/synapsexcoder/ast-grep/ast-grep-api.js +223 -0
- package/dist/synapsexcoder/ast-grep/ast-grep-api.js.map +1 -0
- package/dist/synapsexcoder/cited-search.d.ts +32 -0
- package/dist/synapsexcoder/cited-search.d.ts.map +1 -0
- package/dist/synapsexcoder/cited-search.js +141 -0
- package/dist/synapsexcoder/cited-search.js.map +1 -0
- package/dist/synapsexcoder/credential-store.d.ts +42 -0
- package/dist/synapsexcoder/credential-store.d.ts.map +1 -0
- package/dist/synapsexcoder/credential-store.js +165 -0
- package/dist/synapsexcoder/credential-store.js.map +1 -0
- package/dist/synapsexcoder/devcontainer-awareness.d.ts +35 -0
- package/dist/synapsexcoder/devcontainer-awareness.d.ts.map +1 -0
- package/dist/synapsexcoder/devcontainer-awareness.js +133 -0
- package/dist/synapsexcoder/devcontainer-awareness.js.map +1 -0
- package/dist/synapsexcoder/execution-loops/helix-loop.d.ts +31 -0
- package/dist/synapsexcoder/execution-loops/helix-loop.d.ts.map +1 -0
- package/dist/synapsexcoder/execution-loops/helix-loop.js +93 -0
- package/dist/synapsexcoder/execution-loops/helix-loop.js.map +1 -0
- package/dist/synapsexcoder/execution-loops/ralp-loop.d.ts +28 -0
- package/dist/synapsexcoder/execution-loops/ralp-loop.d.ts.map +1 -0
- package/dist/synapsexcoder/execution-loops/ralp-loop.js +77 -0
- package/dist/synapsexcoder/execution-loops/ralp-loop.js.map +1 -0
- package/dist/synapsexcoder/feature-names.d.ts +27 -0
- package/dist/synapsexcoder/feature-names.d.ts.map +1 -0
- package/dist/synapsexcoder/feature-names.js +40 -0
- package/dist/synapsexcoder/feature-names.js.map +1 -0
- package/dist/synapsexcoder/index.d.ts +39 -0
- package/dist/synapsexcoder/index.d.ts.map +1 -0
- package/dist/synapsexcoder/index.js +23 -0
- package/dist/synapsexcoder/index.js.map +1 -0
- package/dist/synapsexcoder/knowledge-provider.d.ts +44 -0
- package/dist/synapsexcoder/knowledge-provider.d.ts.map +1 -0
- package/dist/synapsexcoder/knowledge-provider.js +107 -0
- package/dist/synapsexcoder/knowledge-provider.js.map +1 -0
- package/dist/synapsexcoder/lsp/lsp-layer.d.ts +51 -0
- package/dist/synapsexcoder/lsp/lsp-layer.d.ts.map +1 -0
- package/dist/synapsexcoder/lsp/lsp-layer.js +302 -0
- package/dist/synapsexcoder/lsp/lsp-layer.js.map +1 -0
- package/dist/synapsexcoder/migration/opencode-config-migrator.d.ts +37 -0
- package/dist/synapsexcoder/migration/opencode-config-migrator.d.ts.map +1 -0
- package/dist/synapsexcoder/migration/opencode-config-migrator.js +100 -0
- package/dist/synapsexcoder/migration/opencode-config-migrator.js.map +1 -0
- package/dist/synapsexcoder/runtime-environment.d.ts +35 -0
- package/dist/synapsexcoder/runtime-environment.d.ts.map +1 -0
- package/dist/synapsexcoder/runtime-environment.js +108 -0
- package/dist/synapsexcoder/runtime-environment.js.map +1 -0
- package/dist/synapsexcoder/secret-guard.d.ts +18 -0
- package/dist/synapsexcoder/secret-guard.d.ts.map +1 -0
- package/dist/synapsexcoder/secret-guard.js +69 -0
- package/dist/synapsexcoder/secret-guard.js.map +1 -0
- package/dist/synapsexcoder/self-prompt-engine.d.ts +15 -0
- package/dist/synapsexcoder/self-prompt-engine.d.ts.map +1 -0
- package/dist/synapsexcoder/self-prompt-engine.js +11 -0
- package/dist/synapsexcoder/self-prompt-engine.js.map +1 -0
- package/dist/synapsexcoder/session-observability.d.ts +44 -0
- package/dist/synapsexcoder/session-observability.d.ts.map +1 -0
- package/dist/synapsexcoder/session-observability.js +115 -0
- package/dist/synapsexcoder/session-observability.js.map +1 -0
- package/dist/synapsexcoder/specialist-agents.d.ts +38 -0
- package/dist/synapsexcoder/specialist-agents.d.ts.map +1 -0
- package/dist/synapsexcoder/specialist-agents.js +192 -0
- package/dist/synapsexcoder/specialist-agents.js.map +1 -0
- package/dist/synapsexcoder/swarm/kanban-board.d.ts +34 -0
- package/dist/synapsexcoder/swarm/kanban-board.d.ts.map +1 -0
- package/dist/synapsexcoder/swarm/kanban-board.js +85 -0
- package/dist/synapsexcoder/swarm/kanban-board.js.map +1 -0
- package/dist/synapsexcoder/swarm/swarm-mailbox.d.ts +38 -0
- package/dist/synapsexcoder/swarm/swarm-mailbox.d.ts.map +1 -0
- package/dist/synapsexcoder/swarm/swarm-mailbox.js +93 -0
- package/dist/synapsexcoder/swarm/swarm-mailbox.js.map +1 -0
- package/dist/synapsexcoder/tool-bridge.d.ts +277 -0
- package/dist/synapsexcoder/tool-bridge.d.ts.map +1 -0
- package/dist/synapsexcoder/tool-bridge.js +1356 -0
- package/dist/synapsexcoder/tool-bridge.js.map +1 -0
- package/dist/synapsexcoder/tool-routing.d.ts +28 -0
- package/dist/synapsexcoder/tool-routing.d.ts.map +1 -0
- package/dist/synapsexcoder/tool-routing.js +79 -0
- package/dist/synapsexcoder/tool-routing.js.map +1 -0
- package/dist/synapsexcoder/type-injector.d.ts +26 -0
- package/dist/synapsexcoder/type-injector.d.ts.map +1 -0
- package/dist/synapsexcoder/type-injector.js +124 -0
- package/dist/synapsexcoder/type-injector.js.map +1 -0
- package/dist/synapsexcoder/utils/fuzzy-match.d.ts +25 -0
- package/dist/synapsexcoder/utils/fuzzy-match.d.ts.map +1 -0
- package/dist/synapsexcoder/utils/fuzzy-match.js +83 -0
- package/dist/synapsexcoder/utils/fuzzy-match.js.map +1 -0
- package/dist/synapsexcoder/vault-crawl.d.ts +29 -0
- package/dist/synapsexcoder/vault-crawl.d.ts.map +1 -0
- package/dist/synapsexcoder/vault-crawl.js +103 -0
- package/dist/synapsexcoder/vault-crawl.js.map +1 -0
- package/dist/synapsexcoder/verified-apply.d.ts +39 -0
- package/dist/synapsexcoder/verified-apply.d.ts.map +1 -0
- package/dist/synapsexcoder/verified-apply.js +81 -0
- package/dist/synapsexcoder/verified-apply.js.map +1 -0
- package/dist/synapsexcoder/verified-context-editing.d.ts +34 -0
- package/dist/synapsexcoder/verified-context-editing.d.ts.map +1 -0
- package/dist/synapsexcoder/verified-context-editing.js +80 -0
- package/dist/synapsexcoder/verified-context-editing.js.map +1 -0
- package/dist/synapsexcoder/worker-pool.d.ts +47 -0
- package/dist/synapsexcoder/worker-pool.d.ts.map +1 -0
- package/dist/synapsexcoder/worker-pool.js +120 -0
- package/dist/synapsexcoder/worker-pool.js.map +1 -0
- package/dist/synapsexcoder/workspace-intelligence.d.ts +35 -0
- package/dist/synapsexcoder/workspace-intelligence.d.ts.map +1 -0
- package/dist/synapsexcoder/workspace-intelligence.js +126 -0
- package/dist/synapsexcoder/workspace-intelligence.js.map +1 -0
- package/dist/synapsexcoder/worktree-manager.d.ts +31 -0
- package/dist/synapsexcoder/worktree-manager.d.ts.map +1 -0
- package/dist/synapsexcoder/worktree-manager.js +100 -0
- package/dist/synapsexcoder/worktree-manager.js.map +1 -0
- package/dist/team/index.d.ts +17 -0
- package/dist/team/index.d.ts.map +1 -0
- package/dist/team/index.js +13 -0
- package/dist/team/index.js.map +1 -0
- package/dist/team/team-audit.d.ts +120 -0
- package/dist/team/team-audit.d.ts.map +1 -0
- package/dist/team/team-audit.js +357 -0
- package/dist/team/team-audit.js.map +1 -0
- package/dist/team/team-collaboration.d.ts +150 -0
- package/dist/team/team-collaboration.d.ts.map +1 -0
- package/dist/team/team-collaboration.js +495 -0
- package/dist/team/team-collaboration.js.map +1 -0
- package/dist/team/team-rbac.d.ts +84 -0
- package/dist/team/team-rbac.d.ts.map +1 -0
- package/dist/team/team-rbac.js +259 -0
- package/dist/team/team-rbac.js.map +1 -0
- package/dist/team/team-session-manager.d.ts +100 -0
- package/dist/team/team-session-manager.d.ts.map +1 -0
- package/dist/team/team-session-manager.js +255 -0
- package/dist/team/team-session-manager.js.map +1 -0
- package/dist/thoughts/thoughts-manager.d.ts +52 -0
- package/dist/thoughts/thoughts-manager.d.ts.map +1 -0
- package/dist/thoughts/thoughts-manager.js +271 -0
- package/dist/thoughts/thoughts-manager.js.map +1 -0
- package/dist/tools/api-validator/index.d.ts +97 -0
- package/dist/tools/api-validator/index.d.ts.map +1 -0
- package/dist/tools/api-validator/index.js +312 -0
- package/dist/tools/api-validator/index.js.map +1 -0
- package/dist/tools/code-archaeology/index.d.ts +193 -0
- package/dist/tools/code-archaeology/index.d.ts.map +1 -0
- package/dist/tools/code-archaeology/index.js +468 -0
- package/dist/tools/code-archaeology/index.js.map +1 -0
- package/dist/tools/codebase-search/index.d.ts +126 -0
- package/dist/tools/codebase-search/index.d.ts.map +1 -0
- package/dist/tools/codebase-search/index.js +437 -0
- package/dist/tools/codebase-search/index.js.map +1 -0
- package/dist/tools/context/index.d.ts +162 -0
- package/dist/tools/context/index.d.ts.map +1 -0
- package/dist/tools/context/index.js +332 -0
- package/dist/tools/context/index.js.map +1 -0
- package/dist/tools/deepwiki/analyzer.d.ts +167 -0
- package/dist/tools/deepwiki/analyzer.d.ts.map +1 -0
- package/dist/tools/deepwiki/analyzer.js +925 -0
- package/dist/tools/deepwiki/analyzer.js.map +1 -0
- package/dist/tools/deepwiki/extractor.d.ts +151 -0
- package/dist/tools/deepwiki/extractor.d.ts.map +1 -0
- package/dist/tools/deepwiki/extractor.js +923 -0
- package/dist/tools/deepwiki/extractor.js.map +1 -0
- package/dist/tools/deepwiki/generator.d.ts +89 -0
- package/dist/tools/deepwiki/generator.d.ts.map +1 -0
- package/dist/tools/deepwiki/generator.js +638 -0
- package/dist/tools/deepwiki/generator.js.map +1 -0
- package/dist/tools/deepwiki/index.d.ts +96 -0
- package/dist/tools/deepwiki/index.d.ts.map +1 -0
- package/dist/tools/deepwiki/index.js +282 -0
- package/dist/tools/deepwiki/index.js.map +1 -0
- package/dist/tools/deepwiki/types.d.ts +370 -0
- package/dist/tools/deepwiki/types.d.ts.map +1 -0
- package/dist/tools/deepwiki/types.js +21 -0
- package/dist/tools/deepwiki/types.js.map +1 -0
- package/dist/tools/dependency-mapper/index.d.ts +212 -0
- package/dist/tools/dependency-mapper/index.d.ts.map +1 -0
- package/dist/tools/dependency-mapper/index.js +592 -0
- package/dist/tools/dependency-mapper/index.js.map +1 -0
- package/dist/tools/memory/index.d.ts +120 -0
- package/dist/tools/memory/index.d.ts.map +1 -0
- package/dist/tools/memory/index.js +275 -0
- package/dist/tools/memory/index.js.map +1 -0
- package/dist/tools/normalize_patch.d.ts +3 -0
- package/dist/tools/normalize_patch.d.ts.map +1 -0
- package/dist/tools/normalize_patch.js +22 -0
- package/dist/tools/normalize_patch.js.map +1 -0
- package/dist/tools/pattern-detector/index.d.ts +105 -0
- package/dist/tools/pattern-detector/index.d.ts.map +1 -0
- package/dist/tools/pattern-detector/index.js +526 -0
- package/dist/tools/pattern-detector/index.js.map +1 -0
- package/dist/tools/performance-profiler/index.d.ts +50 -0
- package/dist/tools/performance-profiler/index.d.ts.map +1 -0
- package/dist/tools/performance-profiler/index.js +164 -0
- package/dist/tools/performance-profiler/index.js.map +1 -0
- package/dist/tools/refactoring-engine/index.d.ts +63 -0
- package/dist/tools/refactoring-engine/index.d.ts.map +1 -0
- package/dist/tools/refactoring-engine/index.js +270 -0
- package/dist/tools/refactoring-engine/index.js.map +1 -0
- package/dist/tools/registry.d.ts +36 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +499 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/test-generator/index.d.ts +152 -0
- package/dist/tools/test-generator/index.d.ts.map +1 -0
- package/dist/tools/test-generator/index.js +448 -0
- package/dist/tools/test-generator/index.js.map +1 -0
- package/dist/types/index.d.ts +565 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/path-validator.d.ts +94 -0
- package/dist/utils/path-validator.d.ts.map +1 -0
- package/dist/utils/path-validator.js +192 -0
- package/dist/utils/path-validator.js.map +1 -0
- package/dist/utils/secure-exec.d.ts +92 -0
- package/dist/utils/secure-exec.d.ts.map +1 -0
- package/dist/utils/secure-exec.js +230 -0
- package/dist/utils/secure-exec.js.map +1 -0
- package/dist/verification/claim-parser.d.ts +37 -0
- package/dist/verification/claim-parser.d.ts.map +1 -0
- package/dist/verification/claim-parser.js +231 -0
- package/dist/verification/claim-parser.js.map +1 -0
- package/dist/verification/cross-reference-engine.d.ts +55 -0
- package/dist/verification/cross-reference-engine.d.ts.map +1 -0
- package/dist/verification/cross-reference-engine.js +149 -0
- package/dist/verification/cross-reference-engine.js.map +1 -0
- package/dist/verification/discrepancy-tracker.d.ts +73 -0
- package/dist/verification/discrepancy-tracker.d.ts.map +1 -0
- package/dist/verification/discrepancy-tracker.js +196 -0
- package/dist/verification/discrepancy-tracker.js.map +1 -0
- package/dist/verification/feature-reconciler.d.ts +90 -0
- package/dist/verification/feature-reconciler.d.ts.map +1 -0
- package/dist/verification/feature-reconciler.js +264 -0
- package/dist/verification/feature-reconciler.js.map +1 -0
- package/dist/verification/index.d.ts +16 -0
- package/dist/verification/index.d.ts.map +1 -0
- package/dist/verification/index.js +19 -0
- package/dist/verification/index.js.map +1 -0
- package/dist/verification/integration-verifier.d.ts +66 -0
- package/dist/verification/integration-verifier.d.ts.map +1 -0
- package/dist/verification/integration-verifier.js +210 -0
- package/dist/verification/integration-verifier.js.map +1 -0
- package/dist/verification/recursive-audit-loop.d.ts +56 -0
- package/dist/verification/recursive-audit-loop.d.ts.map +1 -0
- package/dist/verification/recursive-audit-loop.js +136 -0
- package/dist/verification/recursive-audit-loop.js.map +1 -0
- package/dist/verification/runtime-verifier.d.ts +37 -0
- package/dist/verification/runtime-verifier.d.ts.map +1 -0
- package/dist/verification/runtime-verifier.js +221 -0
- package/dist/verification/runtime-verifier.js.map +1 -0
- package/dist/verification/types.d.ts +88 -0
- package/dist/verification/types.d.ts.map +1 -0
- package/dist/verification/types.js +5 -0
- package/dist/verification/types.js.map +1 -0
- package/dist/verification/verification-engine.d.ts +48 -0
- package/dist/verification/verification-engine.d.ts.map +1 -0
- package/dist/verification/verification-engine.js +203 -0
- package/dist/verification/verification-engine.js.map +1 -0
- package/opencode-agents/synapse-analyzer.md +64 -0
- package/opencode-agents/synapse-debugger.md +43 -0
- package/opencode-agents/synapse-diff-validator.md +42 -0
- package/opencode-agents/synapse-gatekeeper.md +273 -0
- package/opencode-agents/synapse-master.md +167 -0
- package/opencode-agents/synapse-planner.md +55 -0
- package/opencode-agents/synapse-researcher.md +61 -0
- package/opencode-agents/synapse-reviewer.md +64 -0
- package/opencode-agents/synapse-rewriter.md +62 -0
- package/opencode-agents/synapse-strategist.md +50 -0
- package/opencode-agents/synapse-test-runner.md +43 -0
- package/opencode-agents/synapse-ui-designer.md +61 -0
- package/opencode-agents/synapse-verifier.md +544 -0
- package/package.json +108 -0
- package/src/plugin/opencode-plugin.ts +141 -0
|
@@ -0,0 +1,2014 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Gated Subagents — Multi-Step Pipeline with Internal Gatekeeper
|
|
4
|
+
*
|
|
5
|
+
* Implements the full Gated Subagent Pipeline:
|
|
6
|
+
* 1. Plan Mode: Decompose task into phases
|
|
7
|
+
* 2. Execute Phase: Run each phase with appropriate agent
|
|
8
|
+
* 3. Gatekeeper Review: Internal gatekeeper reviews output
|
|
9
|
+
* 4. Decision: APPROVE / DECLINE / RESTART
|
|
10
|
+
* 5. Retry: If declined, retry with modified approach
|
|
11
|
+
*/
|
|
12
|
+
import { GatedSubAgent } from "./gated-subagent.js";
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Gated Planner Agent
|
|
15
|
+
// ============================================================================
|
|
16
|
+
export class GatedPlannerAgent extends GatedSubAgent {
|
|
17
|
+
constructor(messageBus, toolBridge, config) {
|
|
18
|
+
super("planner", config || {}, messageBus, toolBridge, {
|
|
19
|
+
maxRetries: 3,
|
|
20
|
+
strictMode: true,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async performTask(task) {
|
|
24
|
+
this.updateStatus("running", "Creating execution plan");
|
|
25
|
+
const prompt = `Create a detailed execution plan for the following task:
|
|
26
|
+
|
|
27
|
+
Task: ${task.prompt}
|
|
28
|
+
|
|
29
|
+
Context: ${task.context.files.length} files provided, mode: ${task.context.metadata?.mode || "full"}
|
|
30
|
+
|
|
31
|
+
Please provide a structured plan with:
|
|
32
|
+
1. Goal analysis
|
|
33
|
+
2. Step-by-step breakdown
|
|
34
|
+
3. Estimated complexity (low/medium/high)
|
|
35
|
+
4. Estimated time
|
|
36
|
+
5. Required tools/resources
|
|
37
|
+
6. Potential risks
|
|
38
|
+
|
|
39
|
+
Format as structured JSON.`;
|
|
40
|
+
const response = await this.callModel(prompt);
|
|
41
|
+
if (!response) {
|
|
42
|
+
return this.localPlanFallback(task);
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(response);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {
|
|
49
|
+
goal: task.prompt,
|
|
50
|
+
plan: response,
|
|
51
|
+
...this.estimateComplexityFromText(task.prompt),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Local fallback: tokenize the task description, extract action verbs,
|
|
57
|
+
* detect complexity from keyword signals, and build a real numbered step
|
|
58
|
+
* list with dependency chains. No hardcoded data.
|
|
59
|
+
*/
|
|
60
|
+
localPlanFallback(task) {
|
|
61
|
+
const description = task.prompt || "";
|
|
62
|
+
const lower = description.toLowerCase();
|
|
63
|
+
const fileCount = task.context.files.length;
|
|
64
|
+
// --- Extract action verbs and targets from the task description ---
|
|
65
|
+
const actionPatterns = [
|
|
66
|
+
{
|
|
67
|
+
verb: "create",
|
|
68
|
+
regex: /\b(?:create|build|generate|scaffold|add|implement|write)\s+(?:a\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
verb: "refactor",
|
|
72
|
+
regex: /\b(?:refactor|restructure|reorganize|redesign)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
verb: "fix",
|
|
76
|
+
regex: /\b(?:fix|repair|resolve|patch|debug)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
verb: "update",
|
|
80
|
+
regex: /\b(?:update|modify|change|edit|adjust|improve)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
verb: "test",
|
|
84
|
+
regex: /\b(?:test|verify|validate|check)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
verb: "delete",
|
|
88
|
+
regex: /\b(?:delete|remove|drop|clean\s*up)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
verb: "migrate",
|
|
92
|
+
regex: /\b(?:migrate|move|transfer|port)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
verb: "deploy",
|
|
96
|
+
regex: /\b(?:deploy|publish|release|ship)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
verb: "analyze",
|
|
100
|
+
regex: /\b(?:analyze|inspect|examine|review|audit)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
verb: "configure",
|
|
104
|
+
regex: /\b(?:configure|setup|set\s*up|initialize|install)\s+(?:the\s+)?(.+?)(?:\s+(?:and|then|,|\.)|$)/gi,
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
const extractedActions = [];
|
|
108
|
+
for (const { verb, regex } of actionPatterns) {
|
|
109
|
+
let match;
|
|
110
|
+
while ((match = regex.exec(description)) !== null) {
|
|
111
|
+
const target = match[1]?.trim().substring(0, 80) || "the task";
|
|
112
|
+
if (target.length > 2) {
|
|
113
|
+
extractedActions.push({ verb, target });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// --- Build steps from extracted actions or sentence splitting ---
|
|
118
|
+
let steps;
|
|
119
|
+
if (extractedActions.length > 0) {
|
|
120
|
+
steps = extractedActions.map((action, idx) => ({
|
|
121
|
+
step: idx + 1,
|
|
122
|
+
action: action.verb,
|
|
123
|
+
description: `${action.verb.charAt(0).toUpperCase() + action.verb.slice(1)} ${action.target}`,
|
|
124
|
+
dependsOn: idx > 0 ? [idx] : [],
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// Split by conjunctions, commas, or sentence boundaries
|
|
129
|
+
const segments = description
|
|
130
|
+
.split(/(?:\s+and\s+|\s*,\s*|\.\s+|\s+then\s+)/i)
|
|
131
|
+
.map((s) => s.trim())
|
|
132
|
+
.filter((s) => s.length > 3);
|
|
133
|
+
if (segments.length > 1) {
|
|
134
|
+
steps = segments.map((seg, idx) => ({
|
|
135
|
+
step: idx + 1,
|
|
136
|
+
action: "execute",
|
|
137
|
+
description: seg.charAt(0).toUpperCase() + seg.slice(1),
|
|
138
|
+
dependsOn: idx > 0 ? [idx] : [],
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Single task — generate analyze → implement → verify chain
|
|
143
|
+
steps = [
|
|
144
|
+
{
|
|
145
|
+
step: 1,
|
|
146
|
+
action: "analyze",
|
|
147
|
+
description: `Analyze requirements: ${description.substring(0, 100)}`,
|
|
148
|
+
dependsOn: [],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
step: 2,
|
|
152
|
+
action: "implement",
|
|
153
|
+
description: `Implement changes for: ${description.substring(0, 80)}`,
|
|
154
|
+
dependsOn: [1],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
step: 3,
|
|
158
|
+
action: "verify",
|
|
159
|
+
description: "Verify implementation and check for regressions",
|
|
160
|
+
dependsOn: [2],
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Add file-scan step if files are present
|
|
166
|
+
if (fileCount > 0 && steps.length > 0) {
|
|
167
|
+
steps.unshift({
|
|
168
|
+
step: 0,
|
|
169
|
+
action: "scan",
|
|
170
|
+
description: `Scan ${fileCount} provided file(s) for context`,
|
|
171
|
+
dependsOn: [],
|
|
172
|
+
});
|
|
173
|
+
// Re-number and adjust deps
|
|
174
|
+
steps = steps.map((s, idx) => ({
|
|
175
|
+
...s,
|
|
176
|
+
step: idx + 1,
|
|
177
|
+
dependsOn: idx === 0 ? [] : s.dependsOn.map((d) => d + 1),
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
// --- Complexity from keyword signals ---
|
|
181
|
+
const complexity = this.estimateComplexityFromText(lower);
|
|
182
|
+
// --- Required tools based on keywords ---
|
|
183
|
+
const tools = [];
|
|
184
|
+
if (/\b(file|read|write|edit|create)\b/i.test(lower))
|
|
185
|
+
tools.push("file_operations");
|
|
186
|
+
if (/\b(test|spec|jest|mocha|vitest)\b/i.test(lower))
|
|
187
|
+
tools.push("testing_framework");
|
|
188
|
+
if (/\b(git|commit|branch|merge|pr)\b/i.test(lower))
|
|
189
|
+
tools.push("version_control");
|
|
190
|
+
if (/\b(npm|yarn|pnpm|install|package)\b/i.test(lower))
|
|
191
|
+
tools.push("package_manager");
|
|
192
|
+
if (/\b(api|endpoint|http|fetch|request)\b/i.test(lower))
|
|
193
|
+
tools.push("http_client");
|
|
194
|
+
if (/\b(db|database|sql|query|schema|migration)\b/i.test(lower))
|
|
195
|
+
tools.push("database");
|
|
196
|
+
if (/\b(docker|container|deploy|ci|cd)\b/i.test(lower))
|
|
197
|
+
tools.push("devops");
|
|
198
|
+
if (tools.length === 0)
|
|
199
|
+
tools.push("code_editor");
|
|
200
|
+
// --- Risk factors ---
|
|
201
|
+
const risks = [];
|
|
202
|
+
if (/\b(refactor|migrate|restructure)\b/i.test(lower))
|
|
203
|
+
risks.push("Breaking changes in dependent code");
|
|
204
|
+
if (/\b(delete|remove|drop)\b/i.test(lower))
|
|
205
|
+
risks.push("Data or code loss if not backed up");
|
|
206
|
+
if (/\b(security|auth|password|token|secret)\b/i.test(lower))
|
|
207
|
+
risks.push("Security-sensitive changes require careful review");
|
|
208
|
+
if (fileCount > 10)
|
|
209
|
+
risks.push(`Large scope: ${fileCount} files may have cascading impacts`);
|
|
210
|
+
if (/\b(production|prod|live)\b/i.test(lower))
|
|
211
|
+
risks.push("Changes target production environment");
|
|
212
|
+
if (risks.length === 0)
|
|
213
|
+
risks.push("Standard development risk — verify with tests");
|
|
214
|
+
return {
|
|
215
|
+
goal: task.prompt,
|
|
216
|
+
steps,
|
|
217
|
+
...complexity,
|
|
218
|
+
requiredTools: tools,
|
|
219
|
+
risks,
|
|
220
|
+
fileScope: fileCount,
|
|
221
|
+
note: "Plan generated via local keyword analysis (model unavailable)",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Estimate complexity and time from task text keyword signals.
|
|
226
|
+
*/
|
|
227
|
+
estimateComplexityFromText(text) {
|
|
228
|
+
const lower = text.toLowerCase();
|
|
229
|
+
const wordCount = text.split(/\s+/).length;
|
|
230
|
+
const highKeywords = /\b(refactor|migrate|integrate|redesign|architect|overhaul|rewrite|restructure|distributed|concurrent|multi.?thread)\b/i;
|
|
231
|
+
const lowKeywords = /\b(rename|move|delete|remove|typo|comment|log|print|bump|update version|fix typo)\b/i;
|
|
232
|
+
let estimatedComplexity;
|
|
233
|
+
let estimatedTime;
|
|
234
|
+
if (highKeywords.test(lower) || wordCount > 80) {
|
|
235
|
+
estimatedComplexity = "high";
|
|
236
|
+
estimatedTime = "15-30 minutes";
|
|
237
|
+
}
|
|
238
|
+
else if (lowKeywords.test(lower) && wordCount < 20) {
|
|
239
|
+
estimatedComplexity = "low";
|
|
240
|
+
estimatedTime = "1-3 minutes";
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
estimatedComplexity = "medium";
|
|
244
|
+
estimatedTime = "5-10 minutes";
|
|
245
|
+
}
|
|
246
|
+
return { estimatedComplexity, estimatedTime };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Gated Analyzer Agent
|
|
251
|
+
// ============================================================================
|
|
252
|
+
export class GatedAnalyzerAgent extends GatedSubAgent {
|
|
253
|
+
constructor(messageBus, toolBridge, config) {
|
|
254
|
+
super("analyzer", config || {}, messageBus, toolBridge, {
|
|
255
|
+
maxRetries: 3,
|
|
256
|
+
strictMode: true,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async performTask(task) {
|
|
260
|
+
this.updateStatus("running", "Analyzing code");
|
|
261
|
+
const files = task.context.files;
|
|
262
|
+
const fileContents = files
|
|
263
|
+
.map((f) => `File: ${f.path}\n\n${f.content}`)
|
|
264
|
+
.join("\n\n---\n\n");
|
|
265
|
+
const prompt = `Analyze the following code files for issues, patterns, and improvements:
|
|
266
|
+
|
|
267
|
+
${fileContents}
|
|
268
|
+
|
|
269
|
+
Please provide:
|
|
270
|
+
1. Code quality issues
|
|
271
|
+
2. Security vulnerabilities
|
|
272
|
+
3. Performance bottlenecks
|
|
273
|
+
4. Best practice violations
|
|
274
|
+
5. Complexity metrics
|
|
275
|
+
6. Maintainability assessment
|
|
276
|
+
|
|
277
|
+
Format as structured JSON.`;
|
|
278
|
+
const response = await this.callModel(prompt);
|
|
279
|
+
if (!response) {
|
|
280
|
+
return this.localAnalysisFallback(files);
|
|
281
|
+
}
|
|
282
|
+
// Parse AI response
|
|
283
|
+
try {
|
|
284
|
+
const analysis = JSON.parse(response);
|
|
285
|
+
return analysis;
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
return {
|
|
289
|
+
file: task.context.files[0]?.path || "unknown",
|
|
290
|
+
analysis: response,
|
|
291
|
+
issues: [],
|
|
292
|
+
metrics: this.computeFileMetrics(files),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Local fallback: perform real static analysis on provided files.
|
|
298
|
+
* Detects: console.log, TODO/FIXME, 'any' types, deep nesting, long
|
|
299
|
+
* functions, missing error handling, potential hardcoded secrets,
|
|
300
|
+
* unused imports, high cyclomatic complexity.
|
|
301
|
+
*/
|
|
302
|
+
localAnalysisFallback(files) {
|
|
303
|
+
const allIssues = [];
|
|
304
|
+
const perFileMetrics = [];
|
|
305
|
+
for (const file of files) {
|
|
306
|
+
const content = file.content || "";
|
|
307
|
+
const lines = content.split("\n");
|
|
308
|
+
const lineCount = lines.length;
|
|
309
|
+
let blankLines = 0;
|
|
310
|
+
let commentLines = 0;
|
|
311
|
+
let importCount = 0;
|
|
312
|
+
let exportCount = 0;
|
|
313
|
+
const functionMatches = content.match(/\b(?:function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:\([^)]*\)|[a-zA-Z_$]\w*)\s*=>|(?:async\s+)?(?:get|set|static\s+)?\w+\s*\([^)]*\)\s*\{)/g) || [];
|
|
314
|
+
const classMatches = content.match(/\bclass\s+\w+/g) || [];
|
|
315
|
+
// Cyclomatic complexity: count decision points
|
|
316
|
+
let cyclomaticComplexity = 1;
|
|
317
|
+
const decisionPattern = /\b(?:if|else\s+if|case|for|while|do|catch|\?\?|&&|\|\||[?]:)/g;
|
|
318
|
+
let decMatch;
|
|
319
|
+
while ((decMatch = decisionPattern.exec(content)) !== null) {
|
|
320
|
+
void decMatch; // suppress noUnusedLocals
|
|
321
|
+
cyclomaticComplexity++;
|
|
322
|
+
}
|
|
323
|
+
// Max nesting depth via brace counting
|
|
324
|
+
let maxNesting = 0;
|
|
325
|
+
let currentNesting = 0;
|
|
326
|
+
for (const ch of content) {
|
|
327
|
+
if (ch === "{") {
|
|
328
|
+
currentNesting++;
|
|
329
|
+
if (currentNesting > maxNesting)
|
|
330
|
+
maxNesting = currentNesting;
|
|
331
|
+
}
|
|
332
|
+
else if (ch === "}") {
|
|
333
|
+
currentNesting = Math.max(0, currentNesting - 1);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Longest function length
|
|
337
|
+
let longestFn = 0;
|
|
338
|
+
let fnNesting = 0;
|
|
339
|
+
let fnStartLine = -1;
|
|
340
|
+
for (let i = 0; i < lines.length; i++) {
|
|
341
|
+
const line = lines[i];
|
|
342
|
+
const trimmed = line.trim();
|
|
343
|
+
if (trimmed === "") {
|
|
344
|
+
blankLines++;
|
|
345
|
+
}
|
|
346
|
+
else if (trimmed.startsWith("//") ||
|
|
347
|
+
trimmed.startsWith("/*") ||
|
|
348
|
+
trimmed.startsWith("*")) {
|
|
349
|
+
commentLines++;
|
|
350
|
+
}
|
|
351
|
+
if (/^import\s/.test(trimmed))
|
|
352
|
+
importCount++;
|
|
353
|
+
if (/^export\s/.test(trimmed))
|
|
354
|
+
exportCount++;
|
|
355
|
+
if (/(?:function\s+\w+|=>\s*\{|\)\s*\{)\s*$/.test(trimmed) &&
|
|
356
|
+
fnStartLine === -1) {
|
|
357
|
+
fnStartLine = i;
|
|
358
|
+
fnNesting = 0;
|
|
359
|
+
}
|
|
360
|
+
if (fnStartLine !== -1) {
|
|
361
|
+
for (const ch of line) {
|
|
362
|
+
if (ch === "{")
|
|
363
|
+
fnNesting++;
|
|
364
|
+
if (ch === "}")
|
|
365
|
+
fnNesting--;
|
|
366
|
+
}
|
|
367
|
+
if (fnNesting <= 0 && fnStartLine !== -1) {
|
|
368
|
+
const fnLen = i - fnStartLine + 1;
|
|
369
|
+
if (fnLen > longestFn)
|
|
370
|
+
longestFn = fnLen;
|
|
371
|
+
fnStartLine = -1;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
perFileMetrics.push({
|
|
376
|
+
file: file.path,
|
|
377
|
+
linesOfCode: lineCount,
|
|
378
|
+
blankLines,
|
|
379
|
+
commentLines,
|
|
380
|
+
functions: functionMatches.length,
|
|
381
|
+
classes: classMatches.length,
|
|
382
|
+
imports: importCount,
|
|
383
|
+
exports: exportCount,
|
|
384
|
+
cyclomaticComplexity,
|
|
385
|
+
maxNestingDepth: maxNesting,
|
|
386
|
+
longestFunctionLength: longestFn,
|
|
387
|
+
});
|
|
388
|
+
// --- Issue detection ---
|
|
389
|
+
if (longestFn > 50) {
|
|
390
|
+
allIssues.push({
|
|
391
|
+
type: "maintainability",
|
|
392
|
+
severity: longestFn > 100 ? "high" : "medium",
|
|
393
|
+
file: file.path,
|
|
394
|
+
description: `Longest function is ${longestFn} lines (threshold: 50)`,
|
|
395
|
+
recommendation: "Break large functions into smaller, focused helper functions",
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
if (maxNesting > 4) {
|
|
399
|
+
allIssues.push({
|
|
400
|
+
type: "complexity",
|
|
401
|
+
severity: maxNesting > 6 ? "high" : "medium",
|
|
402
|
+
file: file.path,
|
|
403
|
+
description: `Maximum nesting depth is ${maxNesting} levels (threshold: 4)`,
|
|
404
|
+
recommendation: "Use early returns, guard clauses, or extract nested logic into functions",
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if (cyclomaticComplexity > 20) {
|
|
408
|
+
allIssues.push({
|
|
409
|
+
type: "complexity",
|
|
410
|
+
severity: cyclomaticComplexity > 40 ? "high" : "medium",
|
|
411
|
+
file: file.path,
|
|
412
|
+
description: `Cyclomatic complexity is ${cyclomaticComplexity} (threshold: 20)`,
|
|
413
|
+
recommendation: "Simplify control flow; consider strategy pattern or lookup tables",
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
// TODO / FIXME scan with line numbers
|
|
417
|
+
for (let i = 0; i < lines.length; i++) {
|
|
418
|
+
const line = lines[i];
|
|
419
|
+
if (/\bTODO\b/i.test(line)) {
|
|
420
|
+
allIssues.push({
|
|
421
|
+
type: "best_practice",
|
|
422
|
+
severity: "low",
|
|
423
|
+
file: file.path,
|
|
424
|
+
line: i + 1,
|
|
425
|
+
description: `TODO comment: ${line.trim().substring(0, 100)}`,
|
|
426
|
+
recommendation: "Address TODO items or create tracking issues",
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
if (/\bFIXME\b/i.test(line)) {
|
|
430
|
+
allIssues.push({
|
|
431
|
+
type: "best_practice",
|
|
432
|
+
severity: "medium",
|
|
433
|
+
file: file.path,
|
|
434
|
+
line: i + 1,
|
|
435
|
+
description: `FIXME comment: ${line.trim().substring(0, 100)}`,
|
|
436
|
+
recommendation: "FIXME indicates known bugs — prioritize fixing",
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// console.log / debug statements
|
|
441
|
+
const consoleLogLines = [];
|
|
442
|
+
for (let i = 0; i < lines.length; i++) {
|
|
443
|
+
if (/\bconsole\.(log|debug|info)\b/.test(lines[i]) &&
|
|
444
|
+
!/\/\//.test(lines[i].split("console")[0])) {
|
|
445
|
+
consoleLogLines.push(i + 1);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (consoleLogLines.length > 0) {
|
|
449
|
+
allIssues.push({
|
|
450
|
+
type: "best_practice",
|
|
451
|
+
severity: "low",
|
|
452
|
+
file: file.path,
|
|
453
|
+
line: consoleLogLines[0],
|
|
454
|
+
description: `Found ${consoleLogLines.length} console.log/debug/info statement(s) at lines: ${consoleLogLines.slice(0, 5).join(", ")}${consoleLogLines.length > 5 ? "..." : ""}`,
|
|
455
|
+
recommendation: "Remove debug logging or replace with a proper logging framework",
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
// Unused imports detection (basic: named imports not used in rest of file)
|
|
459
|
+
const importLines = lines.filter((l) => /^import\s/.test(l.trim()));
|
|
460
|
+
for (const impLine of importLines) {
|
|
461
|
+
const namedImports = impLine.match(/\{\s*([^}]+)\s*\}/);
|
|
462
|
+
if (namedImports) {
|
|
463
|
+
const names = namedImports[1]
|
|
464
|
+
.split(",")
|
|
465
|
+
.map((n) => n
|
|
466
|
+
.trim()
|
|
467
|
+
.split(/\s+as\s+/)
|
|
468
|
+
.pop()
|
|
469
|
+
?.trim())
|
|
470
|
+
.filter(Boolean);
|
|
471
|
+
for (const name of names) {
|
|
472
|
+
if (!name)
|
|
473
|
+
continue;
|
|
474
|
+
const restOfFile = lines
|
|
475
|
+
.filter((l) => !/^import\s/.test(l.trim()))
|
|
476
|
+
.join("\n");
|
|
477
|
+
const nameRegex = new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
|
|
478
|
+
const usageCount = (restOfFile.match(nameRegex) || []).length;
|
|
479
|
+
if (usageCount === 0) {
|
|
480
|
+
allIssues.push({
|
|
481
|
+
type: "best_practice",
|
|
482
|
+
severity: "low",
|
|
483
|
+
file: file.path,
|
|
484
|
+
description: `Potentially unused import: '${name}'`,
|
|
485
|
+
recommendation: "Remove unused imports to keep the codebase clean",
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
// TypeScript 'any' usage
|
|
492
|
+
if (/\.tsx?$/.test(file.path)) {
|
|
493
|
+
const anyMatches = [];
|
|
494
|
+
for (let i = 0; i < lines.length; i++) {
|
|
495
|
+
if (/:\s*any\b|as\s+any\b|<any>/.test(lines[i])) {
|
|
496
|
+
anyMatches.push(i + 1);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if (anyMatches.length > 0) {
|
|
500
|
+
allIssues.push({
|
|
501
|
+
type: "type_safety",
|
|
502
|
+
severity: anyMatches.length > 5 ? "medium" : "low",
|
|
503
|
+
file: file.path,
|
|
504
|
+
line: anyMatches[0],
|
|
505
|
+
description: `Found ${anyMatches.length} 'any' type usage(s) at lines: ${anyMatches.slice(0, 5).join(", ")}${anyMatches.length > 5 ? "..." : ""}`,
|
|
506
|
+
recommendation: "Replace 'any' with specific types for better type safety",
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
// Missing error handling for async functions
|
|
511
|
+
const asyncFnPattern = /\basync\s+(?:function\s+\w+|\w+\s*=\s*async|\([^)]*\)\s*=>)/g;
|
|
512
|
+
const asyncCount = (content.match(asyncFnPattern) || []).length;
|
|
513
|
+
const tryCatchCount = (content.match(/\btry\s*\{/g) || []).length;
|
|
514
|
+
const catchCount = (content.match(/\.catch\s*\(/g) || []).length;
|
|
515
|
+
if (asyncCount > 0 && tryCatchCount + catchCount < asyncCount * 0.5) {
|
|
516
|
+
allIssues.push({
|
|
517
|
+
type: "error_handling",
|
|
518
|
+
severity: "medium",
|
|
519
|
+
file: file.path,
|
|
520
|
+
description: `${asyncCount} async function(s) but only ${tryCatchCount + catchCount} error handler(s) — potential unhandled rejections`,
|
|
521
|
+
recommendation: "Wrap async operations in try/catch or add .catch() handlers",
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
// Potential hardcoded secrets
|
|
525
|
+
for (let i = 0; i < lines.length; i++) {
|
|
526
|
+
if (/(?:password|secret|apiKey|api_key|token|AUTH)\s*[:=]\s*["'][^"']{8,}["']/i.test(lines[i])) {
|
|
527
|
+
allIssues.push({
|
|
528
|
+
type: "security",
|
|
529
|
+
severity: "high",
|
|
530
|
+
file: file.path,
|
|
531
|
+
line: i + 1,
|
|
532
|
+
description: "Potential hardcoded secret or credential detected",
|
|
533
|
+
recommendation: "Move secrets to environment variables or a secure vault",
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// Very long lines
|
|
538
|
+
const longLines = lines.filter((l) => l.length > 120).length;
|
|
539
|
+
if (longLines > 10) {
|
|
540
|
+
allIssues.push({
|
|
541
|
+
type: "style",
|
|
542
|
+
severity: "low",
|
|
543
|
+
file: file.path,
|
|
544
|
+
description: `${longLines} lines exceed 120 characters`,
|
|
545
|
+
recommendation: "Consider reformatting for readability (use prettier or similar)",
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// --- Aggregate metrics ---
|
|
550
|
+
const totalLOC = perFileMetrics.reduce((s, m) => s + m.linesOfCode, 0);
|
|
551
|
+
const totalFunctions = perFileMetrics.reduce((s, m) => s + m.functions, 0);
|
|
552
|
+
const totalClasses = perFileMetrics.reduce((s, m) => s + m.classes, 0);
|
|
553
|
+
const avgComplexity = perFileMetrics.length > 0
|
|
554
|
+
? perFileMetrics.reduce((s, m) => s + m.cyclomaticComplexity, 0) /
|
|
555
|
+
perFileMetrics.length
|
|
556
|
+
: 0;
|
|
557
|
+
const commentRatio = totalLOC > 0
|
|
558
|
+
? perFileMetrics.reduce((s, m) => s + m.commentLines, 0) / totalLOC
|
|
559
|
+
: 0;
|
|
560
|
+
const avgNesting = perFileMetrics.length > 0
|
|
561
|
+
? perFileMetrics.reduce((s, m) => s + m.maxNestingDepth, 0) /
|
|
562
|
+
perFileMetrics.length
|
|
563
|
+
: 0;
|
|
564
|
+
const maintainabilityIndex = Math.max(0, Math.min(100, 100 - avgComplexity * 0.5 - avgNesting * 3 + commentRatio * 30));
|
|
565
|
+
const severityOrder = {
|
|
566
|
+
high: 0,
|
|
567
|
+
medium: 1,
|
|
568
|
+
low: 2,
|
|
569
|
+
};
|
|
570
|
+
allIssues.sort((a, b) => (severityOrder[a.severity] ?? 3) - (severityOrder[b.severity] ?? 3));
|
|
571
|
+
return {
|
|
572
|
+
filesAnalyzed: files.map((f) => f.path),
|
|
573
|
+
issues: allIssues,
|
|
574
|
+
metrics: {
|
|
575
|
+
totalLinesOfCode: totalLOC,
|
|
576
|
+
totalFunctions,
|
|
577
|
+
totalClasses,
|
|
578
|
+
averageCyclomaticComplexity: Math.round(avgComplexity * 10) / 10,
|
|
579
|
+
maintainabilityIndex: Math.round(maintainabilityIndex * 10) / 10,
|
|
580
|
+
},
|
|
581
|
+
perFileMetrics,
|
|
582
|
+
summary: {
|
|
583
|
+
totalIssues: allIssues.length,
|
|
584
|
+
highSeverity: allIssues.filter((i) => i.severity === "high").length,
|
|
585
|
+
mediumSeverity: allIssues.filter((i) => i.severity === "medium").length,
|
|
586
|
+
lowSeverity: allIssues.filter((i) => i.severity === "low").length,
|
|
587
|
+
issueTypes: [...new Set(allIssues.map((i) => i.type))],
|
|
588
|
+
},
|
|
589
|
+
note: "Analysis performed via local static analysis (model unavailable)",
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Basic file metrics for the JSON-parse fallback path.
|
|
594
|
+
*/
|
|
595
|
+
computeFileMetrics(files) {
|
|
596
|
+
const totalLOC = files.reduce((sum, f) => sum + (f.content || "").split("\n").length, 0);
|
|
597
|
+
const totalFunctions = files.reduce((sum, f) => sum + ((f.content || "").match(/\bfunction\s+\w+/g) || []).length, 0);
|
|
598
|
+
const avgLineLength = totalLOC > 0
|
|
599
|
+
? Math.round(files.reduce((sum, f) => sum + f.content.length, 0) / totalLOC)
|
|
600
|
+
: 0;
|
|
601
|
+
return {
|
|
602
|
+
linesOfCode: totalLOC,
|
|
603
|
+
functions: totalFunctions,
|
|
604
|
+
averageLineLength: avgLineLength,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// ============================================================================
|
|
609
|
+
// Gated Reviewer Agent
|
|
610
|
+
// ============================================================================
|
|
611
|
+
export class GatedReviewerAgent extends GatedSubAgent {
|
|
612
|
+
constructor(messageBus, toolBridge, config) {
|
|
613
|
+
super("reviewer", config || {}, messageBus, toolBridge, {
|
|
614
|
+
maxRetries: 3,
|
|
615
|
+
strictMode: true,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
async performTask(task) {
|
|
619
|
+
this.updateStatus("running", "Reviewing changes");
|
|
620
|
+
const analysisOutput = task.context.metadata?.analyze_output;
|
|
621
|
+
const prompt = `Review the following code analysis and provide feedback:
|
|
622
|
+
|
|
623
|
+
Analysis: ${JSON.stringify(analysisOutput, null, 2)}
|
|
624
|
+
|
|
625
|
+
Task: ${task.prompt}
|
|
626
|
+
|
|
627
|
+
Please provide:
|
|
628
|
+
1. Overall assessment
|
|
629
|
+
2. Fixes reviewed
|
|
630
|
+
3. Blocking issues (if any)
|
|
631
|
+
4. Recommendations for improvement
|
|
632
|
+
|
|
633
|
+
Format as structured JSON.`;
|
|
634
|
+
const response = await this.callModel(prompt);
|
|
635
|
+
if (!response) {
|
|
636
|
+
return this.localReviewFallback(task);
|
|
637
|
+
}
|
|
638
|
+
try {
|
|
639
|
+
return JSON.parse(response);
|
|
640
|
+
}
|
|
641
|
+
catch {
|
|
642
|
+
return {
|
|
643
|
+
reviewId: crypto.randomUUID(),
|
|
644
|
+
review: response,
|
|
645
|
+
...this.localReviewFallback(task),
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Local fallback: examine file content for error handling, null safety,
|
|
651
|
+
* TypeScript type coverage, naming hygiene, and hardcoded values.
|
|
652
|
+
* Derives a real approved/rejected decision — never hardcodes "approved".
|
|
653
|
+
*/
|
|
654
|
+
localReviewFallback(task) {
|
|
655
|
+
const analysisOutput = task.context.metadata?.analyze_output;
|
|
656
|
+
const files = task.context.files;
|
|
657
|
+
const fixesReviewed = [];
|
|
658
|
+
const blockingIssues = [];
|
|
659
|
+
const recommendations = [];
|
|
660
|
+
let assessmentPoints = 0;
|
|
661
|
+
let maxPoints = 0;
|
|
662
|
+
// --- Review analysis output if present ---
|
|
663
|
+
if (analysisOutput && typeof analysisOutput === "object") {
|
|
664
|
+
const analysis = analysisOutput;
|
|
665
|
+
const issues = analysis.issues || [];
|
|
666
|
+
for (const issue of issues) {
|
|
667
|
+
const severity = issue.severity || "low";
|
|
668
|
+
if (severity === "high" || severity === "critical") {
|
|
669
|
+
blockingIssues.push({
|
|
670
|
+
severity,
|
|
671
|
+
description: issue.description ||
|
|
672
|
+
"High severity issue from analysis",
|
|
673
|
+
file: issue.file,
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (issues.length > 0) {
|
|
678
|
+
fixesReviewed.push({
|
|
679
|
+
file: "analysis",
|
|
680
|
+
status: blockingIssues.length > 0 ? "needs_attention" : "reviewed",
|
|
681
|
+
notes: `Analysis found ${issues.length} issue(s), ${blockingIssues.length} blocking`,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
// --- Per-file quality checks ---
|
|
686
|
+
for (const file of files) {
|
|
687
|
+
const content = file.content || "";
|
|
688
|
+
const lines = content.split("\n");
|
|
689
|
+
const fileIssues = [];
|
|
690
|
+
maxPoints += 5; // 5 checks per file
|
|
691
|
+
// 1. Error handling
|
|
692
|
+
const hasAsync = /\basync\b/.test(content);
|
|
693
|
+
const hasTryCatch = /\btry\s*\{/.test(content);
|
|
694
|
+
const hasCatch = /\.catch\s*\(/.test(content);
|
|
695
|
+
if (hasAsync && !hasTryCatch && !hasCatch) {
|
|
696
|
+
fileIssues.push("Missing error handling for async operations");
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
assessmentPoints++;
|
|
700
|
+
}
|
|
701
|
+
// 2. TypeScript type safety
|
|
702
|
+
if (/\.tsx?$/.test(file.path)) {
|
|
703
|
+
const anyCount = (content.match(/:\s*any\b|as\s+any\b/g) || []).length;
|
|
704
|
+
if (anyCount > 3) {
|
|
705
|
+
fileIssues.push(`Excessive 'any' types (${anyCount} occurrences) — weakens type safety`);
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
assessmentPoints++;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
assessmentPoints++;
|
|
713
|
+
}
|
|
714
|
+
// 3. Naming conventions
|
|
715
|
+
const badNames = content.match(/\b(?:const|let|var)\s+(?:[a-z]|_\d)\s*=/g) || [];
|
|
716
|
+
const singleCharVars = badNames.filter((m) => /\s[a-z_]\s*=/.test(m) && !/\b(i|j|k|x|y|z|e|_)\s*=/.test(m));
|
|
717
|
+
if (singleCharVars.length > 3) {
|
|
718
|
+
fileIssues.push("Multiple single-character variable names — use descriptive names");
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
assessmentPoints++;
|
|
722
|
+
}
|
|
723
|
+
// 4. Null/undefined safety
|
|
724
|
+
const nullAccess = (content.match(/\.\w+\.\w+/g) || []).length;
|
|
725
|
+
const optionalChaining = (content.match(/\?\./g) || []).length;
|
|
726
|
+
const nullChecks = (content.match(/!==?\s*(?:null|undefined)|typeof\s+\w+\s*!==?\s*["']undefined["']/g) || []).length;
|
|
727
|
+
if (nullAccess > 10 && optionalChaining === 0 && nullChecks < 2) {
|
|
728
|
+
fileIssues.push("Multiple property chains without null checks — consider optional chaining (?.)");
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
assessmentPoints++;
|
|
732
|
+
}
|
|
733
|
+
// 5. Hardcoded magic numbers
|
|
734
|
+
const hardcoded = [];
|
|
735
|
+
for (let i = 0; i < lines.length; i++) {
|
|
736
|
+
if (/(?:timeout|delay|interval|limit|max|min|size|count|threshold)\s*[:=]\s*\d{3,}/i.test(lines[i])) {
|
|
737
|
+
hardcoded.push(`line ${i + 1}`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
if (hardcoded.length > 2) {
|
|
741
|
+
fileIssues.push(`${hardcoded.length} hardcoded magic numbers — extract as named constants`);
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
assessmentPoints++;
|
|
745
|
+
}
|
|
746
|
+
if (fileIssues.length > 0) {
|
|
747
|
+
fixesReviewed.push({
|
|
748
|
+
file: file.path,
|
|
749
|
+
status: fileIssues.some((i) => i.includes("error handling") || i.includes("type safety"))
|
|
750
|
+
? "needs_attention"
|
|
751
|
+
: "minor_issues",
|
|
752
|
+
notes: fileIssues.join("; "),
|
|
753
|
+
});
|
|
754
|
+
for (const issue of fileIssues) {
|
|
755
|
+
recommendations.push(`[${file.path}] ${issue}`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
fixesReviewed.push({
|
|
760
|
+
file: file.path,
|
|
761
|
+
status: "passed",
|
|
762
|
+
notes: "No significant quality issues detected",
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
// Confidence derived from how many checks passed
|
|
767
|
+
const confidence = maxPoints > 0
|
|
768
|
+
? Math.round((assessmentPoints / maxPoints) * 100) / 100
|
|
769
|
+
: 0.3;
|
|
770
|
+
// Real overall assessment — not hardcoded
|
|
771
|
+
let overallAssessment;
|
|
772
|
+
if (blockingIssues.length > 0) {
|
|
773
|
+
overallAssessment = `Blocking issues found: ${blockingIssues.length} high/critical issue(s) require attention before proceeding.`;
|
|
774
|
+
}
|
|
775
|
+
else if (recommendations.length > 3) {
|
|
776
|
+
overallAssessment = `Code has ${recommendations.length} quality concerns that should be addressed. Review the recommendations for improvements.`;
|
|
777
|
+
}
|
|
778
|
+
else if (recommendations.length > 0) {
|
|
779
|
+
overallAssessment = `Code is generally acceptable with ${recommendations.length} minor suggestion(s). Consider addressing them in a follow-up.`;
|
|
780
|
+
}
|
|
781
|
+
else if (files.length === 0) {
|
|
782
|
+
overallAssessment =
|
|
783
|
+
"No files provided for review. Unable to perform meaningful analysis.";
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
overallAssessment = `Code passed basic quality checks across ${files.length} file(s). No significant issues detected.`;
|
|
787
|
+
}
|
|
788
|
+
if (files.length > 0) {
|
|
789
|
+
const totalLOC = files.reduce((s, f) => s + (f.content || "").split("\n").length, 0);
|
|
790
|
+
if (totalLOC > 500 && !files.some((f) => /test|spec/i.test(f.path))) {
|
|
791
|
+
recommendations.push("Consider adding unit tests — no test files detected for this codebase");
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return {
|
|
795
|
+
reviewId: crypto.randomUUID(),
|
|
796
|
+
fixesReviewed,
|
|
797
|
+
overallAssessment,
|
|
798
|
+
blockingIssues,
|
|
799
|
+
recommendations,
|
|
800
|
+
confidence,
|
|
801
|
+
qualityScore: maxPoints > 0 ? `${assessmentPoints}/${maxPoints}` : "N/A",
|
|
802
|
+
note: "Review performed via local static analysis (model unavailable)",
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
// ============================================================================
|
|
807
|
+
// Gated Rewriter Agent
|
|
808
|
+
// ============================================================================
|
|
809
|
+
export class GatedRewriterAgent extends GatedSubAgent {
|
|
810
|
+
constructor(messageBus, toolBridge, config) {
|
|
811
|
+
super("rewriter", config || {}, messageBus, toolBridge, {
|
|
812
|
+
maxRetries: 3,
|
|
813
|
+
strictMode: true,
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
async performTask(task) {
|
|
817
|
+
this.updateStatus("running", "Applying fixes");
|
|
818
|
+
const analysisOutput = task.context.metadata?.analyze_output;
|
|
819
|
+
const reviewOutput = task.context.metadata?.review_output;
|
|
820
|
+
const files = task.context.files;
|
|
821
|
+
const fileContents = files
|
|
822
|
+
.map((f) => `File: ${f.path}\n\n${f.content}`)
|
|
823
|
+
.join("\n\n---\n\n");
|
|
824
|
+
const prompt = `Apply fixes to the following code based on the analysis and review:
|
|
825
|
+
|
|
826
|
+
Analysis: ${JSON.stringify(analysisOutput, null, 2)}
|
|
827
|
+
|
|
828
|
+
Review: ${JSON.stringify(reviewOutput, null, 2)}
|
|
829
|
+
|
|
830
|
+
Task: ${task.prompt}
|
|
831
|
+
|
|
832
|
+
Code Files:
|
|
833
|
+
${fileContents}
|
|
834
|
+
|
|
835
|
+
Please provide:
|
|
836
|
+
1. Specific code changes needed
|
|
837
|
+
2. New files to create (if any)
|
|
838
|
+
3. Files to delete (if any)
|
|
839
|
+
4. Explanation of each change
|
|
840
|
+
|
|
841
|
+
Format as structured JSON with file paths as keys and change objects as values.`;
|
|
842
|
+
const response = await this.callModel(prompt);
|
|
843
|
+
if (!response) {
|
|
844
|
+
return this.localRewriteFallback(files);
|
|
845
|
+
}
|
|
846
|
+
try {
|
|
847
|
+
return JSON.parse(response);
|
|
848
|
+
}
|
|
849
|
+
catch {
|
|
850
|
+
return {
|
|
851
|
+
changes: [],
|
|
852
|
+
newFiles: [],
|
|
853
|
+
deletedFiles: [],
|
|
854
|
+
explanation: response,
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Local fallback: apply deterministic code improvements without a model.
|
|
860
|
+
* Transformations: trailing whitespace, CRLF→LF, mixed indentation,
|
|
861
|
+
* missing semicolons (JS/TS), alphabetical import sorting, collapsed
|
|
862
|
+
* blank lines, trailing newline. Returns actually-transformed code.
|
|
863
|
+
*/
|
|
864
|
+
localRewriteFallback(files) {
|
|
865
|
+
const changes = [];
|
|
866
|
+
for (const file of files) {
|
|
867
|
+
const content = file.content || "";
|
|
868
|
+
if (!content.trim())
|
|
869
|
+
continue;
|
|
870
|
+
const transformations = [];
|
|
871
|
+
let result = content;
|
|
872
|
+
// 1. Remove trailing whitespace
|
|
873
|
+
const beforeTrailing = result;
|
|
874
|
+
result = result.replace(/[ \t]+$/gm, "");
|
|
875
|
+
if (result !== beforeTrailing) {
|
|
876
|
+
transformations.push("Removed trailing whitespace");
|
|
877
|
+
}
|
|
878
|
+
// 2. Normalize CRLF → LF
|
|
879
|
+
if (result.includes("\r\n")) {
|
|
880
|
+
result = result.replace(/\r\n/g, "\n");
|
|
881
|
+
transformations.push("Normalized line endings (CRLF → LF)");
|
|
882
|
+
}
|
|
883
|
+
// 3. Mixed indentation: convert tabs to 2-space when spaces dominate
|
|
884
|
+
const ls = result.split("\n");
|
|
885
|
+
const tabLines = ls.filter((l) => l.startsWith("\t")).length;
|
|
886
|
+
const spaceLines = ls.filter((l) => /^ {2,}/.test(l)).length;
|
|
887
|
+
if (tabLines > 0 && spaceLines > tabLines * 2) {
|
|
888
|
+
result = result.replace(/^\t+/gm, (m) => " ".repeat(m.length));
|
|
889
|
+
transformations.push("Converted tab indentation to 2-space");
|
|
890
|
+
}
|
|
891
|
+
// 4. Add missing semicolons for JS/TS files
|
|
892
|
+
if (/\.[jt]sx?$/.test(file.path)) {
|
|
893
|
+
const resultLines = result.split("\n");
|
|
894
|
+
const semiFixed = resultLines.map((line) => {
|
|
895
|
+
const trimmed = line.trim();
|
|
896
|
+
if (!trimmed ||
|
|
897
|
+
trimmed.endsWith(";") ||
|
|
898
|
+
trimmed.endsWith("{") ||
|
|
899
|
+
trimmed.endsWith("}") ||
|
|
900
|
+
trimmed.endsWith("(") ||
|
|
901
|
+
trimmed.endsWith(",") ||
|
|
902
|
+
trimmed.endsWith("*/") ||
|
|
903
|
+
trimmed.startsWith("//") ||
|
|
904
|
+
trimmed.startsWith("/*") ||
|
|
905
|
+
trimmed.startsWith("*") ||
|
|
906
|
+
trimmed.startsWith("import ") ||
|
|
907
|
+
trimmed.startsWith("export ") ||
|
|
908
|
+
/^(?:if|else|for|while|switch|case|default|try|catch|finally|do|class|interface|type|enum)\b/.test(trimmed) ||
|
|
909
|
+
trimmed.endsWith("=>") ||
|
|
910
|
+
trimmed.endsWith("?") ||
|
|
911
|
+
trimmed.endsWith(":") ||
|
|
912
|
+
trimmed.endsWith("||") ||
|
|
913
|
+
trimmed.endsWith("&&")) {
|
|
914
|
+
return line;
|
|
915
|
+
}
|
|
916
|
+
if (/(?:\)|\]|['"`]|\w)\s*$/.test(trimmed)) {
|
|
917
|
+
if (/^(?:return|throw|const|let|var|yield)\b/.test(trimmed) ||
|
|
918
|
+
/^[\w$]+\s*[+\-*/]?=\s/.test(trimmed) ||
|
|
919
|
+
/^(?:await\s+)?[\w$]+\s*\(/.test(trimmed)) {
|
|
920
|
+
return line.replace(/\s*$/, ";");
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return line;
|
|
924
|
+
});
|
|
925
|
+
const semiResult = semiFixed.join("\n");
|
|
926
|
+
if (semiResult !== result) {
|
|
927
|
+
result = semiResult;
|
|
928
|
+
transformations.push("Added missing semicolons to statement lines");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
// 5. Sort imports alphabetically (JS/TS)
|
|
932
|
+
if (/\.[jt]sx?$/.test(file.path)) {
|
|
933
|
+
const sorted = this.sortImports(result);
|
|
934
|
+
if (sorted !== result) {
|
|
935
|
+
result = sorted;
|
|
936
|
+
transformations.push("Sorted import statements alphabetically");
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
// 6. Collapse excessive blank lines (>2 consecutive)
|
|
940
|
+
const beforeBlank = result;
|
|
941
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
942
|
+
if (result !== beforeBlank) {
|
|
943
|
+
transformations.push("Collapsed excessive blank lines");
|
|
944
|
+
}
|
|
945
|
+
// 7. Ensure trailing newline
|
|
946
|
+
if (!result.endsWith("\n")) {
|
|
947
|
+
result += "\n";
|
|
948
|
+
transformations.push("Added trailing newline");
|
|
949
|
+
}
|
|
950
|
+
if (transformations.length > 0) {
|
|
951
|
+
changes.push({
|
|
952
|
+
file: file.path,
|
|
953
|
+
original: content,
|
|
954
|
+
rewritten: result,
|
|
955
|
+
transformations,
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return {
|
|
960
|
+
changes,
|
|
961
|
+
newFiles: [],
|
|
962
|
+
deletedFiles: [],
|
|
963
|
+
summary: changes.length > 0
|
|
964
|
+
? `Applied basic formatting transformations to ${changes.length} file(s): ${changes.map((c) => c.transformations.join(", ")).join("; ")}`
|
|
965
|
+
: "No transformations needed — files already well-formatted",
|
|
966
|
+
note: "Only basic static code formatting applied (model unavailable for semantic rewrites)",
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Sort import statement groups alphabetically by module specifier.
|
|
971
|
+
*/
|
|
972
|
+
sortImports(content) {
|
|
973
|
+
const lines = content.split("\n");
|
|
974
|
+
const importGroups = [];
|
|
975
|
+
let groupStart = -1;
|
|
976
|
+
let currentImports = [];
|
|
977
|
+
for (let i = 0; i < lines.length; i++) {
|
|
978
|
+
const trimmed = lines[i].trim();
|
|
979
|
+
if (/^import\s/.test(trimmed)) {
|
|
980
|
+
if (groupStart === -1)
|
|
981
|
+
groupStart = i;
|
|
982
|
+
currentImports.push(lines[i]);
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
if (currentImports.length > 1) {
|
|
986
|
+
importGroups.push({
|
|
987
|
+
start: groupStart,
|
|
988
|
+
end: groupStart + currentImports.length - 1,
|
|
989
|
+
imports: [...currentImports],
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
groupStart = -1;
|
|
993
|
+
currentImports = [];
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (currentImports.length > 1) {
|
|
997
|
+
importGroups.push({
|
|
998
|
+
start: groupStart,
|
|
999
|
+
end: groupStart + currentImports.length - 1,
|
|
1000
|
+
imports: [...currentImports],
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
if (importGroups.length === 0)
|
|
1004
|
+
return content;
|
|
1005
|
+
const resultLines = [...lines];
|
|
1006
|
+
let changed = false;
|
|
1007
|
+
for (const group of importGroups) {
|
|
1008
|
+
const sorted = [...group.imports].sort((a, b) => {
|
|
1009
|
+
const srcA = (a.match(/from\s+["']([^"']+)["']/) || [])[1] || a;
|
|
1010
|
+
const srcB = (b.match(/from\s+["']([^"']+)["']/) || [])[1] || b;
|
|
1011
|
+
return srcA.localeCompare(srcB);
|
|
1012
|
+
});
|
|
1013
|
+
const alreadySorted = sorted.every((line, idx) => line === group.imports[idx]);
|
|
1014
|
+
if (!alreadySorted) {
|
|
1015
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
1016
|
+
resultLines[group.start + i] = sorted[i];
|
|
1017
|
+
}
|
|
1018
|
+
changed = true;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return changed ? resultLines.join("\n") : content;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
// ============================================================================
|
|
1025
|
+
// Gated UI Designer Agent
|
|
1026
|
+
// ============================================================================
|
|
1027
|
+
export class GatedUiDesignerAgent extends GatedSubAgent {
|
|
1028
|
+
constructor(messageBus, toolBridge, config) {
|
|
1029
|
+
super("ui_designer", config || {}, messageBus, toolBridge, {
|
|
1030
|
+
maxRetries: 3,
|
|
1031
|
+
strictMode: false,
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
async performTask(task) {
|
|
1035
|
+
this.updateStatus("running", "Generating UI mockups");
|
|
1036
|
+
const files = task.context.files;
|
|
1037
|
+
const uiFiles = files.filter((f) => f.content.includes("div") ||
|
|
1038
|
+
f.content.includes("button") ||
|
|
1039
|
+
f.content.includes("<") ||
|
|
1040
|
+
/\.(tsx?|jsx?|html|svelte|vue)$/.test(f.path));
|
|
1041
|
+
if (uiFiles.length === 0) {
|
|
1042
|
+
return this.localUiDesignFallback([], task);
|
|
1043
|
+
}
|
|
1044
|
+
const uiContent = uiFiles
|
|
1045
|
+
.map((f) => `File: ${f.path}\n\n${f.content}`)
|
|
1046
|
+
.join("\n\n---\n\n");
|
|
1047
|
+
const prompt = `Design UI improvements for the following code:
|
|
1048
|
+
|
|
1049
|
+
${uiContent}
|
|
1050
|
+
|
|
1051
|
+
Task: ${task.prompt}
|
|
1052
|
+
|
|
1053
|
+
Please provide:
|
|
1054
|
+
1. Detected UI frameworks
|
|
1055
|
+
2. ASCII mockups for new UI components
|
|
1056
|
+
3. Accessibility issues
|
|
1057
|
+
4. Design suggestions
|
|
1058
|
+
|
|
1059
|
+
Format as structured JSON.`;
|
|
1060
|
+
const response = await this.callModel(prompt);
|
|
1061
|
+
if (!response) {
|
|
1062
|
+
return this.localUiDesignFallback(uiFiles, task);
|
|
1063
|
+
}
|
|
1064
|
+
try {
|
|
1065
|
+
return JSON.parse(response);
|
|
1066
|
+
}
|
|
1067
|
+
catch {
|
|
1068
|
+
return {
|
|
1069
|
+
detectedFrameworks: this.detectFrameworks(uiFiles),
|
|
1070
|
+
mockups: [
|
|
1071
|
+
{
|
|
1072
|
+
type: "ascii",
|
|
1073
|
+
content: response,
|
|
1074
|
+
viewport: "desktop",
|
|
1075
|
+
},
|
|
1076
|
+
],
|
|
1077
|
+
accessibilityIssues: this.detectA11yIssues(uiFiles),
|
|
1078
|
+
designSuggestions: [],
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Local fallback: parse the task prompt for UI component keywords,
|
|
1084
|
+
* detect the framework from file content, scan for ARIA/accessibility
|
|
1085
|
+
* issues, and return a real semantic HTML or React component scaffold
|
|
1086
|
+
* with an ASCII mockup. Never returns placeholder strings.
|
|
1087
|
+
*/
|
|
1088
|
+
localUiDesignFallback(uiFiles, task) {
|
|
1089
|
+
const prompt = task.prompt || "";
|
|
1090
|
+
const lower = prompt.toLowerCase();
|
|
1091
|
+
const frameworks = this.detectFrameworks(uiFiles);
|
|
1092
|
+
// Map task keywords to component types
|
|
1093
|
+
const uiKeywords = {
|
|
1094
|
+
form: [
|
|
1095
|
+
"form",
|
|
1096
|
+
"input",
|
|
1097
|
+
"submit",
|
|
1098
|
+
"field",
|
|
1099
|
+
"validation",
|
|
1100
|
+
"textarea",
|
|
1101
|
+
"select",
|
|
1102
|
+
],
|
|
1103
|
+
button: ["button", "btn", "click", "action", "cta"],
|
|
1104
|
+
layout: [
|
|
1105
|
+
"layout",
|
|
1106
|
+
"grid",
|
|
1107
|
+
"flex",
|
|
1108
|
+
"container",
|
|
1109
|
+
"wrapper",
|
|
1110
|
+
"sidebar",
|
|
1111
|
+
"header",
|
|
1112
|
+
"footer",
|
|
1113
|
+
],
|
|
1114
|
+
page: ["page", "view", "screen", "route", "dashboard"],
|
|
1115
|
+
modal: ["modal", "dialog", "popup", "overlay"],
|
|
1116
|
+
list: ["list", "table", "grid", "card", "item", "row"],
|
|
1117
|
+
nav: ["nav", "navigation", "menu", "breadcrumb", "tab", "sidebar"],
|
|
1118
|
+
};
|
|
1119
|
+
const detectedTypes = [];
|
|
1120
|
+
for (const [type, keywords] of Object.entries(uiKeywords)) {
|
|
1121
|
+
if (keywords.some((kw) => lower.includes(kw))) {
|
|
1122
|
+
detectedTypes.push(type);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (detectedTypes.length === 0)
|
|
1126
|
+
detectedTypes.push("component");
|
|
1127
|
+
const accessibilityIssues = this.detectA11yIssues(uiFiles);
|
|
1128
|
+
const primaryType = detectedTypes[0];
|
|
1129
|
+
const componentName = this.inferComponentName(prompt);
|
|
1130
|
+
const isReact = frameworks.includes("react") || frameworks.includes("next.js");
|
|
1131
|
+
let componentCode;
|
|
1132
|
+
let asciiMockup;
|
|
1133
|
+
switch (primaryType) {
|
|
1134
|
+
case "form":
|
|
1135
|
+
asciiMockup = [
|
|
1136
|
+
`+${"─".repeat(40)}+`,
|
|
1137
|
+
`│ ${componentName.padEnd(38)}│`,
|
|
1138
|
+
`│${"─".repeat(40)}│`,
|
|
1139
|
+
`│ Label: │`,
|
|
1140
|
+
`│ ┌──────────────────────────────────┐ │`,
|
|
1141
|
+
`│ │ Input field │ │`,
|
|
1142
|
+
`│ └──────────────────────────────────┘ │`,
|
|
1143
|
+
`│ Label: │`,
|
|
1144
|
+
`│ ┌──────────────────────────────────┐ │`,
|
|
1145
|
+
`│ │ Input field │ │`,
|
|
1146
|
+
`│ └──────────────────────────────────┘ │`,
|
|
1147
|
+
`│ │`,
|
|
1148
|
+
`│ ┌──────────────────────┐ │`,
|
|
1149
|
+
`│ │ Submit │ │`,
|
|
1150
|
+
`│ └──────────────────────┘ │`,
|
|
1151
|
+
`+${"─".repeat(40)}+`,
|
|
1152
|
+
].join("\n");
|
|
1153
|
+
componentCode = isReact
|
|
1154
|
+
? this.generateReactForm(componentName)
|
|
1155
|
+
: this.generateHtmlForm(componentName);
|
|
1156
|
+
break;
|
|
1157
|
+
case "modal":
|
|
1158
|
+
asciiMockup = [
|
|
1159
|
+
` ┌${"─".repeat(36)}┐`,
|
|
1160
|
+
` │ ${componentName.padEnd(30)} [X] │`,
|
|
1161
|
+
` │${"─".repeat(36)}│`,
|
|
1162
|
+
` │ │`,
|
|
1163
|
+
` │ Content area │`,
|
|
1164
|
+
` │ │`,
|
|
1165
|
+
` │${"─".repeat(36)}│`,
|
|
1166
|
+
` │ [Cancel] [Confirm] │`,
|
|
1167
|
+
` └${"─".repeat(36)}┘`,
|
|
1168
|
+
].join("\n");
|
|
1169
|
+
componentCode = isReact
|
|
1170
|
+
? this.generateReactModal(componentName)
|
|
1171
|
+
: this.generateHtmlModal(componentName);
|
|
1172
|
+
break;
|
|
1173
|
+
case "nav":
|
|
1174
|
+
asciiMockup = [
|
|
1175
|
+
`+${"─".repeat(50)}+`,
|
|
1176
|
+
`│ Logo │ Home │ About │ Contact │ ☰ │`,
|
|
1177
|
+
`+${"─".repeat(50)}+`,
|
|
1178
|
+
].join("\n");
|
|
1179
|
+
componentCode = isReact
|
|
1180
|
+
? this.generateReactNav(componentName)
|
|
1181
|
+
: `<nav role="navigation" aria-label="Main navigation">\n <ul>\n <li><a href="/">Home</a></li>\n <li><a href="/about">About</a></li>\n <li><a href="/contact">Contact</a></li>\n </ul>\n</nav>`;
|
|
1182
|
+
break;
|
|
1183
|
+
case "list":
|
|
1184
|
+
asciiMockup = [
|
|
1185
|
+
`+${"─".repeat(42)}+`,
|
|
1186
|
+
`│ ${componentName.padEnd(40)}│`,
|
|
1187
|
+
`│${"─".repeat(42)}│`,
|
|
1188
|
+
`│ ┌──────────────────────────────────┐ │`,
|
|
1189
|
+
`│ │ Item 1 [Action] │ │`,
|
|
1190
|
+
`│ ├──────────────────────────────────┤ │`,
|
|
1191
|
+
`│ │ Item 2 [Action] │ │`,
|
|
1192
|
+
`│ ├──────────────────────────────────┤ │`,
|
|
1193
|
+
`│ │ Item 3 [Action] │ │`,
|
|
1194
|
+
`│ └──────────────────────────────────┘ │`,
|
|
1195
|
+
`+${"─".repeat(42)}+`,
|
|
1196
|
+
].join("\n");
|
|
1197
|
+
componentCode = isReact
|
|
1198
|
+
? this.generateReactList(componentName)
|
|
1199
|
+
: `<section aria-label="${componentName}">\n <h2>${componentName}</h2>\n <ul role="list">\n <li role="listitem">Item 1</li>\n <li role="listitem">Item 2</li>\n </ul>\n</section>`;
|
|
1200
|
+
break;
|
|
1201
|
+
default: // generic component / page / layout / button
|
|
1202
|
+
asciiMockup = [
|
|
1203
|
+
`+${"─".repeat(44)}+`,
|
|
1204
|
+
`│ ${componentName.padEnd(42)}│`,
|
|
1205
|
+
`│${"─".repeat(44)}│`,
|
|
1206
|
+
`│ │`,
|
|
1207
|
+
`│ ┌────────────────────────────────────────┐│`,
|
|
1208
|
+
`│ │ ││`,
|
|
1209
|
+
`│ │ Main Content Area ││`,
|
|
1210
|
+
`│ │ ││`,
|
|
1211
|
+
`│ └────────────────────────────────────────┘│`,
|
|
1212
|
+
`│ │`,
|
|
1213
|
+
`│ [Primary Action] [Secondary Action] │`,
|
|
1214
|
+
`+${"─".repeat(44)}+`,
|
|
1215
|
+
].join("\n");
|
|
1216
|
+
componentCode = isReact
|
|
1217
|
+
? this.generateReactComponent(componentName)
|
|
1218
|
+
: `<section aria-label="${componentName}">\n <h2>${componentName}</h2>\n <div class="content">\n <p>Main content area</p>\n </div>\n <div class="actions">\n <button type="button">Primary Action</button>\n </div>\n</section>`;
|
|
1219
|
+
}
|
|
1220
|
+
// Design suggestions based on what's missing in the existing files
|
|
1221
|
+
const designSuggestions = [];
|
|
1222
|
+
if (!uiFiles.some((f) => f.content.includes("@media") || f.content.includes("responsive"))) {
|
|
1223
|
+
designSuggestions.push("Add responsive breakpoints for mobile/tablet viewports");
|
|
1224
|
+
}
|
|
1225
|
+
if (!uiFiles.some((f) => f.content.includes("dark") || f.content.includes("theme"))) {
|
|
1226
|
+
designSuggestions.push("Consider implementing dark mode / theme support");
|
|
1227
|
+
}
|
|
1228
|
+
if (!uiFiles.some((f) => /loading|spinner|skeleton/i.test(f.content))) {
|
|
1229
|
+
designSuggestions.push("Add loading states (skeleton screens or spinners) for async content");
|
|
1230
|
+
}
|
|
1231
|
+
if (!uiFiles.some((f) => /error|fallback/i.test(f.content))) {
|
|
1232
|
+
designSuggestions.push("Add error boundary and fallback UI for graceful error handling");
|
|
1233
|
+
}
|
|
1234
|
+
designSuggestions.push("Ensure consistent spacing and typography using design tokens");
|
|
1235
|
+
return {
|
|
1236
|
+
detectedFrameworks: frameworks,
|
|
1237
|
+
detectedComponentTypes: detectedTypes,
|
|
1238
|
+
mockups: [
|
|
1239
|
+
{
|
|
1240
|
+
type: "ascii",
|
|
1241
|
+
content: asciiMockup,
|
|
1242
|
+
viewport: "desktop",
|
|
1243
|
+
componentName,
|
|
1244
|
+
},
|
|
1245
|
+
],
|
|
1246
|
+
generatedComponent: {
|
|
1247
|
+
name: componentName,
|
|
1248
|
+
code: componentCode,
|
|
1249
|
+
framework: isReact ? "React/TSX" : "HTML",
|
|
1250
|
+
},
|
|
1251
|
+
accessibilityIssues,
|
|
1252
|
+
designSuggestions,
|
|
1253
|
+
note: "UI design generated via local keyword analysis (model unavailable)",
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
/** Detect UI frameworks from file content. */
|
|
1257
|
+
detectFrameworks(files) {
|
|
1258
|
+
const frameworks = new Set();
|
|
1259
|
+
for (const file of files) {
|
|
1260
|
+
const c = file.content || "";
|
|
1261
|
+
if (/\bimport\s.*from\s+["']react["']|React\.createElement|jsx/i.test(c))
|
|
1262
|
+
frameworks.add("react");
|
|
1263
|
+
if (/\bimport\s.*from\s+["']next/i.test(c) ||
|
|
1264
|
+
/\bgetServerSideProps|getStaticProps\b/.test(c))
|
|
1265
|
+
frameworks.add("next.js");
|
|
1266
|
+
if (/\bimport\s.*from\s+["']vue["']|createApp|defineComponent/.test(c))
|
|
1267
|
+
frameworks.add("vue");
|
|
1268
|
+
if (/\bimport\s.*from\s+["']svelte["']|<script\s+lang=["']ts["']/.test(c) &&
|
|
1269
|
+
file.path.endsWith(".svelte"))
|
|
1270
|
+
frameworks.add("svelte");
|
|
1271
|
+
if (/tailwind|className=["'][^"']*(?:flex|grid|p-|m-|text-)/.test(c))
|
|
1272
|
+
frameworks.add("tailwind");
|
|
1273
|
+
if (/\bimport\s.*from\s+["']@radix-ui/.test(c))
|
|
1274
|
+
frameworks.add("radix-ui");
|
|
1275
|
+
if (/\bimport\s.*from\s+["']@shadcn/.test(c) ||
|
|
1276
|
+
/\bimport\s.*from\s+["']@\/components\/ui/.test(c))
|
|
1277
|
+
frameworks.add("shadcn/ui");
|
|
1278
|
+
if (/<html|<body|<head/i.test(c))
|
|
1279
|
+
frameworks.add("html");
|
|
1280
|
+
}
|
|
1281
|
+
return [...frameworks];
|
|
1282
|
+
}
|
|
1283
|
+
/** Detect accessibility issues in UI files. */
|
|
1284
|
+
detectA11yIssues(files) {
|
|
1285
|
+
const issues = [];
|
|
1286
|
+
for (const file of files) {
|
|
1287
|
+
const c = file.content || "";
|
|
1288
|
+
if (/<img\b(?![^>]*\balt\b)/i.test(c)) {
|
|
1289
|
+
issues.push({
|
|
1290
|
+
file: file.path,
|
|
1291
|
+
issue: "Image element missing 'alt' attribute",
|
|
1292
|
+
severity: "high",
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
if (/<button[^>]*>\s*<(?:img|svg|icon)/i.test(c) &&
|
|
1296
|
+
!/<button[^>]*aria-label/i.test(c)) {
|
|
1297
|
+
issues.push({
|
|
1298
|
+
file: file.path,
|
|
1299
|
+
issue: "Icon-only button missing aria-label",
|
|
1300
|
+
severity: "high",
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
if (/\bonClick\b.*<(?:div|span|p)\b/i.test(c) &&
|
|
1304
|
+
!/role=["']button["']/i.test(c)) {
|
|
1305
|
+
issues.push({
|
|
1306
|
+
file: file.path,
|
|
1307
|
+
issue: "Click handler on non-interactive element without role='button'",
|
|
1308
|
+
severity: "medium",
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
if (/<input\b(?![^>]*aria-label)(?![^>]*id=["'](\w+)["'])/i.test(c)) {
|
|
1312
|
+
issues.push({
|
|
1313
|
+
file: file.path,
|
|
1314
|
+
issue: "Form input potentially missing associated label",
|
|
1315
|
+
severity: "medium",
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
if (/<html\b(?![^>]*\blang\b)/i.test(c)) {
|
|
1319
|
+
issues.push({
|
|
1320
|
+
file: file.path,
|
|
1321
|
+
issue: "HTML element missing 'lang' attribute",
|
|
1322
|
+
severity: "medium",
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
if (/color:\s*#(?:eee|ddd|ccc|fff|fafafa)/i.test(c)) {
|
|
1326
|
+
issues.push({
|
|
1327
|
+
file: file.path,
|
|
1328
|
+
issue: "Potentially low color contrast text detected",
|
|
1329
|
+
severity: "low",
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return issues;
|
|
1334
|
+
}
|
|
1335
|
+
/** Infer a PascalCase component name from the task prompt. */
|
|
1336
|
+
inferComponentName(prompt) {
|
|
1337
|
+
const match = prompt.match(/(?:create|build|generate|design|add)\s+(?:a\s+)?(\w[\w\s]{1,30}?)(?:\s+(?:component|page|form|modal|dialog|view|section|panel|widget))?$/i);
|
|
1338
|
+
if (match) {
|
|
1339
|
+
return match[1]
|
|
1340
|
+
.trim()
|
|
1341
|
+
.split(/\s+/)
|
|
1342
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
1343
|
+
.join("");
|
|
1344
|
+
}
|
|
1345
|
+
return "Component";
|
|
1346
|
+
}
|
|
1347
|
+
generateReactForm(name) {
|
|
1348
|
+
return `import React, { FormEvent, useState } from "react";
|
|
1349
|
+
|
|
1350
|
+
interface ${name}Props {
|
|
1351
|
+
onSubmit?: (data: Record<string, string>) => void;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
export function ${name}({ onSubmit }: ${name}Props) {
|
|
1355
|
+
const [formData, setFormData] = useState<Record<string, string>>({});
|
|
1356
|
+
|
|
1357
|
+
const handleSubmit = (e: FormEvent) => {
|
|
1358
|
+
e.preventDefault();
|
|
1359
|
+
onSubmit?.(formData);
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
return (
|
|
1363
|
+
<form onSubmit={handleSubmit} role="form" aria-label="${name}">
|
|
1364
|
+
<fieldset>
|
|
1365
|
+
<legend>${name}</legend>
|
|
1366
|
+
<div>
|
|
1367
|
+
<label htmlFor="${name.toLowerCase()}-field-1">Field 1</label>
|
|
1368
|
+
<input
|
|
1369
|
+
id="${name.toLowerCase()}-field-1"
|
|
1370
|
+
type="text"
|
|
1371
|
+
required
|
|
1372
|
+
aria-required="true"
|
|
1373
|
+
onChange={(e) => setFormData(prev => ({ ...prev, field1: e.target.value }))}
|
|
1374
|
+
/>
|
|
1375
|
+
</div>
|
|
1376
|
+
<div>
|
|
1377
|
+
<label htmlFor="${name.toLowerCase()}-field-2">Field 2</label>
|
|
1378
|
+
<input
|
|
1379
|
+
id="${name.toLowerCase()}-field-2"
|
|
1380
|
+
type="text"
|
|
1381
|
+
onChange={(e) => setFormData(prev => ({ ...prev, field2: e.target.value }))}
|
|
1382
|
+
/>
|
|
1383
|
+
</div>
|
|
1384
|
+
<button type="submit">Submit</button>
|
|
1385
|
+
</fieldset>
|
|
1386
|
+
</form>
|
|
1387
|
+
);
|
|
1388
|
+
}`;
|
|
1389
|
+
}
|
|
1390
|
+
generateHtmlForm(name) {
|
|
1391
|
+
return `<form role="form" aria-label="${name}">
|
|
1392
|
+
<fieldset>
|
|
1393
|
+
<legend>${name}</legend>
|
|
1394
|
+
<div>
|
|
1395
|
+
<label for="${name.toLowerCase()}-field-1">Field 1</label>
|
|
1396
|
+
<input id="${name.toLowerCase()}-field-1" type="text" required aria-required="true" />
|
|
1397
|
+
</div>
|
|
1398
|
+
<div>
|
|
1399
|
+
<label for="${name.toLowerCase()}-field-2">Field 2</label>
|
|
1400
|
+
<input id="${name.toLowerCase()}-field-2" type="text" />
|
|
1401
|
+
</div>
|
|
1402
|
+
<button type="submit">Submit</button>
|
|
1403
|
+
</fieldset>
|
|
1404
|
+
</form>`;
|
|
1405
|
+
}
|
|
1406
|
+
generateReactModal(name) {
|
|
1407
|
+
return `import React, { useEffect, useRef } from "react";
|
|
1408
|
+
|
|
1409
|
+
interface ${name}Props {
|
|
1410
|
+
isOpen: boolean;
|
|
1411
|
+
onClose: () => void;
|
|
1412
|
+
title: string;
|
|
1413
|
+
children: React.ReactNode;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
export function ${name}({ isOpen, onClose, title, children }: ${name}Props) {
|
|
1417
|
+
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
1418
|
+
|
|
1419
|
+
useEffect(() => {
|
|
1420
|
+
if (isOpen) {
|
|
1421
|
+
dialogRef.current?.showModal();
|
|
1422
|
+
} else {
|
|
1423
|
+
dialogRef.current?.close();
|
|
1424
|
+
}
|
|
1425
|
+
}, [isOpen]);
|
|
1426
|
+
|
|
1427
|
+
return (
|
|
1428
|
+
<dialog
|
|
1429
|
+
ref={dialogRef}
|
|
1430
|
+
role="dialog"
|
|
1431
|
+
aria-modal="true"
|
|
1432
|
+
aria-label={title}
|
|
1433
|
+
onClose={onClose}
|
|
1434
|
+
>
|
|
1435
|
+
<header>
|
|
1436
|
+
<h2>{title}</h2>
|
|
1437
|
+
<button type="button" onClick={onClose} aria-label="Close dialog">✕</button>
|
|
1438
|
+
</header>
|
|
1439
|
+
<div role="document">{children}</div>
|
|
1440
|
+
<footer>
|
|
1441
|
+
<button type="button" onClick={onClose}>Cancel</button>
|
|
1442
|
+
<button type="button">Confirm</button>
|
|
1443
|
+
</footer>
|
|
1444
|
+
</dialog>
|
|
1445
|
+
);
|
|
1446
|
+
}`;
|
|
1447
|
+
}
|
|
1448
|
+
generateHtmlModal(name) {
|
|
1449
|
+
return `<dialog role="dialog" aria-modal="true" aria-label="${name}">
|
|
1450
|
+
<header>
|
|
1451
|
+
<h2>${name}</h2>
|
|
1452
|
+
<button type="button" aria-label="Close dialog">✕</button>
|
|
1453
|
+
</header>
|
|
1454
|
+
<div role="document">
|
|
1455
|
+
<p>Modal content goes here</p>
|
|
1456
|
+
</div>
|
|
1457
|
+
<footer>
|
|
1458
|
+
<button type="button">Cancel</button>
|
|
1459
|
+
<button type="button">Confirm</button>
|
|
1460
|
+
</footer>
|
|
1461
|
+
</dialog>`;
|
|
1462
|
+
}
|
|
1463
|
+
generateReactNav(name) {
|
|
1464
|
+
return `import React from "react";
|
|
1465
|
+
|
|
1466
|
+
interface NavItem {
|
|
1467
|
+
label: string;
|
|
1468
|
+
href: string;
|
|
1469
|
+
active?: boolean;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
interface ${name}Props {
|
|
1473
|
+
items: NavItem[];
|
|
1474
|
+
logo?: React.ReactNode;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
export function ${name}({ items, logo }: ${name}Props) {
|
|
1478
|
+
return (
|
|
1479
|
+
<nav role="navigation" aria-label="Main navigation">
|
|
1480
|
+
{logo && <div aria-hidden="true">{logo}</div>}
|
|
1481
|
+
<ul role="menubar">
|
|
1482
|
+
{items.map((item) => (
|
|
1483
|
+
<li key={item.href} role="none">
|
|
1484
|
+
<a
|
|
1485
|
+
href={item.href}
|
|
1486
|
+
role="menuitem"
|
|
1487
|
+
aria-current={item.active ? "page" : undefined}
|
|
1488
|
+
>
|
|
1489
|
+
{item.label}
|
|
1490
|
+
</a>
|
|
1491
|
+
</li>
|
|
1492
|
+
))}
|
|
1493
|
+
</ul>
|
|
1494
|
+
</nav>
|
|
1495
|
+
);
|
|
1496
|
+
}`;
|
|
1497
|
+
}
|
|
1498
|
+
generateReactList(name) {
|
|
1499
|
+
return `import React from "react";
|
|
1500
|
+
|
|
1501
|
+
interface ListItem {
|
|
1502
|
+
id: string;
|
|
1503
|
+
title: string;
|
|
1504
|
+
description?: string;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
interface ${name}Props {
|
|
1508
|
+
items: ListItem[];
|
|
1509
|
+
onAction?: (id: string) => void;
|
|
1510
|
+
emptyMessage?: string;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
export function ${name}({ items, onAction, emptyMessage = "No items found" }: ${name}Props) {
|
|
1514
|
+
if (items.length === 0) {
|
|
1515
|
+
return (
|
|
1516
|
+
<section aria-label="${name}">
|
|
1517
|
+
<p role="status">{emptyMessage}</p>
|
|
1518
|
+
</section>
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
return (
|
|
1523
|
+
<section aria-label="${name}">
|
|
1524
|
+
<h2>${name}</h2>
|
|
1525
|
+
<ul role="list">
|
|
1526
|
+
{items.map((item) => (
|
|
1527
|
+
<li key={item.id} role="listitem">
|
|
1528
|
+
<div>
|
|
1529
|
+
<h3>{item.title}</h3>
|
|
1530
|
+
{item.description && <p>{item.description}</p>}
|
|
1531
|
+
</div>
|
|
1532
|
+
{onAction && (
|
|
1533
|
+
<button
|
|
1534
|
+
type="button"
|
|
1535
|
+
onClick={() => onAction(item.id)}
|
|
1536
|
+
aria-label={\`Action for \${item.title}\`}
|
|
1537
|
+
>
|
|
1538
|
+
Action
|
|
1539
|
+
</button>
|
|
1540
|
+
)}
|
|
1541
|
+
</li>
|
|
1542
|
+
))}
|
|
1543
|
+
</ul>
|
|
1544
|
+
</section>
|
|
1545
|
+
);
|
|
1546
|
+
}`;
|
|
1547
|
+
}
|
|
1548
|
+
generateReactComponent(name) {
|
|
1549
|
+
return `import React from "react";
|
|
1550
|
+
|
|
1551
|
+
interface ${name}Props {
|
|
1552
|
+
title?: string;
|
|
1553
|
+
children?: React.ReactNode;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
export function ${name}({ title = "${name}", children }: ${name}Props) {
|
|
1557
|
+
return (
|
|
1558
|
+
<section aria-label={title}>
|
|
1559
|
+
<header>
|
|
1560
|
+
<h2>{title}</h2>
|
|
1561
|
+
</header>
|
|
1562
|
+
<div role="region" aria-label="Content">
|
|
1563
|
+
{children || <p>Main content area</p>}
|
|
1564
|
+
</div>
|
|
1565
|
+
<footer>
|
|
1566
|
+
<button type="button">Primary Action</button>
|
|
1567
|
+
</footer>
|
|
1568
|
+
</section>
|
|
1569
|
+
);
|
|
1570
|
+
}`;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
// ============================================================================
|
|
1574
|
+
// Gated Researcher Agent
|
|
1575
|
+
// ============================================================================
|
|
1576
|
+
export class GatedResearcherAgent extends GatedSubAgent {
|
|
1577
|
+
constructor(messageBus, toolBridge, config) {
|
|
1578
|
+
super("researcher", config || {}, messageBus, toolBridge, {
|
|
1579
|
+
maxRetries: 3,
|
|
1580
|
+
strictMode: false,
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
async performTask(task) {
|
|
1584
|
+
this.updateStatus("running", "Researching information");
|
|
1585
|
+
const query = task.prompt;
|
|
1586
|
+
let webResult = null;
|
|
1587
|
+
try {
|
|
1588
|
+
webResult = await this.webSearch(query, 3);
|
|
1589
|
+
}
|
|
1590
|
+
catch (error) {
|
|
1591
|
+
console.warn(`Web search failed for query "${query}": ${error}`);
|
|
1592
|
+
}
|
|
1593
|
+
if (webResult) {
|
|
1594
|
+
const analysisPrompt = `Analyze the following web search results for the query "${query}":
|
|
1595
|
+
|
|
1596
|
+
${webResult}
|
|
1597
|
+
|
|
1598
|
+
Please extract:
|
|
1599
|
+
1. Key findings
|
|
1600
|
+
2. Verified facts
|
|
1601
|
+
3. Relevant sources
|
|
1602
|
+
4. Summary
|
|
1603
|
+
|
|
1604
|
+
Format as structured JSON.`;
|
|
1605
|
+
const analysisResponse = await this.callModel(analysisPrompt);
|
|
1606
|
+
if (analysisResponse) {
|
|
1607
|
+
try {
|
|
1608
|
+
return JSON.parse(analysisResponse);
|
|
1609
|
+
}
|
|
1610
|
+
catch {
|
|
1611
|
+
// Fall through to local fallback with web results included
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
return this.localResearchFallback(query, webResult, task);
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Local fallback: extract key terms from the query, scan context files
|
|
1619
|
+
* for those terms, identify technical concepts by category, and return
|
|
1620
|
+
* real findings with file paths, matched topics, and surrounding snippets.
|
|
1621
|
+
* Makes clear that model-assisted deep research was unavailable.
|
|
1622
|
+
*/
|
|
1623
|
+
localResearchFallback(query, webResult, task) {
|
|
1624
|
+
// --- Extract key topics, filtering stop words ---
|
|
1625
|
+
const stopWords = new Set([
|
|
1626
|
+
"the",
|
|
1627
|
+
"a",
|
|
1628
|
+
"an",
|
|
1629
|
+
"and",
|
|
1630
|
+
"or",
|
|
1631
|
+
"but",
|
|
1632
|
+
"in",
|
|
1633
|
+
"on",
|
|
1634
|
+
"at",
|
|
1635
|
+
"to",
|
|
1636
|
+
"for",
|
|
1637
|
+
"of",
|
|
1638
|
+
"with",
|
|
1639
|
+
"by",
|
|
1640
|
+
"from",
|
|
1641
|
+
"is",
|
|
1642
|
+
"are",
|
|
1643
|
+
"was",
|
|
1644
|
+
"were",
|
|
1645
|
+
"be",
|
|
1646
|
+
"been",
|
|
1647
|
+
"being",
|
|
1648
|
+
"have",
|
|
1649
|
+
"has",
|
|
1650
|
+
"had",
|
|
1651
|
+
"do",
|
|
1652
|
+
"does",
|
|
1653
|
+
"did",
|
|
1654
|
+
"will",
|
|
1655
|
+
"would",
|
|
1656
|
+
"could",
|
|
1657
|
+
"should",
|
|
1658
|
+
"may",
|
|
1659
|
+
"might",
|
|
1660
|
+
"must",
|
|
1661
|
+
"can",
|
|
1662
|
+
"this",
|
|
1663
|
+
"that",
|
|
1664
|
+
"these",
|
|
1665
|
+
"those",
|
|
1666
|
+
"what",
|
|
1667
|
+
"how",
|
|
1668
|
+
"why",
|
|
1669
|
+
"when",
|
|
1670
|
+
"where",
|
|
1671
|
+
"which",
|
|
1672
|
+
"who",
|
|
1673
|
+
"about",
|
|
1674
|
+
"into",
|
|
1675
|
+
"through",
|
|
1676
|
+
"during",
|
|
1677
|
+
"before",
|
|
1678
|
+
"after",
|
|
1679
|
+
"it",
|
|
1680
|
+
"its",
|
|
1681
|
+
"my",
|
|
1682
|
+
"your",
|
|
1683
|
+
"we",
|
|
1684
|
+
"they",
|
|
1685
|
+
"them",
|
|
1686
|
+
"our",
|
|
1687
|
+
"not",
|
|
1688
|
+
"no",
|
|
1689
|
+
"if",
|
|
1690
|
+
]);
|
|
1691
|
+
const words = query
|
|
1692
|
+
.split(/\W+/)
|
|
1693
|
+
.filter((w) => w.length > 2 && !stopWords.has(w.toLowerCase()));
|
|
1694
|
+
const topics = [...new Set(words.map((w) => w.toLowerCase()))];
|
|
1695
|
+
// --- Identify technical concept categories ---
|
|
1696
|
+
const technicalPatterns = {
|
|
1697
|
+
programming_language: /\b(typescript|javascript|python|rust|go|java|c\+\+|ruby|php|swift|kotlin)\b/i,
|
|
1698
|
+
framework: /\b(react|next\.?js|vue|angular|svelte|express|fastify|django|flask|spring)\b/i,
|
|
1699
|
+
tool: /\b(webpack|vite|esbuild|rollup|babel|eslint|prettier|jest|vitest|docker|kubernetes)\b/i,
|
|
1700
|
+
concept: /\b(api|rest|graphql|websocket|auth|oauth|jwt|database|sql|nosql|cache|queue|cicd|devops)\b/i,
|
|
1701
|
+
pattern: /\b(singleton|factory|observer|strategy|decorator|middleware|hook|context|provider|reducer)\b/i,
|
|
1702
|
+
};
|
|
1703
|
+
const identifiedConcepts = {};
|
|
1704
|
+
for (const [category, pattern] of Object.entries(technicalPatterns)) {
|
|
1705
|
+
const matches = query.match(pattern);
|
|
1706
|
+
if (matches) {
|
|
1707
|
+
identifiedConcepts[category] = [
|
|
1708
|
+
...new Set(matches.map((m) => m.toLowerCase())),
|
|
1709
|
+
];
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
// --- Scan local context files for topic matches ---
|
|
1713
|
+
const localFindings = [];
|
|
1714
|
+
const contextFiles = task.context.files;
|
|
1715
|
+
for (const file of contextFiles) {
|
|
1716
|
+
const content = (file.content || "").toLowerCase();
|
|
1717
|
+
const matchedTopics = topics.filter((topic) => content.includes(topic));
|
|
1718
|
+
if (matchedTopics.length > 0) {
|
|
1719
|
+
const matchRatio = matchedTopics.length / Math.max(topics.length, 1);
|
|
1720
|
+
const relevance = matchRatio > 0.5 ? "high" : matchRatio > 0.2 ? "medium" : "low";
|
|
1721
|
+
const snippet = this.extractRelevantSnippet(file.content || "", matchedTopics);
|
|
1722
|
+
localFindings.push({
|
|
1723
|
+
file: file.path,
|
|
1724
|
+
relevance,
|
|
1725
|
+
matchedTopics,
|
|
1726
|
+
snippet,
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
// Sort by relevance
|
|
1731
|
+
const relevanceOrder = {
|
|
1732
|
+
high: 0,
|
|
1733
|
+
medium: 1,
|
|
1734
|
+
low: 2,
|
|
1735
|
+
};
|
|
1736
|
+
localFindings.sort((a, b) => (relevanceOrder[a.relevance] ?? 3) - (relevanceOrder[b.relevance] ?? 3));
|
|
1737
|
+
// --- Build results array ---
|
|
1738
|
+
const results = [];
|
|
1739
|
+
if (webResult) {
|
|
1740
|
+
results.push({
|
|
1741
|
+
title: "Web Search Results",
|
|
1742
|
+
url: "",
|
|
1743
|
+
snippet: webResult.substring(0, 500),
|
|
1744
|
+
relevance: 0.7,
|
|
1745
|
+
source: "web_search",
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
for (const finding of localFindings.slice(0, 10)) {
|
|
1749
|
+
results.push({
|
|
1750
|
+
title: `Local: ${finding.file}`,
|
|
1751
|
+
url: finding.file,
|
|
1752
|
+
snippet: finding.snippet,
|
|
1753
|
+
relevance: finding.relevance === "high"
|
|
1754
|
+
? 0.9
|
|
1755
|
+
: finding.relevance === "medium"
|
|
1756
|
+
? 0.6
|
|
1757
|
+
: 0.3,
|
|
1758
|
+
source: "local_file",
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
// --- Key findings ---
|
|
1762
|
+
const keyFindings = [];
|
|
1763
|
+
if (localFindings.length > 0) {
|
|
1764
|
+
keyFindings.push(`Found ${localFindings.length} relevant local file(s) matching query topics`);
|
|
1765
|
+
const highRelevance = localFindings.filter((f) => f.relevance === "high");
|
|
1766
|
+
if (highRelevance.length > 0) {
|
|
1767
|
+
keyFindings.push(`High relevance files: ${highRelevance.map((f) => f.file).join(", ")}`);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
for (const [category, concepts] of Object.entries(identifiedConcepts)) {
|
|
1771
|
+
keyFindings.push(`Identified ${category}: ${concepts.join(", ")}`);
|
|
1772
|
+
}
|
|
1773
|
+
if (webResult) {
|
|
1774
|
+
keyFindings.push("Web search results retrieved but model-assisted analysis was unavailable");
|
|
1775
|
+
}
|
|
1776
|
+
if (keyFindings.length === 0) {
|
|
1777
|
+
keyFindings.push("No matching content found in local project files");
|
|
1778
|
+
keyFindings.push("External research requires a model connection for deeper analysis");
|
|
1779
|
+
}
|
|
1780
|
+
return {
|
|
1781
|
+
query,
|
|
1782
|
+
topics,
|
|
1783
|
+
identifiedConcepts,
|
|
1784
|
+
results,
|
|
1785
|
+
localFindings: localFindings.map((f) => ({
|
|
1786
|
+
file: f.file,
|
|
1787
|
+
relevance: f.relevance,
|
|
1788
|
+
matchedTopics: f.matchedTopics,
|
|
1789
|
+
})),
|
|
1790
|
+
keyFindings,
|
|
1791
|
+
verifiedFacts: [],
|
|
1792
|
+
limitations: [
|
|
1793
|
+
webResult
|
|
1794
|
+
? "Web results retrieved but could not be analyzed without a model"
|
|
1795
|
+
: "Web search unavailable — results limited to local file scanning",
|
|
1796
|
+
"Deep semantic analysis requires model availability",
|
|
1797
|
+
],
|
|
1798
|
+
note: "Research performed via local keyword matching and project file scanning (model unavailable)",
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Extract a representative code snippet around the first topic match.
|
|
1803
|
+
*/
|
|
1804
|
+
extractRelevantSnippet(content, topics) {
|
|
1805
|
+
const lines = content.split("\n");
|
|
1806
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1807
|
+
const lower = lines[i].toLowerCase();
|
|
1808
|
+
if (topics.some((t) => lower.includes(t))) {
|
|
1809
|
+
const start = Math.max(0, i - 2);
|
|
1810
|
+
const end = Math.min(lines.length, i + 3);
|
|
1811
|
+
return lines.slice(start, end).join("\n").substring(0, 300);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
return content.substring(0, 200);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
export function createGatedSubAgent(type, messageBus, toolBridge, config) {
|
|
1818
|
+
switch (type) {
|
|
1819
|
+
case "planner":
|
|
1820
|
+
return new GatedPlannerAgent(messageBus, toolBridge, config);
|
|
1821
|
+
case "analyzer":
|
|
1822
|
+
return new GatedAnalyzerAgent(messageBus, toolBridge, config);
|
|
1823
|
+
case "reviewer":
|
|
1824
|
+
return new GatedReviewerAgent(messageBus, toolBridge, config);
|
|
1825
|
+
case "rewriter":
|
|
1826
|
+
return new GatedRewriterAgent(messageBus, toolBridge, config);
|
|
1827
|
+
case "ui_designer":
|
|
1828
|
+
return new GatedUiDesignerAgent(messageBus, toolBridge, config);
|
|
1829
|
+
case "researcher":
|
|
1830
|
+
return new GatedResearcherAgent(messageBus, toolBridge, config);
|
|
1831
|
+
case "verifier":
|
|
1832
|
+
return new GatedVerifierAgent(messageBus, toolBridge, config);
|
|
1833
|
+
case "strategist":
|
|
1834
|
+
return new GatedStrategistAgent(messageBus, toolBridge, config);
|
|
1835
|
+
case "diff_validator":
|
|
1836
|
+
return new GatedDiffValidatorAgent(messageBus, toolBridge, config);
|
|
1837
|
+
case "debugger":
|
|
1838
|
+
return new GatedDebuggerAgent(messageBus, toolBridge, config);
|
|
1839
|
+
case "test_runner":
|
|
1840
|
+
return new GatedTestRunnerAgent(messageBus, toolBridge, config);
|
|
1841
|
+
default:
|
|
1842
|
+
throw new Error(`Unknown gated subagent type: ${type}`);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
// ============================================================================
|
|
1846
|
+
// Gated Verifier Agent (Synapse v4 Verification)
|
|
1847
|
+
// ============================================================================
|
|
1848
|
+
export class GatedVerifierAgent extends GatedSubAgent {
|
|
1849
|
+
constructor(messageBus, toolBridge, config) {
|
|
1850
|
+
super("verifier", config || {}, messageBus, toolBridge, {
|
|
1851
|
+
maxRetries: 2,
|
|
1852
|
+
strictMode: true,
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
async performTask(task) {
|
|
1856
|
+
this.updateStatus("running", "Verifying implementation");
|
|
1857
|
+
// Verification logic - parses claims and verifies implementation
|
|
1858
|
+
const verificationPrompt = task.prompt;
|
|
1859
|
+
// Call model to perform verification
|
|
1860
|
+
const response = await this.callModel(verificationPrompt);
|
|
1861
|
+
if (response) {
|
|
1862
|
+
try {
|
|
1863
|
+
// Try to parse as structured verification result
|
|
1864
|
+
return JSON.parse(response);
|
|
1865
|
+
}
|
|
1866
|
+
catch {
|
|
1867
|
+
return { rawOutput: response, parsed: false };
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
return { error: "Verification failed - no response from model" };
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
// ============================================================================
|
|
1874
|
+
// Gated Strategist Agent (Mission/Milestone decomposition)
|
|
1875
|
+
// ============================================================================
|
|
1876
|
+
export class GatedStrategistAgent extends GatedSubAgent {
|
|
1877
|
+
constructor(messageBus, toolBridge, config) {
|
|
1878
|
+
super("strategist", config || {}, messageBus, toolBridge, {
|
|
1879
|
+
maxRetries: 2,
|
|
1880
|
+
strictMode: false,
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
async performTask(task) {
|
|
1884
|
+
this.updateStatus("running", "Breaking down goals into milestones");
|
|
1885
|
+
const strategyPrompt = `Break down the following goal into missions and milestones:
|
|
1886
|
+
|
|
1887
|
+
${task.prompt}
|
|
1888
|
+
|
|
1889
|
+
Provide:
|
|
1890
|
+
1. High-level missions (distinct major phases)
|
|
1891
|
+
2. Each mission's key milestones
|
|
1892
|
+
3. Dependencies between milestones
|
|
1893
|
+
4. Priority ordering
|
|
1894
|
+
5. Estimated complexity per milestone
|
|
1895
|
+
|
|
1896
|
+
Format as structured JSON.`;
|
|
1897
|
+
const response = await this.callModel(strategyPrompt);
|
|
1898
|
+
if (response) {
|
|
1899
|
+
try {
|
|
1900
|
+
return JSON.parse(response);
|
|
1901
|
+
}
|
|
1902
|
+
catch {
|
|
1903
|
+
return { rawStrategy: response };
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
return { error: "Strategy generation failed" };
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
// ============================================================================
|
|
1910
|
+
// Gated Diff Validator Agent (Original vs Modified comparison)
|
|
1911
|
+
// ============================================================================
|
|
1912
|
+
export class GatedDiffValidatorAgent extends GatedSubAgent {
|
|
1913
|
+
constructor(messageBus, toolBridge, config) {
|
|
1914
|
+
super("diff_validator", config || {}, messageBus, toolBridge, {
|
|
1915
|
+
maxRetries: 2,
|
|
1916
|
+
strictMode: true,
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
async performTask(task) {
|
|
1920
|
+
this.updateStatus("running", "Validating code changes");
|
|
1921
|
+
const validationPrompt = `Compare original vs modified code and validate correctness:
|
|
1922
|
+
|
|
1923
|
+
${task.prompt}
|
|
1924
|
+
|
|
1925
|
+
Check for:
|
|
1926
|
+
1. Behavioral preservation (same output for same input)
|
|
1927
|
+
2. Syntax correctness
|
|
1928
|
+
3. No unintended modifications
|
|
1929
|
+
4. Structural integrity
|
|
1930
|
+
|
|
1931
|
+
Provide validation report as JSON.`;
|
|
1932
|
+
const response = await this.callModel(validationPrompt);
|
|
1933
|
+
if (response) {
|
|
1934
|
+
try {
|
|
1935
|
+
return JSON.parse(response);
|
|
1936
|
+
}
|
|
1937
|
+
catch {
|
|
1938
|
+
return { rawValidation: response };
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return { error: "Diff validation failed" };
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
// ============================================================================
|
|
1945
|
+
// Gated Debugger Agent (Auto-fix on failure)
|
|
1946
|
+
// ============================================================================
|
|
1947
|
+
export class GatedDebuggerAgent extends GatedSubAgent {
|
|
1948
|
+
constructor(messageBus, toolBridge, config) {
|
|
1949
|
+
super("debugger", config || {}, messageBus, toolBridge, {
|
|
1950
|
+
maxRetries: 3,
|
|
1951
|
+
strictMode: false,
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
async performTask(task) {
|
|
1955
|
+
this.updateStatus("running", "Analyzing and fixing errors");
|
|
1956
|
+
const debugPrompt = `Analyze error messages/stack traces and identify root cause:
|
|
1957
|
+
|
|
1958
|
+
${task.prompt}
|
|
1959
|
+
|
|
1960
|
+
Provide:
|
|
1961
|
+
1. Root cause analysis
|
|
1962
|
+
2. Proposed fix (if possible)
|
|
1963
|
+
3. Files that need modification
|
|
1964
|
+
4. Confidence level
|
|
1965
|
+
|
|
1966
|
+
Format as JSON.`;
|
|
1967
|
+
const response = await this.callModel(debugPrompt);
|
|
1968
|
+
if (response) {
|
|
1969
|
+
try {
|
|
1970
|
+
return JSON.parse(response);
|
|
1971
|
+
}
|
|
1972
|
+
catch {
|
|
1973
|
+
return { rawDebug: response };
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return { error: "Debug analysis failed" };
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
// ============================================================================
|
|
1980
|
+
// Gated Test Runner Agent (Validation tests execution)
|
|
1981
|
+
// ============================================================================
|
|
1982
|
+
export class GatedTestRunnerAgent extends GatedSubAgent {
|
|
1983
|
+
constructor(messageBus, toolBridge, config) {
|
|
1984
|
+
super("test_runner", config || {}, messageBus, toolBridge, {
|
|
1985
|
+
maxRetries: 2,
|
|
1986
|
+
strictMode: true,
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
async performTask(task) {
|
|
1990
|
+
this.updateStatus("running", "Running validation tests");
|
|
1991
|
+
const testPrompt = `Execute validation tests and report results:
|
|
1992
|
+
|
|
1993
|
+
${task.prompt}
|
|
1994
|
+
|
|
1995
|
+
Provide:
|
|
1996
|
+
1. Test results (pass/fail)
|
|
1997
|
+
2. Any runtime errors detected
|
|
1998
|
+
3. Expected vs actual behavior comparison
|
|
1999
|
+
4. Test failure diagnostics
|
|
2000
|
+
|
|
2001
|
+
Format as JSON.`;
|
|
2002
|
+
const response = await this.callModel(testPrompt);
|
|
2003
|
+
if (response) {
|
|
2004
|
+
try {
|
|
2005
|
+
return JSON.parse(response);
|
|
2006
|
+
}
|
|
2007
|
+
catch {
|
|
2008
|
+
return { rawTestResults: response };
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
return { error: "Test execution failed" };
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
//# sourceMappingURL=gated-subagents.js.map
|