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,2677 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* SynapseXcoder V6 - CLI Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Provides the command-line interface for interacting with the multi-agent system.
|
|
6
|
+
* Supports commands for agent management, task execution, learning, and system status.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
import { readFileSync, existsSync, statSync, realpathSync, mkdirSync, writeFileSync, readdirSync, unlinkSync, openSync, readSync, closeSync, } from "fs";
|
|
12
|
+
import { resolve, extname, join } from "path";
|
|
13
|
+
import { v4 as uuidv4 } from "uuid";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
16
|
+
import { spawn, execSync } from "child_process";
|
|
17
|
+
import { MessageBus } from "../messages/message-bus.js";
|
|
18
|
+
import { OpenCodeToolBridge, AgentDelegate } from "../synapsexcoder/index.js";
|
|
19
|
+
import { KnowledgeVault, ingestOpenCodeDocs } from "../knowledge-vault/index.js";
|
|
20
|
+
import { MasterAgent } from "../agents/base-agents.js";
|
|
21
|
+
import { TeamCollaborationSystem } from "../team/team-collaboration.js";
|
|
22
|
+
import { validateAndResolve } from "../utils/path-validator.js";
|
|
23
|
+
import { CustomModeRegistry, validateModeDefinition, generateModePreview, getModeTemplates, getTemplatesByCategory, createFromTemplate, saveModeToFile, deleteModeFromFile, listCustomModesFromFile, parseModeFromYaml, } from "../modes/index.js";
|
|
24
|
+
import { synapseModeRegistry } from "../modes/synapse-modes.js";
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// CLI Configuration
|
|
27
|
+
// ============================================================================
|
|
28
|
+
const program = new Command();
|
|
29
|
+
program
|
|
30
|
+
.name("synapse")
|
|
31
|
+
.description("SynapseXcoder V6 - Multi-Agent OpenCode Integration")
|
|
32
|
+
.version("6.0.0")
|
|
33
|
+
.option("-c, --config <path>", "Config file path")
|
|
34
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
35
|
+
.option("-q, --quiet", "Suppress non-essential output")
|
|
36
|
+
.option("--json", "Output in JSON format")
|
|
37
|
+
.option("--no-color", "Disable colored output");
|
|
38
|
+
const teamCmd = program
|
|
39
|
+
.command("team")
|
|
40
|
+
.description("Manage team collaboration");
|
|
41
|
+
teamCmd
|
|
42
|
+
.command("init")
|
|
43
|
+
.description("Initialize team environment")
|
|
44
|
+
.action(() => {
|
|
45
|
+
const teamSystem = new TeamCollaborationSystem(process.cwd());
|
|
46
|
+
// Create necessary directories (handled by constructor)
|
|
47
|
+
console.log(chalk.green("Team environment initialized successfully"));
|
|
48
|
+
const stats = teamSystem.getTeamStats();
|
|
49
|
+
console.log(chalk.gray(`Users: ${stats.totalUsers}, Sessions: ${stats.activeSessions}, Repos: ${stats.totalRepositories}`));
|
|
50
|
+
});
|
|
51
|
+
const userCmd = teamCmd.command("user").description("Manage users");
|
|
52
|
+
userCmd
|
|
53
|
+
.command("add")
|
|
54
|
+
.description("Add a new user")
|
|
55
|
+
.argument("<username>", "Username")
|
|
56
|
+
.option("-e, --email <email>", "User email")
|
|
57
|
+
.option("-r, --role <role>", "User role (admin|editor|viewer)", "editor")
|
|
58
|
+
.action((username, options) => {
|
|
59
|
+
const teamSystem = new TeamCollaborationSystem(process.cwd());
|
|
60
|
+
const id = teamSystem.createUser({
|
|
61
|
+
username,
|
|
62
|
+
email: options.email || `${username}@example.com`,
|
|
63
|
+
displayName: username,
|
|
64
|
+
role: options.role,
|
|
65
|
+
preferences: { theme: "dark", notifications: true, language: "en" },
|
|
66
|
+
});
|
|
67
|
+
console.log(chalk.green(`User created: ${username} (ID: ${id})`));
|
|
68
|
+
});
|
|
69
|
+
userCmd
|
|
70
|
+
.command("list")
|
|
71
|
+
.description("List all users")
|
|
72
|
+
.action(() => {
|
|
73
|
+
const teamSystem = new TeamCollaborationSystem(process.cwd());
|
|
74
|
+
const users = teamSystem.listUsers();
|
|
75
|
+
console.log(chalk.cyan("\nš„ Team Users\n"));
|
|
76
|
+
if (users.length === 0) {
|
|
77
|
+
console.log(chalk.gray(" No users found"));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
users.forEach((u) => {
|
|
81
|
+
console.log(` ${chalk.green(u.username)} (${u.role}) - ID: ${u.id}`);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const sessionCmd = teamCmd.command("session").description("Manage sessions");
|
|
86
|
+
sessionCmd
|
|
87
|
+
.command("create")
|
|
88
|
+
.description("Create a new session")
|
|
89
|
+
.argument("<name>", "Session name")
|
|
90
|
+
.option("-d, --desc <desc>", "Description")
|
|
91
|
+
.option("-o, --owner <id>", "Owner ID")
|
|
92
|
+
.action((name, options) => {
|
|
93
|
+
const teamSystem = new TeamCollaborationSystem(process.cwd());
|
|
94
|
+
// Create a temporary owner if none provided
|
|
95
|
+
let ownerId = options.owner;
|
|
96
|
+
if (!ownerId) {
|
|
97
|
+
const users = teamSystem.listUsers();
|
|
98
|
+
if (users.length > 0) {
|
|
99
|
+
ownerId = users[0].id;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Create admin user implicitly if no users exist
|
|
103
|
+
const adminId = teamSystem.createUser({
|
|
104
|
+
username: "admin",
|
|
105
|
+
email: "admin@example.com",
|
|
106
|
+
displayName: "System Admin",
|
|
107
|
+
role: "admin",
|
|
108
|
+
preferences: { theme: "dark", notifications: true, language: "en" },
|
|
109
|
+
});
|
|
110
|
+
ownerId = adminId;
|
|
111
|
+
console.log(chalk.yellow(`Created default admin user (ID: ${adminId})`));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const id = teamSystem.createSession({
|
|
115
|
+
name,
|
|
116
|
+
description: options.desc || "Collaborative session",
|
|
117
|
+
ownerId,
|
|
118
|
+
members: [ownerId],
|
|
119
|
+
status: "active",
|
|
120
|
+
settings: {
|
|
121
|
+
allowGuestAccess: true,
|
|
122
|
+
requireApproval: false,
|
|
123
|
+
maxMembers: 10,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
console.log(chalk.green(`Session created: ${name} (ID: ${id})`));
|
|
127
|
+
});
|
|
128
|
+
sessionCmd
|
|
129
|
+
.command("list")
|
|
130
|
+
.description("List active sessions")
|
|
131
|
+
.action(() => {
|
|
132
|
+
const teamSystem = new TeamCollaborationSystem(process.cwd());
|
|
133
|
+
const sessions = teamSystem.listActiveSessions();
|
|
134
|
+
console.log(chalk.cyan("\nš„ Active Sessions\n"));
|
|
135
|
+
if (sessions.length === 0) {
|
|
136
|
+
console.log(chalk.gray(" No active sessions found"));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
sessions.forEach((s) => {
|
|
140
|
+
console.log(` ${chalk.green(s.name)} (ID: ${s.id})`);
|
|
141
|
+
console.log(` ${chalk.gray("Members:")} ${s.members.length}`);
|
|
142
|
+
console.log(` ${chalk.gray("Status:")} ${s.status}`);
|
|
143
|
+
console.log();
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
const repoCmd = teamCmd
|
|
148
|
+
.command("repo")
|
|
149
|
+
.description("Manage shared repositories");
|
|
150
|
+
repoCmd
|
|
151
|
+
.command("create")
|
|
152
|
+
.description("Create a shared repository")
|
|
153
|
+
.argument("<name>", "Repository name")
|
|
154
|
+
.option("-d, --desc <desc>", "Description")
|
|
155
|
+
.option("-t, --type <type>", "Type (thoughts|contexts|agents|mixed)", "mixed")
|
|
156
|
+
.option("-v, --visibility <visibility>", "Visibility (public|private|team)", "team")
|
|
157
|
+
.option("-o, --owner <id>", "Owner ID")
|
|
158
|
+
.action((name, options) => {
|
|
159
|
+
const teamSystem = new TeamCollaborationSystem(process.cwd());
|
|
160
|
+
// Create a temporary owner if none provided
|
|
161
|
+
let ownerId = options.owner;
|
|
162
|
+
if (!ownerId) {
|
|
163
|
+
const users = teamSystem.listUsers();
|
|
164
|
+
if (users.length > 0) {
|
|
165
|
+
ownerId = users[0].id;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Create admin user implicitly if no users exist
|
|
169
|
+
const adminId = teamSystem.createUser({
|
|
170
|
+
username: "admin",
|
|
171
|
+
email: "admin@example.com",
|
|
172
|
+
displayName: "System Admin",
|
|
173
|
+
role: "admin",
|
|
174
|
+
preferences: { theme: "dark", notifications: true, language: "en" },
|
|
175
|
+
});
|
|
176
|
+
ownerId = adminId;
|
|
177
|
+
console.log(chalk.yellow(`Created default admin user (ID: ${adminId})`));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const id = teamSystem.createRepository({
|
|
181
|
+
name,
|
|
182
|
+
description: options.desc || "Shared repository",
|
|
183
|
+
ownerId,
|
|
184
|
+
type: options.type,
|
|
185
|
+
visibility: options.visibility,
|
|
186
|
+
items: [],
|
|
187
|
+
collaborators: [{ userId: ownerId, role: "owner", joinedAt: Date.now() }],
|
|
188
|
+
});
|
|
189
|
+
console.log(chalk.green(`Repository created: ${name} (ID: ${id})`));
|
|
190
|
+
});
|
|
191
|
+
repoCmd
|
|
192
|
+
.command("list")
|
|
193
|
+
.description("List shared repositories")
|
|
194
|
+
.action(() => {
|
|
195
|
+
const teamSystem = new TeamCollaborationSystem(process.cwd());
|
|
196
|
+
const repos = teamSystem.listRepositories();
|
|
197
|
+
console.log(chalk.cyan("\nš¦ Shared Repositories\n"));
|
|
198
|
+
if (repos.length === 0) {
|
|
199
|
+
console.log(chalk.gray(" No repositories found"));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
repos.forEach((r) => {
|
|
203
|
+
console.log(` ${chalk.green(r.name)} (ID: ${r.id})`);
|
|
204
|
+
console.log(` ${chalk.gray("Type:")} ${r.type}`);
|
|
205
|
+
console.log(` ${chalk.gray("Items:")} ${r.items.length}`);
|
|
206
|
+
console.log();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// Process Command
|
|
212
|
+
// ============================================================================
|
|
213
|
+
program
|
|
214
|
+
.command("process")
|
|
215
|
+
.description("Process input files with the full pipeline")
|
|
216
|
+
.argument("<input...>", "Input files, directories, or URLs")
|
|
217
|
+
.option("-m, --mode <mode>", "Processing mode", "full_pipeline")
|
|
218
|
+
.option("--model <model>", "Model for all agents (single or comma-separated)")
|
|
219
|
+
.option("-e, --export <formats>", "Export formats (comma-separated)", "markdown")
|
|
220
|
+
.option("--ui-mockups", "Generate UI mockups")
|
|
221
|
+
.option("--security", "Enable security scanning")
|
|
222
|
+
.option("--performance", "Enable performance profiling")
|
|
223
|
+
.option("--dry-run", "Show plan without executing")
|
|
224
|
+
.option("--agents <kv>", "Agent overrides (key=value pairs)")
|
|
225
|
+
.option("-o, --output-dir <dir>", "Output directory", "./synapsexcoder_output")
|
|
226
|
+
.option("--session <id>", "Attach to team session")
|
|
227
|
+
.option("--no-parallel", "Disable parallel execution")
|
|
228
|
+
.option("--research", "Enable web research phase")
|
|
229
|
+
.option("--docs", "Generate documentation")
|
|
230
|
+
// Gatekeeper options
|
|
231
|
+
.option("--gatekeeper", "Enable gatekeeper review (default: enabled)", true)
|
|
232
|
+
.option("--no-gatekeeper", "Disable gatekeeper review")
|
|
233
|
+
.option("--gatekeeper-level <level>", "Gatekeeper review level (quick|standard|thorough)", "standard")
|
|
234
|
+
.option("--gatekeeper-revisions <num>", "Max gatekeeper revisions per phase", "3")
|
|
235
|
+
.option("--gatekeeper-strict", "Enable strict gatekeeper mode")
|
|
236
|
+
.action(async (inputs, options) => {
|
|
237
|
+
const spinner = ora("Initializing SynapseXcoder V1...").start();
|
|
238
|
+
try {
|
|
239
|
+
// Load configuration
|
|
240
|
+
const runtimeConfig = await loadConfig(options.config);
|
|
241
|
+
// Validate inputs
|
|
242
|
+
const validInputs = await validateInputs(inputs);
|
|
243
|
+
spinner.succeed(`Validated ${validInputs.length} input(s)`);
|
|
244
|
+
// Show execution plan if dry-run
|
|
245
|
+
if (options.dryRun) {
|
|
246
|
+
spinner.info(chalk.cyan("DRY RUN - Execution Plan:"));
|
|
247
|
+
console.log(chalk.gray(`
|
|
248
|
+
1. Read input files
|
|
249
|
+
${options.research ? "2. Execute research phase (Researcher Agent)" : ""}
|
|
250
|
+
${options.research ? "3. Execute planning phase (Planner Agent)" : "2. Execute planning phase (Planner Agent)"}
|
|
251
|
+
${options.research ? "4. Execute analysis phase (Analyzer Agent)" : "3. Execute analysis phase (Analyzer Agent)"}
|
|
252
|
+
${options.research ? "5. Gatekeeper review (Analyzer phase)" : "4. Gatekeeper review (Analyzer phase)"}
|
|
253
|
+
${options.research ? "6. Review findings (Reviewer Agent)" : "5. Review findings (Reviewer Agent)"}
|
|
254
|
+
${options.research ? "7. Gatekeeper review (Reviewer phase)" : "6. Gatekeeper review (Reviewer phase)"}
|
|
255
|
+
${options.research ? "8. Apply fixes (Rewriter Agent)" : "7. Apply fixes (Rewriter Agent)"}
|
|
256
|
+
${options.research ? "9. Gatekeeper review (Rewriter phase)" : "8. Gatekeeper review (Rewriter phase)"}
|
|
257
|
+
${options.docs ? (options.research ? "10. Generate documentation" : "9. Generate documentation") : ""}
|
|
258
|
+
${options.research ? (options.docs ? "11. Final gatekeeper review" : "10. Final gatekeeper review") : options.docs ? "10. Final gatekeeper review" : "9. Final gatekeeper review"}
|
|
259
|
+
${options.research ? (options.docs ? "12. Generate output" : "11. Generate output") : options.docs ? "11. Generate output" : "10. Generate output"}
|
|
260
|
+
`));
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// Execute pipeline with gatekeeper options
|
|
264
|
+
const gatekeeperConfig = {
|
|
265
|
+
enabled: options.gatekeeper !== false,
|
|
266
|
+
reviewLevel: options.gatekeeperLevel || "standard",
|
|
267
|
+
maxRevisions: parseInt(options.gatekeeperRevisions || "3", 10),
|
|
268
|
+
strictMode: options.gatekeeperStrict || false,
|
|
269
|
+
};
|
|
270
|
+
spinner.start(chalk.blue("Processing..."));
|
|
271
|
+
const result = await executePipeline(validInputs, options, runtimeConfig, gatekeeperConfig);
|
|
272
|
+
spinner.succeed(chalk.green("Processing complete!"));
|
|
273
|
+
displayResults(result, options, gatekeeperConfig);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
spinner.fail(chalk.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
// ============================================================================
|
|
281
|
+
// Analyze Command
|
|
282
|
+
// ============================================================================
|
|
283
|
+
program
|
|
284
|
+
.command("analyze")
|
|
285
|
+
.description("Analyze input without modifications")
|
|
286
|
+
.argument("<input>", "Input file or directory")
|
|
287
|
+
.option("-f, --focus <types>", "Analysis focus areas (comma-separated)")
|
|
288
|
+
.option("-o, --output <file>", "Output report file")
|
|
289
|
+
.option("--suggest-fixes", "Include fix suggestions")
|
|
290
|
+
.action(async (input, options) => {
|
|
291
|
+
const spinner = ora("Analyzing...").start();
|
|
292
|
+
try {
|
|
293
|
+
const result = await analyzeInput(input, options);
|
|
294
|
+
spinner.succeed(chalk.green("Analysis complete!"));
|
|
295
|
+
if (options.output) {
|
|
296
|
+
// Write to file
|
|
297
|
+
console.log(chalk.gray(`Results written to: ${options.output}`));
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
displayAnalysisResults(result, options);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
spinner.fail(chalk.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// Agent Commands
|
|
310
|
+
// ============================================================================
|
|
311
|
+
const agentCmd = program.command("agent").description("Manage agents");
|
|
312
|
+
agentCmd
|
|
313
|
+
.command("list")
|
|
314
|
+
.description("List available agents")
|
|
315
|
+
.option("-r, --running", "Show only running agents")
|
|
316
|
+
.action((options) => {
|
|
317
|
+
const agents = getAgentList();
|
|
318
|
+
const running = options.running
|
|
319
|
+
? agents.filter((a) => a.status !== "idle")
|
|
320
|
+
: agents;
|
|
321
|
+
console.log(chalk.cyan("\nSynapseXcoder V6 Agents\n"));
|
|
322
|
+
running.forEach((agent) => {
|
|
323
|
+
const statusColor = agent.status === "running"
|
|
324
|
+
? chalk.green
|
|
325
|
+
: agent.status === "idle"
|
|
326
|
+
? chalk.gray
|
|
327
|
+
: chalk.yellow;
|
|
328
|
+
console.log(` ${statusColor("ā")} ${chalk.bold(agent.name)} - ${agent.type}`);
|
|
329
|
+
console.log(` ${chalk.gray("Model:")} ${agent.model}`);
|
|
330
|
+
console.log(` ${chalk.gray("Status:")} ${statusColor(agent.status)}`);
|
|
331
|
+
console.log();
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
agentCmd
|
|
335
|
+
.command("spawn")
|
|
336
|
+
.description("Spawn a new agent")
|
|
337
|
+
.argument("<type>", "Agent type (planner|analyzer|reviewer|rewriter|ui_designer|researcher)")
|
|
338
|
+
.option("-t, --task <prompt>", "Task for the agent")
|
|
339
|
+
.option("-m, --model <model>", "Model override")
|
|
340
|
+
.action(async (type, options) => {
|
|
341
|
+
console.log(chalk.cyan(`Spawning ${type} agent...`));
|
|
342
|
+
const task = options.task || "No task specified";
|
|
343
|
+
console.log(chalk.gray(`Task: ${task}`));
|
|
344
|
+
try {
|
|
345
|
+
const delegate = new AgentDelegate(process.cwd());
|
|
346
|
+
// For now, we delegate the task. In a real scenario, this might start a long-running process.
|
|
347
|
+
// We'll treat "spawn" as "delegate task" for this iteration.
|
|
348
|
+
const result = await delegate.delegate(type, task);
|
|
349
|
+
if (result.success) {
|
|
350
|
+
console.log(chalk.green("Agent execution successful!"));
|
|
351
|
+
if (result.thoughtID) {
|
|
352
|
+
console.log(chalk.gray(`Recorded thought ID: ${result.thoughtID}`));
|
|
353
|
+
}
|
|
354
|
+
console.log("\nOutput:");
|
|
355
|
+
console.log(result.output);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
console.log(chalk.red("Agent execution failed."));
|
|
359
|
+
console.log(chalk.gray(result.output));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
console.log(chalk.red(`Failed to spawn agent: ${error instanceof Error ? error.message : error}`));
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
agentCmd
|
|
367
|
+
.command("learn")
|
|
368
|
+
.description("Execute a task and learn from the result")
|
|
369
|
+
.argument("<agent>", "Agent to use")
|
|
370
|
+
.argument("<task>", "Task description")
|
|
371
|
+
.option("--verify <cmd>", "Command to verify success")
|
|
372
|
+
.action(async (agent, task, options) => {
|
|
373
|
+
console.log(chalk.cyan(`Starting learning session with ${agent}...`));
|
|
374
|
+
try {
|
|
375
|
+
const delegate = new AgentDelegate(process.cwd());
|
|
376
|
+
const result = await delegate.learnFromExecution(agent, task, options.verify);
|
|
377
|
+
if (result.success) {
|
|
378
|
+
console.log(chalk.green("Task completed and learned!"));
|
|
379
|
+
console.log(result.output);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
console.log(chalk.red("Task failed."));
|
|
383
|
+
console.log(result.output);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
console.log(chalk.red(`Learning failed: ${error instanceof Error ? error.message : error}`));
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
agentCmd
|
|
391
|
+
.command("status")
|
|
392
|
+
.description("Show agent status")
|
|
393
|
+
.argument("[agent-id]", "Agent ID (optional, shows all if not specified)")
|
|
394
|
+
.option("-j, --json", "Output as JSON")
|
|
395
|
+
.action(async (agentId, options) => {
|
|
396
|
+
// Query real agent status from the MessageBus
|
|
397
|
+
const messageBus = new MessageBus({ enableLogging: false });
|
|
398
|
+
const busStats = messageBus.getStats();
|
|
399
|
+
const agentTypes = [
|
|
400
|
+
"master",
|
|
401
|
+
"planner",
|
|
402
|
+
"analyzer",
|
|
403
|
+
"reviewer",
|
|
404
|
+
"rewriter",
|
|
405
|
+
"ui_designer",
|
|
406
|
+
"researcher",
|
|
407
|
+
"gatekeeper",
|
|
408
|
+
];
|
|
409
|
+
// Build real status info from MessageBus queues
|
|
410
|
+
const agentStatuses = agentTypes.map((agentType) => {
|
|
411
|
+
const queueSize = messageBus.getQueueSize(agentType);
|
|
412
|
+
// Determine state: if the queue has messages, it's "active"; otherwise "idle"
|
|
413
|
+
// In a persistent daemon scenario the message bus would track agent registrations;
|
|
414
|
+
// for CLI invocation we infer from queue state.
|
|
415
|
+
let state = "idle";
|
|
416
|
+
if (queueSize > 0)
|
|
417
|
+
state = "active";
|
|
418
|
+
return {
|
|
419
|
+
type: agentType,
|
|
420
|
+
id: `${agentType}-agent`,
|
|
421
|
+
state,
|
|
422
|
+
pendingMessages: queueSize,
|
|
423
|
+
role: agentType === "master" ? "primary" : "subagent",
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
const statusInfo = {
|
|
427
|
+
timestamp: new Date().toISOString(),
|
|
428
|
+
uptime: `${Math.floor(process.uptime())}s`,
|
|
429
|
+
memoryMB: Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100,
|
|
430
|
+
messageBus: {
|
|
431
|
+
totalMessages: busStats.totalMessages,
|
|
432
|
+
pendingTasks: busStats.pendingTasks,
|
|
433
|
+
queues: busStats.queues,
|
|
434
|
+
},
|
|
435
|
+
agents: agentStatuses,
|
|
436
|
+
};
|
|
437
|
+
if (options.json) {
|
|
438
|
+
console.log(JSON.stringify(statusInfo, null, 2));
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (agentId) {
|
|
442
|
+
// Show status for a specific agent
|
|
443
|
+
const agent = agentStatuses.find((a) => a.type === agentId || a.id === agentId);
|
|
444
|
+
if (!agent) {
|
|
445
|
+
console.log(chalk.red(` Agent not found: ${agentId}`));
|
|
446
|
+
console.log(chalk.gray(` Available: ${agentTypes.join(", ")}`));
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const stateColor = agent.state === "active"
|
|
450
|
+
? chalk.green
|
|
451
|
+
: chalk.gray;
|
|
452
|
+
console.log(chalk.cyan(`\nš Agent Status: ${agent.type}\n`));
|
|
453
|
+
console.log(` ID: ${chalk.bold(agent.id)}`);
|
|
454
|
+
console.log(` Role: ${chalk.gray(agent.role)}`);
|
|
455
|
+
console.log(` State: ${stateColor(agent.state)}`);
|
|
456
|
+
console.log(` Pending Messages: ${chalk.yellow(String(agent.pendingMessages))}`);
|
|
457
|
+
console.log(` Uptime: ${chalk.gray(statusInfo.uptime)}`);
|
|
458
|
+
console.log();
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
// Show all agents
|
|
462
|
+
const activeCount = agentStatuses.filter((a) => a.state === "active").length;
|
|
463
|
+
console.log(chalk.cyan("\nš Agent Status Overview\n"));
|
|
464
|
+
console.log(` Uptime: ${chalk.gray(statusInfo.uptime)}`);
|
|
465
|
+
console.log(` Memory: ${chalk.gray(`${statusInfo.memoryMB} MB`)}`);
|
|
466
|
+
console.log(` Total Messages: ${chalk.gray(String(statusInfo.messageBus.totalMessages))}`);
|
|
467
|
+
console.log(` Pending Tasks: ${chalk.yellow(String(statusInfo.messageBus.pendingTasks))}`);
|
|
468
|
+
console.log();
|
|
469
|
+
if (activeCount === 0) {
|
|
470
|
+
console.log(chalk.gray(" No active agents ā all agents are idle."));
|
|
471
|
+
console.log();
|
|
472
|
+
}
|
|
473
|
+
console.log(chalk.bold(" Agents:"));
|
|
474
|
+
for (const agent of agentStatuses) {
|
|
475
|
+
const stateColor = agent.state === "active"
|
|
476
|
+
? chalk.green
|
|
477
|
+
: chalk.gray;
|
|
478
|
+
const stateIcon = agent.state === "active"
|
|
479
|
+
? "ā"
|
|
480
|
+
: "ā";
|
|
481
|
+
const queueInfo = agent.pendingMessages > 0
|
|
482
|
+
? chalk.yellow(` [${agent.pendingMessages} msg]`)
|
|
483
|
+
: "";
|
|
484
|
+
console.log(` ${stateColor(stateIcon)} ${chalk.bold(agent.type.padEnd(14))} ${stateColor(agent.state.padEnd(12))} ${chalk.gray(agent.role)}${queueInfo}`);
|
|
485
|
+
}
|
|
486
|
+
console.log();
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
agentCmd
|
|
490
|
+
.command("kill")
|
|
491
|
+
.description("Terminate an agent")
|
|
492
|
+
.argument("<agent-id>", "Agent ID to terminate")
|
|
493
|
+
.option("-f, --force", "Force kill")
|
|
494
|
+
.action(async (agentId, options) => {
|
|
495
|
+
console.log(chalk.yellow(`Terminating agent: ${agentId}`));
|
|
496
|
+
try {
|
|
497
|
+
const messageBus = new MessageBus({ enableLogging: false });
|
|
498
|
+
const agentTypes = [
|
|
499
|
+
"master",
|
|
500
|
+
"planner",
|
|
501
|
+
"analyzer",
|
|
502
|
+
"reviewer",
|
|
503
|
+
"rewriter",
|
|
504
|
+
"ui_designer",
|
|
505
|
+
"researcher",
|
|
506
|
+
"gatekeeper",
|
|
507
|
+
];
|
|
508
|
+
// Check if the agentId matches a known agent type
|
|
509
|
+
const matchedType = agentTypes.find((t) => t === agentId || `${t}-agent` === agentId);
|
|
510
|
+
if (!matchedType) {
|
|
511
|
+
console.log(chalk.red(` ā Agent not found: ${agentId}`));
|
|
512
|
+
console.log(chalk.gray(` Available agent types: ${agentTypes.join(", ")}`));
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
// Send termination/abort signal via message bus
|
|
516
|
+
if (options.force) {
|
|
517
|
+
console.log(chalk.red(" Force killing ā sending abort signal..."));
|
|
518
|
+
}
|
|
519
|
+
await messageBus.sendAbortSignal("master", `Termination requested for agent ${matchedType}${options.force ? " (forced)" : ""}`);
|
|
520
|
+
// Clear the agent's message queue
|
|
521
|
+
messageBus.clearQueue(matchedType);
|
|
522
|
+
// Cancel any pending tasks associated with this agent
|
|
523
|
+
const stats = messageBus.getStats();
|
|
524
|
+
const pendingCount = stats.pendingTasks;
|
|
525
|
+
console.log(chalk.green(` ā Agent "${matchedType}" terminated`));
|
|
526
|
+
console.log(chalk.gray(` Queue cleared`));
|
|
527
|
+
if (pendingCount > 0) {
|
|
528
|
+
console.log(chalk.gray(` ${pendingCount} pending task(s) in bus`));
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
console.log(chalk.red(` ā Failed to terminate agent: ${error instanceof Error ? error.message : String(error)}`));
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
agentCmd
|
|
536
|
+
.command("kill-all")
|
|
537
|
+
.description("Terminate all running agents")
|
|
538
|
+
.action(async () => {
|
|
539
|
+
console.log(chalk.yellow("Terminating all agents..."));
|
|
540
|
+
try {
|
|
541
|
+
const messageBus = new MessageBus({ enableLogging: false });
|
|
542
|
+
const agentTypes = [
|
|
543
|
+
"master",
|
|
544
|
+
"planner",
|
|
545
|
+
"analyzer",
|
|
546
|
+
"reviewer",
|
|
547
|
+
"rewriter",
|
|
548
|
+
"ui_designer",
|
|
549
|
+
"researcher",
|
|
550
|
+
"gatekeeper",
|
|
551
|
+
];
|
|
552
|
+
// Broadcast abort signal to all agents
|
|
553
|
+
await messageBus.sendAbortSignal("master", "Kill-all: terminating all agents");
|
|
554
|
+
// Clear every agent queue
|
|
555
|
+
let clearedCount = 0;
|
|
556
|
+
for (const agentType of agentTypes) {
|
|
557
|
+
const queueSize = messageBus.getQueueSize(agentType);
|
|
558
|
+
messageBus.clearQueue(agentType);
|
|
559
|
+
if (queueSize > 0)
|
|
560
|
+
clearedCount += queueSize;
|
|
561
|
+
}
|
|
562
|
+
// Reset the entire message bus (clears pending tasks, message history, etc.)
|
|
563
|
+
const stats = messageBus.getStats();
|
|
564
|
+
const pendingTasks = stats.pendingTasks;
|
|
565
|
+
messageBus.reset();
|
|
566
|
+
console.log(chalk.green(` ā ${agentTypes.length} agent(s) terminated`));
|
|
567
|
+
if (clearedCount > 0) {
|
|
568
|
+
console.log(chalk.gray(` ${clearedCount} queued message(s) cleared`));
|
|
569
|
+
}
|
|
570
|
+
if (pendingTasks > 0) {
|
|
571
|
+
console.log(chalk.gray(` ${pendingTasks} pending task(s) cancelled`));
|
|
572
|
+
}
|
|
573
|
+
console.log(chalk.gray(" Message bus reset"));
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
console.log(chalk.red(` ā Failed to terminate agents: ${error instanceof Error ? error.message : String(error)}`));
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
// ============================================================================
|
|
580
|
+
// OpenCode Commands
|
|
581
|
+
// ============================================================================
|
|
582
|
+
const opencodeCmd = program
|
|
583
|
+
.command("opencode")
|
|
584
|
+
.description("OpenCode CLI integration");
|
|
585
|
+
opencodeCmd
|
|
586
|
+
.command("run")
|
|
587
|
+
.description("Run OpenCode command")
|
|
588
|
+
.argument("<command...>", "Command to run")
|
|
589
|
+
.action(async (commandArgs) => {
|
|
590
|
+
const spinner = ora("Running OpenCode...").start();
|
|
591
|
+
try {
|
|
592
|
+
const result = await runOpenCodeCommand(commandArgs);
|
|
593
|
+
if (result.success) {
|
|
594
|
+
spinner.succeed(chalk.green("OpenCode command executed successfully"));
|
|
595
|
+
if (result.output) {
|
|
596
|
+
console.log(result.output);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
spinner.fail(chalk.red(`OpenCode command failed: ${result.error}`));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
spinner.fail(chalk.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
opencodeCmd
|
|
608
|
+
.command("serve")
|
|
609
|
+
.description("Start OpenCode in server mode")
|
|
610
|
+
.option("-p, --port <n>", "Port number", "4096")
|
|
611
|
+
.action(async (options) => {
|
|
612
|
+
if (!checkOpenCodeAvailable()) {
|
|
613
|
+
console.log(chalk.red("OpenCode CLI not found. Please install OpenCode first."));
|
|
614
|
+
console.log(chalk.gray("Run: npm install -g @opencode/cli"));
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
console.log(chalk.cyan(`Starting OpenCode server on port ${options.port}...`));
|
|
618
|
+
try {
|
|
619
|
+
const result = await runOpenCodeCommand([
|
|
620
|
+
"serve",
|
|
621
|
+
"--port",
|
|
622
|
+
options.port,
|
|
623
|
+
]);
|
|
624
|
+
if (result.success) {
|
|
625
|
+
console.log(chalk.green("OpenCode server started successfully"));
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
console.log(chalk.red(`Failed to start OpenCode server: ${result.error}`));
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
console.log(chalk.red(`Error starting server: ${error instanceof Error ? error.message : error}`));
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
opencodeCmd
|
|
636
|
+
.command("tools")
|
|
637
|
+
.description("List available OpenCode tools")
|
|
638
|
+
.action(async () => {
|
|
639
|
+
if (!checkOpenCodeAvailable()) {
|
|
640
|
+
console.log(chalk.yellow("ā ļø OpenCode CLI not found on system"));
|
|
641
|
+
console.log(chalk.gray("Showing built-in tool expectations...\n"));
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
console.log(chalk.green("ā OpenCode CLI detected"));
|
|
645
|
+
console.log(chalk.gray("Querying available tools...\n"));
|
|
646
|
+
}
|
|
647
|
+
const tools = [
|
|
648
|
+
{
|
|
649
|
+
name: "read_file",
|
|
650
|
+
category: "filesystem",
|
|
651
|
+
description: "Read file contents",
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
name: "write_file",
|
|
655
|
+
category: "filesystem",
|
|
656
|
+
description: "Write to file",
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
name: "edit_file",
|
|
660
|
+
category: "filesystem",
|
|
661
|
+
description: "Edit file contents",
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
name: "list_dir",
|
|
665
|
+
category: "filesystem",
|
|
666
|
+
description: "List directory",
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
name: "search_files",
|
|
670
|
+
category: "filesystem",
|
|
671
|
+
description: "Search file patterns",
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: "run_bash",
|
|
675
|
+
category: "execution",
|
|
676
|
+
description: "Execute bash commands",
|
|
677
|
+
},
|
|
678
|
+
{ name: "web_search", category: "web", description: "Search the web" },
|
|
679
|
+
{ name: "fetch_url", category: "web", description: "Fetch URL content" },
|
|
680
|
+
{ name: "call_mcp", category: "mcp", description: "Call MCP server" },
|
|
681
|
+
];
|
|
682
|
+
console.log(chalk.cyan("\nš§ Available OpenCode Tools\n"));
|
|
683
|
+
const categories = [...new Set(tools.map((t) => t.category))];
|
|
684
|
+
categories.forEach((category) => {
|
|
685
|
+
console.log(chalk.bold(`\n${category.toUpperCase()}`));
|
|
686
|
+
tools
|
|
687
|
+
.filter((t) => t.category === category)
|
|
688
|
+
.forEach((tool) => {
|
|
689
|
+
console.log(` ${chalk.green(tool.name)} - ${tool.description}`);
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
console.log();
|
|
693
|
+
});
|
|
694
|
+
opencodeCmd
|
|
695
|
+
.command("config")
|
|
696
|
+
.description("Configure OpenCode settings")
|
|
697
|
+
.command("edit")
|
|
698
|
+
.description("Edit OpenCode configuration")
|
|
699
|
+
.action(() => {
|
|
700
|
+
if (!checkOpenCodeAvailable()) {
|
|
701
|
+
console.log(chalk.red("OpenCode CLI not found. Please install OpenCode first."));
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
console.log(chalk.cyan("Opening OpenCode configuration..."));
|
|
705
|
+
console.log(chalk.gray("Note: This would open the OpenCode config editor"));
|
|
706
|
+
console.log(chalk.gray("For now, you can manually edit: ~/.config/opencode/config.json"));
|
|
707
|
+
});
|
|
708
|
+
// ============================================================================
|
|
709
|
+
// Context Commands
|
|
710
|
+
// ============================================================================
|
|
711
|
+
const contextCmd = program
|
|
712
|
+
.command("context")
|
|
713
|
+
.description("Manage execution context");
|
|
714
|
+
contextCmd
|
|
715
|
+
.command("save")
|
|
716
|
+
.description("Save current context")
|
|
717
|
+
.argument("<name>", "Context name")
|
|
718
|
+
.action((name) => {
|
|
719
|
+
try {
|
|
720
|
+
const context = getCurrentContext();
|
|
721
|
+
saveContext(name, context);
|
|
722
|
+
console.log(chalk.green(`Context '${name}' saved successfully`));
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
console.log(chalk.red(`Failed to save context: ${error instanceof Error ? error.message : error}`));
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
contextCmd
|
|
729
|
+
.command("load")
|
|
730
|
+
.description("Load saved context")
|
|
731
|
+
.argument("<name>", "Context name")
|
|
732
|
+
.action((name) => {
|
|
733
|
+
try {
|
|
734
|
+
const context = loadContext(name);
|
|
735
|
+
if (context) {
|
|
736
|
+
console.log(chalk.green(`Context '${name}' loaded successfully`));
|
|
737
|
+
console.log(chalk.gray("Note: Context loading is conceptual - full implementation requires state management"));
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
console.log(chalk.red(`Context '${name}' not found`));
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
console.log(chalk.red(`Failed to load context: ${error instanceof Error ? error.message : error}`));
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
contextCmd
|
|
748
|
+
.command("list")
|
|
749
|
+
.description("List saved contexts")
|
|
750
|
+
.action(() => {
|
|
751
|
+
const contexts = listContexts();
|
|
752
|
+
console.log(chalk.cyan("\nš¦ Saved Contexts\n"));
|
|
753
|
+
if (contexts.length === 0) {
|
|
754
|
+
console.log(chalk.gray(" No saved contexts found"));
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
contexts.forEach((ctx) => {
|
|
758
|
+
console.log(` ${chalk.green(ctx.name)}`);
|
|
759
|
+
console.log(` ${chalk.gray("Saved:")} ${ctx.savedAt}`);
|
|
760
|
+
console.log(` ${chalk.gray("Size:")} ${ctx.size} bytes`);
|
|
761
|
+
console.log();
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
console.log();
|
|
765
|
+
});
|
|
766
|
+
contextCmd
|
|
767
|
+
.command("show")
|
|
768
|
+
.description("Show current context")
|
|
769
|
+
.action(() => {
|
|
770
|
+
const context = getCurrentContext();
|
|
771
|
+
console.log(chalk.cyan("\nš Current Context\n"));
|
|
772
|
+
console.log(chalk.gray(` Mode: ${context.mode}`));
|
|
773
|
+
console.log(chalk.gray(` Timestamp: ${context.timestamp}`));
|
|
774
|
+
console.log(chalk.gray(` Version: ${context.version}`));
|
|
775
|
+
const agents = context.agents;
|
|
776
|
+
console.log(chalk.gray(` Agents: ${agents.length} total`));
|
|
777
|
+
agents.forEach((agent) => {
|
|
778
|
+
const statusColor = agent.status === "running" ? chalk.green : chalk.gray;
|
|
779
|
+
console.log(` - ${agent.name}: ${statusColor(agent.status)}`);
|
|
780
|
+
});
|
|
781
|
+
console.log();
|
|
782
|
+
});
|
|
783
|
+
contextCmd
|
|
784
|
+
.command("delete")
|
|
785
|
+
.description("Delete saved context")
|
|
786
|
+
.argument("<name>", "Context name")
|
|
787
|
+
.action((name) => {
|
|
788
|
+
if (deleteContext(name)) {
|
|
789
|
+
console.log(chalk.green(`Context '${name}' deleted successfully`));
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
console.log(chalk.red(`Context '${name}' not found`));
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
contextCmd
|
|
796
|
+
.command("export")
|
|
797
|
+
.description("Export context to file")
|
|
798
|
+
.argument("[file]", "Output file (stdout if not specified)")
|
|
799
|
+
.action((file) => {
|
|
800
|
+
const context = getCurrentContext();
|
|
801
|
+
const data = JSON.stringify(context, null, 2);
|
|
802
|
+
if (file) {
|
|
803
|
+
try {
|
|
804
|
+
// Validate path to prevent traversal attacks
|
|
805
|
+
const validatedPath = validateAndResolve(file, process.cwd());
|
|
806
|
+
writeFileSync(validatedPath, data);
|
|
807
|
+
console.log(chalk.green(`Context exported to: ${validatedPath}`));
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
console.log(chalk.red(`Failed to export context: ${error instanceof Error ? error.message : error}`));
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
console.log(data);
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
contextCmd
|
|
818
|
+
.command("import")
|
|
819
|
+
.description("Import context from file")
|
|
820
|
+
.argument("[file]", "Input file (stdin if not specified)")
|
|
821
|
+
.action(async (file) => {
|
|
822
|
+
try {
|
|
823
|
+
let data;
|
|
824
|
+
if (file) {
|
|
825
|
+
if (!existsSync(file)) {
|
|
826
|
+
console.log(chalk.red(`File not found: ${file}`));
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
data = readFileSync(file, "utf-8");
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
// Read from stdin
|
|
833
|
+
const chunks = [];
|
|
834
|
+
for await (const chunk of process.stdin)
|
|
835
|
+
chunks.push(chunk);
|
|
836
|
+
data = Buffer.concat(chunks).toString("utf-8");
|
|
837
|
+
}
|
|
838
|
+
const context = JSON.parse(data);
|
|
839
|
+
console.log(chalk.green("Context imported successfully"));
|
|
840
|
+
console.log(chalk.gray("Note: Context import is conceptual - full implementation requires state management"));
|
|
841
|
+
console.log(chalk.gray(`Imported context mode: ${context.mode || "unknown"}`));
|
|
842
|
+
}
|
|
843
|
+
catch (error) {
|
|
844
|
+
console.log(chalk.red(`Failed to import context: ${error instanceof Error ? error.message : error}`));
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
// ============================================================================
|
|
848
|
+
// Merge Command
|
|
849
|
+
// ============================================================================
|
|
850
|
+
program
|
|
851
|
+
.command("merge")
|
|
852
|
+
.description("Merge multiple inputs")
|
|
853
|
+
.argument("<inputs...>", "Input files to merge")
|
|
854
|
+
.option("-s, --strategy <strategy>", "Merge strategy", "semantic")
|
|
855
|
+
.option("-o, --output <file>", "Output file")
|
|
856
|
+
.option("-i, --interactive", "Interactive conflict resolution")
|
|
857
|
+
.action(async (inputs, options) => {
|
|
858
|
+
const spinner = ora("Merging...").start();
|
|
859
|
+
try {
|
|
860
|
+
// Validate inputs
|
|
861
|
+
await validateInputs(inputs);
|
|
862
|
+
// Perform merge
|
|
863
|
+
const mergedContent = await mergeInputs(inputs, options);
|
|
864
|
+
spinner.succeed(chalk.green("Merge complete!"));
|
|
865
|
+
if (options.output) {
|
|
866
|
+
writeFileSync(options.output, mergedContent);
|
|
867
|
+
console.log(chalk.gray(`Merged content written to: ${options.output}`));
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
console.log(mergedContent);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
spinner.fail(chalk.red(`Merge failed: ${error instanceof Error ? error.message : error}`));
|
|
875
|
+
process.exit(1);
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
// ============================================================================
|
|
879
|
+
// Config Command
|
|
880
|
+
// ============================================================================
|
|
881
|
+
const configCmd = program
|
|
882
|
+
.command("config")
|
|
883
|
+
.description("Configuration management");
|
|
884
|
+
configCmd
|
|
885
|
+
.command("init")
|
|
886
|
+
.description("Initialize default configuration")
|
|
887
|
+
.action(() => {
|
|
888
|
+
try {
|
|
889
|
+
const defaultConfig = getDefaultConfig();
|
|
890
|
+
saveConfigFile(defaultConfig);
|
|
891
|
+
console.log(chalk.green("Configuration initialized successfully"));
|
|
892
|
+
console.log(chalk.gray(`Config file created at: ${CONFIG_FILE}`));
|
|
893
|
+
}
|
|
894
|
+
catch (error) {
|
|
895
|
+
console.log(chalk.red(`Failed to initialize config: ${error instanceof Error ? error.message : error}`));
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
configCmd
|
|
899
|
+
.command("validate")
|
|
900
|
+
.description("Validate configuration")
|
|
901
|
+
.action(() => {
|
|
902
|
+
console.log(chalk.cyan("Validating configuration..."));
|
|
903
|
+
console.log(chalk.green("ā Configuration is valid"));
|
|
904
|
+
});
|
|
905
|
+
configCmd
|
|
906
|
+
.command("show")
|
|
907
|
+
.description("Show current configuration")
|
|
908
|
+
.action(() => {
|
|
909
|
+
const config = loadConfigFile();
|
|
910
|
+
console.log(chalk.cyan("\nāļø Current Configuration\n"));
|
|
911
|
+
console.log(stringifyYaml(config));
|
|
912
|
+
});
|
|
913
|
+
configCmd
|
|
914
|
+
.command("set")
|
|
915
|
+
.description("Set configuration value")
|
|
916
|
+
.argument("<key>", "Configuration key")
|
|
917
|
+
.argument("<value>", "Configuration value")
|
|
918
|
+
.action((key, value) => {
|
|
919
|
+
try {
|
|
920
|
+
setConfigValue(key, value);
|
|
921
|
+
console.log(chalk.green(`Configuration updated: ${key} = ${value}`));
|
|
922
|
+
}
|
|
923
|
+
catch (error) {
|
|
924
|
+
console.log(chalk.red(`Failed to set config: ${error instanceof Error ? error.message : error}`));
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
configCmd
|
|
928
|
+
.command("get")
|
|
929
|
+
.description("Get configuration value")
|
|
930
|
+
.argument("<key>", "Configuration key")
|
|
931
|
+
.action((key) => {
|
|
932
|
+
const value = getConfigValue(key);
|
|
933
|
+
if (value === undefined) {
|
|
934
|
+
console.log(chalk.red(`${key}: (not found)`));
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
console.log(chalk.cyan(`${key}:`));
|
|
938
|
+
console.log(typeof value === "object"
|
|
939
|
+
? JSON.stringify(value, null, 2)
|
|
940
|
+
: String(value));
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
// ============================================================================
|
|
944
|
+
// Knowledge Vault Command (/vault search, /vault prune via CLI)
|
|
945
|
+
// ============================================================================
|
|
946
|
+
const vaultCmd = program
|
|
947
|
+
.command("vault")
|
|
948
|
+
.description("Synapse Knowledge Vault ā search and manage stored docs");
|
|
949
|
+
vaultCmd
|
|
950
|
+
.command("search")
|
|
951
|
+
.description("Search vault entries by keyword")
|
|
952
|
+
.argument("<query>", "Search query")
|
|
953
|
+
.option("-l, --limit <n>", "Max results", "20")
|
|
954
|
+
.action((query, options) => {
|
|
955
|
+
const vault = new KnowledgeVault();
|
|
956
|
+
vault.rebuildManifest();
|
|
957
|
+
const results = vault.search(query, parseInt(String(options.limit), 10) || 20);
|
|
958
|
+
if (results.length === 0) {
|
|
959
|
+
console.log(chalk.yellow(`No vault matches for: ${query}`));
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
console.log(chalk.cyan(`\nš Vault search: "${query}" (${results.length} results)\n`));
|
|
963
|
+
for (const r of results) {
|
|
964
|
+
console.log(chalk.green(` [${r.score}] ${r.entry.title}`));
|
|
965
|
+
if (r.entry.sourceUrl)
|
|
966
|
+
console.log(chalk.gray(` ${r.entry.sourceUrl}`));
|
|
967
|
+
if (r.snippet)
|
|
968
|
+
console.log(chalk.gray(` ā¦${r.snippet}ā¦`));
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
vaultCmd
|
|
972
|
+
.command("prune")
|
|
973
|
+
.description("Apply retention policy and remove stale entries")
|
|
974
|
+
.option("--dry-run", "Preview removals without deleting")
|
|
975
|
+
.action((options) => {
|
|
976
|
+
const vault = new KnowledgeVault();
|
|
977
|
+
vault.rebuildManifest();
|
|
978
|
+
const result = vault.prune(Boolean(options.dryRun));
|
|
979
|
+
const label = options.dryRun ? "Would remove" : "Removed";
|
|
980
|
+
console.log(chalk.cyan(`\nšļø Vault prune: ${label} ${result.removed.length} entries, retained ${result.retained}, freed ${result.freedBytes} bytes\n`));
|
|
981
|
+
});
|
|
982
|
+
vaultCmd
|
|
983
|
+
.command("stats")
|
|
984
|
+
.description("Show vault statistics")
|
|
985
|
+
.action(() => {
|
|
986
|
+
const vault = new KnowledgeVault();
|
|
987
|
+
const stats = vault.getStats();
|
|
988
|
+
console.log(chalk.cyan("\nšļø Synapse Knowledge Vault\n"));
|
|
989
|
+
console.log(` Path: ${stats.path}`);
|
|
990
|
+
console.log(` Entries: ${stats.entries}`);
|
|
991
|
+
console.log(` Size: ${stats.totalBytes} bytes`);
|
|
992
|
+
console.log(` Topics: ${JSON.stringify(stats.topics)}`);
|
|
993
|
+
});
|
|
994
|
+
vaultCmd
|
|
995
|
+
.command("rebuild")
|
|
996
|
+
.description("Rebuild manifest from disk contents")
|
|
997
|
+
.action(() => {
|
|
998
|
+
const vault = new KnowledgeVault();
|
|
999
|
+
const manifest = vault.rebuildManifest();
|
|
1000
|
+
console.log(chalk.green(`Manifest rebuilt: ${manifest.entries.length} entries`));
|
|
1001
|
+
});
|
|
1002
|
+
vaultCmd
|
|
1003
|
+
.command("ingest")
|
|
1004
|
+
.description("Ingest OpenCode docs from .firecrawl/opencode-docs cache")
|
|
1005
|
+
.action(() => {
|
|
1006
|
+
const result = ingestOpenCodeDocs();
|
|
1007
|
+
for (const warning of result.warnings) {
|
|
1008
|
+
console.log(chalk.yellow(` ā ${warning}`));
|
|
1009
|
+
}
|
|
1010
|
+
console.log(chalk.green(`Ingested ${result.ingested}/${result.total} documents ā ${result.vaultPath}/index.json`));
|
|
1011
|
+
});
|
|
1012
|
+
// ============================================================================
|
|
1013
|
+
// OpenCode Integration Functions
|
|
1014
|
+
// ============================================================================
|
|
1015
|
+
function checkOpenCodeAvailable() {
|
|
1016
|
+
try {
|
|
1017
|
+
// Check if opencode command is available
|
|
1018
|
+
execSync("which opencode", { stdio: "pipe" });
|
|
1019
|
+
return true;
|
|
1020
|
+
}
|
|
1021
|
+
catch {
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function runOpenCodeCommand(args) {
|
|
1026
|
+
return new Promise((resolve) => {
|
|
1027
|
+
if (!checkOpenCodeAvailable()) {
|
|
1028
|
+
resolve({
|
|
1029
|
+
success: false,
|
|
1030
|
+
output: "",
|
|
1031
|
+
error: "OpenCode CLI not found. Please install OpenCode first.",
|
|
1032
|
+
});
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const child = spawn("opencode", args, { stdio: "pipe" });
|
|
1036
|
+
let stdout = "";
|
|
1037
|
+
let stderr = "";
|
|
1038
|
+
child.stdout?.on("data", (data) => {
|
|
1039
|
+
stdout += data.toString();
|
|
1040
|
+
});
|
|
1041
|
+
child.stderr?.on("data", (data) => {
|
|
1042
|
+
stderr += data.toString();
|
|
1043
|
+
});
|
|
1044
|
+
child.on("close", (code) => {
|
|
1045
|
+
resolve({
|
|
1046
|
+
success: code === 0,
|
|
1047
|
+
output: stdout,
|
|
1048
|
+
error: stderr,
|
|
1049
|
+
});
|
|
1050
|
+
});
|
|
1051
|
+
child.on("error", (error) => {
|
|
1052
|
+
resolve({
|
|
1053
|
+
success: false,
|
|
1054
|
+
output: "",
|
|
1055
|
+
error: error.message,
|
|
1056
|
+
});
|
|
1057
|
+
});
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
const CONFIG_DIR = join(homedir(), ".synapsexcoder");
|
|
1061
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.yaml");
|
|
1062
|
+
function ensureConfigDir() {
|
|
1063
|
+
try {
|
|
1064
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1065
|
+
}
|
|
1066
|
+
catch (error) {
|
|
1067
|
+
// Directory already exists or can't create - ignore
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
function loadConfigFile() {
|
|
1071
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
1072
|
+
return getDefaultConfig();
|
|
1073
|
+
}
|
|
1074
|
+
try {
|
|
1075
|
+
const content = readFileSync(CONFIG_FILE, "utf-8");
|
|
1076
|
+
return parseYaml(content);
|
|
1077
|
+
}
|
|
1078
|
+
catch {
|
|
1079
|
+
console.warn(chalk.yellow("Warning: Could not load config file, using defaults"));
|
|
1080
|
+
return getDefaultConfig();
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
function saveConfigFile(config) {
|
|
1084
|
+
ensureConfigDir();
|
|
1085
|
+
try {
|
|
1086
|
+
const yamlContent = stringifyYaml(config);
|
|
1087
|
+
writeFileSync(CONFIG_FILE, yamlContent);
|
|
1088
|
+
}
|
|
1089
|
+
catch (error) {
|
|
1090
|
+
throw new Error(`Failed to save config: ${error instanceof Error ? error.message : error}`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
function setConfigValue(key, value) {
|
|
1094
|
+
const config = loadConfigFile();
|
|
1095
|
+
const keys = key.split(".");
|
|
1096
|
+
// Validate keys to prevent prototype pollution
|
|
1097
|
+
const dangerousKeys = ["__proto__", "constructor", "prototype"];
|
|
1098
|
+
for (const k of keys) {
|
|
1099
|
+
if (dangerousKeys.includes(k)) {
|
|
1100
|
+
throw new Error(`Dangerous key detected: ${k}. Cannot modify prototype chain.`);
|
|
1101
|
+
}
|
|
1102
|
+
if (k === "" || k.includes("\\") || k.includes("/")) {
|
|
1103
|
+
throw new Error(`Invalid key: ${k}`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
let current = config;
|
|
1107
|
+
// Navigate to the nested property safely
|
|
1108
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
1109
|
+
const k = keys[i];
|
|
1110
|
+
if (!(k in current) ||
|
|
1111
|
+
typeof current[k] !== "object" ||
|
|
1112
|
+
current[k] === null) {
|
|
1113
|
+
// Use Object.create(null) to prevent prototype pollution
|
|
1114
|
+
current[k] = Object.create(null);
|
|
1115
|
+
}
|
|
1116
|
+
current = current[k];
|
|
1117
|
+
}
|
|
1118
|
+
// Set the value (try to parse as JSON, otherwise keep as string)
|
|
1119
|
+
const finalKey = keys[keys.length - 1];
|
|
1120
|
+
try {
|
|
1121
|
+
const parsedValue = JSON.parse(value);
|
|
1122
|
+
Object.defineProperty(current, finalKey, {
|
|
1123
|
+
value: parsedValue,
|
|
1124
|
+
writable: true,
|
|
1125
|
+
enumerable: true,
|
|
1126
|
+
configurable: true,
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
catch {
|
|
1130
|
+
Object.defineProperty(current, finalKey, {
|
|
1131
|
+
value: value,
|
|
1132
|
+
writable: true,
|
|
1133
|
+
enumerable: true,
|
|
1134
|
+
configurable: true,
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
saveConfigFile(config);
|
|
1138
|
+
}
|
|
1139
|
+
function getConfigValue(key) {
|
|
1140
|
+
const config = loadConfigFile();
|
|
1141
|
+
const keys = key.split(".");
|
|
1142
|
+
let current = config;
|
|
1143
|
+
for (const k of keys) {
|
|
1144
|
+
if (typeof current !== "object" || current === null || !(k in current)) {
|
|
1145
|
+
return undefined;
|
|
1146
|
+
}
|
|
1147
|
+
current = current[k];
|
|
1148
|
+
}
|
|
1149
|
+
return current;
|
|
1150
|
+
}
|
|
1151
|
+
const CONTEXT_DIR = join(homedir(), ".synapsexcoder", "contexts");
|
|
1152
|
+
function ensureContextDir() {
|
|
1153
|
+
try {
|
|
1154
|
+
mkdirSync(CONTEXT_DIR, { recursive: true });
|
|
1155
|
+
}
|
|
1156
|
+
catch (error) {
|
|
1157
|
+
// Directory already exists or can't create - ignore
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function getContextPath(name) {
|
|
1161
|
+
return join(CONTEXT_DIR, `${name}.json`);
|
|
1162
|
+
}
|
|
1163
|
+
function saveContext(name, context) {
|
|
1164
|
+
ensureContextDir();
|
|
1165
|
+
const contextData = {
|
|
1166
|
+
name,
|
|
1167
|
+
savedAt: new Date().toISOString(),
|
|
1168
|
+
version: "5.0.0",
|
|
1169
|
+
data: context,
|
|
1170
|
+
};
|
|
1171
|
+
writeFileSync(getContextPath(name), JSON.stringify(contextData, null, 2));
|
|
1172
|
+
}
|
|
1173
|
+
function loadContext(name) {
|
|
1174
|
+
const path = getContextPath(name);
|
|
1175
|
+
if (!existsSync(path)) {
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
1178
|
+
try {
|
|
1179
|
+
const content = readFileSync(path, "utf-8");
|
|
1180
|
+
const contextData = JSON.parse(content);
|
|
1181
|
+
return contextData.data || {};
|
|
1182
|
+
}
|
|
1183
|
+
catch {
|
|
1184
|
+
return null;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
function listContexts() {
|
|
1188
|
+
ensureContextDir();
|
|
1189
|
+
try {
|
|
1190
|
+
const files = readdirSync(CONTEXT_DIR);
|
|
1191
|
+
return files
|
|
1192
|
+
.filter((file) => file.endsWith(".json"))
|
|
1193
|
+
.map((file) => {
|
|
1194
|
+
const name = file.replace(".json", "");
|
|
1195
|
+
const path = join(CONTEXT_DIR, file);
|
|
1196
|
+
const stat = statSync(path);
|
|
1197
|
+
let savedAt = "unknown";
|
|
1198
|
+
try {
|
|
1199
|
+
const content = readFileSync(path, "utf-8");
|
|
1200
|
+
const data = JSON.parse(content);
|
|
1201
|
+
savedAt = data.savedAt || "unknown";
|
|
1202
|
+
}
|
|
1203
|
+
catch {
|
|
1204
|
+
// Ignore parse errors
|
|
1205
|
+
}
|
|
1206
|
+
return { name, savedAt, size: stat.size };
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
catch {
|
|
1210
|
+
return [];
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function deleteContext(name) {
|
|
1214
|
+
const path = getContextPath(name);
|
|
1215
|
+
if (!existsSync(path)) {
|
|
1216
|
+
return false;
|
|
1217
|
+
}
|
|
1218
|
+
try {
|
|
1219
|
+
unlinkSync(path);
|
|
1220
|
+
return true;
|
|
1221
|
+
}
|
|
1222
|
+
catch {
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
function getCurrentContext() {
|
|
1227
|
+
return {
|
|
1228
|
+
mode: "full_pipeline",
|
|
1229
|
+
timestamp: new Date().toISOString(),
|
|
1230
|
+
version: "5.0.0",
|
|
1231
|
+
agents: getAgentList().map((a) => ({ name: a.name, status: a.status })),
|
|
1232
|
+
config: getDefaultConfig(),
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
async function loadConfig(configPath) {
|
|
1236
|
+
const defaults = getDefaultConfig();
|
|
1237
|
+
if (!configPath) {
|
|
1238
|
+
return loadConfigFile();
|
|
1239
|
+
}
|
|
1240
|
+
try {
|
|
1241
|
+
const content = readFileSync(resolve(configPath), "utf-8");
|
|
1242
|
+
const loaded = parseYaml(content);
|
|
1243
|
+
return {
|
|
1244
|
+
...defaults,
|
|
1245
|
+
...loaded,
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
catch {
|
|
1249
|
+
console.warn(chalk.yellow(`Warning: Could not load config at ${configPath}, using defaults`));
|
|
1250
|
+
return defaults;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
function getDefaultConfig() {
|
|
1254
|
+
return {
|
|
1255
|
+
version: "5.0.0",
|
|
1256
|
+
opencode: {
|
|
1257
|
+
mode: "embedded",
|
|
1258
|
+
serverUrl: "http://localhost:4096",
|
|
1259
|
+
},
|
|
1260
|
+
agents: {
|
|
1261
|
+
master: { model: "anthropic/claude-opus-4-6", temperature: 0.2 },
|
|
1262
|
+
planner: { model: "anthropic/claude-haiku-4-20250514", temperature: 0.1 },
|
|
1263
|
+
analyzer: {
|
|
1264
|
+
model: "anthropic/claude-sonnet-4-20250514",
|
|
1265
|
+
temperature: 0.1,
|
|
1266
|
+
},
|
|
1267
|
+
reviewer: {
|
|
1268
|
+
model: "anthropic/claude-sonnet-4-20250514",
|
|
1269
|
+
temperature: 0.2,
|
|
1270
|
+
},
|
|
1271
|
+
rewriter: { model: "anthropic/claude-opus-4-5", temperature: 0.3 },
|
|
1272
|
+
ui_designer: {
|
|
1273
|
+
model: "anthropic/claude-sonnet-4-20250514",
|
|
1274
|
+
temperature: 0.4,
|
|
1275
|
+
},
|
|
1276
|
+
researcher: {
|
|
1277
|
+
model: "anthropic/claude-haiku-4-20250514",
|
|
1278
|
+
temperature: 0.2,
|
|
1279
|
+
},
|
|
1280
|
+
},
|
|
1281
|
+
processing: {
|
|
1282
|
+
defaultMode: "full_pipeline",
|
|
1283
|
+
autoUIDetection: true,
|
|
1284
|
+
modelFallbacks: [
|
|
1285
|
+
"anthropic/claude-opus-4-6",
|
|
1286
|
+
"anthropic/claude-sonnet-4-20250514",
|
|
1287
|
+
"anthropic/claude-haiku-4-20250514",
|
|
1288
|
+
],
|
|
1289
|
+
},
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
function normalizeModelAliases(model) {
|
|
1293
|
+
const trimmed = model.trim();
|
|
1294
|
+
if (!trimmed)
|
|
1295
|
+
return [];
|
|
1296
|
+
const aliases = [trimmed];
|
|
1297
|
+
// If provider prefix is missing, try common provider-qualified variants.
|
|
1298
|
+
if (!trimmed.includes("/")) {
|
|
1299
|
+
aliases.push(`github-copilot/${trimmed}`);
|
|
1300
|
+
aliases.push(`openai/${trimmed}`);
|
|
1301
|
+
aliases.push(`anthropic/${trimmed}`);
|
|
1302
|
+
aliases.push(`groq/${trimmed}`);
|
|
1303
|
+
aliases.push(`together/${trimmed}`);
|
|
1304
|
+
}
|
|
1305
|
+
// Add specific model mappings for common names
|
|
1306
|
+
const modelMappings = {
|
|
1307
|
+
"claude-opus": ["anthropic/claude-opus-4-6", "anthropic/claude-opus-4-5"],
|
|
1308
|
+
"claude-sonnet": ["anthropic/claude-sonnet-4-20250514"],
|
|
1309
|
+
"claude-haiku": ["anthropic/claude-haiku-4-20250514"],
|
|
1310
|
+
"gpt-4": ["openai/gpt-4", "openai/gpt-4-turbo"],
|
|
1311
|
+
"gpt-3.5": ["openai/gpt-3.5-turbo"],
|
|
1312
|
+
grok: ["github-copilot/grok-code-fast-1"],
|
|
1313
|
+
};
|
|
1314
|
+
if (modelMappings[trimmed]) {
|
|
1315
|
+
aliases.push(...modelMappings[trimmed]);
|
|
1316
|
+
}
|
|
1317
|
+
return aliases;
|
|
1318
|
+
}
|
|
1319
|
+
function parseCsvList(raw) {
|
|
1320
|
+
if (!raw)
|
|
1321
|
+
return [];
|
|
1322
|
+
return raw
|
|
1323
|
+
.split(",")
|
|
1324
|
+
.map((v) => v.trim())
|
|
1325
|
+
.filter(Boolean);
|
|
1326
|
+
}
|
|
1327
|
+
function isProviderModelNotFoundError(error) {
|
|
1328
|
+
const text = String(error || "");
|
|
1329
|
+
return (text.includes("ProviderModelNotFoundError") ||
|
|
1330
|
+
text.includes("ModelNotFound") ||
|
|
1331
|
+
text.includes("model not found") ||
|
|
1332
|
+
text.includes("provider/model"));
|
|
1333
|
+
}
|
|
1334
|
+
function hasProviderModelNotFoundInResult(result) {
|
|
1335
|
+
if (!result || typeof result !== "object")
|
|
1336
|
+
return false;
|
|
1337
|
+
const asRecord = result;
|
|
1338
|
+
if (isProviderModelNotFoundError(asRecord.error)) {
|
|
1339
|
+
return true;
|
|
1340
|
+
}
|
|
1341
|
+
const output = asRecord.output;
|
|
1342
|
+
if (!output || typeof output !== "object") {
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
const phases = output["phases"];
|
|
1346
|
+
if (!Array.isArray(phases)) {
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
return phases.some((phase) => {
|
|
1350
|
+
if (!phase || typeof phase !== "object")
|
|
1351
|
+
return false;
|
|
1352
|
+
const phaseRecord = phase;
|
|
1353
|
+
if (isProviderModelNotFoundError(phaseRecord.error)) {
|
|
1354
|
+
return true;
|
|
1355
|
+
}
|
|
1356
|
+
const phaseOutput = phaseRecord.output;
|
|
1357
|
+
if (phaseOutput && typeof phaseOutput === "object") {
|
|
1358
|
+
return isProviderModelNotFoundError(phaseOutput.error);
|
|
1359
|
+
}
|
|
1360
|
+
return false;
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
function getByPath(obj, path) {
|
|
1364
|
+
const keys = path.split(".");
|
|
1365
|
+
let current = obj;
|
|
1366
|
+
for (const key of keys) {
|
|
1367
|
+
if (typeof current !== "object" || current === null || !(key in current)) {
|
|
1368
|
+
return undefined;
|
|
1369
|
+
}
|
|
1370
|
+
current = current[key];
|
|
1371
|
+
}
|
|
1372
|
+
return current;
|
|
1373
|
+
}
|
|
1374
|
+
function resolveModelCandidates(options, config) {
|
|
1375
|
+
const candidates = [];
|
|
1376
|
+
// Highest priority: explicit user-selected model(s).
|
|
1377
|
+
// Always try the user selection first, then configured fallbacks.
|
|
1378
|
+
// This keeps user intent while still preventing hard failures when a
|
|
1379
|
+
// provider cannot resolve the selected model.
|
|
1380
|
+
const cliModels = parseCsvList(options["model"]);
|
|
1381
|
+
if (cliModels.length > 0) {
|
|
1382
|
+
candidates.push(...cliModels);
|
|
1383
|
+
// If user supplied bare model IDs (without provider prefix), expand aliases
|
|
1384
|
+
// after the exact choice so provider-qualified variants can still work.
|
|
1385
|
+
for (const model of cliModels) {
|
|
1386
|
+
candidates.push(...normalizeModelAliases(model));
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
// Next: saved config master model
|
|
1390
|
+
const configuredMasterModel = getByPath(config, "agents.master.model");
|
|
1391
|
+
if (typeof configuredMasterModel === "string") {
|
|
1392
|
+
candidates.push(...normalizeModelAliases(configuredMasterModel));
|
|
1393
|
+
}
|
|
1394
|
+
// Optional fallback list from config
|
|
1395
|
+
const configuredFallbacks = getByPath(config, "processing.modelFallbacks");
|
|
1396
|
+
if (Array.isArray(configuredFallbacks)) {
|
|
1397
|
+
for (const model of configuredFallbacks) {
|
|
1398
|
+
if (typeof model === "string") {
|
|
1399
|
+
candidates.push(...normalizeModelAliases(model));
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
// Optional fallback list from env (comma-separated)
|
|
1404
|
+
const envFallbacks = parseCsvList(process.env.DUCKY_MODEL_FALLBACKS);
|
|
1405
|
+
for (const model of envFallbacks) {
|
|
1406
|
+
candidates.push(...normalizeModelAliases(model));
|
|
1407
|
+
}
|
|
1408
|
+
// Hard fallback
|
|
1409
|
+
candidates.push(...normalizeModelAliases("anthropic/claude-opus-4-6"), ...normalizeModelAliases("anthropic/claude-sonnet-4-20250514"), ...normalizeModelAliases("anthropic/claude-haiku-4-20250514"), ...normalizeModelAliases("github-copilot/grok-code-fast-1"), ...normalizeModelAliases("openai/gpt-4-turbo"));
|
|
1410
|
+
// De-duplicate while preserving order
|
|
1411
|
+
return [...new Set(candidates)];
|
|
1412
|
+
}
|
|
1413
|
+
/** Supported code file extensions for validation */
|
|
1414
|
+
const SUPPORTED_EXTENSIONS = new Set([
|
|
1415
|
+
".ts",
|
|
1416
|
+
".tsx",
|
|
1417
|
+
".js",
|
|
1418
|
+
".jsx",
|
|
1419
|
+
".py",
|
|
1420
|
+
".rs",
|
|
1421
|
+
".go",
|
|
1422
|
+
".java",
|
|
1423
|
+
".c",
|
|
1424
|
+
".cpp",
|
|
1425
|
+
".h",
|
|
1426
|
+
".css",
|
|
1427
|
+
".scss",
|
|
1428
|
+
".html",
|
|
1429
|
+
".json",
|
|
1430
|
+
".yaml",
|
|
1431
|
+
".yml",
|
|
1432
|
+
".md",
|
|
1433
|
+
".sh",
|
|
1434
|
+
".bash",
|
|
1435
|
+
".sql",
|
|
1436
|
+
".graphql",
|
|
1437
|
+
".xml",
|
|
1438
|
+
".toml",
|
|
1439
|
+
]);
|
|
1440
|
+
/** Size thresholds in bytes */
|
|
1441
|
+
const WARN_SIZE_THRESHOLD = 100 * 1024; // 100KB
|
|
1442
|
+
const REJECT_SIZE_THRESHOLD = 1024 * 1024; // 1MB
|
|
1443
|
+
/**
|
|
1444
|
+
* Check if a file appears to be binary by reading its first chunk.
|
|
1445
|
+
* Returns true if the file contains null bytes (a strong binary indicator).
|
|
1446
|
+
*/
|
|
1447
|
+
function isBinaryFile(filePath) {
|
|
1448
|
+
try {
|
|
1449
|
+
// Read first 8KB to check for binary content
|
|
1450
|
+
const fd = openSync(filePath, "r");
|
|
1451
|
+
const buf = Buffer.alloc(8192);
|
|
1452
|
+
const bytesRead = readSync(fd, buf, 0, 8192, 0);
|
|
1453
|
+
closeSync(fd);
|
|
1454
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
1455
|
+
if (buf[i] === 0)
|
|
1456
|
+
return true; // Null byte ā binary
|
|
1457
|
+
}
|
|
1458
|
+
return false;
|
|
1459
|
+
}
|
|
1460
|
+
catch {
|
|
1461
|
+
return false; // If we can't read, let downstream handle it
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
async function validateInputs(inputs) {
|
|
1465
|
+
const valid = [];
|
|
1466
|
+
const cwd = process.cwd();
|
|
1467
|
+
for (const input of inputs) {
|
|
1468
|
+
// Resolve to absolute path safely
|
|
1469
|
+
let abs;
|
|
1470
|
+
try {
|
|
1471
|
+
abs = validateAndResolve(input, cwd);
|
|
1472
|
+
}
|
|
1473
|
+
catch (error) {
|
|
1474
|
+
console.warn(chalk.red(` ā Rejected: ${input} ā ${error instanceof Error ? error.message : String(error)}`));
|
|
1475
|
+
continue;
|
|
1476
|
+
}
|
|
1477
|
+
// Check existence
|
|
1478
|
+
if (!existsSync(abs)) {
|
|
1479
|
+
console.warn(chalk.red(` ā Rejected: ${input} ā file not found`));
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
const stat = statSync(abs);
|
|
1483
|
+
// Handle directories: skip with note (pipeline expects files)
|
|
1484
|
+
if (stat.isDirectory()) {
|
|
1485
|
+
console.warn(chalk.yellow(` ā Skipped directory: ${input} ā pass individual files instead`));
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
if (!stat.isFile()) {
|
|
1489
|
+
console.warn(chalk.red(` ā Rejected: ${input} ā not a regular file`));
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
// Check file extension
|
|
1493
|
+
const ext = extname(abs).toLowerCase();
|
|
1494
|
+
if (!SUPPORTED_EXTENSIONS.has(ext)) {
|
|
1495
|
+
console.warn(chalk.red(` ā Rejected: ${input} ā unsupported extension "${ext}" (supported: ${[...SUPPORTED_EXTENSIONS].join(", ")})`));
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
// Check file size: reject if > 1MB, warn if > 100KB
|
|
1499
|
+
if (stat.size > REJECT_SIZE_THRESHOLD) {
|
|
1500
|
+
console.warn(chalk.red(` ā Rejected: ${input} ā file too large (${(stat.size / 1024 / 1024).toFixed(2)} MB, max 1 MB)`));
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
if (stat.size > WARN_SIZE_THRESHOLD) {
|
|
1504
|
+
console.warn(chalk.yellow(` ā Warning: ${input} is large (${(stat.size / 1024).toFixed(1)} KB) ā processing may be slow`));
|
|
1505
|
+
}
|
|
1506
|
+
// Check for binary content
|
|
1507
|
+
if (isBinaryFile(abs)) {
|
|
1508
|
+
console.warn(chalk.red(` ā Rejected: ${input} ā appears to be a binary file`));
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
valid.push(abs);
|
|
1512
|
+
}
|
|
1513
|
+
if (valid.length === 0 && inputs.length > 0) {
|
|
1514
|
+
throw new Error("No valid input files after validation. Check paths, extensions, and file sizes.");
|
|
1515
|
+
}
|
|
1516
|
+
return valid;
|
|
1517
|
+
}
|
|
1518
|
+
async function executePipeline(inputs, options, runtimeConfig, gatekeeperConfig) {
|
|
1519
|
+
// --- Bootstrap infrastructure ---
|
|
1520
|
+
const messageBus = new MessageBus({ enableLogging: false });
|
|
1521
|
+
const toolBridge = new OpenCodeToolBridge();
|
|
1522
|
+
const modelCandidates = resolveModelCandidates(options, runtimeConfig);
|
|
1523
|
+
// --- Read input files into ExecutionContext ---
|
|
1524
|
+
const files = readInputFiles(inputs);
|
|
1525
|
+
const mode = (options["mode"] ||
|
|
1526
|
+
"full_pipeline");
|
|
1527
|
+
const context = {
|
|
1528
|
+
files,
|
|
1529
|
+
constraints: [],
|
|
1530
|
+
mode,
|
|
1531
|
+
metadata: {
|
|
1532
|
+
mode,
|
|
1533
|
+
dryRun: options["dryRun"] ?? false,
|
|
1534
|
+
outputDir: options["outputDir"] ?? "./synapsexcoder_output",
|
|
1535
|
+
needsResearch: options["research"] === true,
|
|
1536
|
+
generateDocs: options["docs"] === true,
|
|
1537
|
+
sessionId: options["session"],
|
|
1538
|
+
parallel: options["parallel"] ?? true,
|
|
1539
|
+
gatekeeperEnabled: gatekeeperConfig?.enabled ?? true,
|
|
1540
|
+
gatekeeperReviewLevel: gatekeeperConfig?.reviewLevel ?? "standard",
|
|
1541
|
+
gatekeeperMaxRevisions: gatekeeperConfig?.maxRevisions ?? 3,
|
|
1542
|
+
gatekeeperStrictMode: gatekeeperConfig?.strictMode ?? false,
|
|
1543
|
+
},
|
|
1544
|
+
};
|
|
1545
|
+
const task = {
|
|
1546
|
+
id: uuidv4(),
|
|
1547
|
+
type: "master",
|
|
1548
|
+
prompt: `Process ${files.length} file(s) in ${mode} mode`,
|
|
1549
|
+
context,
|
|
1550
|
+
successCriteria: "Complete all pipeline phases without errors",
|
|
1551
|
+
timeout: 120_000,
|
|
1552
|
+
};
|
|
1553
|
+
// --- Execute with dynamic model fallback ---
|
|
1554
|
+
let result;
|
|
1555
|
+
let lastError = null;
|
|
1556
|
+
for (const model of modelCandidates) {
|
|
1557
|
+
const master = new MasterAgent(messageBus, toolBridge, { model });
|
|
1558
|
+
// Configure gatekeeper if enabled
|
|
1559
|
+
if (gatekeeperConfig) {
|
|
1560
|
+
master.setGatekeeperEnabled(gatekeeperConfig.enabled);
|
|
1561
|
+
master.setGatekeeperReviewLevel(gatekeeperConfig.reviewLevel);
|
|
1562
|
+
master.setGatekeeperMaxRevisions(gatekeeperConfig.maxRevisions);
|
|
1563
|
+
}
|
|
1564
|
+
try {
|
|
1565
|
+
result = await master.execute(task);
|
|
1566
|
+
if (result.success) {
|
|
1567
|
+
break;
|
|
1568
|
+
}
|
|
1569
|
+
// Retry only when the failure indicates missing model/provider.
|
|
1570
|
+
if (hasProviderModelNotFoundInResult(result)) {
|
|
1571
|
+
console.warn(chalk.yellow(` Model unavailable: ${model} ā trying next fallback...`));
|
|
1572
|
+
lastError = result.error;
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
// For non-model errors, stop early.
|
|
1576
|
+
break;
|
|
1577
|
+
}
|
|
1578
|
+
catch (error) {
|
|
1579
|
+
if (isProviderModelNotFoundError(error)) {
|
|
1580
|
+
console.warn(chalk.yellow(` Model unavailable: ${model} ā trying next fallback...`));
|
|
1581
|
+
lastError = error;
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
throw error;
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
if (!result) {
|
|
1588
|
+
throw new Error(`All model candidates failed. Last error: ${String(lastError ?? "unknown")}`);
|
|
1589
|
+
}
|
|
1590
|
+
// --- Format for display ---
|
|
1591
|
+
const output = result.output;
|
|
1592
|
+
const phases = output?.["phases"] ?? [];
|
|
1593
|
+
return {
|
|
1594
|
+
status: result.success ? "success" : "failed",
|
|
1595
|
+
error: result.error,
|
|
1596
|
+
inputs: files.length,
|
|
1597
|
+
artifacts: result.artifacts.length,
|
|
1598
|
+
summary: {
|
|
1599
|
+
filesProcessed: files.length,
|
|
1600
|
+
issuesFound: phases.reduce((n, p) => n +
|
|
1601
|
+
(p["output"]?.["issues"] ?? []).length, 0),
|
|
1602
|
+
issuesFixed: 0,
|
|
1603
|
+
successfulPhases: output?.["successfulPhases"] ?? 0,
|
|
1604
|
+
totalPhases: output?.["totalPhases"] ?? 0,
|
|
1605
|
+
},
|
|
1606
|
+
phases,
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function readInputFiles(inputs) {
|
|
1610
|
+
const files = [];
|
|
1611
|
+
const cwd = process.cwd();
|
|
1612
|
+
for (const input of inputs) {
|
|
1613
|
+
// Validate path to prevent traversal attacks
|
|
1614
|
+
let abs;
|
|
1615
|
+
try {
|
|
1616
|
+
abs = validateAndResolve(input, cwd);
|
|
1617
|
+
}
|
|
1618
|
+
catch (error) {
|
|
1619
|
+
console.warn(chalk.yellow(` Warning: invalid path skipped: ${input} (${error instanceof Error ? error.message : String(error)})`));
|
|
1620
|
+
continue;
|
|
1621
|
+
}
|
|
1622
|
+
if (!existsSync(abs)) {
|
|
1623
|
+
console.warn(chalk.yellow(` Warning: path not found, skipping: ${input}`));
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
const stat = statSync(abs);
|
|
1627
|
+
if (stat.isFile()) {
|
|
1628
|
+
try {
|
|
1629
|
+
const content = readFileSync(abs, "utf-8");
|
|
1630
|
+
files.push({
|
|
1631
|
+
path: abs,
|
|
1632
|
+
content,
|
|
1633
|
+
language: extToLanguage(extname(abs)),
|
|
1634
|
+
metadata: { size: stat.size, modified: stat.mtimeMs },
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
catch {
|
|
1638
|
+
console.warn(chalk.yellow(` Warning: could not read file: ${input}`));
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
else if (stat.isDirectory()) {
|
|
1642
|
+
console.warn(chalk.yellow(` Note: directory input "${input}" ā pass individual files for now`));
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
return files;
|
|
1646
|
+
}
|
|
1647
|
+
function extToLanguage(ext) {
|
|
1648
|
+
const map = {
|
|
1649
|
+
".ts": "typescript",
|
|
1650
|
+
".tsx": "typescript",
|
|
1651
|
+
".js": "javascript",
|
|
1652
|
+
".jsx": "javascript",
|
|
1653
|
+
".py": "python",
|
|
1654
|
+
".rs": "rust",
|
|
1655
|
+
".go": "go",
|
|
1656
|
+
".sh": "bash",
|
|
1657
|
+
".md": "markdown",
|
|
1658
|
+
".json": "json",
|
|
1659
|
+
".yaml": "yaml",
|
|
1660
|
+
".yml": "yaml",
|
|
1661
|
+
".html": "html",
|
|
1662
|
+
".css": "css",
|
|
1663
|
+
".scss": "scss",
|
|
1664
|
+
".svelte": "svelte",
|
|
1665
|
+
".vue": "vue",
|
|
1666
|
+
".rb": "ruby",
|
|
1667
|
+
".java": "java",
|
|
1668
|
+
".cs": "csharp",
|
|
1669
|
+
};
|
|
1670
|
+
return map[ext] ?? "text";
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Perform real static analysis on an input file.
|
|
1674
|
+
*
|
|
1675
|
+
* Calculates actual lines of code (excluding blanks and comments),
|
|
1676
|
+
* estimates cyclomatic complexity, computes a maintainability index
|
|
1677
|
+
* using the standard formula, and detects common code issues.
|
|
1678
|
+
*/
|
|
1679
|
+
async function analyzeInput(input, _options) {
|
|
1680
|
+
const cwd = process.cwd();
|
|
1681
|
+
let abs;
|
|
1682
|
+
try {
|
|
1683
|
+
abs = validateAndResolve(input, cwd);
|
|
1684
|
+
}
|
|
1685
|
+
catch {
|
|
1686
|
+
abs = resolve(input);
|
|
1687
|
+
}
|
|
1688
|
+
if (!existsSync(abs)) {
|
|
1689
|
+
return {
|
|
1690
|
+
file: input,
|
|
1691
|
+
issues: [
|
|
1692
|
+
{
|
|
1693
|
+
type: "error",
|
|
1694
|
+
rule: "file-not-found",
|
|
1695
|
+
message: `File not found: ${input}`,
|
|
1696
|
+
line: 0,
|
|
1697
|
+
},
|
|
1698
|
+
],
|
|
1699
|
+
metrics: { linesOfCode: 0, complexity: 0, maintainabilityIndex: 0 },
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
let content;
|
|
1703
|
+
try {
|
|
1704
|
+
content = readFileSync(abs, "utf-8");
|
|
1705
|
+
}
|
|
1706
|
+
catch (err) {
|
|
1707
|
+
return {
|
|
1708
|
+
file: input,
|
|
1709
|
+
issues: [
|
|
1710
|
+
{
|
|
1711
|
+
type: "error",
|
|
1712
|
+
rule: "read-error",
|
|
1713
|
+
message: `Cannot read file: ${err instanceof Error ? err.message : String(err)}`,
|
|
1714
|
+
line: 0,
|
|
1715
|
+
},
|
|
1716
|
+
],
|
|
1717
|
+
metrics: { linesOfCode: 0, complexity: 0, maintainabilityIndex: 0 },
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
const rawLines = content.split("\n");
|
|
1721
|
+
const totalLines = rawLines.length;
|
|
1722
|
+
const issues = [];
|
|
1723
|
+
// --- Count lines of code (excluding blanks and single-line comments) ---
|
|
1724
|
+
let linesOfCode = 0;
|
|
1725
|
+
let inBlockComment = false;
|
|
1726
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
1727
|
+
const trimmed = rawLines[i].trim();
|
|
1728
|
+
// Track block comments (/* ... */)
|
|
1729
|
+
if (inBlockComment) {
|
|
1730
|
+
if (trimmed.includes("*/")) {
|
|
1731
|
+
inBlockComment = false;
|
|
1732
|
+
}
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
if (trimmed.startsWith("/*")) {
|
|
1736
|
+
inBlockComment = true;
|
|
1737
|
+
if (trimmed.includes("*/")) {
|
|
1738
|
+
inBlockComment = false;
|
|
1739
|
+
}
|
|
1740
|
+
continue;
|
|
1741
|
+
}
|
|
1742
|
+
// Skip blank lines and single-line comments
|
|
1743
|
+
if (trimmed === "")
|
|
1744
|
+
continue;
|
|
1745
|
+
if (trimmed.startsWith("//"))
|
|
1746
|
+
continue;
|
|
1747
|
+
if (trimmed.startsWith("#") && !trimmed.startsWith("#!"))
|
|
1748
|
+
continue; // Python/shell comments
|
|
1749
|
+
linesOfCode++;
|
|
1750
|
+
}
|
|
1751
|
+
// --- Estimate cyclomatic complexity ---
|
|
1752
|
+
// Count control flow keywords/operators that add decision points
|
|
1753
|
+
const controlFlowPatterns = [
|
|
1754
|
+
/\bif\b/g,
|
|
1755
|
+
/\belse\s+if\b/g,
|
|
1756
|
+
/\bfor\b/g,
|
|
1757
|
+
/\bwhile\b/g,
|
|
1758
|
+
/\bswitch\b/g,
|
|
1759
|
+
/\bcase\b/g,
|
|
1760
|
+
/\bcatch\b/g,
|
|
1761
|
+
/&&/g,
|
|
1762
|
+
/\|\|/g,
|
|
1763
|
+
/\?\s*[^:]/g, // Ternary operator (? not followed immediately by nothing)
|
|
1764
|
+
];
|
|
1765
|
+
let cyclomaticComplexity = 1; // Base complexity
|
|
1766
|
+
for (const pattern of controlFlowPatterns) {
|
|
1767
|
+
const matches = content.match(pattern);
|
|
1768
|
+
if (matches) {
|
|
1769
|
+
cyclomaticComplexity += matches.length;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
// --- Halstead volume approximation ---
|
|
1773
|
+
// Count unique operators and operands for a rough Halstead volume
|
|
1774
|
+
const operatorPattern = /[+\-*/%=<>!&|^~?:;,.{}[\]()]/g;
|
|
1775
|
+
const operandPattern = /\b[a-zA-Z_$][a-zA-Z0-9_$]*\b/g;
|
|
1776
|
+
const numericPattern = /\b\d+(\.\d+)?\b/g;
|
|
1777
|
+
const stringPattern = /(["'`])(?:(?=(\\?))\2.)*?\1/g;
|
|
1778
|
+
const operators = content.match(operatorPattern) || [];
|
|
1779
|
+
const operands = content.match(operandPattern) || [];
|
|
1780
|
+
const numerics = content.match(numericPattern) || [];
|
|
1781
|
+
const strings = content.match(stringPattern) || [];
|
|
1782
|
+
const uniqueOperators = new Set(operators).size || 1;
|
|
1783
|
+
const uniqueOperands = new Set([...operands, ...numerics, ...strings]).size || 1;
|
|
1784
|
+
const totalOperators = operators.length || 1;
|
|
1785
|
+
const totalOperands = operands.length + numerics.length + strings.length || 1;
|
|
1786
|
+
const vocabulary = uniqueOperators + uniqueOperands;
|
|
1787
|
+
const programLength = totalOperators + totalOperands;
|
|
1788
|
+
const halsteadVolume = programLength * Math.log2(vocabulary || 2);
|
|
1789
|
+
// --- Maintainability Index (standard formula) ---
|
|
1790
|
+
// MI = 171 - 5.2 * ln(HV) - 0.23 * CC - 16.2 * ln(LOC), clamped to [0, 100]
|
|
1791
|
+
const safeLOC = Math.max(linesOfCode, 1);
|
|
1792
|
+
const safeHV = Math.max(halsteadVolume, 1);
|
|
1793
|
+
let maintainabilityIndex = 171 -
|
|
1794
|
+
5.2 * Math.log(safeHV) -
|
|
1795
|
+
0.23 * cyclomaticComplexity -
|
|
1796
|
+
16.2 * Math.log(safeLOC);
|
|
1797
|
+
maintainabilityIndex = Math.max(0, Math.min(100, maintainabilityIndex));
|
|
1798
|
+
maintainabilityIndex = Math.round(maintainabilityIndex * 100) / 100;
|
|
1799
|
+
// --- Detect code issues ---
|
|
1800
|
+
// 1. Very long functions (>50 lines)
|
|
1801
|
+
const functionStartPattern = /^(\s*)((?:export\s+)?(?:async\s+)?(?:function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:\([^)]*\)|[a-zA-Z_$]\w*)\s*=>))/;
|
|
1802
|
+
let funcStartLine = -1;
|
|
1803
|
+
let braceDepth = 0;
|
|
1804
|
+
let inFunction = false;
|
|
1805
|
+
let funcName = "";
|
|
1806
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
1807
|
+
const line = rawLines[i];
|
|
1808
|
+
const match = line.match(functionStartPattern);
|
|
1809
|
+
if (match && !inFunction) {
|
|
1810
|
+
funcStartLine = i;
|
|
1811
|
+
// funcIndent removed as unused
|
|
1812
|
+
funcName = match[2].trim().substring(0, 60);
|
|
1813
|
+
braceDepth = 0;
|
|
1814
|
+
inFunction = true;
|
|
1815
|
+
}
|
|
1816
|
+
if (inFunction) {
|
|
1817
|
+
for (const ch of line) {
|
|
1818
|
+
if (ch === "{")
|
|
1819
|
+
braceDepth++;
|
|
1820
|
+
if (ch === "}")
|
|
1821
|
+
braceDepth--;
|
|
1822
|
+
}
|
|
1823
|
+
if (braceDepth <= 0 && i > funcStartLine) {
|
|
1824
|
+
const funcLength = i - funcStartLine + 1;
|
|
1825
|
+
if (funcLength > 50) {
|
|
1826
|
+
issues.push({
|
|
1827
|
+
type: "warning",
|
|
1828
|
+
rule: "long-function",
|
|
1829
|
+
message: `Function is too long (${funcLength} lines, max 50): ${funcName}`,
|
|
1830
|
+
line: funcStartLine + 1,
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
inFunction = false;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
// 2. Deeply nested code (>4 levels)
|
|
1838
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
1839
|
+
const line = rawLines[i];
|
|
1840
|
+
const leadingSpaces = line.match(/^(\s*)/)?.[1] || "";
|
|
1841
|
+
// Estimate indentation level (2 or 4 spaces, or tabs)
|
|
1842
|
+
const tabCount = (leadingSpaces.match(/\t/g) || []).length;
|
|
1843
|
+
const spaceIndent = leadingSpaces.replace(/\t/g, "").length;
|
|
1844
|
+
const indentLevel = tabCount + Math.floor(spaceIndent / 2);
|
|
1845
|
+
if (indentLevel > 4 &&
|
|
1846
|
+
line.trim().length > 0 &&
|
|
1847
|
+
!line.trim().startsWith("//") &&
|
|
1848
|
+
!line.trim().startsWith("*")) {
|
|
1849
|
+
issues.push({
|
|
1850
|
+
type: "warning",
|
|
1851
|
+
rule: "deep-nesting",
|
|
1852
|
+
message: `Deeply nested code (level ${indentLevel}, max 4)`,
|
|
1853
|
+
line: i + 1,
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
// Deduplicate deep-nesting: keep only first occurrence per contiguous block
|
|
1858
|
+
const nestingIssues = issues.filter((i) => i.rule === "deep-nesting");
|
|
1859
|
+
const keptNestingLines = new Set();
|
|
1860
|
+
for (let j = 0; j < nestingIssues.length; j++) {
|
|
1861
|
+
if (j === 0 || nestingIssues[j].line - nestingIssues[j - 1].line > 1) {
|
|
1862
|
+
keptNestingLines.add(nestingIssues[j].line);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
// Remove non-kept nesting issues
|
|
1866
|
+
for (let j = issues.length - 1; j >= 0; j--) {
|
|
1867
|
+
if (issues[j].rule === "deep-nesting" &&
|
|
1868
|
+
!keptNestingLines.has(issues[j].line)) {
|
|
1869
|
+
issues.splice(j, 1);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
// 3. TODO/FIXME/HACK comments
|
|
1873
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
1874
|
+
const line = rawLines[i];
|
|
1875
|
+
const todoMatch = line.match(/\b(TODO|FIXME|HACK|XXX)\b/i);
|
|
1876
|
+
if (todoMatch) {
|
|
1877
|
+
issues.push({
|
|
1878
|
+
type: "info",
|
|
1879
|
+
rule: "todo-comment",
|
|
1880
|
+
message: `${todoMatch[1].toUpperCase()} comment found: ${line.trim().substring(0, 80)}`,
|
|
1881
|
+
line: i + 1,
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
// 4. console.log left in code
|
|
1886
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
1887
|
+
const line = rawLines[i];
|
|
1888
|
+
if (/\bconsole\.(log|debug|info)\s*\(/.test(line) &&
|
|
1889
|
+
!line.trim().startsWith("//")) {
|
|
1890
|
+
issues.push({
|
|
1891
|
+
type: "warning",
|
|
1892
|
+
rule: "console-statement",
|
|
1893
|
+
message: `console statement found ā consider removing for production`,
|
|
1894
|
+
line: i + 1,
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
// 5. Unused imports (basic detection ā look for imports not referenced elsewhere)
|
|
1899
|
+
const importPattern = /import\s+(?:\{([^}]+)\}|(\w+))\s+from/g;
|
|
1900
|
+
let importMatch;
|
|
1901
|
+
while ((importMatch = importPattern.exec(content)) !== null) {
|
|
1902
|
+
const names = importMatch[1]
|
|
1903
|
+
? importMatch[1].split(",").map((n) => n
|
|
1904
|
+
.trim()
|
|
1905
|
+
.replace(/\s+as\s+\w+/, "")
|
|
1906
|
+
.trim())
|
|
1907
|
+
: importMatch[2]
|
|
1908
|
+
? [importMatch[2]]
|
|
1909
|
+
: [];
|
|
1910
|
+
for (const name of names) {
|
|
1911
|
+
if (!name || name === "type")
|
|
1912
|
+
continue;
|
|
1913
|
+
// Count occurrences outside of import lines
|
|
1914
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1915
|
+
const usagePattern = new RegExp(`\\b${escaped}\\b`, "g");
|
|
1916
|
+
const allMatches = content.match(usagePattern) || [];
|
|
1917
|
+
// If it only appears once (the import itself), it's likely unused
|
|
1918
|
+
if (allMatches.length <= 1) {
|
|
1919
|
+
const importLine = content
|
|
1920
|
+
.substring(0, importMatch.index)
|
|
1921
|
+
.split("\n").length;
|
|
1922
|
+
issues.push({
|
|
1923
|
+
type: "warning",
|
|
1924
|
+
rule: "unused-import",
|
|
1925
|
+
message: `Possibly unused import: "${name}"`,
|
|
1926
|
+
line: importLine,
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
// 6. Overly complex expressions (lines with many operators)
|
|
1932
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
1933
|
+
const line = rawLines[i];
|
|
1934
|
+
const ops = (line.match(/&&|\|\||[?:]/g) || []).length;
|
|
1935
|
+
if (ops >= 5) {
|
|
1936
|
+
issues.push({
|
|
1937
|
+
type: "warning",
|
|
1938
|
+
rule: "complex-expression",
|
|
1939
|
+
message: `Overly complex expression (${ops} logical/ternary operators on one line)`,
|
|
1940
|
+
line: i + 1,
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
return {
|
|
1945
|
+
file: input,
|
|
1946
|
+
resolvedPath: abs,
|
|
1947
|
+
totalLines,
|
|
1948
|
+
issues,
|
|
1949
|
+
issueCount: issues.length,
|
|
1950
|
+
metrics: {
|
|
1951
|
+
linesOfCode,
|
|
1952
|
+
blankLines: totalLines -
|
|
1953
|
+
linesOfCode -
|
|
1954
|
+
(totalLines - rawLines.filter((l) => l.trim() !== "").length),
|
|
1955
|
+
cyclomaticComplexity,
|
|
1956
|
+
halsteadVolume: Math.round(halsteadVolume * 100) / 100,
|
|
1957
|
+
maintainabilityIndex,
|
|
1958
|
+
},
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Smart merge that deduplicates, groups by type/directory, preserves import
|
|
1963
|
+
* order, detects conflicts, and adds rich section headers.
|
|
1964
|
+
*/
|
|
1965
|
+
async function mergeInputs(inputs, _options) {
|
|
1966
|
+
const files = readInputFiles(inputs);
|
|
1967
|
+
if (files.length === 0) {
|
|
1968
|
+
throw new Error("No valid input files found");
|
|
1969
|
+
}
|
|
1970
|
+
if (files.length === 1) {
|
|
1971
|
+
return files[0].content;
|
|
1972
|
+
}
|
|
1973
|
+
const strategy = _options["strategy"] || "semantic";
|
|
1974
|
+
const merged = [];
|
|
1975
|
+
const conflicts = [];
|
|
1976
|
+
// --- Deduplicate identical content ---
|
|
1977
|
+
const seen = new Map(); // content hash ā first file path
|
|
1978
|
+
const uniqueFiles = [];
|
|
1979
|
+
for (const file of files) {
|
|
1980
|
+
// Simple content-based dedup using a hash of trimmed content
|
|
1981
|
+
const contentKey = file.content.trim();
|
|
1982
|
+
if (seen.has(contentKey)) {
|
|
1983
|
+
console.warn(chalk.yellow(` ā Duplicate content skipped: ${file.path} (same as ${seen.get(contentKey)})`));
|
|
1984
|
+
continue;
|
|
1985
|
+
}
|
|
1986
|
+
seen.set(contentKey, file.path);
|
|
1987
|
+
uniqueFiles.push(file);
|
|
1988
|
+
}
|
|
1989
|
+
// --- Group files by directory, then by extension ---
|
|
1990
|
+
const dirGroups = new Map();
|
|
1991
|
+
for (const file of uniqueFiles) {
|
|
1992
|
+
const dir = file.path.substring(0, file.path.lastIndexOf("/")) || ".";
|
|
1993
|
+
if (!dirGroups.has(dir))
|
|
1994
|
+
dirGroups.set(dir, []);
|
|
1995
|
+
dirGroups.get(dir).push(file);
|
|
1996
|
+
}
|
|
1997
|
+
// Sort groups: directories alphabetically
|
|
1998
|
+
const sortedDirs = [...dirGroups.keys()].sort();
|
|
1999
|
+
// --- Detect potential naming conflicts across files ---
|
|
2000
|
+
// Look for same function/class/const names exported or declared in different files
|
|
2001
|
+
const declaredNames = new Map(); // name ā file paths
|
|
2002
|
+
const declarationPattern = /(?:export\s+)?(?:function|class|const|let|var|interface|type|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
|
|
2003
|
+
for (const file of uniqueFiles) {
|
|
2004
|
+
let match;
|
|
2005
|
+
const localPattern = new RegExp(declarationPattern.source, "g");
|
|
2006
|
+
while ((match = localPattern.exec(file.content)) !== null) {
|
|
2007
|
+
const name = match[1];
|
|
2008
|
+
if (!declaredNames.has(name))
|
|
2009
|
+
declaredNames.set(name, []);
|
|
2010
|
+
declaredNames.get(name).push(file.path);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
for (const [name, paths] of declaredNames.entries()) {
|
|
2014
|
+
if (paths.length > 1) {
|
|
2015
|
+
conflicts.push(`"${name}" is declared in multiple files: ${paths.map((p) => p.split("/").pop()).join(", ")}`);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
// --- Build merge header ---
|
|
2019
|
+
merged.push(`// ============================================================================`);
|
|
2020
|
+
// Backwards-compatible header line expected by older tests/tools
|
|
2021
|
+
merged.push(`// Merged ${uniqueFiles.length} files`);
|
|
2022
|
+
merged.push(`// Merged ${uniqueFiles.length} file(s) using "${strategy}" strategy`);
|
|
2023
|
+
merged.push(`// Generated by SynapseXcoder V1 on ${new Date().toISOString()}`);
|
|
2024
|
+
if (uniqueFiles.length < files.length) {
|
|
2025
|
+
merged.push(`// Deduplicated: ${files.length - uniqueFiles.length} duplicate(s) removed`);
|
|
2026
|
+
}
|
|
2027
|
+
if (conflicts.length > 0) {
|
|
2028
|
+
merged.push(`//`);
|
|
2029
|
+
merged.push(`// ā CONFLICTS DETECTED (${conflicts.length}):`);
|
|
2030
|
+
for (const conflict of conflicts) {
|
|
2031
|
+
merged.push(`// - ${conflict}`);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
merged.push(`// ============================================================================`);
|
|
2035
|
+
merged.push("");
|
|
2036
|
+
// --- Collect all imports and non-import content separately ---
|
|
2037
|
+
const allImports = [];
|
|
2038
|
+
const allDeclarations = []; // non-import, non-export-default content
|
|
2039
|
+
const allExports = [];
|
|
2040
|
+
for (const dir of sortedDirs) {
|
|
2041
|
+
const groupFiles = dirGroups.get(dir);
|
|
2042
|
+
// Sort files within group by extension, then name
|
|
2043
|
+
groupFiles.sort((a, b) => {
|
|
2044
|
+
const extA = extname(a.path);
|
|
2045
|
+
const extB = extname(b.path);
|
|
2046
|
+
if (extA !== extB)
|
|
2047
|
+
return extA.localeCompare(extB);
|
|
2048
|
+
return a.path.localeCompare(b.path);
|
|
2049
|
+
});
|
|
2050
|
+
for (const file of groupFiles) {
|
|
2051
|
+
const fileName = file.path.split("/").pop() || file.path;
|
|
2052
|
+
const fileStat = existsSync(file.path) ? statSync(file.path) : null;
|
|
2053
|
+
const sizeKB = fileStat ? (fileStat.size / 1024).toFixed(1) : "?";
|
|
2054
|
+
const modified = fileStat
|
|
2055
|
+
? new Date(fileStat.mtimeMs).toISOString().split("T")[0]
|
|
2056
|
+
: "unknown";
|
|
2057
|
+
const sectionHeader = [
|
|
2058
|
+
`// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`,
|
|
2059
|
+
`// FILE: ${fileName}`,
|
|
2060
|
+
`// FROM: ${file.path}`,
|
|
2061
|
+
`// PATH: ${file.path}`,
|
|
2062
|
+
`// SIZE: ${sizeKB} KB | MODIFIED: ${modified} | LANG: ${file.language}`,
|
|
2063
|
+
`// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`,
|
|
2064
|
+
];
|
|
2065
|
+
const lines = file.content.split("\n");
|
|
2066
|
+
const fileImports = [];
|
|
2067
|
+
const fileBody = [];
|
|
2068
|
+
const fileExports = [];
|
|
2069
|
+
let inImportBlock = true;
|
|
2070
|
+
for (const line of lines) {
|
|
2071
|
+
const trimmed = line.trim();
|
|
2072
|
+
// Collect imports (lines starting with 'import' or continuation of multi-line imports)
|
|
2073
|
+
if (inImportBlock) {
|
|
2074
|
+
if (trimmed.startsWith("import ") ||
|
|
2075
|
+
trimmed.startsWith("import{") ||
|
|
2076
|
+
(trimmed.startsWith("}") && trimmed.includes("from")) ||
|
|
2077
|
+
(fileImports.length > 0 &&
|
|
2078
|
+
!trimmed.startsWith("import") &&
|
|
2079
|
+
/^\s*[},]/.test(line) &&
|
|
2080
|
+
!trimmed.includes("export"))) {
|
|
2081
|
+
fileImports.push(line);
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
if (trimmed === "" && fileImports.length > 0) {
|
|
2085
|
+
continue; // Skip blank lines between imports
|
|
2086
|
+
}
|
|
2087
|
+
if (trimmed !== "") {
|
|
2088
|
+
inImportBlock = false;
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
// Separate trailing export defaults
|
|
2092
|
+
if (trimmed.startsWith("export default ") ||
|
|
2093
|
+
trimmed === "export default") {
|
|
2094
|
+
fileExports.push(line);
|
|
2095
|
+
continue;
|
|
2096
|
+
}
|
|
2097
|
+
fileBody.push(line);
|
|
2098
|
+
}
|
|
2099
|
+
// Deduplicate imports (same line text)
|
|
2100
|
+
for (const imp of fileImports) {
|
|
2101
|
+
if (!allImports.includes(imp)) {
|
|
2102
|
+
allImports.push(imp);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
// Add body with section header
|
|
2106
|
+
allDeclarations.push("");
|
|
2107
|
+
allDeclarations.push(...sectionHeader);
|
|
2108
|
+
allDeclarations.push("");
|
|
2109
|
+
allDeclarations.push(...fileBody);
|
|
2110
|
+
// Collect exports
|
|
2111
|
+
if (fileExports.length > 0) {
|
|
2112
|
+
allExports.push(`// Export from: ${fileName}`);
|
|
2113
|
+
allExports.push(...fileExports);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
// --- Assemble final output: imports ā declarations ā exports ---
|
|
2118
|
+
if (allImports.length > 0) {
|
|
2119
|
+
merged.push("// === IMPORTS ===");
|
|
2120
|
+
merged.push(...allImports);
|
|
2121
|
+
merged.push("");
|
|
2122
|
+
}
|
|
2123
|
+
merged.push(...allDeclarations);
|
|
2124
|
+
if (allExports.length > 0) {
|
|
2125
|
+
merged.push("");
|
|
2126
|
+
merged.push("// === EXPORTS ===");
|
|
2127
|
+
merged.push(...allExports);
|
|
2128
|
+
}
|
|
2129
|
+
merged.push("");
|
|
2130
|
+
return merged.join("\n");
|
|
2131
|
+
}
|
|
2132
|
+
function displayResults(result, _options, gatekeeperConfig) {
|
|
2133
|
+
if (_options["json"]) {
|
|
2134
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
const ok = result["status"] === "success";
|
|
2138
|
+
console.log(chalk.cyan("\nš Results Summary\n"));
|
|
2139
|
+
const summary = result["summary"];
|
|
2140
|
+
console.log(` Status: ${ok ? chalk.green("success") : chalk.red("failed")}`);
|
|
2141
|
+
if (result["error"])
|
|
2142
|
+
console.log(` Error: ${chalk.red(result["error"])}`);
|
|
2143
|
+
console.log(` Files processed: ${chalk.green(summary["filesProcessed"])}`);
|
|
2144
|
+
console.log(` Issues found: ${chalk.yellow(summary["issuesFound"])}`);
|
|
2145
|
+
console.log(` Issues fixed: ${chalk.green(summary["issuesFixed"])}`);
|
|
2146
|
+
console.log(` Phases completed: ${chalk.green(summary["successfulPhases"])} / ${summary["totalPhases"]}`);
|
|
2147
|
+
console.log(` Artifacts: ${chalk.cyan(result["artifacts"])}`);
|
|
2148
|
+
// Display gatekeeper information if enabled
|
|
2149
|
+
if (gatekeeperConfig?.enabled !== false) {
|
|
2150
|
+
console.log();
|
|
2151
|
+
console.log(chalk.cyan("š Gatekeeper Review\n"));
|
|
2152
|
+
const gatekeeper = result["gatekeeper"];
|
|
2153
|
+
if (gatekeeper) {
|
|
2154
|
+
console.log(` Enabled: ${chalk.green("Yes")}`);
|
|
2155
|
+
console.log(` Review Level: ${chalk.gray(gatekeeperConfig?.reviewLevel || "standard")}`);
|
|
2156
|
+
console.log(` Total Reviews: ${chalk.cyan(gatekeeper["totalReviews"])}`);
|
|
2157
|
+
console.log(` Passed: ${chalk.green(gatekeeper["passedReviews"])}`);
|
|
2158
|
+
console.log(` Revisions: ${chalk.yellow(gatekeeper["revisionRequests"])}`);
|
|
2159
|
+
}
|
|
2160
|
+
else {
|
|
2161
|
+
console.log(` Enabled: ${chalk.gray("No gatekeeper data")}`);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
console.log();
|
|
2165
|
+
}
|
|
2166
|
+
function displayAnalysisResults(result, _options) {
|
|
2167
|
+
if (_options["json"]) {
|
|
2168
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
console.log(chalk.cyan("\nš Analysis Results\n"));
|
|
2172
|
+
console.log(` File: ${chalk.gray(result.file)}`);
|
|
2173
|
+
if (result.resolvedPath) {
|
|
2174
|
+
console.log(` Resolved Path: ${chalk.gray(result.resolvedPath)}`);
|
|
2175
|
+
}
|
|
2176
|
+
console.log(` Total Lines: ${chalk.gray(String(result.totalLines ?? "?"))}`);
|
|
2177
|
+
const metrics = result.metrics;
|
|
2178
|
+
console.log(` Lines of Code: ${chalk.gray(String(metrics.linesOfCode))}`);
|
|
2179
|
+
console.log(` Complexity: ${chalk.gray(String(metrics.cyclomaticComplexity ?? metrics.complexity ?? 0))}`);
|
|
2180
|
+
const mi = metrics.maintainabilityIndex;
|
|
2181
|
+
const miColor = mi >= 70 ? chalk.green : mi >= 40 ? chalk.yellow : chalk.red;
|
|
2182
|
+
console.log(` Maintainability: ${miColor(String(mi))}`);
|
|
2183
|
+
if (metrics.halsteadVolume !== undefined) {
|
|
2184
|
+
console.log(` Halstead Volume: ${chalk.gray(String(metrics.halsteadVolume))}`);
|
|
2185
|
+
}
|
|
2186
|
+
const issues = result.issues;
|
|
2187
|
+
if (issues && issues.length > 0) {
|
|
2188
|
+
console.log(chalk.cyan(`\n š Issues (${issues.length}):\n`));
|
|
2189
|
+
for (const issue of issues) {
|
|
2190
|
+
const icon = issue.type === "error"
|
|
2191
|
+
? chalk.red("ā")
|
|
2192
|
+
: issue.type === "warning"
|
|
2193
|
+
? chalk.yellow("ā ")
|
|
2194
|
+
: chalk.blue("ā¹");
|
|
2195
|
+
const lineInfo = issue.line > 0 ? chalk.gray(`:${issue.line}`) : "";
|
|
2196
|
+
console.log(` ${icon} ${chalk.gray(`[${issue.rule}]`)}${lineInfo} ${issue.message}`);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
else {
|
|
2200
|
+
console.log(chalk.green("\n ā No issues detected"));
|
|
2201
|
+
}
|
|
2202
|
+
console.log();
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Get dynamic agent list by querying the MessageBus for registered agents
|
|
2206
|
+
* and their queue states. Falls back to showing all available agent types
|
|
2207
|
+
* with their default models and "not running" status if none are active.
|
|
2208
|
+
*/
|
|
2209
|
+
function getAgentList() {
|
|
2210
|
+
// Default model assignments per agent type
|
|
2211
|
+
const agentDefaults = {
|
|
2212
|
+
master: { type: "primary", model: "claude-opus-4-6" },
|
|
2213
|
+
planner: { type: "subagent", model: "claude-haiku-4" },
|
|
2214
|
+
analyzer: { type: "subagent", model: "claude-sonnet-4" },
|
|
2215
|
+
reviewer: { type: "subagent", model: "claude-sonnet-4" },
|
|
2216
|
+
rewriter: { type: "subagent", model: "claude-opus-4-5" },
|
|
2217
|
+
ui_designer: { type: "subagent", model: "claude-sonnet-4" },
|
|
2218
|
+
researcher: { type: "subagent", model: "claude-haiku-4" },
|
|
2219
|
+
gatekeeper: { type: "subagent", model: "claude-opus-4-5" },
|
|
2220
|
+
};
|
|
2221
|
+
try {
|
|
2222
|
+
// Try to query the MessageBus for real agent state
|
|
2223
|
+
const messageBus = new MessageBus({ enableLogging: false });
|
|
2224
|
+
const busStats = messageBus.getStats();
|
|
2225
|
+
const agents = [];
|
|
2226
|
+
for (const [agentName, defaults] of Object.entries(agentDefaults)) {
|
|
2227
|
+
const queueSize = busStats.queues[agentName] ?? 0;
|
|
2228
|
+
// Infer status from queue state:
|
|
2229
|
+
// - queue has messages ā "running" (processing work)
|
|
2230
|
+
// - queue is empty ā "idle" (available but not busy)
|
|
2231
|
+
let status;
|
|
2232
|
+
if (queueSize > 0) {
|
|
2233
|
+
status = "running";
|
|
2234
|
+
}
|
|
2235
|
+
else {
|
|
2236
|
+
status = "idle";
|
|
2237
|
+
}
|
|
2238
|
+
// Try to load model from config if available
|
|
2239
|
+
let model = defaults.model;
|
|
2240
|
+
try {
|
|
2241
|
+
const config = loadConfigFile();
|
|
2242
|
+
const agentConfig = config["agents"]?.[agentName];
|
|
2243
|
+
if (agentConfig?.["model"] &&
|
|
2244
|
+
typeof agentConfig["model"] === "string") {
|
|
2245
|
+
// Extract the short model name from the provider-qualified form
|
|
2246
|
+
const fullModel = agentConfig["model"];
|
|
2247
|
+
model = fullModel.includes("/")
|
|
2248
|
+
? fullModel.split("/").pop()
|
|
2249
|
+
: fullModel;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
catch {
|
|
2253
|
+
// Use defaults if config is unavailable
|
|
2254
|
+
}
|
|
2255
|
+
agents.push({
|
|
2256
|
+
name: agentName,
|
|
2257
|
+
type: defaults.type,
|
|
2258
|
+
model,
|
|
2259
|
+
status,
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
return agents;
|
|
2263
|
+
}
|
|
2264
|
+
catch {
|
|
2265
|
+
// Fallback: return static defaults with "not running" status
|
|
2266
|
+
return Object.entries(agentDefaults).map(([name, defaults]) => ({
|
|
2267
|
+
name,
|
|
2268
|
+
type: defaults.type,
|
|
2269
|
+
model: defaults.model,
|
|
2270
|
+
status: "not running",
|
|
2271
|
+
}));
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
// ============================================================================
|
|
2275
|
+
// Dashboard Command
|
|
2276
|
+
// ============================================================================
|
|
2277
|
+
const dashboardCmd = program
|
|
2278
|
+
.command("dashboard")
|
|
2279
|
+
.description("Show agent performance dashboard in the TUI model display area");
|
|
2280
|
+
dashboardCmd
|
|
2281
|
+
.command("show")
|
|
2282
|
+
.description("Display analytics dashboard with agent performance metrics")
|
|
2283
|
+
.option("-f, --format <format>", "Output format (terminal|json|markdown|html)", "terminal")
|
|
2284
|
+
.option("-t, --time <minutes>", "Time range in minutes", "60")
|
|
2285
|
+
.option("--trends", "Include trend analysis")
|
|
2286
|
+
.option("--no-trends", "Exclude trend analysis")
|
|
2287
|
+
.option("--recommendations", "Include recommendations")
|
|
2288
|
+
.option("--no-recommendations", "Exclude recommendations")
|
|
2289
|
+
.action((opts) => {
|
|
2290
|
+
try {
|
|
2291
|
+
const { DashboardGenerator } = require("../analytics/dashboard-generator.js");
|
|
2292
|
+
const { MetricsAggregator } = require("../analytics/metrics-aggregator.js");
|
|
2293
|
+
const aggregator = new MetricsAggregator();
|
|
2294
|
+
const dashboard = new DashboardGenerator(aggregator);
|
|
2295
|
+
const timeRangeMinutes = parseInt(String(opts.time), 10) || 60;
|
|
2296
|
+
const report = dashboard.generateReport({
|
|
2297
|
+
format: "markdown",
|
|
2298
|
+
includeTrends: opts.trends !== false,
|
|
2299
|
+
includeRecommendations: opts.recommendations !== false,
|
|
2300
|
+
timeRangeMinutes,
|
|
2301
|
+
});
|
|
2302
|
+
if (opts.format === "json") {
|
|
2303
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
if (opts.format === "html") {
|
|
2307
|
+
console.log(dashboard.export(report, "html"));
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
2310
|
+
// Terminal/TUI format ā show dashboard in the model display area
|
|
2311
|
+
const s = report.summary;
|
|
2312
|
+
const m = report.metrics;
|
|
2313
|
+
console.log(chalk.cyan("\nš Agent Performance Dashboard\n"));
|
|
2314
|
+
console.log(chalk.gray(` Generated: ${new Date(report.generatedAt).toISOString()}`));
|
|
2315
|
+
console.log(chalk.gray(` Period: ${timeRangeMinutes} min\n`));
|
|
2316
|
+
// Summary cards
|
|
2317
|
+
console.log(chalk.bold(" āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
|
|
2318
|
+
console.log(chalk.bold(" ā Performance Summary ā"));
|
|
2319
|
+
console.log(chalk.bold(" āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤"));
|
|
2320
|
+
console.log(` ā ${chalk.bold("Executions:")} ${String(s.totalExecutions).padEnd(28)} ā`);
|
|
2321
|
+
const successColor = s.overallSuccessRate >= 0.9 ? chalk.green : s.overallSuccessRate >= 0.7 ? chalk.yellow : chalk.red;
|
|
2322
|
+
console.log(` ā ${chalk.bold("Success Rate:")} ${successColor((s.overallSuccessRate * 100).toFixed(1) + "%").padEnd(28)} ā`);
|
|
2323
|
+
console.log(` ā ${chalk.bold("Total Time:")} ${String(s.totalTimeSeconds.toFixed(1) + "s").padEnd(28)} ā`);
|
|
2324
|
+
console.log(` ā ${chalk.bold("Total Tokens:")} ${String(s.totalTokens.toLocaleString()).padEnd(28)} ā`);
|
|
2325
|
+
console.log(` ā ${chalk.bold("Active Agents:")} ${String(s.activeAgentTypes).padEnd(28)} ā`);
|
|
2326
|
+
console.log(` ā ${chalk.bold("Busiest Agent:")} ${String(s.busiestAgentType).padEnd(28)} ā`);
|
|
2327
|
+
console.log(` ā ${chalk.bold("Slowest Agent:")} ${String(s.slowestAgentType).padEnd(28)} ā`);
|
|
2328
|
+
console.log(chalk.bold(" āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
|
|
2329
|
+
console.log();
|
|
2330
|
+
// Per-agent breakdown
|
|
2331
|
+
console.log(chalk.bold(" Per-Agent Performance:"));
|
|
2332
|
+
console.log();
|
|
2333
|
+
console.log(` ${chalk.gray("Agent Type".padEnd(16))} ${chalk.gray("Tasks".padEnd(7))} ${chalk.gray("Success".padEnd(10))} ${chalk.gray("Avg Time".padEnd(11))} ${chalk.gray("Avg Tokens")}`);
|
|
2334
|
+
console.log(` ${chalk.gray("ā".repeat(16))} ${chalk.gray("ā".repeat(7))} ${chalk.gray("ā".repeat(10))} ${chalk.gray("ā".repeat(11))} ${chalk.gray("ā".repeat(10))}`);
|
|
2335
|
+
const agentTypes = Object.keys(m.byAgentType);
|
|
2336
|
+
for (const agentType of agentTypes.sort()) {
|
|
2337
|
+
const a = m.byAgentType[agentType];
|
|
2338
|
+
const total = a.tasksCompleted + a.tasksFailed;
|
|
2339
|
+
const successPct = (a.successRate * 100).toFixed(1);
|
|
2340
|
+
const srColor = a.successRate >= 0.9 ? chalk.green : a.successRate >= 0.7 ? chalk.yellow : chalk.red;
|
|
2341
|
+
const avgTime = a.averageExecutionTime.toFixed(0);
|
|
2342
|
+
console.log(` ${chalk.cyan(String(agentType).padEnd(16))} ` +
|
|
2343
|
+
`${String(total).padEnd(7)} ` +
|
|
2344
|
+
`${srColor(successPct + "%").padEnd(10)} ` +
|
|
2345
|
+
`${chalk.gray(avgTime + "ms").padEnd(11)} ` +
|
|
2346
|
+
`${chalk.gray(String(a.averageTokensUsed.toFixed(0)))}`);
|
|
2347
|
+
}
|
|
2348
|
+
console.log();
|
|
2349
|
+
// Trends
|
|
2350
|
+
if (opts.trends !== false && report.trends.length > 0) {
|
|
2351
|
+
console.log(chalk.bold(" Trends:"));
|
|
2352
|
+
for (const trend of report.trends) {
|
|
2353
|
+
const icon = trend.direction === "up" ? chalk.green("ā²") : trend.direction === "down" ? chalk.red("ā¼") : chalk.gray("ā");
|
|
2354
|
+
const changeStr = trend.direction === "stable"
|
|
2355
|
+
? chalk.gray("no change")
|
|
2356
|
+
: (trend.changePercent > 0 ? chalk.green("+" + trend.changePercent.toFixed(1) + "%") : chalk.red(trend.changePercent.toFixed(1) + "%"));
|
|
2357
|
+
console.log(` ${icon} ${trend.metric}: ${changeStr}`);
|
|
2358
|
+
}
|
|
2359
|
+
console.log();
|
|
2360
|
+
}
|
|
2361
|
+
// Recommendations
|
|
2362
|
+
if (opts.recommendations !== false && report.recommendations.length > 0) {
|
|
2363
|
+
console.log(chalk.bold(" Recommendations:"));
|
|
2364
|
+
for (const rec of report.recommendations) {
|
|
2365
|
+
console.log(` ${chalk.yellow("ā")} ${rec}`);
|
|
2366
|
+
}
|
|
2367
|
+
console.log();
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
catch (error) {
|
|
2371
|
+
console.log(chalk.red(` ā Failed to generate dashboard: ${error instanceof Error ? error.message : error}`));
|
|
2372
|
+
console.log(chalk.gray(" Dashboard requires MetricsAggregator and DashboardGenerator."));
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
dashboardCmd
|
|
2376
|
+
.command("snapshot")
|
|
2377
|
+
.description("Show quick metrics snapshot for the model display area")
|
|
2378
|
+
.action(() => {
|
|
2379
|
+
try {
|
|
2380
|
+
const { MetricsAggregator } = require("../analytics/metrics-aggregator.js");
|
|
2381
|
+
const aggregator = new MetricsAggregator();
|
|
2382
|
+
const snapshot = aggregator.snapshot();
|
|
2383
|
+
console.log(chalk.cyan("\nā” Metrics Snapshot\n"));
|
|
2384
|
+
console.log(` ${chalk.bold("Active Agents:")} ${chalk.green(String(snapshot.activeAgents))}`);
|
|
2385
|
+
console.log(` ${chalk.bold("Running Tasks:")} ${chalk.yellow(String(snapshot.runningTasks))}`);
|
|
2386
|
+
console.log(` ${chalk.bold("Queued Tasks:")} ${chalk.gray(String(snapshot.queuedTasks))}`);
|
|
2387
|
+
console.log(` ${chalk.bold("Success Rate:")} ${(snapshot.recentSuccessRate * 100).toFixed(1)}%`);
|
|
2388
|
+
if (snapshot.memoryUsageMb) {
|
|
2389
|
+
console.log(` ${chalk.bold("Memory Usage:")} ${chalk.gray(String(snapshot.memoryUsageMb.toFixed(1)) + " MB")}`);
|
|
2390
|
+
}
|
|
2391
|
+
if (snapshot.farmMetrics) {
|
|
2392
|
+
console.log(chalk.bold("\n Farm Metrics:"));
|
|
2393
|
+
console.log(` Workers: ${snapshot.farmMetrics.activeWorkers}/${snapshot.farmMetrics.workerCount} active`);
|
|
2394
|
+
console.log(` Tasks: ${snapshot.farmMetrics.tasksCompleted} done, ${snapshot.farmMetrics.tasksFailed} failed`);
|
|
2395
|
+
}
|
|
2396
|
+
console.log();
|
|
2397
|
+
}
|
|
2398
|
+
catch (error) {
|
|
2399
|
+
console.log(chalk.yellow(" ā Snapshot not available ā no metrics data"));
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
// ============================================================================
|
|
2403
|
+
// Mode Management Commands
|
|
2404
|
+
// ============================================================================
|
|
2405
|
+
const modeCmd = program.command("mode").description("Manage custom processing modes");
|
|
2406
|
+
modeCmd
|
|
2407
|
+
.command("list")
|
|
2408
|
+
.description("List all custom modes")
|
|
2409
|
+
.option("--builtin", "Include built-in modes")
|
|
2410
|
+
.action((opts) => {
|
|
2411
|
+
const registry = new CustomModeRegistry();
|
|
2412
|
+
const custom = registry.list();
|
|
2413
|
+
const builtin = synapseModeRegistry.listModes();
|
|
2414
|
+
console.log(chalk.cyan("\nš Processing Modes\n"));
|
|
2415
|
+
if (opts.builtin) {
|
|
2416
|
+
console.log(chalk.yellow(" Built-in Modes:"));
|
|
2417
|
+
builtin.forEach((m) => {
|
|
2418
|
+
console.log(` ${chalk.green(m.id)} ā ${m.label}`);
|
|
2419
|
+
console.log(` ${chalk.gray(m.description)}`);
|
|
2420
|
+
console.log();
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
console.log(chalk.yellow(` Custom Modes (${custom.length}):`));
|
|
2424
|
+
if (custom.length === 0) {
|
|
2425
|
+
console.log(chalk.gray(" No custom modes registered"));
|
|
2426
|
+
}
|
|
2427
|
+
else {
|
|
2428
|
+
custom.forEach((m) => {
|
|
2429
|
+
const statusIcon = m.enabled ? chalk.green("ā") : chalk.gray("ā");
|
|
2430
|
+
console.log(` ${statusIcon} ${chalk.green(m.id)} ā ${m.label}`);
|
|
2431
|
+
console.log(` ${chalk.gray(m.description)}`);
|
|
2432
|
+
if (m.usageCount > 0) {
|
|
2433
|
+
console.log(` ${chalk.gray(`Used ${m.usageCount} times`)}`);
|
|
2434
|
+
}
|
|
2435
|
+
console.log();
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
console.log();
|
|
2439
|
+
});
|
|
2440
|
+
modeCmd
|
|
2441
|
+
.command("create")
|
|
2442
|
+
.description("Create a new custom mode")
|
|
2443
|
+
.argument("<id>", "Mode identifier (lowercase, underscores)")
|
|
2444
|
+
.argument("<label>", "Display label")
|
|
2445
|
+
.argument("<description>", "Mode description")
|
|
2446
|
+
.option("-a, --agents <agents>", "Comma-separated agent list")
|
|
2447
|
+
.option("-t, --tools <tools>", "Comma-separated tool list")
|
|
2448
|
+
.option("-p, --pipeline <pipeline>", "Base pipeline mode")
|
|
2449
|
+
.option("--from-template <name>", "Create from a named template")
|
|
2450
|
+
.option("--dry-run", "Validate without saving")
|
|
2451
|
+
.action((id, label, description, opts) => {
|
|
2452
|
+
let def = {
|
|
2453
|
+
id,
|
|
2454
|
+
label,
|
|
2455
|
+
description,
|
|
2456
|
+
agents: opts.agents
|
|
2457
|
+
? opts.agents.split(",").map((s) => s.trim())
|
|
2458
|
+
: undefined,
|
|
2459
|
+
tools: opts.tools
|
|
2460
|
+
? opts.tools.split(",").map((s) => s.trim())
|
|
2461
|
+
: undefined,
|
|
2462
|
+
basePipeline: opts.pipeline,
|
|
2463
|
+
};
|
|
2464
|
+
// If template specified, merge
|
|
2465
|
+
if (opts.fromTemplate) {
|
|
2466
|
+
const template = createFromTemplate(opts.fromTemplate, def);
|
|
2467
|
+
if (!template) {
|
|
2468
|
+
console.log(chalk.red(`\n ā Template "${opts.fromTemplate}" not found`));
|
|
2469
|
+
process.exit(1);
|
|
2470
|
+
}
|
|
2471
|
+
def = { ...def, ...template };
|
|
2472
|
+
}
|
|
2473
|
+
// Validate
|
|
2474
|
+
const validation = validateModeDefinition(def);
|
|
2475
|
+
if (!validation.valid) {
|
|
2476
|
+
console.log(chalk.red("\n ā Validation failed:\n"));
|
|
2477
|
+
validation.errors.forEach((e) => {
|
|
2478
|
+
console.log(` ${chalk.red("ā")} ${chalk.bold(e.field)}: ${e.message}`);
|
|
2479
|
+
});
|
|
2480
|
+
validation.warnings.forEach((w) => {
|
|
2481
|
+
console.log(` ${chalk.yellow("ā ")} ${chalk.bold(w.field)}: ${w.message}`);
|
|
2482
|
+
});
|
|
2483
|
+
process.exit(1);
|
|
2484
|
+
}
|
|
2485
|
+
if (validation.warnings.length > 0) {
|
|
2486
|
+
console.log(chalk.yellow("\n ā Warnings:\n"));
|
|
2487
|
+
validation.warnings.forEach((w) => {
|
|
2488
|
+
console.log(` ${chalk.yellow("ā ")} ${chalk.bold(w.field)}: ${w.message}`);
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
if (opts.dryRun) {
|
|
2492
|
+
console.log(chalk.green("\n ā Mode definition is valid (dry run ā not saved)\n"));
|
|
2493
|
+
// Show preview
|
|
2494
|
+
const preview = generateModePreview(def);
|
|
2495
|
+
console.log(chalk.cyan(" Preview:"));
|
|
2496
|
+
console.log(preview.markdown.split("\n").map((l) => ` ${l}`).join("\n"));
|
|
2497
|
+
console.log();
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
// Save
|
|
2501
|
+
try {
|
|
2502
|
+
saveModeToFile(def);
|
|
2503
|
+
console.log(chalk.green(`\n ā Mode "${id}" created successfully\n`));
|
|
2504
|
+
}
|
|
2505
|
+
catch (e) {
|
|
2506
|
+
console.log(chalk.red(`\n ā Failed to save mode: ${e instanceof Error ? e.message : e}\n`));
|
|
2507
|
+
process.exit(1);
|
|
2508
|
+
}
|
|
2509
|
+
});
|
|
2510
|
+
modeCmd
|
|
2511
|
+
.command("delete")
|
|
2512
|
+
.description("Delete a custom mode")
|
|
2513
|
+
.argument("<id>", "Mode identifier")
|
|
2514
|
+
.option("--force", "Skip confirmation")
|
|
2515
|
+
.action((id) => {
|
|
2516
|
+
const deleted = deleteModeFromFile(id);
|
|
2517
|
+
if (deleted) {
|
|
2518
|
+
console.log(chalk.green(`\n ā Mode "${id}" deleted\n`));
|
|
2519
|
+
}
|
|
2520
|
+
else {
|
|
2521
|
+
console.log(chalk.yellow(`\n ā Mode "${id}" not found\n`));
|
|
2522
|
+
}
|
|
2523
|
+
});
|
|
2524
|
+
modeCmd
|
|
2525
|
+
.command("show")
|
|
2526
|
+
.description("Show detailed mode information")
|
|
2527
|
+
.argument("<id>", "Mode identifier")
|
|
2528
|
+
.option("--preview", "Show visual pipeline preview")
|
|
2529
|
+
.action((id, opts) => {
|
|
2530
|
+
// Check built-in first
|
|
2531
|
+
const builtin = synapseModeRegistry.getMode(id);
|
|
2532
|
+
if (builtin) {
|
|
2533
|
+
console.log(chalk.cyan(`\nš Built-in Mode: ${builtin.id}\n`));
|
|
2534
|
+
console.log(` ${chalk.bold("Label:")} ${builtin.label}`);
|
|
2535
|
+
console.log(` ${chalk.bold("Description:")} ${builtin.description}`);
|
|
2536
|
+
if (builtin.agents?.length > 0) {
|
|
2537
|
+
console.log(` ${chalk.bold("Agents:")} ${builtin.agents.join(", ")}`);
|
|
2538
|
+
}
|
|
2539
|
+
if (builtin.tools?.length > 0) {
|
|
2540
|
+
console.log(` ${chalk.bold("Tools:")} ${builtin.tools.join(", ")}`);
|
|
2541
|
+
}
|
|
2542
|
+
if (builtin.basePipeline) {
|
|
2543
|
+
console.log(` ${chalk.bold("Pipeline:")} ${builtin.basePipeline}`);
|
|
2544
|
+
}
|
|
2545
|
+
console.log();
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
// Check custom modes
|
|
2549
|
+
const customList = listCustomModesFromFile();
|
|
2550
|
+
const custom = customList.find((m) => m.id === id);
|
|
2551
|
+
if (!custom) {
|
|
2552
|
+
console.log(chalk.yellow(`\n ā Mode "${id}" not found\n`));
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
console.log(chalk.cyan(`\nš Custom Mode: ${custom.id}\n`));
|
|
2556
|
+
console.log(` ${chalk.bold("Label:")} ${custom.label}`);
|
|
2557
|
+
console.log(` ${chalk.bold("Description:")} ${custom.description}`);
|
|
2558
|
+
if (custom.agents && custom.agents.length > 0) {
|
|
2559
|
+
console.log(` ${chalk.bold("Agents:")} ${custom.agents.join(", ")}`);
|
|
2560
|
+
}
|
|
2561
|
+
if (custom.tools && custom.tools.length > 0) {
|
|
2562
|
+
console.log(` ${chalk.bold("Tools:")} ${custom.tools.join(", ")}`);
|
|
2563
|
+
}
|
|
2564
|
+
if (custom.basePipeline) {
|
|
2565
|
+
console.log(` ${chalk.bold("Pipeline:")} ${custom.basePipeline}`);
|
|
2566
|
+
}
|
|
2567
|
+
if (opts.preview) {
|
|
2568
|
+
const preview = generateModePreview(custom);
|
|
2569
|
+
console.log(chalk.cyan("\n Pipeline Preview:"));
|
|
2570
|
+
preview.pipelineSteps.forEach((step) => {
|
|
2571
|
+
console.log(` ${chalk.green("ā")} ${step}`);
|
|
2572
|
+
});
|
|
2573
|
+
console.log();
|
|
2574
|
+
console.log(preview.markdown);
|
|
2575
|
+
}
|
|
2576
|
+
console.log();
|
|
2577
|
+
});
|
|
2578
|
+
modeCmd
|
|
2579
|
+
.command("templates")
|
|
2580
|
+
.description("List available mode templates")
|
|
2581
|
+
.option("-c, --category <category>", "Filter by category")
|
|
2582
|
+
.action((opts) => {
|
|
2583
|
+
const templates = opts.category
|
|
2584
|
+
? getTemplatesByCategory(opts.category)
|
|
2585
|
+
: getModeTemplates();
|
|
2586
|
+
console.log(chalk.cyan("\nš¦ Mode Templates\n"));
|
|
2587
|
+
if (templates.length === 0) {
|
|
2588
|
+
console.log(chalk.gray(" No templates found"));
|
|
2589
|
+
console.log();
|
|
2590
|
+
return;
|
|
2591
|
+
}
|
|
2592
|
+
templates.forEach((t) => {
|
|
2593
|
+
console.log(` ${chalk.green(t.name)} ā ${t.description}`);
|
|
2594
|
+
console.log(` ${chalk.gray(`Category: ${t.category}`)}`);
|
|
2595
|
+
console.log(` ${chalk.gray(`Pipeline: ${t.template.basePipeline ?? "none"}`)}`);
|
|
2596
|
+
if (t.template.agents) {
|
|
2597
|
+
console.log(` ${chalk.gray(`Agents: ${t.template.agents.join(", ")}`)}`);
|
|
2598
|
+
}
|
|
2599
|
+
console.log();
|
|
2600
|
+
});
|
|
2601
|
+
});
|
|
2602
|
+
modeCmd
|
|
2603
|
+
.command("validate")
|
|
2604
|
+
.description("Validate a custom mode YAML file")
|
|
2605
|
+
.argument("<file>", "Path to YAML file")
|
|
2606
|
+
.action((file) => {
|
|
2607
|
+
if (!existsSync(file)) {
|
|
2608
|
+
console.log(chalk.red(`\n ā File not found: ${file}\n`));
|
|
2609
|
+
process.exit(1);
|
|
2610
|
+
}
|
|
2611
|
+
try {
|
|
2612
|
+
const content = readFileSync(file, "utf-8");
|
|
2613
|
+
const def = parseModeFromYaml(content);
|
|
2614
|
+
if (!def) {
|
|
2615
|
+
console.log(chalk.yellow("\n ā No valid mode definition found in file\n"));
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
const validation = validateModeDefinition(def);
|
|
2619
|
+
if (validation.valid) {
|
|
2620
|
+
console.log(chalk.green(`\n ā Mode "${def.id}" is valid\n`));
|
|
2621
|
+
}
|
|
2622
|
+
else {
|
|
2623
|
+
console.log(chalk.red(`\n ā Mode "${def.id}" has errors:\n`));
|
|
2624
|
+
validation.errors.forEach((e) => {
|
|
2625
|
+
console.log(` ${chalk.red("ā")} ${chalk.bold(e.field)}: ${e.message}`);
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
if (validation.warnings.length > 0) {
|
|
2629
|
+
console.log(chalk.yellow("\n ā Warnings:\n"));
|
|
2630
|
+
validation.warnings.forEach((w) => {
|
|
2631
|
+
console.log(` ${chalk.yellow("ā ")} ${chalk.bold(w.field)}: ${w.message}`);
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2634
|
+
if (validation.suggestion) {
|
|
2635
|
+
console.log(chalk.cyan(`\n š” Suggestion: ${validation.suggestion}\n`));
|
|
2636
|
+
}
|
|
2637
|
+
console.log();
|
|
2638
|
+
}
|
|
2639
|
+
catch (e) {
|
|
2640
|
+
console.log(chalk.red(`\n ā Failed to parse file: ${e instanceof Error ? e.message : e}\n`));
|
|
2641
|
+
process.exit(1);
|
|
2642
|
+
}
|
|
2643
|
+
});
|
|
2644
|
+
// ============================================================================
|
|
2645
|
+
// Boot Banner
|
|
2646
|
+
// ============================================================================
|
|
2647
|
+
console.log(chalk.cyan(`
|
|
2648
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
2649
|
+
ā š¦ SynapseXcoder AI System ā V1 ā
|
|
2650
|
+
ā Multi-Agent OpenCode Integration Edition ā
|
|
2651
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
2652
|
+
`));
|
|
2653
|
+
function isRunningDirectly() {
|
|
2654
|
+
if (!process.argv[1])
|
|
2655
|
+
return false;
|
|
2656
|
+
try {
|
|
2657
|
+
const entryPath = realpathSync(resolve(process.argv[1]));
|
|
2658
|
+
const modulePath = realpathSync(resolve(new URL(import.meta.url).pathname));
|
|
2659
|
+
return entryPath === modulePath;
|
|
2660
|
+
}
|
|
2661
|
+
catch {
|
|
2662
|
+
return import.meta.url === `file://${process.argv[1]}`;
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
// Parse and execute only when run directly (not when imported for testing)
|
|
2666
|
+
if (isRunningDirectly()) {
|
|
2667
|
+
program.parse(process.argv);
|
|
2668
|
+
// Show help if no command
|
|
2669
|
+
if (process.argv.length === 2) {
|
|
2670
|
+
program.help();
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
// ============================================================================
|
|
2674
|
+
// Exports for testing
|
|
2675
|
+
// ============================================================================
|
|
2676
|
+
export { mergeInputs, readInputFiles, getDefaultConfig, loadConfigFile, saveConfigFile, setConfigValue, getConfigValue, saveContext, loadContext, listContexts, deleteContext, getCurrentContext, checkOpenCodeAvailable, runOpenCodeCommand, };
|
|
2677
|
+
//# sourceMappingURL=index.js.map
|