@intrect/openswarm 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +544 -0
- package/config.example.yaml +107 -0
- package/dist/adapters/base.d.ts +8 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +104 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claude.d.ts +13 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/claude.js +318 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex.d.ts +14 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +366 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/cryptoQuantAdapter.d.ts +85 -0
- package/dist/adapters/cryptoQuantAdapter.d.ts.map +1 -0
- package/dist/adapters/cryptoQuantAdapter.js +238 -0
- package/dist/adapters/cryptoQuantAdapter.js.map +1 -0
- package/dist/adapters/index.d.ts +17 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +47 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/processRegistry.d.ts +38 -0
- package/dist/adapters/processRegistry.d.ts.map +1 -0
- package/dist/adapters/processRegistry.js +147 -0
- package/dist/adapters/processRegistry.js.map +1 -0
- package/dist/adapters/streamBuffer.d.ts +59 -0
- package/dist/adapters/streamBuffer.d.ts.map +1 -0
- package/dist/adapters/streamBuffer.js +123 -0
- package/dist/adapters/streamBuffer.js.map +1 -0
- package/dist/adapters/types.d.ts +65 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +6 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/agents/agentBus.d.ts +160 -0
- package/dist/agents/agentBus.d.ts.map +1 -0
- package/dist/agents/agentBus.js +350 -0
- package/dist/agents/agentBus.js.map +1 -0
- package/dist/agents/agentPair.d.ts +210 -0
- package/dist/agents/agentPair.d.ts.map +1 -0
- package/dist/agents/agentPair.js +420 -0
- package/dist/agents/agentPair.js.map +1 -0
- package/dist/agents/auditor.d.ts +27 -0
- package/dist/agents/auditor.d.ts.map +1 -0
- package/dist/agents/auditor.js +237 -0
- package/dist/agents/auditor.js.map +1 -0
- package/dist/agents/cliStreamParser.d.ts +18 -0
- package/dist/agents/cliStreamParser.d.ts.map +1 -0
- package/dist/agents/cliStreamParser.js +156 -0
- package/dist/agents/cliStreamParser.js.map +1 -0
- package/dist/agents/documenter.d.ts +31 -0
- package/dist/agents/documenter.d.ts.map +1 -0
- package/dist/agents/documenter.js +285 -0
- package/dist/agents/documenter.js.map +1 -0
- package/dist/agents/index.d.ts +10 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +10 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/pairMetrics.d.ts +63 -0
- package/dist/agents/pairMetrics.d.ts.map +1 -0
- package/dist/agents/pairMetrics.js +231 -0
- package/dist/agents/pairMetrics.js.map +1 -0
- package/dist/agents/pairPipeline.d.ts +155 -0
- package/dist/agents/pairPipeline.d.ts.map +1 -0
- package/dist/agents/pairPipeline.js +793 -0
- package/dist/agents/pairPipeline.js.map +1 -0
- package/dist/agents/pairWebhook.d.ts +59 -0
- package/dist/agents/pairWebhook.d.ts.map +1 -0
- package/dist/agents/pairWebhook.js +242 -0
- package/dist/agents/pairWebhook.js.map +1 -0
- package/dist/agents/pipelineFormat.d.ts +11 -0
- package/dist/agents/pipelineFormat.d.ts.map +1 -0
- package/dist/agents/pipelineFormat.js +164 -0
- package/dist/agents/pipelineFormat.js.map +1 -0
- package/dist/agents/pipelineGuards.d.ts +23 -0
- package/dist/agents/pipelineGuards.d.ts.map +1 -0
- package/dist/agents/pipelineGuards.js +175 -0
- package/dist/agents/pipelineGuards.js.map +1 -0
- package/dist/agents/reviewer.d.ts +37 -0
- package/dist/agents/reviewer.d.ts.map +1 -0
- package/dist/agents/reviewer.js +213 -0
- package/dist/agents/reviewer.js.map +1 -0
- package/dist/agents/skillDocumenter.d.ts +23 -0
- package/dist/agents/skillDocumenter.d.ts.map +1 -0
- package/dist/agents/skillDocumenter.js +218 -0
- package/dist/agents/skillDocumenter.js.map +1 -0
- package/dist/agents/tester.d.ts +37 -0
- package/dist/agents/tester.d.ts.map +1 -0
- package/dist/agents/tester.js +308 -0
- package/dist/agents/tester.js.map +1 -0
- package/dist/agents/worker.d.ts +30 -0
- package/dist/agents/worker.d.ts.map +1 -0
- package/dist/agents/worker.js +128 -0
- package/dist/agents/worker.js.map +1 -0
- package/dist/automation/autonomousRunner.d.ts +123 -0
- package/dist/automation/autonomousRunner.d.ts.map +1 -0
- package/dist/automation/autonomousRunner.js +1082 -0
- package/dist/automation/autonomousRunner.js.map +1 -0
- package/dist/automation/ciWorker.d.ts +51 -0
- package/dist/automation/ciWorker.d.ts.map +1 -0
- package/dist/automation/ciWorker.js +282 -0
- package/dist/automation/ciWorker.js.map +1 -0
- package/dist/automation/conflictResolver.d.ts +29 -0
- package/dist/automation/conflictResolver.d.ts.map +1 -0
- package/dist/automation/conflictResolver.js +261 -0
- package/dist/automation/conflictResolver.js.map +1 -0
- package/dist/automation/dailyReporter.d.ts +26 -0
- package/dist/automation/dailyReporter.d.ts.map +1 -0
- package/dist/automation/dailyReporter.js +132 -0
- package/dist/automation/dailyReporter.js.map +1 -0
- package/dist/automation/index.d.ts +7 -0
- package/dist/automation/index.d.ts.map +1 -0
- package/dist/automation/index.js +7 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/automation/longRunningMonitor.d.ts +26 -0
- package/dist/automation/longRunningMonitor.d.ts.map +1 -0
- package/dist/automation/longRunningMonitor.js +231 -0
- package/dist/automation/longRunningMonitor.js.map +1 -0
- package/dist/automation/prOwnership.d.ts +18 -0
- package/dist/automation/prOwnership.d.ts.map +1 -0
- package/dist/automation/prOwnership.js +61 -0
- package/dist/automation/prOwnership.js.map +1 -0
- package/dist/automation/prProcessor.d.ts +62 -0
- package/dist/automation/prProcessor.d.ts.map +1 -0
- package/dist/automation/prProcessor.js +700 -0
- package/dist/automation/prProcessor.js.map +1 -0
- package/dist/automation/runnerExecution.d.ts +49 -0
- package/dist/automation/runnerExecution.d.ts.map +1 -0
- package/dist/automation/runnerExecution.js +663 -0
- package/dist/automation/runnerExecution.js.map +1 -0
- package/dist/automation/runnerState.d.ts +170 -0
- package/dist/automation/runnerState.d.ts.map +1 -0
- package/dist/automation/runnerState.js +495 -0
- package/dist/automation/runnerState.js.map +1 -0
- package/dist/automation/runnerTypes.d.ts +46 -0
- package/dist/automation/runnerTypes.d.ts.map +1 -0
- package/dist/automation/runnerTypes.js +5 -0
- package/dist/automation/runnerTypes.js.map +1 -0
- package/dist/automation/scheduler.d.ts +75 -0
- package/dist/automation/scheduler.d.ts.map +1 -0
- package/dist/automation/scheduler.js +394 -0
- package/dist/automation/scheduler.js.map +1 -0
- package/dist/cli/promptHandler.d.ts +13 -0
- package/dist/cli/promptHandler.d.ts.map +1 -0
- package/dist/cli/promptHandler.js +189 -0
- package/dist/cli/promptHandler.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +138 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/config.d.ts +308 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +529 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/eventHub.d.ts +194 -0
- package/dist/core/eventHub.d.ts.map +1 -0
- package/dist/core/eventHub.js +136 -0
- package/dist/core/eventHub.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/service.d.ts +27 -0
- package/dist/core/service.d.ts.map +1 -0
- package/dist/core/service.js +438 -0
- package/dist/core/service.js.map +1 -0
- package/dist/core/traceCollector.d.ts +105 -0
- package/dist/core/traceCollector.d.ts.map +1 -0
- package/dist/core/traceCollector.js +141 -0
- package/dist/core/traceCollector.js.map +1 -0
- package/dist/core/types.d.ts +413 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -0
- package/dist/discord/discordCore.d.ts +104 -0
- package/dist/discord/discordCore.d.ts.map +1 -0
- package/dist/discord/discordCore.js +698 -0
- package/dist/discord/discordCore.js.map +1 -0
- package/dist/discord/discordHandlers.d.ts +86 -0
- package/dist/discord/discordHandlers.d.ts.map +1 -0
- package/dist/discord/discordHandlers.js +849 -0
- package/dist/discord/discordHandlers.js.map +1 -0
- package/dist/discord/discordPair.d.ts +6 -0
- package/dist/discord/discordPair.d.ts.map +1 -0
- package/dist/discord/discordPair.js +567 -0
- package/dist/discord/discordPair.js.map +1 -0
- package/dist/discord/index.d.ts +4 -0
- package/dist/discord/index.d.ts.map +1 -0
- package/dist/discord/index.js +11 -0
- package/dist/discord/index.js.map +1 -0
- package/dist/github/github.d.ts +236 -0
- package/dist/github/github.d.ts.map +1 -0
- package/dist/github/github.js +535 -0
- package/dist/github/github.js.map +1 -0
- package/dist/github/index.d.ts +2 -0
- package/dist/github/index.d.ts.map +1 -0
- package/dist/github/index.js +2 -0
- package/dist/github/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge/analyzer.d.ts +36 -0
- package/dist/knowledge/analyzer.d.ts.map +1 -0
- package/dist/knowledge/analyzer.js +170 -0
- package/dist/knowledge/analyzer.js.map +1 -0
- package/dist/knowledge/gitInfo.d.ts +10 -0
- package/dist/knowledge/gitInfo.d.ts.map +1 -0
- package/dist/knowledge/gitInfo.js +134 -0
- package/dist/knowledge/gitInfo.js.map +1 -0
- package/dist/knowledge/graph.d.ts +45 -0
- package/dist/knowledge/graph.d.ts.map +1 -0
- package/dist/knowledge/graph.js +262 -0
- package/dist/knowledge/graph.js.map +1 -0
- package/dist/knowledge/graphqlExporter.d.ts +64 -0
- package/dist/knowledge/graphqlExporter.d.ts.map +1 -0
- package/dist/knowledge/graphqlExporter.js +333 -0
- package/dist/knowledge/graphqlExporter.js.map +1 -0
- package/dist/knowledge/index.d.ts +58 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/knowledge/index.js +212 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/knowledge/repository.d.ts +64 -0
- package/dist/knowledge/repository.d.ts.map +1 -0
- package/dist/knowledge/repository.js +250 -0
- package/dist/knowledge/repository.js.map +1 -0
- package/dist/knowledge/riskOnAnalyzer.d.ts +79 -0
- package/dist/knowledge/riskOnAnalyzer.d.ts.map +1 -0
- package/dist/knowledge/riskOnAnalyzer.js +243 -0
- package/dist/knowledge/riskOnAnalyzer.js.map +1 -0
- package/dist/knowledge/scanner.d.ts +14 -0
- package/dist/knowledge/scanner.d.ts.map +1 -0
- package/dist/knowledge/scanner.js +350 -0
- package/dist/knowledge/scanner.js.map +1 -0
- package/dist/knowledge/store.d.ts +23 -0
- package/dist/knowledge/store.d.ts.map +1 -0
- package/dist/knowledge/store.js +86 -0
- package/dist/knowledge/store.js.map +1 -0
- package/dist/knowledge/types.d.ts +288 -0
- package/dist/knowledge/types.d.ts.map +1 -0
- package/dist/knowledge/types.js +111 -0
- package/dist/knowledge/types.js.map +1 -0
- package/dist/linear/index.d.ts +3 -0
- package/dist/linear/index.d.ts.map +1 -0
- package/dist/linear/index.js +3 -0
- package/dist/linear/index.js.map +1 -0
- package/dist/linear/linear.d.ts +160 -0
- package/dist/linear/linear.d.ts.map +1 -0
- package/dist/linear/linear.js +983 -0
- package/dist/linear/linear.js.map +1 -0
- package/dist/linear/projectUpdater.d.ts +23 -0
- package/dist/linear/projectUpdater.d.ts.map +1 -0
- package/dist/linear/projectUpdater.js +347 -0
- package/dist/linear/projectUpdater.js.map +1 -0
- package/dist/locale/en.d.ts +3 -0
- package/dist/locale/en.d.ts.map +1 -0
- package/dist/locale/en.js +436 -0
- package/dist/locale/en.js.map +1 -0
- package/dist/locale/index.d.ts +28 -0
- package/dist/locale/index.d.ts.map +1 -0
- package/dist/locale/index.js +89 -0
- package/dist/locale/index.js.map +1 -0
- package/dist/locale/ko.d.ts +3 -0
- package/dist/locale/ko.d.ts.map +1 -0
- package/dist/locale/ko.js +436 -0
- package/dist/locale/ko.js.map +1 -0
- package/dist/locale/prompts/en.d.ts +3 -0
- package/dist/locale/prompts/en.d.ts.map +1 -0
- package/dist/locale/prompts/en.js +278 -0
- package/dist/locale/prompts/en.js.map +1 -0
- package/dist/locale/prompts/ko.d.ts +3 -0
- package/dist/locale/prompts/ko.d.ts.map +1 -0
- package/dist/locale/prompts/ko.js +279 -0
- package/dist/locale/prompts/ko.js.map +1 -0
- package/dist/locale/types.d.ts +407 -0
- package/dist/locale/types.d.ts.map +1 -0
- package/dist/locale/types.js +5 -0
- package/dist/locale/types.js.map +1 -0
- package/dist/memory/codex.d.ts +93 -0
- package/dist/memory/codex.d.ts.map +1 -0
- package/dist/memory/codex.js +366 -0
- package/dist/memory/codex.js.map +1 -0
- package/dist/memory/compaction.d.ts +21 -0
- package/dist/memory/compaction.d.ts.map +1 -0
- package/dist/memory/compaction.js +205 -0
- package/dist/memory/compaction.js.map +1 -0
- package/dist/memory/index.d.ts +13 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +13 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memoryCore.d.ts +178 -0
- package/dist/memory/memoryCore.d.ts.map +1 -0
- package/dist/memory/memoryCore.js +623 -0
- package/dist/memory/memoryCore.js.map +1 -0
- package/dist/memory/memoryOps.d.ts +90 -0
- package/dist/memory/memoryOps.d.ts.map +1 -0
- package/dist/memory/memoryOps.js +606 -0
- package/dist/memory/memoryOps.js.map +1 -0
- package/dist/orchestration/conflictDetector.d.ts +15 -0
- package/dist/orchestration/conflictDetector.d.ts.map +1 -0
- package/dist/orchestration/conflictDetector.js +130 -0
- package/dist/orchestration/conflictDetector.js.map +1 -0
- package/dist/orchestration/decisionEngine.d.ts +177 -0
- package/dist/orchestration/decisionEngine.d.ts.map +1 -0
- package/dist/orchestration/decisionEngine.js +496 -0
- package/dist/orchestration/decisionEngine.js.map +1 -0
- package/dist/orchestration/index.d.ts +5 -0
- package/dist/orchestration/index.d.ts.map +1 -0
- package/dist/orchestration/index.js +5 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/taskParser.d.ts +67 -0
- package/dist/orchestration/taskParser.d.ts.map +1 -0
- package/dist/orchestration/taskParser.js +542 -0
- package/dist/orchestration/taskParser.js.map +1 -0
- package/dist/orchestration/taskScheduler.d.ts +141 -0
- package/dist/orchestration/taskScheduler.d.ts.map +1 -0
- package/dist/orchestration/taskScheduler.js +317 -0
- package/dist/orchestration/taskScheduler.js.map +1 -0
- package/dist/orchestration/workflow.d.ts +145 -0
- package/dist/orchestration/workflow.d.ts.map +1 -0
- package/dist/orchestration/workflow.js +301 -0
- package/dist/orchestration/workflow.js.map +1 -0
- package/dist/runners/cliRunner.d.ts +11 -0
- package/dist/runners/cliRunner.d.ts.map +1 -0
- package/dist/runners/cliRunner.js +194 -0
- package/dist/runners/cliRunner.js.map +1 -0
- package/dist/support/apiCache.d.ts +85 -0
- package/dist/support/apiCache.d.ts.map +1 -0
- package/dist/support/apiCache.js +163 -0
- package/dist/support/apiCache.js.map +1 -0
- package/dist/support/chat.d.ts +3 -0
- package/dist/support/chat.d.ts.map +1 -0
- package/dist/support/chat.js +304 -0
- package/dist/support/chat.js.map +1 -0
- package/dist/support/chatBackend.d.ts +25 -0
- package/dist/support/chatBackend.d.ts.map +1 -0
- package/dist/support/chatBackend.js +235 -0
- package/dist/support/chatBackend.js.map +1 -0
- package/dist/support/chatMemory.d.ts +71 -0
- package/dist/support/chatMemory.d.ts.map +1 -0
- package/dist/support/chatMemory.js +119 -0
- package/dist/support/chatMemory.js.map +1 -0
- package/dist/support/chatTui.d.ts +3 -0
- package/dist/support/chatTui.d.ts.map +1 -0
- package/dist/support/chatTui.js +998 -0
- package/dist/support/chatTui.js.map +1 -0
- package/dist/support/costTracker.d.ts +29 -0
- package/dist/support/costTracker.d.ts.map +1 -0
- package/dist/support/costTracker.js +113 -0
- package/dist/support/costTracker.js.map +1 -0
- package/dist/support/dashboardHtml.d.ts +3 -0
- package/dist/support/dashboardHtml.d.ts.map +1 -0
- package/dist/support/dashboardHtml.js +2070 -0
- package/dist/support/dashboardHtml.js.map +1 -0
- package/dist/support/delete-beliefs.d.ts +2 -0
- package/dist/support/delete-beliefs.d.ts.map +1 -0
- package/dist/support/delete-beliefs.js +34 -0
- package/dist/support/delete-beliefs.js.map +1 -0
- package/dist/support/dev.d.ts +55 -0
- package/dist/support/dev.d.ts.map +1 -0
- package/dist/support/dev.js +298 -0
- package/dist/support/dev.js.map +1 -0
- package/dist/support/editParser.d.ts +37 -0
- package/dist/support/editParser.d.ts.map +1 -0
- package/dist/support/editParser.js +365 -0
- package/dist/support/editParser.js.map +1 -0
- package/dist/support/gitStatus.d.ts +21 -0
- package/dist/support/gitStatus.d.ts.map +1 -0
- package/dist/support/gitStatus.js +108 -0
- package/dist/support/gitStatus.js.map +1 -0
- package/dist/support/gitTracker.d.ts +30 -0
- package/dist/support/gitTracker.d.ts.map +1 -0
- package/dist/support/gitTracker.js +143 -0
- package/dist/support/gitTracker.js.map +1 -0
- package/dist/support/index.d.ts +13 -0
- package/dist/support/index.d.ts.map +1 -0
- package/dist/support/index.js +13 -0
- package/dist/support/index.js.map +1 -0
- package/dist/support/planner.d.ts +58 -0
- package/dist/support/planner.d.ts.map +1 -0
- package/dist/support/planner.js +395 -0
- package/dist/support/planner.js.map +1 -0
- package/dist/support/projectMapper.d.ts +46 -0
- package/dist/support/projectMapper.d.ts.map +1 -0
- package/dist/support/projectMapper.js +259 -0
- package/dist/support/projectMapper.js.map +1 -0
- package/dist/support/quotaTracker.d.ts +29 -0
- package/dist/support/quotaTracker.d.ts.map +1 -0
- package/dist/support/quotaTracker.js +89 -0
- package/dist/support/quotaTracker.js.map +1 -0
- package/dist/support/rateLimiter.d.ts +101 -0
- package/dist/support/rateLimiter.d.ts.map +1 -0
- package/dist/support/rateLimiter.js +219 -0
- package/dist/support/rateLimiter.js.map +1 -0
- package/dist/support/rollback.d.ts +61 -0
- package/dist/support/rollback.d.ts.map +1 -0
- package/dist/support/rollback.js +328 -0
- package/dist/support/rollback.js.map +1 -0
- package/dist/support/stuckDetector.d.ts +68 -0
- package/dist/support/stuckDetector.d.ts.map +1 -0
- package/dist/support/stuckDetector.js +174 -0
- package/dist/support/stuckDetector.js.map +1 -0
- package/dist/support/timeWindow.d.ts +60 -0
- package/dist/support/timeWindow.d.ts.map +1 -0
- package/dist/support/timeWindow.js +236 -0
- package/dist/support/timeWindow.js.map +1 -0
- package/dist/support/web.d.ts +11 -0
- package/dist/support/web.d.ts.map +1 -0
- package/dist/support/web.js +938 -0
- package/dist/support/web.js.map +1 -0
- package/dist/support/workflowLinear.d.ts +99 -0
- package/dist/support/workflowLinear.d.ts.map +1 -0
- package/dist/support/workflowLinear.js +257 -0
- package/dist/support/workflowLinear.js.map +1 -0
- package/dist/support/worktreeManager.d.ts +20 -0
- package/dist/support/worktreeManager.d.ts.map +1 -0
- package/dist/support/worktreeManager.js +144 -0
- package/dist/support/worktreeManager.js.map +1 -0
- package/dist/taskState/store.d.ts +101 -0
- package/dist/taskState/store.d.ts.map +1 -0
- package/dist/taskState/store.js +346 -0
- package/dist/taskState/store.js.map +1 -0
- package/package.json +70 -0
- package/templates/AGENTS.md +432 -0
- package/templates/BOOT.md +25 -0
- package/templates/BOOTSTRAP.md +50 -0
- package/templates/CHANGELOG_AUDIT.md +74 -0
- package/templates/HEARTBEAT.md +86 -0
- package/templates/IDENTITY.md +27 -0
- package/templates/ISSUE_ANALYSIS.md +31 -0
- package/templates/PR_LAND.md +75 -0
- package/templates/PR_REVIEW.md +97 -0
- package/templates/SOUL.dev.md +52 -0
- package/templates/SOUL.md +81 -0
- package/templates/TOOLS.md +52 -0
- package/templates/USER.md +22 -0
|
@@ -0,0 +1,998 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
// OpenSwarm - Rich TUI Chat Interface
|
|
3
|
+
// Claude Code style tabbed interface with real-time updates
|
|
4
|
+
import blessed from 'blessed';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { loadConfig } from '../core/config.js';
|
|
10
|
+
import { getDefaultAdapterName } from '../adapters/index.js';
|
|
11
|
+
import { getDefaultChatModel, resolveChatModel, runChatCompletion, shortenChatModel } from './chatBackend.js';
|
|
12
|
+
// Constants
|
|
13
|
+
const CHAT_DIR = resolve(homedir(), '.openswarm', 'chat');
|
|
14
|
+
// Render guard: blessed는 동시 render() 호출 시 화면이 검은색으로 깨질 수 있음
|
|
15
|
+
let renderScheduled = false;
|
|
16
|
+
let screenRef = null;
|
|
17
|
+
function safeRender() {
|
|
18
|
+
if (renderScheduled || !screenRef)
|
|
19
|
+
return;
|
|
20
|
+
renderScheduled = true;
|
|
21
|
+
process.nextTick(() => {
|
|
22
|
+
renderScheduled = false;
|
|
23
|
+
try {
|
|
24
|
+
screenRef.render();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// render 실패 시 복구 시도
|
|
28
|
+
try {
|
|
29
|
+
screenRef.alloc();
|
|
30
|
+
screenRef.render();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// 무시 — 다음 사이클에서 복구
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// Session Management
|
|
39
|
+
async function ensureChatDir() {
|
|
40
|
+
await mkdir(CHAT_DIR, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
async function saveSession(session) {
|
|
43
|
+
await ensureChatDir();
|
|
44
|
+
session.updatedAt = new Date().toISOString();
|
|
45
|
+
const path = resolve(CHAT_DIR, `${session.id}.json`);
|
|
46
|
+
await writeFile(path, JSON.stringify(session, null, 2));
|
|
47
|
+
}
|
|
48
|
+
async function loadSession(id) {
|
|
49
|
+
const path = resolve(CHAT_DIR, `${id}.json`);
|
|
50
|
+
if (!existsSync(path))
|
|
51
|
+
return null;
|
|
52
|
+
const data = JSON.parse(await readFile(path, 'utf-8'));
|
|
53
|
+
const provider = data.provider ?? inferProvider(undefined, data.model);
|
|
54
|
+
return {
|
|
55
|
+
...data,
|
|
56
|
+
provider,
|
|
57
|
+
model: data.model ?? getDefaultChatModel(provider),
|
|
58
|
+
totalCost: data.totalCost ?? 0,
|
|
59
|
+
totalTokens: data.totalTokens ?? 0,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function generateSessionId() {
|
|
63
|
+
const now = new Date();
|
|
64
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
65
|
+
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
|
|
66
|
+
}
|
|
67
|
+
function inferProvider(provider, model) {
|
|
68
|
+
if (provider)
|
|
69
|
+
return provider;
|
|
70
|
+
if (model?.startsWith('gpt-') || model?.includes('codex'))
|
|
71
|
+
return 'codex';
|
|
72
|
+
return loadDefaultProvider();
|
|
73
|
+
}
|
|
74
|
+
function loadDefaultProvider() {
|
|
75
|
+
try {
|
|
76
|
+
return loadConfig().adapter ?? getDefaultAdapterName();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return getDefaultAdapterName();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Shared chat backend
|
|
83
|
+
async function callChatModel(prompt, provider, model, sessionId, onStream) {
|
|
84
|
+
const result = await runChatCompletion({
|
|
85
|
+
prompt,
|
|
86
|
+
provider,
|
|
87
|
+
model,
|
|
88
|
+
sessionId: provider === 'claude' ? sessionId : undefined,
|
|
89
|
+
timeoutMs: 180000,
|
|
90
|
+
onText: onStream,
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
response: result.response,
|
|
94
|
+
sessionId: result.sessionId ?? '',
|
|
95
|
+
cost: result.cost ?? 0,
|
|
96
|
+
tokens: result.tokens ?? 0,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Warhammer 40k Loading Messages
|
|
100
|
+
const LOADING_MESSAGES = [
|
|
101
|
+
'Initializing cogitator arrays',
|
|
102
|
+
'Querying data-vault archives',
|
|
103
|
+
'Accessing servitor protocols',
|
|
104
|
+
'Compiling neural responses',
|
|
105
|
+
'Interfacing with the Noosphere',
|
|
106
|
+
'Scanning data-streams',
|
|
107
|
+
'Calibrating logic engines',
|
|
108
|
+
'Decoding transmission packets',
|
|
109
|
+
'Loading archive databases',
|
|
110
|
+
'Synchronizing machine protocols',
|
|
111
|
+
'Analyzing pattern matrices',
|
|
112
|
+
'Establishing neural link',
|
|
113
|
+
'Processing data-core output',
|
|
114
|
+
'Running diagnostics sequence',
|
|
115
|
+
'Activating response circuits',
|
|
116
|
+
];
|
|
117
|
+
const SPINNER_FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
118
|
+
// UI Components - Claude Code Style
|
|
119
|
+
function createUI() {
|
|
120
|
+
const screen = blessed.screen({
|
|
121
|
+
smartCSR: true,
|
|
122
|
+
title: 'OpenSwarm Chat',
|
|
123
|
+
fullUnicode: true,
|
|
124
|
+
terminal: 'xterm-256color',
|
|
125
|
+
forceUnicode: true,
|
|
126
|
+
warnings: false, // Suppress warnings that can corrupt display
|
|
127
|
+
dockBorders: true, // Better border handling in tmux
|
|
128
|
+
fastCSR: true, // Faster rendering for streaming
|
|
129
|
+
});
|
|
130
|
+
const colors = {
|
|
131
|
+
bg: '#1a1a1a',
|
|
132
|
+
statusBg: '#2d3748',
|
|
133
|
+
statusFg: '#e2e8f0',
|
|
134
|
+
tabActiveBg: '#4a5568',
|
|
135
|
+
tabActiveFg: '#f7fafc',
|
|
136
|
+
tabInactiveBg: '#2d3748',
|
|
137
|
+
tabInactiveFg: '#a0aec0',
|
|
138
|
+
border: '#4a5568',
|
|
139
|
+
borderActive: '#667eea',
|
|
140
|
+
inputBorder: '#48bb78',
|
|
141
|
+
scrollbar: '#4a5568',
|
|
142
|
+
userMessage: '#60a5fa',
|
|
143
|
+
assistantMessage: '#34d399',
|
|
144
|
+
dimText: '#718096',
|
|
145
|
+
};
|
|
146
|
+
const statusBar = blessed.box({
|
|
147
|
+
top: 0,
|
|
148
|
+
left: 0,
|
|
149
|
+
width: '100%',
|
|
150
|
+
height: 1,
|
|
151
|
+
tags: true,
|
|
152
|
+
style: {
|
|
153
|
+
fg: colors.statusFg,
|
|
154
|
+
bg: colors.statusBg,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
const tabBar = blessed.box({
|
|
158
|
+
top: 1,
|
|
159
|
+
left: 0,
|
|
160
|
+
width: '100%',
|
|
161
|
+
height: 1,
|
|
162
|
+
tags: true,
|
|
163
|
+
style: {
|
|
164
|
+
fg: colors.tabInactiveFg,
|
|
165
|
+
bg: colors.tabInactiveBg,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
// Chat tab content - clean borders (adjusted for taller input box)
|
|
169
|
+
const chatLog = blessed.log({
|
|
170
|
+
top: 2,
|
|
171
|
+
left: 0,
|
|
172
|
+
width: '100%',
|
|
173
|
+
height: '100%-9', // Increased from -7 to -9 for 5-line input box
|
|
174
|
+
scrollable: true,
|
|
175
|
+
alwaysScroll: true,
|
|
176
|
+
mouse: true,
|
|
177
|
+
scrollbar: {
|
|
178
|
+
ch: '│',
|
|
179
|
+
track: {
|
|
180
|
+
bg: '#1a1a1a',
|
|
181
|
+
},
|
|
182
|
+
style: {
|
|
183
|
+
fg: colors.scrollbar,
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
tags: true,
|
|
187
|
+
border: { type: 'line' },
|
|
188
|
+
style: {
|
|
189
|
+
fg: '#e2e8f0',
|
|
190
|
+
bg: '#1a1a1a',
|
|
191
|
+
border: {
|
|
192
|
+
fg: colors.border,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
const projectsBox = blessed.box({
|
|
197
|
+
top: 2,
|
|
198
|
+
left: 0,
|
|
199
|
+
width: '100%',
|
|
200
|
+
height: '100%-9', // Adjusted for taller input box
|
|
201
|
+
content: '{center}{#718096-fg}Loading projects...{/}{/center}',
|
|
202
|
+
tags: true,
|
|
203
|
+
scrollable: true,
|
|
204
|
+
mouse: true,
|
|
205
|
+
scrollbar: {
|
|
206
|
+
ch: '│',
|
|
207
|
+
style: { fg: colors.scrollbar },
|
|
208
|
+
},
|
|
209
|
+
border: { type: 'line' },
|
|
210
|
+
style: {
|
|
211
|
+
fg: '#e2e8f0',
|
|
212
|
+
bg: '#1a1a1a',
|
|
213
|
+
border: { fg: colors.border },
|
|
214
|
+
},
|
|
215
|
+
hidden: true,
|
|
216
|
+
});
|
|
217
|
+
const tasksBox = blessed.box({
|
|
218
|
+
top: 2,
|
|
219
|
+
left: 0,
|
|
220
|
+
width: '100%',
|
|
221
|
+
height: '100%-9', // Adjusted for taller input box
|
|
222
|
+
content: '{center}{#718096-fg}Loading tasks...{/}{/center}',
|
|
223
|
+
tags: true,
|
|
224
|
+
scrollable: true,
|
|
225
|
+
mouse: true,
|
|
226
|
+
scrollbar: {
|
|
227
|
+
ch: '│',
|
|
228
|
+
style: { fg: colors.scrollbar },
|
|
229
|
+
},
|
|
230
|
+
border: { type: 'line' },
|
|
231
|
+
style: {
|
|
232
|
+
fg: '#e2e8f0',
|
|
233
|
+
bg: '#1a1a1a',
|
|
234
|
+
border: { fg: colors.border },
|
|
235
|
+
},
|
|
236
|
+
hidden: true,
|
|
237
|
+
});
|
|
238
|
+
const stuckBox = blessed.box({
|
|
239
|
+
top: 2,
|
|
240
|
+
left: 0,
|
|
241
|
+
width: '100%',
|
|
242
|
+
height: '100%-9',
|
|
243
|
+
content: '{center}{#718096-fg}Loading stuck issues...{/}{/center}',
|
|
244
|
+
tags: true,
|
|
245
|
+
scrollable: true,
|
|
246
|
+
alwaysScroll: true,
|
|
247
|
+
mouse: true,
|
|
248
|
+
keys: true,
|
|
249
|
+
vi: true,
|
|
250
|
+
scrollbar: {
|
|
251
|
+
ch: '█',
|
|
252
|
+
style: { fg: colors.scrollbar },
|
|
253
|
+
},
|
|
254
|
+
style: {
|
|
255
|
+
border: { fg: colors.border },
|
|
256
|
+
},
|
|
257
|
+
hidden: true,
|
|
258
|
+
});
|
|
259
|
+
const logsBox = blessed.log({
|
|
260
|
+
top: 2,
|
|
261
|
+
left: 0,
|
|
262
|
+
width: '100%',
|
|
263
|
+
height: '100%-9', // Adjusted for taller input box
|
|
264
|
+
scrollable: true,
|
|
265
|
+
alwaysScroll: true,
|
|
266
|
+
mouse: true,
|
|
267
|
+
scrollbar: {
|
|
268
|
+
ch: '│',
|
|
269
|
+
style: { fg: colors.scrollbar },
|
|
270
|
+
},
|
|
271
|
+
tags: true,
|
|
272
|
+
border: { type: 'line' },
|
|
273
|
+
style: {
|
|
274
|
+
fg: '#e2e8f0',
|
|
275
|
+
bg: '#1a1a1a',
|
|
276
|
+
border: { fg: colors.border },
|
|
277
|
+
},
|
|
278
|
+
hidden: true,
|
|
279
|
+
});
|
|
280
|
+
// Input box - textarea for multiline + Korean support
|
|
281
|
+
const inputBox = blessed.textarea({
|
|
282
|
+
bottom: 1,
|
|
283
|
+
left: 0,
|
|
284
|
+
width: '100%',
|
|
285
|
+
height: 5, // Increased height for multiline
|
|
286
|
+
inputOnFocus: true,
|
|
287
|
+
border: { type: 'line' },
|
|
288
|
+
label: ' {#718096-fg}Message (Shift+Enter: newline, Enter: send){/} ',
|
|
289
|
+
tags: true,
|
|
290
|
+
keys: true, // Enable key handling
|
|
291
|
+
mouse: true,
|
|
292
|
+
scrollable: true,
|
|
293
|
+
alwaysScroll: false,
|
|
294
|
+
style: {
|
|
295
|
+
fg: '#f7fafc',
|
|
296
|
+
bg: '#1a1a1a',
|
|
297
|
+
border: { fg: colors.border },
|
|
298
|
+
focus: {
|
|
299
|
+
border: { fg: colors.borderActive },
|
|
300
|
+
bg: '#0d1117',
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
const helpBar = blessed.box({
|
|
305
|
+
bottom: 0,
|
|
306
|
+
left: 0,
|
|
307
|
+
width: '100%',
|
|
308
|
+
height: 1,
|
|
309
|
+
content: ' {#718096-fg}Tab{/} Switch {#718096-fg}Enter{/} Send {#718096-fg}Shift+Enter{/} Newline {#718096-fg}Esc{/} Exit Input {#718096-fg}i{/} Focus Input {#718096-fg}Ctrl+C{/} Exit {#718096-fg}/help{/} Cmds',
|
|
310
|
+
tags: true,
|
|
311
|
+
style: {
|
|
312
|
+
fg: '#a0aec0',
|
|
313
|
+
bg: colors.statusBg,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
screen.append(statusBar);
|
|
317
|
+
screen.append(tabBar);
|
|
318
|
+
screen.append(chatLog);
|
|
319
|
+
screen.append(projectsBox);
|
|
320
|
+
screen.append(tasksBox);
|
|
321
|
+
screen.append(stuckBox);
|
|
322
|
+
screen.append(logsBox);
|
|
323
|
+
screen.append(inputBox);
|
|
324
|
+
screen.append(helpBar);
|
|
325
|
+
return {
|
|
326
|
+
screen,
|
|
327
|
+
statusBar,
|
|
328
|
+
tabBar,
|
|
329
|
+
chatLog,
|
|
330
|
+
projectsBox,
|
|
331
|
+
tasksBox,
|
|
332
|
+
stuckBox,
|
|
333
|
+
logsBox,
|
|
334
|
+
inputBox,
|
|
335
|
+
helpBar,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// Tab Management
|
|
339
|
+
function updateTabBar(ui, currentTab) {
|
|
340
|
+
const tabs = [
|
|
341
|
+
{ key: '1', name: 'Chat', icon: '💬' },
|
|
342
|
+
{ key: '2', name: 'Projects', icon: '📁' },
|
|
343
|
+
{ key: '3', name: 'Tasks', icon: '✓' },
|
|
344
|
+
{ key: '4', name: 'Stuck', icon: '⚠' },
|
|
345
|
+
{ key: '5', name: 'Logs', icon: '📝' },
|
|
346
|
+
];
|
|
347
|
+
const content = tabs.map((tab, idx) => {
|
|
348
|
+
if (idx === currentTab) {
|
|
349
|
+
// Active tab - highlighted
|
|
350
|
+
return `{#4a5568-bg}{#f7fafc-fg}{bold} ${tab.icon} ${tab.name} {/bold}{/}{/}`;
|
|
351
|
+
}
|
|
352
|
+
// Inactive tab - dimmed
|
|
353
|
+
return `{#2d3748-bg}{#a0aec0-fg} ${tab.icon} ${tab.name} {/}{/}`;
|
|
354
|
+
}).join(' ');
|
|
355
|
+
ui.tabBar.setContent(' ' + content);
|
|
356
|
+
}
|
|
357
|
+
function switchTab(state, ui, tabIndex) {
|
|
358
|
+
state.currentTab = tabIndex;
|
|
359
|
+
ui.chatLog.hide();
|
|
360
|
+
ui.projectsBox.hide();
|
|
361
|
+
ui.tasksBox.hide();
|
|
362
|
+
ui.stuckBox.hide();
|
|
363
|
+
ui.logsBox.hide();
|
|
364
|
+
switch (tabIndex) {
|
|
365
|
+
case 0:
|
|
366
|
+
ui.chatLog.show();
|
|
367
|
+
break;
|
|
368
|
+
case 1:
|
|
369
|
+
ui.projectsBox.show();
|
|
370
|
+
loadProjectsData(ui.projectsBox);
|
|
371
|
+
break;
|
|
372
|
+
case 2:
|
|
373
|
+
ui.tasksBox.show();
|
|
374
|
+
loadTasksData(ui.tasksBox);
|
|
375
|
+
break;
|
|
376
|
+
case 3:
|
|
377
|
+
ui.stuckBox.show();
|
|
378
|
+
loadStuckData(ui.stuckBox);
|
|
379
|
+
break;
|
|
380
|
+
case 4:
|
|
381
|
+
ui.logsBox.show();
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
updateTabBar(ui, tabIndex);
|
|
385
|
+
safeRender();
|
|
386
|
+
}
|
|
387
|
+
// Data Loaders
|
|
388
|
+
async function loadProjectsData(box) {
|
|
389
|
+
try {
|
|
390
|
+
const response = await fetch('http://127.0.0.1:3847/api/projects');
|
|
391
|
+
const projects = await response.json();
|
|
392
|
+
if (projects.length === 0) {
|
|
393
|
+
box.setContent('\n{center}{#718096-fg}No projects tracked{/}{/center}');
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const lines = [
|
|
397
|
+
'',
|
|
398
|
+
` {#a0aec0-fg}${projects.length} project${projects.length > 1 ? 's' : ''} tracked{/}`,
|
|
399
|
+
'',
|
|
400
|
+
];
|
|
401
|
+
for (const p of projects) {
|
|
402
|
+
const status = p.enabled ? '{#34d399-fg}●{/}' : '{#718096-fg}○{/}';
|
|
403
|
+
const running = p.running.length > 0 ? `{#60a5fa-fg}${p.running.length} running{/}` : '';
|
|
404
|
+
const queued = p.queued.length > 0 ? `{#f59e0b-fg}${p.queued.length} queued{/}` : '';
|
|
405
|
+
const tasks = [running, queued].filter(Boolean).join(' · ');
|
|
406
|
+
lines.push(` ${status} {bold}${p.name}{/bold}`);
|
|
407
|
+
if (tasks) {
|
|
408
|
+
lines.push(` ${tasks}`);
|
|
409
|
+
}
|
|
410
|
+
lines.push(` {#718096-fg}${p.path}{/}`);
|
|
411
|
+
lines.push('');
|
|
412
|
+
}
|
|
413
|
+
box.setContent(lines.join('\n'));
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
box.setContent(`\n{center}{#ef4444-fg}Failed to load projects{/}\n{#718096-fg}${err}{/}{/center}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function loadTasksData(box) {
|
|
420
|
+
try {
|
|
421
|
+
const response = await fetch('http://127.0.0.1:3847/api/pipeline');
|
|
422
|
+
const { stages } = await response.json();
|
|
423
|
+
if (stages.length === 0) {
|
|
424
|
+
box.setContent('\n{center}{#718096-fg}No pipeline events{/}{/center}');
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
// Build task info map and extract stage events
|
|
428
|
+
const taskInfo = new Map();
|
|
429
|
+
const allStageEvents = [];
|
|
430
|
+
for (const event of stages) {
|
|
431
|
+
if (event.type === 'task:started' && event.data.taskId) {
|
|
432
|
+
taskInfo.set(event.data.taskId, { title: event.data.title, issueIdentifier: event.data.issueIdentifier });
|
|
433
|
+
}
|
|
434
|
+
else if (event.type === 'pipeline:stage' && event.data.taskId && event.data.stage) {
|
|
435
|
+
allStageEvents.push({
|
|
436
|
+
taskId: event.data.taskId,
|
|
437
|
+
stage: event.data.stage,
|
|
438
|
+
status: event.data.status || '',
|
|
439
|
+
model: event.data.model,
|
|
440
|
+
inputTokens: event.data.inputTokens,
|
|
441
|
+
outputTokens: event.data.outputTokens,
|
|
442
|
+
costUsd: event.data.costUsd,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (allStageEvents.length === 0) {
|
|
447
|
+
box.setContent('\n{center}{#718096-fg}No active pipeline stages{/}{/center}');
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
// Render pipeline table
|
|
451
|
+
const recentStages = allStageEvents.slice(-15).reverse();
|
|
452
|
+
const lines = [
|
|
453
|
+
'',
|
|
454
|
+
` {#34d399-fg}{bold}Pipeline Events{/bold} {#718096-fg}(${recentStages.length} recent){/}{/}`,
|
|
455
|
+
'',
|
|
456
|
+
` {#718096-fg}${'TASK'.padEnd(12)} ${'STAGE'.padEnd(10)} ${'MODEL'.padEnd(12)} ${'TOKENS'.padEnd(15)} STATUS{/}`,
|
|
457
|
+
` {#444444-fg}${'─'.repeat(70)}{/}`,
|
|
458
|
+
];
|
|
459
|
+
for (const ev of recentStages) {
|
|
460
|
+
const info = taskInfo.get(ev.taskId);
|
|
461
|
+
const task = (info?.issueIdentifier || ev.taskId.slice(0, 8)).padEnd(12).slice(0, 12);
|
|
462
|
+
const stage = ev.stage.padEnd(10).slice(0, 10);
|
|
463
|
+
const statusMap = {
|
|
464
|
+
start: ['◐', '#f59e0b'],
|
|
465
|
+
complete: ['●', '#34d399'],
|
|
466
|
+
fail: ['✗', '#ef4444'],
|
|
467
|
+
};
|
|
468
|
+
const [icon, color] = statusMap[ev.status] || ['○', '#718096'];
|
|
469
|
+
let model = '';
|
|
470
|
+
if (ev.model?.includes('sonnet-4-5'))
|
|
471
|
+
model = 'sonnet-4.5';
|
|
472
|
+
else if (ev.model?.includes('haiku-4-5'))
|
|
473
|
+
model = 'haiku-4.5';
|
|
474
|
+
else if (ev.model?.includes('opus-4'))
|
|
475
|
+
model = 'opus-4';
|
|
476
|
+
else if (ev.model)
|
|
477
|
+
model = ev.model.split('-').pop() || '';
|
|
478
|
+
model = model.padEnd(12).slice(0, 12);
|
|
479
|
+
let tokens = '';
|
|
480
|
+
if (ev.inputTokens || ev.outputTokens) {
|
|
481
|
+
const inK = ev.inputTokens ? Math.round(ev.inputTokens / 1000) : 0;
|
|
482
|
+
const outK = ev.outputTokens ? Math.round(ev.outputTokens / 1000) : 0;
|
|
483
|
+
tokens = `${inK}k/${outK}k`;
|
|
484
|
+
if (ev.costUsd != null)
|
|
485
|
+
tokens += ` $${ev.costUsd.toFixed(2)}`;
|
|
486
|
+
}
|
|
487
|
+
tokens = tokens.padEnd(15).slice(0, 15);
|
|
488
|
+
lines.push(` {#34d399-fg}${task}{/} {#718096-fg}${stage}{/} {#34d399-fg}${model}{/} {#718096-fg}${tokens}{/} {${color}-fg}${icon} ${ev.status}{/}`);
|
|
489
|
+
}
|
|
490
|
+
box.setContent(lines.join('\n'));
|
|
491
|
+
}
|
|
492
|
+
catch (err) {
|
|
493
|
+
box.setContent(`\n{center}{#ef4444-fg}Failed to load pipeline{/}\n{#718096-fg}${err}{/}{/center}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
async function loadStuckData(box) {
|
|
497
|
+
try {
|
|
498
|
+
const response = await fetch('http://127.0.0.1:3847/api/stuck-issues');
|
|
499
|
+
const { stuckIssues, failedIssues } = await response.json();
|
|
500
|
+
const totalStuck = stuckIssues.length;
|
|
501
|
+
const totalFailed = failedIssues.length;
|
|
502
|
+
const total = totalStuck + totalFailed;
|
|
503
|
+
if (total === 0) {
|
|
504
|
+
box.setContent('\n{center}{#34d399-fg}✓ All issues healthy{/}{/center}');
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const lines = [
|
|
508
|
+
'',
|
|
509
|
+
` {#ef4444-fg}⚠ ${total} issue${total > 1 ? 's' : ''} need attention{/}`,
|
|
510
|
+
'',
|
|
511
|
+
];
|
|
512
|
+
// Stuck issues
|
|
513
|
+
if (totalStuck > 0) {
|
|
514
|
+
lines.push(` {#f59e0b-fg}{bold}⏱ STUCK (${totalStuck}){/bold}{/}`);
|
|
515
|
+
lines.push('');
|
|
516
|
+
for (const issue of stuckIssues) {
|
|
517
|
+
const priorityIcon = issue.priority === 1 ? '{#ef4444-fg}🔴{/}' : issue.priority === 2 ? '{#f59e0b-fg}🟡{/}' : '{#718096-fg}⚪{/}';
|
|
518
|
+
lines.push(` ${priorityIcon} {bold}${issue.identifier}{/bold}`);
|
|
519
|
+
lines.push(` ${issue.title.substring(0, 60)}${issue.title.length > 60 ? '...' : ''}`);
|
|
520
|
+
lines.push(` {#f59e0b-fg}${issue.reason}{/}`);
|
|
521
|
+
if (issue.project?.name) {
|
|
522
|
+
lines.push(` {#718096-fg}📁 ${issue.project.name}{/}`);
|
|
523
|
+
}
|
|
524
|
+
lines.push('');
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
// Failed issues
|
|
528
|
+
if (totalFailed > 0) {
|
|
529
|
+
lines.push(` {#ef4444-fg}{bold}✖ FAILED (${totalFailed}){/bold}{/}`);
|
|
530
|
+
lines.push('');
|
|
531
|
+
for (const issue of failedIssues) {
|
|
532
|
+
const priorityIcon = issue.priority === 1 ? '{#ef4444-fg}🔴{/}' : issue.priority === 2 ? '{#f59e0b-fg}🟡{/}' : '{#718096-fg}⚪{/}';
|
|
533
|
+
lines.push(` ${priorityIcon} {bold}${issue.identifier}{/bold}`);
|
|
534
|
+
lines.push(` ${issue.title.substring(0, 60)}${issue.title.length > 60 ? '...' : ''}`);
|
|
535
|
+
lines.push(` {#ef4444-fg}${issue.reason}{/}`);
|
|
536
|
+
if (issue.project?.name) {
|
|
537
|
+
lines.push(` {#718096-fg}📁 ${issue.project.name}{/}`);
|
|
538
|
+
}
|
|
539
|
+
lines.push('');
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
box.setContent(lines.join('\n'));
|
|
543
|
+
}
|
|
544
|
+
catch (err) {
|
|
545
|
+
box.setContent(`\n{center}{#ef4444-fg}Failed to load stuck issues{/}\n{#718096-fg}${err}{/}{/center}`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
// Loading Spinner (inline in chat)
|
|
549
|
+
function startSpinner(ui) {
|
|
550
|
+
let frameIndex = 0;
|
|
551
|
+
const loadingMessage = LOADING_MESSAGES[Math.floor(Math.random() * LOADING_MESSAGES.length)];
|
|
552
|
+
const lines = ui.chatLog.getLines();
|
|
553
|
+
const lineIndex = lines.length;
|
|
554
|
+
const interval = setInterval(() => {
|
|
555
|
+
const spinner = SPINNER_FRAMES[frameIndex % SPINNER_FRAMES.length];
|
|
556
|
+
const content = ` {#667eea-fg}${spinner}{/} {#718096-fg}${loadingMessage}...{/}`;
|
|
557
|
+
ui.chatLog.setLine(lineIndex, content);
|
|
558
|
+
ui.chatLog.setScrollPerc(100);
|
|
559
|
+
safeRender();
|
|
560
|
+
frameIndex++;
|
|
561
|
+
}, 80);
|
|
562
|
+
return { interval, lineIndex };
|
|
563
|
+
}
|
|
564
|
+
function stopSpinner(ui, spinnerData) {
|
|
565
|
+
clearInterval(spinnerData.interval);
|
|
566
|
+
ui.chatLog.deleteLine(spinnerData.lineIndex);
|
|
567
|
+
safeRender();
|
|
568
|
+
}
|
|
569
|
+
// Chat Logic
|
|
570
|
+
async function sendMessage(state, ui, message) {
|
|
571
|
+
if (!message.trim())
|
|
572
|
+
return;
|
|
573
|
+
ui.chatLog.log('');
|
|
574
|
+
ui.chatLog.log(`{#60a5fa-fg}{bold}▸ You{/bold}{/}`);
|
|
575
|
+
ui.chatLog.log(` ${message}`);
|
|
576
|
+
ui.chatLog.log('');
|
|
577
|
+
ui.chatLog.setScrollPerc(100);
|
|
578
|
+
safeRender();
|
|
579
|
+
state.session.messages.push({ role: 'user', content: message });
|
|
580
|
+
ui.chatLog.log(`{#34d399-fg}{bold}▸ Assistant{/bold}{/}`);
|
|
581
|
+
const assistantHeaderLine = ui.chatLog.getLines().length - 1;
|
|
582
|
+
let assistantContent = '';
|
|
583
|
+
let lastRenderTime = 0;
|
|
584
|
+
let spinnerStopped = false;
|
|
585
|
+
let contentStartLine = assistantHeaderLine + 1;
|
|
586
|
+
const spinnerData = startSpinner(ui);
|
|
587
|
+
try {
|
|
588
|
+
const result = await callChatModel(message, state.session.provider, state.session.model, state.session.claudeSessionId, (chunk, isThinking) => {
|
|
589
|
+
// Handle thinking notification (show/resume spinner)
|
|
590
|
+
if (isThinking) {
|
|
591
|
+
if (spinnerStopped) {
|
|
592
|
+
// Resume spinner for thinking phase
|
|
593
|
+
spinnerStopped = false;
|
|
594
|
+
const newSpinner = startSpinner(ui);
|
|
595
|
+
Object.assign(spinnerData, newSpinner);
|
|
596
|
+
}
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
// Stop spinner on first text chunk
|
|
600
|
+
if (!spinnerStopped && chunk) {
|
|
601
|
+
stopSpinner(ui, spinnerData);
|
|
602
|
+
spinnerStopped = true;
|
|
603
|
+
}
|
|
604
|
+
if (!chunk)
|
|
605
|
+
return;
|
|
606
|
+
assistantContent += chunk;
|
|
607
|
+
// Throttle rendering for smoother streaming (30fps)
|
|
608
|
+
const now = Date.now();
|
|
609
|
+
if (now - lastRenderTime < 33)
|
|
610
|
+
return;
|
|
611
|
+
lastRenderTime = now;
|
|
612
|
+
// Clear previous content lines (안전한 역순 삭제)
|
|
613
|
+
const currentLines = ui.chatLog.getLines().length;
|
|
614
|
+
const deleteCount = Math.max(0, Math.min(currentLines - contentStartLine, currentLines));
|
|
615
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
616
|
+
ui.chatLog.deleteLine(contentStartLine);
|
|
617
|
+
}
|
|
618
|
+
// Add updated content line by line with proper empty line handling
|
|
619
|
+
const contentLines = assistantContent.split('\n');
|
|
620
|
+
for (const line of contentLines) {
|
|
621
|
+
// Always add line, even if empty (preserves paragraph breaks)
|
|
622
|
+
ui.chatLog.log(line ? ` ${line}` : ' ');
|
|
623
|
+
}
|
|
624
|
+
ui.chatLog.setScrollPerc(100);
|
|
625
|
+
safeRender();
|
|
626
|
+
});
|
|
627
|
+
// Ensure spinner is stopped
|
|
628
|
+
if (!spinnerStopped) {
|
|
629
|
+
stopSpinner(ui, spinnerData);
|
|
630
|
+
spinnerStopped = true;
|
|
631
|
+
}
|
|
632
|
+
if (state.session.provider === 'claude' && result.sessionId) {
|
|
633
|
+
state.session.claudeSessionId = result.sessionId;
|
|
634
|
+
}
|
|
635
|
+
// Update session stats
|
|
636
|
+
state.session.totalCost += result.cost;
|
|
637
|
+
state.session.totalTokens += result.tokens;
|
|
638
|
+
// Finalize assistant message with cost
|
|
639
|
+
// Clear streaming content first (안전한 삭제)
|
|
640
|
+
const finalLines = ui.chatLog.getLines().length;
|
|
641
|
+
const finalDeleteCount = Math.max(0, Math.min(finalLines - contentStartLine, finalLines));
|
|
642
|
+
for (let i = 0; i < finalDeleteCount; i++) {
|
|
643
|
+
ui.chatLog.deleteLine(contentStartLine);
|
|
644
|
+
}
|
|
645
|
+
// Add final content line by line with proper empty line handling
|
|
646
|
+
const contentLines = result.response.split('\n');
|
|
647
|
+
for (const line of contentLines) {
|
|
648
|
+
// Always add line, even if empty (preserves paragraph breaks)
|
|
649
|
+
ui.chatLog.log(line ? ` ${line}` : ' ');
|
|
650
|
+
}
|
|
651
|
+
// Add cost info if available
|
|
652
|
+
if (result.cost > 0) {
|
|
653
|
+
ui.chatLog.log(` {#718096-fg}${result.tokens} tokens · $${result.cost.toFixed(4)}{/}`);
|
|
654
|
+
}
|
|
655
|
+
ui.chatLog.log('');
|
|
656
|
+
state.session.messages.push({
|
|
657
|
+
role: 'assistant',
|
|
658
|
+
content: result.response,
|
|
659
|
+
cost: result.cost,
|
|
660
|
+
});
|
|
661
|
+
await saveSession(state.session);
|
|
662
|
+
updateStatusBar(state, ui);
|
|
663
|
+
safeRender();
|
|
664
|
+
}
|
|
665
|
+
catch (err) {
|
|
666
|
+
if (!spinnerStopped) {
|
|
667
|
+
stopSpinner(ui, spinnerData);
|
|
668
|
+
}
|
|
669
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
670
|
+
ui.chatLog.log(`{#ef4444-fg}{bold}✗ Error{/bold}{/}`);
|
|
671
|
+
ui.chatLog.log(` ${msg}`);
|
|
672
|
+
ui.chatLog.log('');
|
|
673
|
+
state.session.messages.pop(); // Remove user message on failure
|
|
674
|
+
safeRender();
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// Status Bar Update
|
|
678
|
+
function updateStatusBar(state, ui) {
|
|
679
|
+
const modelShort = shortenChatModel(state.session.model);
|
|
680
|
+
const cost = state.session.totalCost > 0 ? `$${state.session.totalCost.toFixed(4)}` : '$0.00';
|
|
681
|
+
const msgs = state.session.messages.length;
|
|
682
|
+
const status = [
|
|
683
|
+
'{bold}OpenSwarm{/bold}',
|
|
684
|
+
`{#718096-fg}│{/}`,
|
|
685
|
+
`{#a0aec0-fg}${state.session.id}{/}`,
|
|
686
|
+
`{#718096-fg}│{/}`,
|
|
687
|
+
`{#c084fc-fg}${state.session.provider}{/}`,
|
|
688
|
+
`{#718096-fg}│{/}`,
|
|
689
|
+
`{#60a5fa-fg}${modelShort}{/}`,
|
|
690
|
+
`{#718096-fg}│{/}`,
|
|
691
|
+
`{#a0aec0-fg}${msgs} messages{/}`,
|
|
692
|
+
`{#718096-fg}│{/}`,
|
|
693
|
+
`{#34d399-fg}${cost}{/}`,
|
|
694
|
+
].join(' ');
|
|
695
|
+
ui.statusBar.setContent(' ' + status);
|
|
696
|
+
}
|
|
697
|
+
// Command Handler
|
|
698
|
+
async function handleCommand(cmd, state, ui) {
|
|
699
|
+
const [command, ...args] = cmd.slice(1).split(' ');
|
|
700
|
+
switch (command) {
|
|
701
|
+
case 'clear':
|
|
702
|
+
case 'c':
|
|
703
|
+
state.session.messages = [];
|
|
704
|
+
state.session.claudeSessionId = undefined;
|
|
705
|
+
state.session.totalCost = 0;
|
|
706
|
+
state.session.totalTokens = 0;
|
|
707
|
+
ui.chatLog.setContent('');
|
|
708
|
+
ui.chatLog.log('');
|
|
709
|
+
ui.chatLog.log('{#34d399-fg}✓ Conversation cleared{/}');
|
|
710
|
+
ui.chatLog.log('');
|
|
711
|
+
updateStatusBar(state, ui);
|
|
712
|
+
safeRender();
|
|
713
|
+
break;
|
|
714
|
+
case 'provider':
|
|
715
|
+
case 'p': {
|
|
716
|
+
const next = args[0];
|
|
717
|
+
ui.chatLog.log('');
|
|
718
|
+
if (!next) {
|
|
719
|
+
ui.chatLog.log(` {bold}Current provider:{/bold} {#c084fc-fg}${state.session.provider}{/}`);
|
|
720
|
+
ui.chatLog.log(' {#718096-fg}Available providers:{/}');
|
|
721
|
+
ui.chatLog.log(' {#a0aec0-fg}claude{/}');
|
|
722
|
+
ui.chatLog.log(' {#a0aec0-fg}codex{/}');
|
|
723
|
+
}
|
|
724
|
+
else if (next !== 'claude' && next !== 'codex') {
|
|
725
|
+
ui.chatLog.log(` {#ef4444-fg}Unknown provider: ${next}{/}`);
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
state.session.provider = next;
|
|
729
|
+
state.session.model = getDefaultChatModel(next);
|
|
730
|
+
state.session.claudeSessionId = undefined;
|
|
731
|
+
ui.chatLog.log(` {#34d399-fg}✓ Provider changed to {bold}${next}{/bold}{/}`);
|
|
732
|
+
ui.chatLog.log(` {#34d399-fg}✓ Model changed to {bold}${state.session.model}{/bold}{/}`);
|
|
733
|
+
updateStatusBar(state, ui);
|
|
734
|
+
}
|
|
735
|
+
ui.chatLog.log('');
|
|
736
|
+
safeRender();
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
case 'model':
|
|
740
|
+
case 'm': {
|
|
741
|
+
const newModel = args[0];
|
|
742
|
+
ui.chatLog.log('');
|
|
743
|
+
if (!newModel) {
|
|
744
|
+
ui.chatLog.log(` {bold}Current provider:{/bold} {#c084fc-fg}${state.session.provider}{/}`);
|
|
745
|
+
ui.chatLog.log(` {bold}Current model:{/bold} {#60a5fa-fg}${shortenChatModel(state.session.model)}{/}`);
|
|
746
|
+
ui.chatLog.log('');
|
|
747
|
+
ui.chatLog.log(' {#718096-fg}Available models:{/}');
|
|
748
|
+
if (state.session.provider === 'claude') {
|
|
749
|
+
ui.chatLog.log(' {#a0aec0-fg}sonnet{/} {#718096-fg}→{/} claude-sonnet-4-5');
|
|
750
|
+
ui.chatLog.log(' {#a0aec0-fg}haiku{/} {#718096-fg}→{/} claude-haiku-4-5');
|
|
751
|
+
ui.chatLog.log(' {#a0aec0-fg}opus{/} {#718096-fg}→{/} claude-opus-4-6');
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
ui.chatLog.log(' {#a0aec0-fg}codex{/} {#718096-fg}→{/} gpt-5-codex');
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
state.session.model = resolveChatModel(newModel, state.session.provider);
|
|
759
|
+
state.session.claudeSessionId = undefined;
|
|
760
|
+
const shortName = shortenChatModel(state.session.model);
|
|
761
|
+
ui.chatLog.log(` {#34d399-fg}✓ Model changed to {bold}${shortName}{/bold}{/}`);
|
|
762
|
+
updateStatusBar(state, ui);
|
|
763
|
+
}
|
|
764
|
+
ui.chatLog.log('');
|
|
765
|
+
safeRender();
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
case 'save': {
|
|
769
|
+
const name = args[0] || state.session.id;
|
|
770
|
+
state.session.id = name;
|
|
771
|
+
await saveSession(state.session);
|
|
772
|
+
ui.chatLog.log('');
|
|
773
|
+
ui.chatLog.log(` {#34d399-fg}✓ Session saved: {bold}${name}{/bold}{/}`);
|
|
774
|
+
ui.chatLog.log('');
|
|
775
|
+
updateStatusBar(state, ui);
|
|
776
|
+
safeRender();
|
|
777
|
+
break;
|
|
778
|
+
}
|
|
779
|
+
case 'help':
|
|
780
|
+
case 'h':
|
|
781
|
+
case '?':
|
|
782
|
+
ui.chatLog.log('');
|
|
783
|
+
ui.chatLog.log(' {bold}Available Commands{/bold}');
|
|
784
|
+
ui.chatLog.log('');
|
|
785
|
+
ui.chatLog.log(' {#60a5fa-fg}/clear{/} Clear conversation');
|
|
786
|
+
ui.chatLog.log(' {#60a5fa-fg}/provider{/} [id] Change provider {#718096-fg}(claude/codex){/}');
|
|
787
|
+
ui.chatLog.log(' {#60a5fa-fg}/model{/} [name] Change model {#718096-fg}(sonnet/haiku/opus){/}');
|
|
788
|
+
ui.chatLog.log(' {#60a5fa-fg}/save{/} [name] Save session');
|
|
789
|
+
ui.chatLog.log(' {#60a5fa-fg}/help{/} Show this help');
|
|
790
|
+
ui.chatLog.log('');
|
|
791
|
+
ui.chatLog.log(' {bold}Navigation{/bold}');
|
|
792
|
+
ui.chatLog.log('');
|
|
793
|
+
ui.chatLog.log(' {#718096-fg}1-4{/} Switch tabs directly');
|
|
794
|
+
ui.chatLog.log(' {#718096-fg}Tab/Shift+Tab{/} Cycle through tabs');
|
|
795
|
+
ui.chatLog.log(' {#718096-fg}Esc{/} Exit input mode (blur)');
|
|
796
|
+
ui.chatLog.log(' {#718096-fg}i / Enter{/} Focus input (from chat)');
|
|
797
|
+
ui.chatLog.log(' {#718096-fg}Ctrl+C{/} Exit (double press to confirm)');
|
|
798
|
+
ui.chatLog.log('');
|
|
799
|
+
safeRender();
|
|
800
|
+
break;
|
|
801
|
+
default:
|
|
802
|
+
ui.chatLog.log('');
|
|
803
|
+
ui.chatLog.log(` {#ef4444-fg}Unknown command: /{bold}${command}{/bold}{/}`);
|
|
804
|
+
ui.chatLog.log(` {#718096-fg}Type {/}{#60a5fa-fg}/help{/}{#718096-fg} for available commands{/}`);
|
|
805
|
+
ui.chatLog.log('');
|
|
806
|
+
safeRender();
|
|
807
|
+
}
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
// Main
|
|
811
|
+
export async function main() {
|
|
812
|
+
const defaultProvider = loadDefaultProvider();
|
|
813
|
+
const loadArg = process.argv[2];
|
|
814
|
+
let session;
|
|
815
|
+
if (loadArg && loadArg !== '--' && !loadArg.startsWith('-')) {
|
|
816
|
+
const loaded = await loadSession(loadArg);
|
|
817
|
+
if (loaded) {
|
|
818
|
+
session = loaded;
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
session = {
|
|
822
|
+
id: loadArg,
|
|
823
|
+
provider: defaultProvider,
|
|
824
|
+
model: getDefaultChatModel(defaultProvider),
|
|
825
|
+
messages: [],
|
|
826
|
+
totalCost: 0,
|
|
827
|
+
totalTokens: 0,
|
|
828
|
+
createdAt: new Date().toISOString(),
|
|
829
|
+
updatedAt: new Date().toISOString(),
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
session = {
|
|
835
|
+
id: generateSessionId(),
|
|
836
|
+
provider: defaultProvider,
|
|
837
|
+
model: getDefaultChatModel(defaultProvider),
|
|
838
|
+
messages: [],
|
|
839
|
+
totalCost: 0,
|
|
840
|
+
totalTokens: 0,
|
|
841
|
+
createdAt: new Date().toISOString(),
|
|
842
|
+
updatedAt: new Date().toISOString(),
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
const state = {
|
|
846
|
+
session,
|
|
847
|
+
currentTab: 0,
|
|
848
|
+
inputMode: 'normal',
|
|
849
|
+
multilineBuffer: [],
|
|
850
|
+
showBinary: false,
|
|
851
|
+
diagnostics: {
|
|
852
|
+
lastResponseTime: 0,
|
|
853
|
+
avgTokensPerSec: 0,
|
|
854
|
+
totalRequests: 0,
|
|
855
|
+
},
|
|
856
|
+
};
|
|
857
|
+
const ui = createUI();
|
|
858
|
+
screenRef = ui.screen; // safeRender용 참조 설정
|
|
859
|
+
updateStatusBar(state, ui);
|
|
860
|
+
updateTabBar(ui, state.currentTab);
|
|
861
|
+
// Restore chat history - Claude Code style with proper line breaks
|
|
862
|
+
for (const msg of session.messages) {
|
|
863
|
+
ui.chatLog.log('');
|
|
864
|
+
if (msg.role === 'user') {
|
|
865
|
+
ui.chatLog.log(`{#60a5fa-fg}{bold}▸ You{/bold}{/}`);
|
|
866
|
+
// Split multiline user messages
|
|
867
|
+
const userLines = msg.content.split('\n');
|
|
868
|
+
for (const line of userLines) {
|
|
869
|
+
ui.chatLog.log(line ? ` ${line}` : ' ');
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
ui.chatLog.log(`{#34d399-fg}{bold}▸ Assistant{/bold}{/}`);
|
|
874
|
+
// Split multiline assistant messages properly
|
|
875
|
+
const assistantLines = msg.content.split('\n');
|
|
876
|
+
for (const line of assistantLines) {
|
|
877
|
+
ui.chatLog.log(line ? ` ${line}` : ' ');
|
|
878
|
+
}
|
|
879
|
+
if (msg.cost) {
|
|
880
|
+
ui.chatLog.log(` {#718096-fg}$${msg.cost.toFixed(4)}{/}`);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (session.messages.length > 0) {
|
|
885
|
+
ui.chatLog.log('');
|
|
886
|
+
}
|
|
887
|
+
// Key bindings
|
|
888
|
+
ui.screen.key(['1'], () => switchTab(state, ui, 0));
|
|
889
|
+
ui.screen.key(['2'], () => switchTab(state, ui, 1));
|
|
890
|
+
ui.screen.key(['3'], () => switchTab(state, ui, 2));
|
|
891
|
+
ui.screen.key(['4'], () => switchTab(state, ui, 3));
|
|
892
|
+
ui.screen.key(['5'], () => switchTab(state, ui, 4));
|
|
893
|
+
ui.screen.key(['tab'], () => {
|
|
894
|
+
const next = (state.currentTab + 1) % 5;
|
|
895
|
+
switchTab(state, ui, next);
|
|
896
|
+
});
|
|
897
|
+
ui.screen.key(['S-tab'], () => {
|
|
898
|
+
const prev = (state.currentTab - 1 + 5) % 5;
|
|
899
|
+
switchTab(state, ui, prev);
|
|
900
|
+
});
|
|
901
|
+
// Ctrl+C: Clear input or exit (Claude Code style)
|
|
902
|
+
let ctrlCPressed = false;
|
|
903
|
+
ui.screen.key(['C-c'], async () => {
|
|
904
|
+
const currentValue = ui.inputBox.getValue();
|
|
905
|
+
if (currentValue && currentValue.trim()) {
|
|
906
|
+
// If input has text, just clear it
|
|
907
|
+
ui.inputBox.clearValue();
|
|
908
|
+
ui.inputBox.focus();
|
|
909
|
+
safeRender();
|
|
910
|
+
ctrlCPressed = false;
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
// If input is empty, exit with double Ctrl+C
|
|
914
|
+
if (ctrlCPressed) {
|
|
915
|
+
await saveSession(state.session);
|
|
916
|
+
process.exit(0);
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
ctrlCPressed = true;
|
|
920
|
+
ui.statusBar.setContent(' {#f59e0b-fg}Press Ctrl+C again to exit{/}');
|
|
921
|
+
safeRender();
|
|
922
|
+
setTimeout(() => {
|
|
923
|
+
ctrlCPressed = false;
|
|
924
|
+
updateStatusBar(state, ui);
|
|
925
|
+
safeRender();
|
|
926
|
+
}, 2000);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
// Escape: Clear input and blur (exit input mode)
|
|
931
|
+
ui.screen.key(['escape'], () => {
|
|
932
|
+
const currentValue = ui.inputBox.getValue();
|
|
933
|
+
if (currentValue && currentValue.trim()) {
|
|
934
|
+
// Clear input if has content
|
|
935
|
+
ui.inputBox.clearValue();
|
|
936
|
+
}
|
|
937
|
+
// Blur input box to exit input mode
|
|
938
|
+
ui.chatLog.focus();
|
|
939
|
+
safeRender();
|
|
940
|
+
});
|
|
941
|
+
// Enter in chatLog: focus input
|
|
942
|
+
ui.chatLog.key(['enter', 'i'], () => {
|
|
943
|
+
ui.inputBox.focus();
|
|
944
|
+
safeRender();
|
|
945
|
+
});
|
|
946
|
+
// Shift+Enter: Insert newline (handled by textarea by default)
|
|
947
|
+
// Enter: Submit message
|
|
948
|
+
ui.inputBox.key(['enter'], async () => {
|
|
949
|
+
const value = ui.inputBox.getValue();
|
|
950
|
+
const trimmed = value.trim();
|
|
951
|
+
if (!trimmed)
|
|
952
|
+
return;
|
|
953
|
+
ui.inputBox.clearValue();
|
|
954
|
+
ui.inputBox.focus();
|
|
955
|
+
safeRender();
|
|
956
|
+
if (trimmed.startsWith('/')) {
|
|
957
|
+
await handleCommand(trimmed, state, ui);
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
await sendMessage(state, ui, trimmed);
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
// Focus input by default
|
|
964
|
+
ui.inputBox.focus();
|
|
965
|
+
// Handle terminal resize (important for tmux) — debounce로 연속 resize 시 깜빡임 방지
|
|
966
|
+
let resizeTimer = null;
|
|
967
|
+
process.stdout.on('resize', () => {
|
|
968
|
+
if (resizeTimer)
|
|
969
|
+
clearTimeout(resizeTimer);
|
|
970
|
+
resizeTimer = setTimeout(() => {
|
|
971
|
+
ui.screen.alloc();
|
|
972
|
+
ui.screen.realloc();
|
|
973
|
+
safeRender();
|
|
974
|
+
}, 50);
|
|
975
|
+
});
|
|
976
|
+
// Render
|
|
977
|
+
safeRender();
|
|
978
|
+
// Auto-refresh Projects/Tasks/Stuck tabs every 5s
|
|
979
|
+
setInterval(() => {
|
|
980
|
+
if (state.currentTab === 1)
|
|
981
|
+
loadProjectsData(ui.projectsBox);
|
|
982
|
+
if (state.currentTab === 2)
|
|
983
|
+
loadTasksData(ui.tasksBox);
|
|
984
|
+
if (state.currentTab === 3)
|
|
985
|
+
loadStuckData(ui.stuckBox);
|
|
986
|
+
safeRender();
|
|
987
|
+
}, 5000);
|
|
988
|
+
// System logs (example - hook into eventHub in real implementation)
|
|
989
|
+
ui.logsBox.log('{gray-fg}System initialized{/gray-fg}');
|
|
990
|
+
}
|
|
991
|
+
// Auto-run if called directly
|
|
992
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
993
|
+
main().catch((err) => {
|
|
994
|
+
console.error('Fatal:', err);
|
|
995
|
+
process.exit(1);
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
//# sourceMappingURL=chatTui.js.map
|