nova-terminal-ai 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/bin/nova +38 -0
- package/bin/nova.js +11 -0
- package/dist/commands/SmartCompletion.d.ts +71 -0
- package/dist/commands/SmartCompletion.d.ts.map +1 -0
- package/dist/commands/SmartCompletion.js +377 -0
- package/dist/commands/SmartCompletion.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/packages/cli/src/commands/SmartCompletion.d.ts +71 -0
- package/dist/packages/cli/src/commands/SmartCompletion.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/SmartCompletion.js +377 -0
- package/dist/packages/cli/src/commands/SmartCompletion.js.map +1 -0
- package/dist/packages/cli/src/index.d.ts +2 -0
- package/dist/packages/cli/src/index.d.ts.map +1 -0
- package/dist/packages/cli/src/index.js +5 -0
- package/dist/packages/cli/src/index.js.map +1 -0
- package/dist/packages/cli/src/startup/IFlowRepl.d.ts +50 -0
- package/dist/packages/cli/src/startup/IFlowRepl.d.ts.map +1 -0
- package/dist/packages/cli/src/startup/IFlowRepl.js +178 -0
- package/dist/packages/cli/src/startup/IFlowRepl.js.map +1 -0
- package/dist/packages/cli/src/startup/InkBasedRepl.d.ts +151 -0
- package/dist/packages/cli/src/startup/InkBasedRepl.d.ts.map +1 -0
- package/dist/packages/cli/src/startup/InkBasedRepl.js +1415 -0
- package/dist/packages/cli/src/startup/InkBasedRepl.js.map +1 -0
- package/dist/packages/cli/src/startup/InteractiveRepl.d.ts +141 -0
- package/dist/packages/cli/src/startup/InteractiveRepl.d.ts.map +1 -0
- package/dist/packages/cli/src/startup/InteractiveRepl.js +2561 -0
- package/dist/packages/cli/src/startup/InteractiveRepl.js.map +1 -0
- package/dist/packages/cli/src/startup/NovaApp.d.ts +57 -0
- package/dist/packages/cli/src/startup/NovaApp.d.ts.map +1 -0
- package/dist/packages/cli/src/startup/NovaApp.js +1978 -0
- package/dist/packages/cli/src/startup/NovaApp.js.map +1 -0
- package/dist/packages/cli/src/startup/index.d.ts +5 -0
- package/dist/packages/cli/src/startup/index.d.ts.map +1 -0
- package/dist/packages/cli/src/startup/index.js +4 -0
- package/dist/packages/cli/src/startup/index.js.map +1 -0
- package/dist/packages/cli/src/startup/parseArgs.d.ts +47 -0
- package/dist/packages/cli/src/startup/parseArgs.d.ts.map +1 -0
- package/dist/packages/cli/src/startup/parseArgs.js +262 -0
- package/dist/packages/cli/src/startup/parseArgs.js.map +1 -0
- package/dist/packages/cli/src/ui/IFlowDropdown.d.ts +63 -0
- package/dist/packages/cli/src/ui/IFlowDropdown.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/IFlowDropdown.js +362 -0
- package/dist/packages/cli/src/ui/IFlowDropdown.js.map +1 -0
- package/dist/packages/cli/src/ui/ModernReplUI.d.ts +55 -0
- package/dist/packages/cli/src/ui/ModernReplUI.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/ModernReplUI.js +207 -0
- package/dist/packages/cli/src/ui/ModernReplUI.js.map +1 -0
- package/dist/packages/cli/src/ui/SimpleSelector2.d.ts +28 -0
- package/dist/packages/cli/src/ui/SimpleSelector2.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/SimpleSelector2.js +181 -0
- package/dist/packages/cli/src/ui/SimpleSelector2.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ActiveCursor.d.ts +128 -0
- package/dist/packages/cli/src/ui/components/ActiveCursor.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ActiveCursor.js +273 -0
- package/dist/packages/cli/src/ui/components/ActiveCursor.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ConfirmDialog.d.ts +51 -0
- package/dist/packages/cli/src/ui/components/ConfirmDialog.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ConfirmDialog.js +147 -0
- package/dist/packages/cli/src/ui/components/ConfirmDialog.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ErrorPanel.d.ts +33 -0
- package/dist/packages/cli/src/ui/components/ErrorPanel.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ErrorPanel.js +309 -0
- package/dist/packages/cli/src/ui/components/ErrorPanel.js.map +1 -0
- package/dist/packages/cli/src/ui/components/InkAppRunner.d.ts +18 -0
- package/dist/packages/cli/src/ui/components/InkAppRunner.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/InkAppRunner.js +33 -0
- package/dist/packages/cli/src/ui/components/InkAppRunner.js.map +1 -0
- package/dist/packages/cli/src/ui/components/InkComponents.d.ts +126 -0
- package/dist/packages/cli/src/ui/components/InkComponents.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/InkComponents.js +216 -0
- package/dist/packages/cli/src/ui/components/InkComponents.js.map +1 -0
- package/dist/packages/cli/src/ui/components/NovaInkApp.d.ts +11 -0
- package/dist/packages/cli/src/ui/components/NovaInkApp.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/NovaInkApp.js +148 -0
- package/dist/packages/cli/src/ui/components/NovaInkApp.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ProgressBar.d.ts +65 -0
- package/dist/packages/cli/src/ui/components/ProgressBar.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ProgressBar.js +135 -0
- package/dist/packages/cli/src/ui/components/ProgressBar.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ProgressIndicator.d.ts +41 -0
- package/dist/packages/cli/src/ui/components/ProgressIndicator.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ProgressIndicator.js +235 -0
- package/dist/packages/cli/src/ui/components/ProgressIndicator.js.map +1 -0
- package/dist/packages/cli/src/ui/components/QuickActions.d.ts +36 -0
- package/dist/packages/cli/src/ui/components/QuickActions.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/QuickActions.js +328 -0
- package/dist/packages/cli/src/ui/components/QuickActions.js.map +1 -0
- package/dist/packages/cli/src/ui/components/SimpleErrorPanel.d.ts +16 -0
- package/dist/packages/cli/src/ui/components/SimpleErrorPanel.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/SimpleErrorPanel.js +193 -0
- package/dist/packages/cli/src/ui/components/SimpleErrorPanel.js.map +1 -0
- package/dist/packages/cli/src/ui/components/StatusBar.d.ts +30 -0
- package/dist/packages/cli/src/ui/components/StatusBar.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/StatusBar.js +154 -0
- package/dist/packages/cli/src/ui/components/StatusBar.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.d.ts +109 -0
- package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.js +335 -0
- package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.d.ts +59 -0
- package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.js +172 -0
- package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.js.map +1 -0
- package/dist/packages/cli/src/ui/components/TodoProgressPanel.d.ts +91 -0
- package/dist/packages/cli/src/ui/components/TodoProgressPanel.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/TodoProgressPanel.js +284 -0
- package/dist/packages/cli/src/ui/components/TodoProgressPanel.js.map +1 -0
- package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.d.ts +89 -0
- package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.js +246 -0
- package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.js.map +1 -0
- package/dist/packages/cli/src/ui/components/UserMessageHighlight.d.ts +48 -0
- package/dist/packages/cli/src/ui/components/UserMessageHighlight.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/UserMessageHighlight.js +196 -0
- package/dist/packages/cli/src/ui/components/UserMessageHighlight.js.map +1 -0
- package/dist/packages/cli/src/ui/components/index.d.ts +17 -0
- package/dist/packages/cli/src/ui/components/index.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/components/index.js +18 -0
- package/dist/packages/cli/src/ui/components/index.js.map +1 -0
- package/dist/packages/cli/src/ui/ink-prototype.d.ts +3 -0
- package/dist/packages/cli/src/ui/ink-prototype.d.ts.map +1 -0
- package/dist/packages/cli/src/ui/ink-prototype.js +160 -0
- package/dist/packages/cli/src/ui/ink-prototype.js.map +1 -0
- package/dist/packages/cli/src/utils/CliUI.d.ts +163 -0
- package/dist/packages/cli/src/utils/CliUI.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/CliUI.js +292 -0
- package/dist/packages/cli/src/utils/CliUI.js.map +1 -0
- package/dist/packages/cli/src/utils/CompletionHelper.d.ts +112 -0
- package/dist/packages/cli/src/utils/CompletionHelper.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/CompletionHelper.js +304 -0
- package/dist/packages/cli/src/utils/CompletionHelper.js.map +1 -0
- package/dist/packages/cli/src/utils/EnhancedCompleter.d.ts +107 -0
- package/dist/packages/cli/src/utils/EnhancedCompleter.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/EnhancedCompleter.js +428 -0
- package/dist/packages/cli/src/utils/EnhancedCompleter.js.map +1 -0
- package/dist/packages/cli/src/utils/ErrorEnhancer.d.ts +103 -0
- package/dist/packages/cli/src/utils/ErrorEnhancer.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/ErrorEnhancer.js +350 -0
- package/dist/packages/cli/src/utils/ErrorEnhancer.js.map +1 -0
- package/dist/packages/cli/src/utils/OutputFormatter.d.ts +65 -0
- package/dist/packages/cli/src/utils/OutputFormatter.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/OutputFormatter.js +145 -0
- package/dist/packages/cli/src/utils/OutputFormatter.js.map +1 -0
- package/dist/packages/cli/src/utils/index.d.ts +5 -0
- package/dist/packages/cli/src/utils/index.d.ts.map +1 -0
- package/dist/packages/cli/src/utils/index.js +8 -0
- package/dist/packages/cli/src/utils/index.js.map +1 -0
- package/dist/packages/cli/tsconfig.tsbuildinfo +1 -0
- package/dist/packages/core/src/agents/AgentOrchestrator.d.ts +147 -0
- package/dist/packages/core/src/agents/AgentOrchestrator.d.ts.map +1 -0
- package/dist/packages/core/src/agents/AgentOrchestrator.js +358 -0
- package/dist/packages/core/src/agents/AgentOrchestrator.js.map +1 -0
- package/dist/packages/core/src/agents/index.d.ts +3 -0
- package/dist/packages/core/src/agents/index.d.ts.map +1 -0
- package/dist/packages/core/src/agents/index.js +5 -0
- package/dist/packages/core/src/agents/index.js.map +1 -0
- package/dist/packages/core/src/analysis/ProjectAnalyzer.d.ts +95 -0
- package/dist/packages/core/src/analysis/ProjectAnalyzer.d.ts.map +1 -0
- package/dist/packages/core/src/analysis/ProjectAnalyzer.js +656 -0
- package/dist/packages/core/src/analysis/ProjectAnalyzer.js.map +1 -0
- package/dist/packages/core/src/audit/AuditLogger.d.ts +140 -0
- package/dist/packages/core/src/audit/AuditLogger.d.ts.map +1 -0
- package/dist/packages/core/src/audit/AuditLogger.js +357 -0
- package/dist/packages/core/src/audit/AuditLogger.js.map +1 -0
- package/dist/packages/core/src/audit/index.d.ts +3 -0
- package/dist/packages/core/src/audit/index.d.ts.map +1 -0
- package/dist/packages/core/src/audit/index.js +5 -0
- package/dist/packages/core/src/audit/index.js.map +1 -0
- package/dist/packages/core/src/auth/AuthManager.d.ts +38 -0
- package/dist/packages/core/src/auth/AuthManager.d.ts.map +1 -0
- package/dist/packages/core/src/auth/AuthManager.js +120 -0
- package/dist/packages/core/src/auth/AuthManager.js.map +1 -0
- package/dist/packages/core/src/auth/index.d.ts +3 -0
- package/dist/packages/core/src/auth/index.d.ts.map +1 -0
- package/dist/packages/core/src/auth/index.js +2 -0
- package/dist/packages/core/src/auth/index.js.map +1 -0
- package/dist/packages/core/src/config/ConfigManager.d.ts +47 -0
- package/dist/packages/core/src/config/ConfigManager.d.ts.map +1 -0
- package/dist/packages/core/src/config/ConfigManager.js +1197 -0
- package/dist/packages/core/src/config/ConfigManager.js.map +1 -0
- package/dist/packages/core/src/config/index.d.ts +2 -0
- package/dist/packages/core/src/config/index.d.ts.map +1 -0
- package/dist/packages/core/src/config/index.js +2 -0
- package/dist/packages/core/src/config/index.js.map +1 -0
- package/dist/packages/core/src/context/ContextBuilder.d.ts +39 -0
- package/dist/packages/core/src/context/ContextBuilder.d.ts.map +1 -0
- package/dist/packages/core/src/context/ContextBuilder.js +132 -0
- package/dist/packages/core/src/context/ContextBuilder.js.map +1 -0
- package/dist/packages/core/src/context/ContextCompressor.d.ts +147 -0
- package/dist/packages/core/src/context/ContextCompressor.d.ts.map +1 -0
- package/dist/packages/core/src/context/ContextCompressor.js +451 -0
- package/dist/packages/core/src/context/ContextCompressor.js.map +1 -0
- package/dist/packages/core/src/context/LayeredMemoryManager.d.ts +160 -0
- package/dist/packages/core/src/context/LayeredMemoryManager.d.ts.map +1 -0
- package/dist/packages/core/src/context/LayeredMemoryManager.js +505 -0
- package/dist/packages/core/src/context/LayeredMemoryManager.js.map +1 -0
- package/dist/packages/core/src/context/MemoryDiscovery.d.ts +33 -0
- package/dist/packages/core/src/context/MemoryDiscovery.d.ts.map +1 -0
- package/dist/packages/core/src/context/MemoryDiscovery.js +146 -0
- package/dist/packages/core/src/context/MemoryDiscovery.js.map +1 -0
- package/dist/packages/core/src/context/defaultSystemPrompt.d.ts +12 -0
- package/dist/packages/core/src/context/defaultSystemPrompt.d.ts.map +1 -0
- package/dist/packages/core/src/context/defaultSystemPrompt.js +32 -0
- package/dist/packages/core/src/context/defaultSystemPrompt.js.map +1 -0
- package/dist/packages/core/src/context/index.d.ts +9 -0
- package/dist/packages/core/src/context/index.d.ts.map +1 -0
- package/dist/packages/core/src/context/index.js +5 -0
- package/dist/packages/core/src/context/index.js.map +1 -0
- package/dist/packages/core/src/extensions/SkillGenerator.d.ts +77 -0
- package/dist/packages/core/src/extensions/SkillGenerator.d.ts.map +1 -0
- package/dist/packages/core/src/extensions/SkillGenerator.js +323 -0
- package/dist/packages/core/src/extensions/SkillGenerator.js.map +1 -0
- package/dist/packages/core/src/extensions/SkillInstaller.d.ts +74 -0
- package/dist/packages/core/src/extensions/SkillInstaller.d.ts.map +1 -0
- package/dist/packages/core/src/extensions/SkillInstaller.js +216 -0
- package/dist/packages/core/src/extensions/SkillInstaller.js.map +1 -0
- package/dist/packages/core/src/extensions/SkillRegistry.d.ts +99 -0
- package/dist/packages/core/src/extensions/SkillRegistry.d.ts.map +1 -0
- package/dist/packages/core/src/extensions/SkillRegistry.js +263 -0
- package/dist/packages/core/src/extensions/SkillRegistry.js.map +1 -0
- package/dist/packages/core/src/extensions/SkillValidator.d.ts +51 -0
- package/dist/packages/core/src/extensions/SkillValidator.d.ts.map +1 -0
- package/dist/packages/core/src/extensions/SkillValidator.js +465 -0
- package/dist/packages/core/src/extensions/SkillValidator.js.map +1 -0
- package/dist/packages/core/src/extensions/index.d.ts +11 -0
- package/dist/packages/core/src/extensions/index.d.ts.map +1 -0
- package/dist/packages/core/src/extensions/index.js +8 -0
- package/dist/packages/core/src/extensions/index.js.map +1 -0
- package/dist/packages/core/src/index.d.ts +14 -0
- package/dist/packages/core/src/index.d.ts.map +1 -0
- package/dist/packages/core/src/index.js +30 -0
- package/dist/packages/core/src/index.js.map +1 -0
- package/dist/packages/core/src/mcp/McpManager.d.ts +94 -0
- package/dist/packages/core/src/mcp/McpManager.d.ts.map +1 -0
- package/dist/packages/core/src/mcp/McpManager.js +494 -0
- package/dist/packages/core/src/mcp/McpManager.js.map +1 -0
- package/dist/packages/core/src/mcp/index.d.ts +2 -0
- package/dist/packages/core/src/mcp/index.d.ts.map +1 -0
- package/dist/packages/core/src/mcp/index.js +3 -0
- package/dist/packages/core/src/mcp/index.js.map +1 -0
- package/dist/packages/core/src/model/ModelClient.d.ts +40 -0
- package/dist/packages/core/src/model/ModelClient.d.ts.map +1 -0
- package/dist/packages/core/src/model/ModelClient.js +163 -0
- package/dist/packages/core/src/model/ModelClient.js.map +1 -0
- package/dist/packages/core/src/model/ModelConnectionTester.d.ts +58 -0
- package/dist/packages/core/src/model/ModelConnectionTester.d.ts.map +1 -0
- package/dist/packages/core/src/model/ModelConnectionTester.js +311 -0
- package/dist/packages/core/src/model/ModelConnectionTester.js.map +1 -0
- package/dist/packages/core/src/model/ModelValidator.d.ts +39 -0
- package/dist/packages/core/src/model/ModelValidator.d.ts.map +1 -0
- package/dist/packages/core/src/model/ModelValidator.js +296 -0
- package/dist/packages/core/src/model/ModelValidator.js.map +1 -0
- package/dist/packages/core/src/model/index.d.ts +7 -0
- package/dist/packages/core/src/model/index.d.ts.map +1 -0
- package/dist/packages/core/src/model/index.js +5 -0
- package/dist/packages/core/src/model/index.js.map +1 -0
- package/dist/packages/core/src/model/providers/AnthropicProvider.d.ts +25 -0
- package/dist/packages/core/src/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/AnthropicProvider.js +258 -0
- package/dist/packages/core/src/model/providers/AnthropicProvider.js.map +1 -0
- package/dist/packages/core/src/model/providers/CodingPlanProvider.d.ts +66 -0
- package/dist/packages/core/src/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/CodingPlanProvider.js +161 -0
- package/dist/packages/core/src/model/providers/CodingPlanProvider.js.map +1 -0
- package/dist/packages/core/src/model/providers/OllamaCloudProvider.d.ts +38 -0
- package/dist/packages/core/src/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/OllamaCloudProvider.js +365 -0
- package/dist/packages/core/src/model/providers/OllamaCloudProvider.js.map +1 -0
- package/dist/packages/core/src/model/providers/OllamaManager.d.ts +72 -0
- package/dist/packages/core/src/model/providers/OllamaManager.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/OllamaManager.js +144 -0
- package/dist/packages/core/src/model/providers/OllamaManager.js.map +1 -0
- package/dist/packages/core/src/model/providers/OllamaProvider.d.ts +23 -0
- package/dist/packages/core/src/model/providers/OllamaProvider.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/OllamaProvider.js +56 -0
- package/dist/packages/core/src/model/providers/OllamaProvider.js.map +1 -0
- package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.d.ts +53 -0
- package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.js +383 -0
- package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.js.map +1 -0
- package/dist/packages/core/src/model/providers/OpenAIProvider.d.ts +12 -0
- package/dist/packages/core/src/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/OpenAIProvider.js +19 -0
- package/dist/packages/core/src/model/providers/OpenAIProvider.js.map +1 -0
- package/dist/packages/core/src/model/providers/index.d.ts +13 -0
- package/dist/packages/core/src/model/providers/index.d.ts.map +1 -0
- package/dist/packages/core/src/model/providers/index.js +7 -0
- package/dist/packages/core/src/model/providers/index.js.map +1 -0
- package/dist/packages/core/src/model/types.d.ts +81 -0
- package/dist/packages/core/src/model/types.d.ts.map +1 -0
- package/dist/packages/core/src/model/types.js +12 -0
- package/dist/packages/core/src/model/types.js.map +1 -0
- package/dist/packages/core/src/security/ApprovalManager.d.ts +56 -0
- package/dist/packages/core/src/security/ApprovalManager.d.ts.map +1 -0
- package/dist/packages/core/src/security/ApprovalManager.js +122 -0
- package/dist/packages/core/src/security/ApprovalManager.js.map +1 -0
- package/dist/packages/core/src/security/FileFilter.d.ts +47 -0
- package/dist/packages/core/src/security/FileFilter.d.ts.map +1 -0
- package/dist/packages/core/src/security/FileFilter.js +111 -0
- package/dist/packages/core/src/security/FileFilter.js.map +1 -0
- package/dist/packages/core/src/security/HookExecutor.d.ts +37 -0
- package/dist/packages/core/src/security/HookExecutor.d.ts.map +1 -0
- package/dist/packages/core/src/security/HookExecutor.js +142 -0
- package/dist/packages/core/src/security/HookExecutor.js.map +1 -0
- package/dist/packages/core/src/security/SandboxExecutor.d.ts +90 -0
- package/dist/packages/core/src/security/SandboxExecutor.d.ts.map +1 -0
- package/dist/packages/core/src/security/SandboxExecutor.js +345 -0
- package/dist/packages/core/src/security/SandboxExecutor.js.map +1 -0
- package/dist/packages/core/src/security/index.d.ts +8 -0
- package/dist/packages/core/src/security/index.d.ts.map +1 -0
- package/dist/packages/core/src/security/index.js +6 -0
- package/dist/packages/core/src/security/index.js.map +1 -0
- package/dist/packages/core/src/session/AgentLoop.d.ts +75 -0
- package/dist/packages/core/src/session/AgentLoop.d.ts.map +1 -0
- package/dist/packages/core/src/session/AgentLoop.js +381 -0
- package/dist/packages/core/src/session/AgentLoop.js.map +1 -0
- package/dist/packages/core/src/session/SessionManager.d.ts +96 -0
- package/dist/packages/core/src/session/SessionManager.d.ts.map +1 -0
- package/dist/packages/core/src/session/SessionManager.js +390 -0
- package/dist/packages/core/src/session/SessionManager.js.map +1 -0
- package/dist/packages/core/src/session/index.d.ts +4 -0
- package/dist/packages/core/src/session/index.d.ts.map +1 -0
- package/dist/packages/core/src/session/index.js +3 -0
- package/dist/packages/core/src/session/index.js.map +1 -0
- package/dist/packages/core/src/telemetry/Telemetry.d.ts +25 -0
- package/dist/packages/core/src/telemetry/Telemetry.d.ts.map +1 -0
- package/dist/packages/core/src/telemetry/Telemetry.js +73 -0
- package/dist/packages/core/src/telemetry/Telemetry.js.map +1 -0
- package/dist/packages/core/src/telemetry/TelemetryService.d.ts +152 -0
- package/dist/packages/core/src/telemetry/TelemetryService.d.ts.map +1 -0
- package/dist/packages/core/src/telemetry/TelemetryService.js +410 -0
- package/dist/packages/core/src/telemetry/TelemetryService.js.map +1 -0
- package/dist/packages/core/src/telemetry/index.d.ts +5 -0
- package/dist/packages/core/src/telemetry/index.d.ts.map +1 -0
- package/dist/packages/core/src/telemetry/index.js +3 -0
- package/dist/packages/core/src/telemetry/index.js.map +1 -0
- package/dist/packages/core/src/testing/AutoFixer.d.ts +98 -0
- package/dist/packages/core/src/testing/AutoFixer.d.ts.map +1 -0
- package/dist/packages/core/src/testing/AutoFixer.js +262 -0
- package/dist/packages/core/src/testing/AutoFixer.js.map +1 -0
- package/dist/packages/core/src/testing/ErrorAnalyzer.d.ts +74 -0
- package/dist/packages/core/src/testing/ErrorAnalyzer.d.ts.map +1 -0
- package/dist/packages/core/src/testing/ErrorAnalyzer.js +407 -0
- package/dist/packages/core/src/testing/ErrorAnalyzer.js.map +1 -0
- package/dist/packages/core/src/testing/TestRunner.d.ts +57 -0
- package/dist/packages/core/src/testing/TestRunner.d.ts.map +1 -0
- package/dist/packages/core/src/testing/TestRunner.js +205 -0
- package/dist/packages/core/src/testing/TestRunner.js.map +1 -0
- package/dist/packages/core/src/testing/agent-cli-tests.d.ts +2 -0
- package/dist/packages/core/src/testing/agent-cli-tests.d.ts.map +1 -0
- package/dist/packages/core/src/testing/agent-cli-tests.js +493 -0
- package/dist/packages/core/src/testing/agent-cli-tests.js.map +1 -0
- package/dist/packages/core/src/testing/index.d.ts +7 -0
- package/dist/packages/core/src/testing/index.d.ts.map +1 -0
- package/dist/packages/core/src/testing/index.js +8 -0
- package/dist/packages/core/src/testing/index.js.map +1 -0
- package/dist/packages/core/src/tools/ToolRegistry.d.ts +72 -0
- package/dist/packages/core/src/tools/ToolRegistry.d.ts.map +1 -0
- package/dist/packages/core/src/tools/ToolRegistry.js +208 -0
- package/dist/packages/core/src/tools/ToolRegistry.js.map +1 -0
- package/dist/packages/core/src/tools/impl/EditFileTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/EditFileTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/EditFileTool.js +76 -0
- package/dist/packages/core/src/tools/impl/EditFileTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/FileProcessor.d.ts +85 -0
- package/dist/packages/core/src/tools/impl/FileProcessor.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/FileProcessor.js +512 -0
- package/dist/packages/core/src/tools/impl/FileProcessor.js.map +1 -0
- package/dist/packages/core/src/tools/impl/FileProcessorTool.d.ts +151 -0
- package/dist/packages/core/src/tools/impl/FileProcessorTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/FileProcessorTool.js +100 -0
- package/dist/packages/core/src/tools/impl/FileProcessorTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/ImageProcessorTool.d.ts +43 -0
- package/dist/packages/core/src/tools/impl/ImageProcessorTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/ImageProcessorTool.js +104 -0
- package/dist/packages/core/src/tools/impl/ImageProcessorTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/ListDirectoryTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/ListDirectoryTool.js +120 -0
- package/dist/packages/core/src/tools/impl/ListDirectoryTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/MemoryTool.d.ts +4 -0
- package/dist/packages/core/src/tools/impl/MemoryTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/MemoryTool.js +80 -0
- package/dist/packages/core/src/tools/impl/MemoryTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/ReadFileTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/ReadFileTool.js +223 -0
- package/dist/packages/core/src/tools/impl/ReadFileTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/SearchContentTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/SearchContentTool.js +65 -0
- package/dist/packages/core/src/tools/impl/SearchContentTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/SearchFileTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/SearchFileTool.js +48 -0
- package/dist/packages/core/src/tools/impl/SearchFileTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/ShellTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/ShellTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/ShellTool.js +92 -0
- package/dist/packages/core/src/tools/impl/ShellTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/TaskTool.d.ts +51 -0
- package/dist/packages/core/src/tools/impl/TaskTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/TaskTool.js +172 -0
- package/dist/packages/core/src/tools/impl/TaskTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/TodoTool.d.ts +31 -0
- package/dist/packages/core/src/tools/impl/TodoTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/TodoTool.js +102 -0
- package/dist/packages/core/src/tools/impl/TodoTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/WebFetchTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/WebFetchTool.js +77 -0
- package/dist/packages/core/src/tools/impl/WebFetchTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/WebSearchTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/WebSearchTool.js +67 -0
- package/dist/packages/core/src/tools/impl/WebSearchTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/WriteFileTool.d.ts +3 -0
- package/dist/packages/core/src/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/WriteFileTool.js +41 -0
- package/dist/packages/core/src/tools/impl/WriteFileTool.js.map +1 -0
- package/dist/packages/core/src/tools/impl/index.d.ts +14 -0
- package/dist/packages/core/src/tools/impl/index.d.ts.map +1 -0
- package/dist/packages/core/src/tools/impl/index.js +21 -0
- package/dist/packages/core/src/tools/impl/index.js.map +1 -0
- package/dist/packages/core/src/tools/index.d.ts +16 -0
- package/dist/packages/core/src/tools/index.d.ts.map +1 -0
- package/dist/packages/core/src/tools/index.js +21 -0
- package/dist/packages/core/src/tools/index.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/execution.d.ts +41 -0
- package/dist/packages/core/src/tools/schemas/execution.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/execution.js +42 -0
- package/dist/packages/core/src/tools/schemas/execution.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/file.d.ts +113 -0
- package/dist/packages/core/src/tools/schemas/file.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/file.js +116 -0
- package/dist/packages/core/src/tools/schemas/file.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/fileProcessorSchema.d.ts +278 -0
- package/dist/packages/core/src/tools/schemas/fileProcessorSchema.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/fileProcessorSchema.js +61 -0
- package/dist/packages/core/src/tools/schemas/fileProcessorSchema.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/index.d.ts +8 -0
- package/dist/packages/core/src/tools/schemas/index.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/index.js +11 -0
- package/dist/packages/core/src/tools/schemas/index.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/memory.d.ts +50 -0
- package/dist/packages/core/src/tools/schemas/memory.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/memory.js +51 -0
- package/dist/packages/core/src/tools/schemas/memory.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/orchestration.d.ts +41 -0
- package/dist/packages/core/src/tools/schemas/orchestration.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/orchestration.js +44 -0
- package/dist/packages/core/src/tools/schemas/orchestration.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/search.d.ts +111 -0
- package/dist/packages/core/src/tools/schemas/search.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/search.js +110 -0
- package/dist/packages/core/src/tools/schemas/search.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/todo.d.ts +29 -0
- package/dist/packages/core/src/tools/schemas/todo.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/todo.js +32 -0
- package/dist/packages/core/src/tools/schemas/todo.js.map +1 -0
- package/dist/packages/core/src/tools/schemas/web.d.ts +84 -0
- package/dist/packages/core/src/tools/schemas/web.d.ts.map +1 -0
- package/dist/packages/core/src/tools/schemas/web.js +85 -0
- package/dist/packages/core/src/tools/schemas/web.js.map +1 -0
- package/dist/packages/core/src/types/config.d.ts +212 -0
- package/dist/packages/core/src/types/config.d.ts.map +1 -0
- package/dist/packages/core/src/types/config.js +5 -0
- package/dist/packages/core/src/types/config.js.map +1 -0
- package/dist/packages/core/src/types/errors.d.ts +92 -0
- package/dist/packages/core/src/types/errors.d.ts.map +1 -0
- package/dist/packages/core/src/types/errors.js +172 -0
- package/dist/packages/core/src/types/errors.js.map +1 -0
- package/dist/packages/core/src/types/index.d.ts +5 -0
- package/dist/packages/core/src/types/index.d.ts.map +1 -0
- package/dist/packages/core/src/types/index.js +8 -0
- package/dist/packages/core/src/types/index.js.map +1 -0
- package/dist/packages/core/src/types/session.d.ts +141 -0
- package/dist/packages/core/src/types/session.d.ts.map +1 -0
- package/dist/packages/core/src/types/session.js +16 -0
- package/dist/packages/core/src/types/session.js.map +1 -0
- package/dist/packages/core/src/types/tools.d.ts +126 -0
- package/dist/packages/core/src/types/tools.d.ts.map +1 -0
- package/dist/packages/core/src/types/tools.js +26 -0
- package/dist/packages/core/src/types/tools.js.map +1 -0
- package/dist/packages/core/src/utils/CheckpointManager.d.ts +100 -0
- package/dist/packages/core/src/utils/CheckpointManager.d.ts.map +1 -0
- package/dist/packages/core/src/utils/CheckpointManager.js +255 -0
- package/dist/packages/core/src/utils/CheckpointManager.js.map +1 -0
- package/dist/packages/core/src/utils/Logger.d.ts +29 -0
- package/dist/packages/core/src/utils/Logger.d.ts.map +1 -0
- package/dist/packages/core/src/utils/Logger.js +77 -0
- package/dist/packages/core/src/utils/Logger.js.map +1 -0
- package/dist/packages/core/src/utils/RetryManager.d.ts +125 -0
- package/dist/packages/core/src/utils/RetryManager.d.ts.map +1 -0
- package/dist/packages/core/src/utils/RetryManager.js +348 -0
- package/dist/packages/core/src/utils/RetryManager.js.map +1 -0
- package/dist/packages/core/src/utils/TokenCounter.d.ts +73 -0
- package/dist/packages/core/src/utils/TokenCounter.d.ts.map +1 -0
- package/dist/packages/core/src/utils/TokenCounter.js +338 -0
- package/dist/packages/core/src/utils/TokenCounter.js.map +1 -0
- package/dist/packages/core/src/utils/VectorMemoryStore.d.ts +110 -0
- package/dist/packages/core/src/utils/VectorMemoryStore.d.ts.map +1 -0
- package/dist/packages/core/src/utils/VectorMemoryStore.js +320 -0
- package/dist/packages/core/src/utils/VectorMemoryStore.js.map +1 -0
- package/dist/packages/core/src/utils/helpers.d.ts +24 -0
- package/dist/packages/core/src/utils/helpers.d.ts.map +1 -0
- package/dist/packages/core/src/utils/helpers.js +77 -0
- package/dist/packages/core/src/utils/helpers.js.map +1 -0
- package/dist/packages/core/src/utils/index.d.ts +11 -0
- package/dist/packages/core/src/utils/index.d.ts.map +1 -0
- package/dist/packages/core/src/utils/index.js +7 -0
- package/dist/packages/core/src/utils/index.js.map +1 -0
- package/dist/startup/IFlowRepl.d.ts +50 -0
- package/dist/startup/IFlowRepl.d.ts.map +1 -0
- package/dist/startup/IFlowRepl.js +178 -0
- package/dist/startup/IFlowRepl.js.map +1 -0
- package/dist/startup/InkBasedRepl.d.ts +151 -0
- package/dist/startup/InkBasedRepl.d.ts.map +1 -0
- package/dist/startup/InkBasedRepl.js +1415 -0
- package/dist/startup/InkBasedRepl.js.map +1 -0
- package/dist/startup/InteractiveRepl.d.ts +141 -0
- package/dist/startup/InteractiveRepl.d.ts.map +1 -0
- package/dist/startup/InteractiveRepl.js +2561 -0
- package/dist/startup/InteractiveRepl.js.map +1 -0
- package/dist/startup/NovaApp.d.ts +57 -0
- package/dist/startup/NovaApp.d.ts.map +1 -0
- package/dist/startup/NovaApp.js +1978 -0
- package/dist/startup/NovaApp.js.map +1 -0
- package/dist/startup/index.d.ts +5 -0
- package/dist/startup/index.d.ts.map +1 -0
- package/dist/startup/index.js +4 -0
- package/dist/startup/index.js.map +1 -0
- package/dist/startup/parseArgs.d.ts +47 -0
- package/dist/startup/parseArgs.d.ts.map +1 -0
- package/dist/startup/parseArgs.js +262 -0
- package/dist/startup/parseArgs.js.map +1 -0
- package/dist/ui/IFlowDropdown.d.ts +63 -0
- package/dist/ui/IFlowDropdown.d.ts.map +1 -0
- package/dist/ui/IFlowDropdown.js +362 -0
- package/dist/ui/IFlowDropdown.js.map +1 -0
- package/dist/ui/ModernReplUI.d.ts +55 -0
- package/dist/ui/ModernReplUI.d.ts.map +1 -0
- package/dist/ui/ModernReplUI.js +207 -0
- package/dist/ui/ModernReplUI.js.map +1 -0
- package/dist/ui/SimpleSelector2.d.ts +28 -0
- package/dist/ui/SimpleSelector2.d.ts.map +1 -0
- package/dist/ui/SimpleSelector2.js +181 -0
- package/dist/ui/SimpleSelector2.js.map +1 -0
- package/dist/ui/components/ActiveCursor.d.ts +128 -0
- package/dist/ui/components/ActiveCursor.d.ts.map +1 -0
- package/dist/ui/components/ActiveCursor.js +273 -0
- package/dist/ui/components/ActiveCursor.js.map +1 -0
- package/dist/ui/components/ConfirmDialog.d.ts +51 -0
- package/dist/ui/components/ConfirmDialog.d.ts.map +1 -0
- package/dist/ui/components/ConfirmDialog.js +147 -0
- package/dist/ui/components/ConfirmDialog.js.map +1 -0
- package/dist/ui/components/ErrorPanel.d.ts +33 -0
- package/dist/ui/components/ErrorPanel.d.ts.map +1 -0
- package/dist/ui/components/ErrorPanel.js +309 -0
- package/dist/ui/components/ErrorPanel.js.map +1 -0
- package/dist/ui/components/InkAppRunner.d.ts +18 -0
- package/dist/ui/components/InkAppRunner.d.ts.map +1 -0
- package/dist/ui/components/InkAppRunner.js +33 -0
- package/dist/ui/components/InkAppRunner.js.map +1 -0
- package/dist/ui/components/InkComponents.d.ts +126 -0
- package/dist/ui/components/InkComponents.d.ts.map +1 -0
- package/dist/ui/components/InkComponents.js +216 -0
- package/dist/ui/components/InkComponents.js.map +1 -0
- package/dist/ui/components/NovaInkApp.d.ts +11 -0
- package/dist/ui/components/NovaInkApp.d.ts.map +1 -0
- package/dist/ui/components/NovaInkApp.js +148 -0
- package/dist/ui/components/NovaInkApp.js.map +1 -0
- package/dist/ui/components/ProgressBar.d.ts +65 -0
- package/dist/ui/components/ProgressBar.d.ts.map +1 -0
- package/dist/ui/components/ProgressBar.js +135 -0
- package/dist/ui/components/ProgressBar.js.map +1 -0
- package/dist/ui/components/ProgressIndicator.d.ts +41 -0
- package/dist/ui/components/ProgressIndicator.d.ts.map +1 -0
- package/dist/ui/components/ProgressIndicator.js +235 -0
- package/dist/ui/components/ProgressIndicator.js.map +1 -0
- package/dist/ui/components/QuickActions.d.ts +36 -0
- package/dist/ui/components/QuickActions.d.ts.map +1 -0
- package/dist/ui/components/QuickActions.js +328 -0
- package/dist/ui/components/QuickActions.js.map +1 -0
- package/dist/ui/components/SimpleErrorPanel.d.ts +16 -0
- package/dist/ui/components/SimpleErrorPanel.d.ts.map +1 -0
- package/dist/ui/components/SimpleErrorPanel.js +193 -0
- package/dist/ui/components/SimpleErrorPanel.js.map +1 -0
- package/dist/ui/components/StatusBar.d.ts +30 -0
- package/dist/ui/components/StatusBar.d.ts.map +1 -0
- package/dist/ui/components/StatusBar.js +154 -0
- package/dist/ui/components/StatusBar.js.map +1 -0
- package/dist/ui/components/ThinkingBlockRenderer.d.ts +109 -0
- package/dist/ui/components/ThinkingBlockRenderer.d.ts.map +1 -0
- package/dist/ui/components/ThinkingBlockRenderer.js +335 -0
- package/dist/ui/components/ThinkingBlockRenderer.js.map +1 -0
- package/dist/ui/components/ThinkingContentDisplay.d.ts +59 -0
- package/dist/ui/components/ThinkingContentDisplay.d.ts.map +1 -0
- package/dist/ui/components/ThinkingContentDisplay.js +172 -0
- package/dist/ui/components/ThinkingContentDisplay.js.map +1 -0
- package/dist/ui/components/TodoProgressPanel.d.ts +91 -0
- package/dist/ui/components/TodoProgressPanel.d.ts.map +1 -0
- package/dist/ui/components/TodoProgressPanel.js +284 -0
- package/dist/ui/components/TodoProgressPanel.js.map +1 -0
- package/dist/ui/components/ToolCallStatusDisplay.d.ts +89 -0
- package/dist/ui/components/ToolCallStatusDisplay.d.ts.map +1 -0
- package/dist/ui/components/ToolCallStatusDisplay.js +246 -0
- package/dist/ui/components/ToolCallStatusDisplay.js.map +1 -0
- package/dist/ui/components/UserMessageHighlight.d.ts +48 -0
- package/dist/ui/components/UserMessageHighlight.d.ts.map +1 -0
- package/dist/ui/components/UserMessageHighlight.js +196 -0
- package/dist/ui/components/UserMessageHighlight.js.map +1 -0
- package/dist/ui/components/index.d.ts +17 -0
- package/dist/ui/components/index.d.ts.map +1 -0
- package/dist/ui/components/index.js +18 -0
- package/dist/ui/components/index.js.map +1 -0
- package/dist/ui/ink-prototype.d.ts +3 -0
- package/dist/ui/ink-prototype.d.ts.map +1 -0
- package/dist/ui/ink-prototype.js +160 -0
- package/dist/ui/ink-prototype.js.map +1 -0
- package/dist/utils/CliUI.d.ts +163 -0
- package/dist/utils/CliUI.d.ts.map +1 -0
- package/dist/utils/CliUI.js +292 -0
- package/dist/utils/CliUI.js.map +1 -0
- package/dist/utils/CompletionHelper.d.ts +112 -0
- package/dist/utils/CompletionHelper.d.ts.map +1 -0
- package/dist/utils/CompletionHelper.js +304 -0
- package/dist/utils/CompletionHelper.js.map +1 -0
- package/dist/utils/EnhancedCompleter.d.ts +107 -0
- package/dist/utils/EnhancedCompleter.d.ts.map +1 -0
- package/dist/utils/EnhancedCompleter.js +428 -0
- package/dist/utils/EnhancedCompleter.js.map +1 -0
- package/dist/utils/ErrorEnhancer.d.ts +103 -0
- package/dist/utils/ErrorEnhancer.d.ts.map +1 -0
- package/dist/utils/ErrorEnhancer.js +350 -0
- package/dist/utils/ErrorEnhancer.js.map +1 -0
- package/dist/utils/OutputFormatter.d.ts +65 -0
- package/dist/utils/OutputFormatter.d.ts.map +1 -0
- package/dist/utils/OutputFormatter.js +145 -0
- package/dist/utils/OutputFormatter.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,1978 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// NovaApp - Application bootstrap and orchestration
|
|
3
|
+
// ============================================================================
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import { ConfigManager } from '../../../core/src/config/ConfigManager.js';
|
|
7
|
+
import { AuthManager } from '../../../core/src/auth/AuthManager.js';
|
|
8
|
+
import { ToolRegistry } from '../../../core/src/tools/ToolRegistry.js';
|
|
9
|
+
import { SessionManager } from '../../../core/src/session/SessionManager.js';
|
|
10
|
+
import { AgentLoop } from '../../../core/src/session/AgentLoop.js';
|
|
11
|
+
import { ModelClient } from '../../../core/src/model/ModelClient.js';
|
|
12
|
+
import { ModelConnectionTester } from '../../../core/src/model/ModelConnectionTester.js';
|
|
13
|
+
import { McpManager } from '../../../core/src/mcp/McpManager.js';
|
|
14
|
+
import { SkillRegistry } from '../../../core/src/extensions/SkillRegistry.js';
|
|
15
|
+
import { SkillInstaller } from '../../../core/src/extensions/SkillInstaller.js';
|
|
16
|
+
import { ContextCompressor } from '../../../core/src/context/ContextCompressor.js';
|
|
17
|
+
import { ApprovalManager } from '../../../core/src/security/ApprovalManager.js';
|
|
18
|
+
import { HookExecutor } from '../../../core/src/security/HookExecutor.js';
|
|
19
|
+
import { NovaError, getErrorMessage } from '../../../core/src/types/errors.js';
|
|
20
|
+
import { readFileHandler, writeFileHandler, editFileHandler, listDirectoryHandler, searchFileHandler, searchContentHandler, shellHandler, webSearchHandler, webFetchHandler, memoryReadHandler, memoryWriteHandler, todoHandler, taskHandler, } from '../../../core/src/tools/impl/index.js';
|
|
21
|
+
import { readFileSchema, writeFileSchema, editFileSchema, listDirectorySchema, searchFileSchema, searchContentSchema, executeCommandSchema, webSearchSchema, webFetchSchema, memoryReadSchema, memoryWriteSchema, todoSchema, taskSchema, } from '../../../core/src/tools/schemas/index.js';
|
|
22
|
+
import { OllamaManager } from '../../../core/src/model/providers/OllamaManager.js';
|
|
23
|
+
import { parseCliArgs } from './parseArgs.js';
|
|
24
|
+
import { InkBasedRepl } from './InkBasedRepl.js';
|
|
25
|
+
/** Providers that require an API key */
|
|
26
|
+
const REQUIRES_API_KEY = new Set(['anthropic', 'openai', 'azure', 'google', 'deepseek',
|
|
27
|
+
'qwen', 'glm', 'moonshot', 'baichuan', 'minimax', 'yi', 'groq', 'mistral', 'together', 'perplexity']);
|
|
28
|
+
/** Providers that use baseUrl but may not need apiKey */
|
|
29
|
+
const BASE_URL_PROVIDERS = new Set(['ollama', 'custom', 'siliconflow']);
|
|
30
|
+
export class NovaApp {
|
|
31
|
+
configManager;
|
|
32
|
+
authManager;
|
|
33
|
+
toolRegistry;
|
|
34
|
+
sessionManager;
|
|
35
|
+
mcpManager;
|
|
36
|
+
skillRegistry;
|
|
37
|
+
contextCompressor;
|
|
38
|
+
approvalManager;
|
|
39
|
+
hookExecutor;
|
|
40
|
+
modelConnectionTester;
|
|
41
|
+
constructor() {
|
|
42
|
+
this.configManager = new ConfigManager();
|
|
43
|
+
}
|
|
44
|
+
async run() {
|
|
45
|
+
try {
|
|
46
|
+
// 1. Parse CLI arguments
|
|
47
|
+
const args = parseCliArgs(process.argv.slice(2));
|
|
48
|
+
// 2. Load configuration
|
|
49
|
+
const config = await this.configManager.load(args.projectDir);
|
|
50
|
+
// 3. Initialize authentication
|
|
51
|
+
this.authManager = new AuthManager();
|
|
52
|
+
await this.authManager.loadCredentials();
|
|
53
|
+
// 4. Handle special commands
|
|
54
|
+
if (args.command === 'config') {
|
|
55
|
+
await this.handleConfigCommand(args);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (args.command === 'auth') {
|
|
59
|
+
await this.handleAuthCommand(args);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Run Ollama auto-discovery before model/provider/ollama commands
|
|
63
|
+
await this.autoDiscoverOllamaModels();
|
|
64
|
+
if (args.command === 'model') {
|
|
65
|
+
await this.handleModelCommand(args);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (args.command === 'provider') {
|
|
69
|
+
await this.handleProviderCommand(args);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (args.command === 'ollama') {
|
|
73
|
+
await this.handleOllamaCommand(args);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (args.command === 'coding-plan') {
|
|
77
|
+
await this.handleCodingPlanCommand(args);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (args.command === 'mcp') {
|
|
81
|
+
await this.handleMcpCommand(args);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (args.command === 'skills') {
|
|
85
|
+
await this.handleSkillsCommand(args);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (args.command === 'version') {
|
|
89
|
+
console.log('nova-cli v0.1.0');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (args.command === 'help') {
|
|
93
|
+
this.printHelp();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// 4.5 First-time setup wizard (only for interactive sessions)
|
|
97
|
+
const noInput = args.noInput || !process.stdin.isTTY;
|
|
98
|
+
if (!noInput && !args.prompt && await this.isFirstTimeSetup(config)) {
|
|
99
|
+
const setupComplete = await this.runFirstTimeSetupWizard(config);
|
|
100
|
+
if (!setupComplete) {
|
|
101
|
+
// User cancelled setup
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 5. Override config with CLI args
|
|
106
|
+
if (args.approvalMode) {
|
|
107
|
+
config.core.defaultApprovalMode = args.approvalMode;
|
|
108
|
+
}
|
|
109
|
+
if (args.maxTurns) {
|
|
110
|
+
config.core.maxTurns = args.maxTurns;
|
|
111
|
+
}
|
|
112
|
+
// 5.5 Initialize core components
|
|
113
|
+
await this.initializeComponents(config, args);
|
|
114
|
+
// 5.6 Determine non-interactive mode (--no-input flag or stdin is not TTY)
|
|
115
|
+
// (already computed above for first-time setup check)
|
|
116
|
+
// 5.7 Test model connections on startup (show status summary)
|
|
117
|
+
if (!noInput && !args.prompt) {
|
|
118
|
+
await this.showConnectionStatus(config);
|
|
119
|
+
}
|
|
120
|
+
// 6. Create model client
|
|
121
|
+
const modelClient = await this.createModelClient(config, args.model, noInput);
|
|
122
|
+
// 7. Start interactive REPL or run single prompt
|
|
123
|
+
const cwd = args.projectDir || process.cwd();
|
|
124
|
+
if (args.prompt) {
|
|
125
|
+
await this.runSinglePrompt(args.prompt, cwd, modelClient, config, args.minimal, args.thinking);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// ---- Session restore logic (-c / -r) ----
|
|
129
|
+
let restoreSessionId;
|
|
130
|
+
if (args.continueSession) {
|
|
131
|
+
// -c: auto-load most recent session
|
|
132
|
+
const latest = this.sessionManager.loadLatestSession();
|
|
133
|
+
if (latest) {
|
|
134
|
+
restoreSessionId = latest.id;
|
|
135
|
+
console.log(`\x1b[36m Continuing session: ${latest.id.slice(0, 8)} (${this.sessionManager.listPersistedSessions(1)[0]?.title || ''})\x1b[0m`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
console.log('\x1b[33m No previous session found. Starting fresh.\x1b[0m');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else if (args.resumeSession) {
|
|
142
|
+
// -r: let user pick from history
|
|
143
|
+
restoreSessionId = await this.pickSessionInteractive();
|
|
144
|
+
}
|
|
145
|
+
else if (args.sessionId) {
|
|
146
|
+
// --resume <id>
|
|
147
|
+
const s = this.sessionManager.loadFromDisk(args.sessionId);
|
|
148
|
+
if (s)
|
|
149
|
+
restoreSessionId = s.id;
|
|
150
|
+
}
|
|
151
|
+
// Use InkBasedRepl for modern UI (similar to Claude Code)
|
|
152
|
+
const repl = new InkBasedRepl({
|
|
153
|
+
modelClient,
|
|
154
|
+
sessionManager: this.sessionManager,
|
|
155
|
+
toolRegistry: this.toolRegistry,
|
|
156
|
+
approvalManager: this.approvalManager,
|
|
157
|
+
authManager: this.authManager,
|
|
158
|
+
config,
|
|
159
|
+
configManager: this.configManager,
|
|
160
|
+
cwd,
|
|
161
|
+
contextCompressor: this.contextCompressor,
|
|
162
|
+
mcpManager: this.mcpManager,
|
|
163
|
+
skillRegistry: this.skillRegistry,
|
|
164
|
+
restoreSessionId,
|
|
165
|
+
json: args.json,
|
|
166
|
+
noInput,
|
|
167
|
+
limit: args.limit,
|
|
168
|
+
});
|
|
169
|
+
await repl.start();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
if (err instanceof NovaError) {
|
|
174
|
+
console.error(`[${err.code}] ${err.message}`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.error('Fatal error:', getErrorMessage(err));
|
|
178
|
+
}
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async initializeComponents(config, args) {
|
|
183
|
+
// Tool Registry
|
|
184
|
+
this.toolRegistry = new ToolRegistry();
|
|
185
|
+
// Get model config to check for built-in search capability
|
|
186
|
+
const modelConfigResult = this.configManager.getModelConfig(config.core.defaultModel);
|
|
187
|
+
this.registerBuiltinTools(args.minimal, args.prompt, modelConfigResult?.model);
|
|
188
|
+
// Session Manager
|
|
189
|
+
this.sessionManager = new SessionManager();
|
|
190
|
+
// Context Compressor (intelligent context management)
|
|
191
|
+
this.contextCompressor = new ContextCompressor({
|
|
192
|
+
maxTokens: config.core.maxTokens * 8 || 128000,
|
|
193
|
+
summaryModel: config.core.defaultModel,
|
|
194
|
+
});
|
|
195
|
+
// Model Connection Tester
|
|
196
|
+
this.modelConnectionTester = new ModelConnectionTester(this.authManager, this.configManager);
|
|
197
|
+
// Skill Registry (auto-discovers skills from ~/.nova/skills/)
|
|
198
|
+
const skillsDir = path.join(os.homedir(), '.nova', 'skills');
|
|
199
|
+
this.skillRegistry = new SkillRegistry(skillsDir);
|
|
200
|
+
try {
|
|
201
|
+
await this.skillRegistry.initialize();
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Skills are optional; don't block startup
|
|
205
|
+
}
|
|
206
|
+
// MCP Manager
|
|
207
|
+
this.mcpManager = new McpManager();
|
|
208
|
+
if (config.mcp) {
|
|
209
|
+
for (const [name, serverConfig] of Object.entries(config.mcp)) {
|
|
210
|
+
if (serverConfig.enabled !== false) {
|
|
211
|
+
try {
|
|
212
|
+
const tools = await this.mcpManager.connect({ name, ...serverConfig });
|
|
213
|
+
// Register MCP tools
|
|
214
|
+
for (const tool of tools) {
|
|
215
|
+
this.toolRegistry.register(tool, async (input) => {
|
|
216
|
+
const result = await this.mcpManager.callToolByNamespacedName(tool.name, input);
|
|
217
|
+
return { content: result };
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
console.warn(`Failed to connect to MCP server "${name}": ${getErrorMessage(err)}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Approval Manager
|
|
228
|
+
this.approvalManager = new ApprovalManager({
|
|
229
|
+
defaultMode: config.core.defaultApprovalMode,
|
|
230
|
+
autoApproveLowRisk: true,
|
|
231
|
+
});
|
|
232
|
+
// Hook Executor
|
|
233
|
+
this.hookExecutor = new HookExecutor({
|
|
234
|
+
workingDirectory: args.projectDir || process.cwd(),
|
|
235
|
+
environment: {},
|
|
236
|
+
});
|
|
237
|
+
if (config.hooks) {
|
|
238
|
+
for (const hook of config.hooks) {
|
|
239
|
+
this.hookExecutor.register({
|
|
240
|
+
event: hook.event,
|
|
241
|
+
command: hook.command,
|
|
242
|
+
timeout: hook.timeout,
|
|
243
|
+
matcher: hook.matcher,
|
|
244
|
+
description: hook.description,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Select tools based on the user's prompt to optimize token usage
|
|
251
|
+
* Returns a list of tool names that are likely needed for the task
|
|
252
|
+
*/
|
|
253
|
+
selectToolsForPrompt(prompt, minimal = false) {
|
|
254
|
+
if (minimal) {
|
|
255
|
+
return ['read_file', 'write_file', 'edit_file', 'list_directory', 'execute_command'];
|
|
256
|
+
}
|
|
257
|
+
const promptLower = prompt.toLowerCase();
|
|
258
|
+
const selectedTools = [];
|
|
259
|
+
// Essential tools (always included)
|
|
260
|
+
const essentialTools = ['read_file', 'write_file', 'edit_file', 'list_directory', 'execute_command', 'search_file', 'search_content'];
|
|
261
|
+
selectedTools.push(...essentialTools);
|
|
262
|
+
// Search tools (if user wants to search/find)
|
|
263
|
+
if (promptLower.includes('搜索') || promptLower.includes('查找') ||
|
|
264
|
+
promptLower.includes('search') || promptLower.includes('find') ||
|
|
265
|
+
promptLower.includes('where') || promptLower.includes('定位')) {
|
|
266
|
+
selectedTools.push('search_file', 'search_content');
|
|
267
|
+
}
|
|
268
|
+
// Web tools (if user asks about web/URL/http)
|
|
269
|
+
if (promptLower.includes('网站') || promptLower.includes('url') ||
|
|
270
|
+
promptLower.includes('http') || promptLower.includes('web') ||
|
|
271
|
+
promptLower.includes('搜索') && (promptLower.includes('网上') || promptLower.includes('online'))) {
|
|
272
|
+
selectedTools.push('web_search', 'web_fetch');
|
|
273
|
+
}
|
|
274
|
+
// Memory tools (if user asks to remember/save)
|
|
275
|
+
if (promptLower.includes('记住') || promptLower.includes('记忆') ||
|
|
276
|
+
promptLower.includes('memory') || promptLower.includes('save') ||
|
|
277
|
+
promptLower.includes('存储') || promptLower.includes('记住')) {
|
|
278
|
+
selectedTools.push('memory_read', 'memory_write');
|
|
279
|
+
}
|
|
280
|
+
// Todo tools (if user mentions task/todo/plan)
|
|
281
|
+
if (promptLower.includes('任务') || promptLower.includes('todo') ||
|
|
282
|
+
promptLower.includes('计划') || promptLower.includes('plan') ||
|
|
283
|
+
promptLower.includes('待办')) {
|
|
284
|
+
selectedTools.push('todo');
|
|
285
|
+
}
|
|
286
|
+
// Task tools (if user mentions agent/sub-agent)
|
|
287
|
+
if (promptLower.includes('agent') || promptLower.includes('子代理') ||
|
|
288
|
+
promptLower.includes('subagent') || promptLower.includes('并行') ||
|
|
289
|
+
promptLower.includes('parallel')) {
|
|
290
|
+
selectedTools.push('task');
|
|
291
|
+
}
|
|
292
|
+
// Remove duplicates
|
|
293
|
+
return [...new Set(selectedTools)];
|
|
294
|
+
}
|
|
295
|
+
registerBuiltinTools(minimal = false, prompt, modelConfig) {
|
|
296
|
+
// All available tools
|
|
297
|
+
const allTools = [
|
|
298
|
+
{ name: 'read_file', handler: readFileHandler, schema: readFileSchema },
|
|
299
|
+
{ name: 'write_file', handler: writeFileHandler, schema: writeFileSchema },
|
|
300
|
+
{ name: 'edit_file', handler: editFileHandler, schema: editFileSchema },
|
|
301
|
+
{ name: 'list_directory', handler: listDirectoryHandler, schema: listDirectorySchema },
|
|
302
|
+
{ name: 'execute_command', handler: shellHandler, schema: executeCommandSchema },
|
|
303
|
+
{ name: 'search_file', handler: searchFileHandler, schema: searchFileSchema },
|
|
304
|
+
{ name: 'search_content', handler: searchContentHandler, schema: searchContentSchema },
|
|
305
|
+
{ name: 'web_search', handler: webSearchHandler, schema: webSearchSchema },
|
|
306
|
+
{ name: 'web_fetch', handler: webFetchHandler, schema: webFetchSchema },
|
|
307
|
+
{ name: 'memory_read', handler: memoryReadHandler, schema: memoryReadSchema },
|
|
308
|
+
{ name: 'memory_write', handler: memoryWriteHandler, schema: memoryWriteSchema },
|
|
309
|
+
{ name: 'todo', handler: todoHandler, schema: todoSchema },
|
|
310
|
+
{ name: 'task', handler: taskHandler, schema: taskSchema },
|
|
311
|
+
];
|
|
312
|
+
// Select tools based on prompt (if provided) or use minimal mode
|
|
313
|
+
let selectedToolNames;
|
|
314
|
+
if (prompt && !minimal) {
|
|
315
|
+
selectedToolNames = this.selectToolsForPrompt(prompt, minimal);
|
|
316
|
+
console.log(`Using ${selectedToolNames.length} tools based on your prompt`);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
// Use essential tools only in minimal mode (saves ~1000 tokens)
|
|
320
|
+
selectedToolNames = minimal
|
|
321
|
+
? ['read_file', 'write_file', 'edit_file', 'list_directory', 'execute_command']
|
|
322
|
+
: allTools.map(t => t.name);
|
|
323
|
+
}
|
|
324
|
+
// Filter out web_search tool if model has built-in search capability
|
|
325
|
+
if (modelConfig?.supportsBuiltinSearch) {
|
|
326
|
+
const filteredTools = selectedToolNames.filter(t => t !== 'web_search');
|
|
327
|
+
if (filteredTools.length !== selectedToolNames.length) {
|
|
328
|
+
console.log(`Model has built-in search capability, excluding web_search tool`);
|
|
329
|
+
}
|
|
330
|
+
selectedToolNames = filteredTools;
|
|
331
|
+
}
|
|
332
|
+
// Register selected tools
|
|
333
|
+
const toolsToRegister = allTools.filter(t => selectedToolNames.includes(t.name));
|
|
334
|
+
for (const { name, handler, schema } of toolsToRegister) {
|
|
335
|
+
this.toolRegistry.register({
|
|
336
|
+
name,
|
|
337
|
+
description: this.getToolDescription(name),
|
|
338
|
+
category: this.getToolCategory(name),
|
|
339
|
+
inputSchema: schema,
|
|
340
|
+
requiresApproval: this.getToolApproval(name),
|
|
341
|
+
riskLevel: this.getToolRisk(name),
|
|
342
|
+
}, handler);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
getToolDescription(name) {
|
|
346
|
+
const descriptions = {
|
|
347
|
+
read_file: 'Read file contents from disk. Use offset/limit for large files (e.g. 200 lines max per read). Always read a file before editing it.',
|
|
348
|
+
write_file: 'Create a NEW file with content. Only use for new files - prefer edit_file for existing files. Set createDirectories=true to auto-create parent dirs.',
|
|
349
|
+
edit_file: 'Edit an existing file by replacing oldText with newText. The oldText must be unique in the file. Prefer this over write_file for modifications.',
|
|
350
|
+
list_directory: 'List files and directories at a path. Use depth=1 by default; avoid recursive=true on large directories to prevent excessive output.',
|
|
351
|
+
search_file: 'Find files by glob pattern (e.g. "*.ts"). Returns relative paths. Prefer this over listing all files.',
|
|
352
|
+
search_content: 'Search file contents using regex. Returns matching lines with context. Use headLimit to cap results.',
|
|
353
|
+
execute_command: 'Execute a shell command (PowerShell on Windows, bash on Linux/macOS). Use for building, testing, git, npm, etc. Set timeout for long-running commands.',
|
|
354
|
+
web_search: 'Search the web for current information. Use when you need up-to-date facts or are unsure about something.',
|
|
355
|
+
web_fetch: 'Fetch and read a web page URL. Returns converted text. Use for reading documentation, articles, or API docs.',
|
|
356
|
+
memory_read: 'Read a value from persistent memory by key and scope. Use for cross-session context.',
|
|
357
|
+
memory_write: 'Write a value to persistent memory with optional TTL and tags. Use to remember important context across turns.',
|
|
358
|
+
todo: 'Track tasks and progress. Use "create" to add tasks, "update" to change status (pending/in_progress/completed), "list" to show current state. Break complex tasks into sub-tasks and track progress.',
|
|
359
|
+
task: 'Launch a sub-agent to perform a specific task. Use subagentType: code-explorer (analyze code), research (gather info), or executor (perform actions).',
|
|
360
|
+
};
|
|
361
|
+
return descriptions[name] || name;
|
|
362
|
+
}
|
|
363
|
+
getToolCategory(name) {
|
|
364
|
+
if (name.startsWith('read_') || name.startsWith('write_') || name.startsWith('edit_') || name.startsWith('list_'))
|
|
365
|
+
return 'file';
|
|
366
|
+
if (name.startsWith('search_'))
|
|
367
|
+
return 'search';
|
|
368
|
+
if (name.startsWith('execute_'))
|
|
369
|
+
return 'execution';
|
|
370
|
+
if (name.startsWith('web_'))
|
|
371
|
+
return 'web';
|
|
372
|
+
if (name === 'todo' || name === 'task')
|
|
373
|
+
return 'orchestration';
|
|
374
|
+
if (name.startsWith('memory_'))
|
|
375
|
+
return 'memory';
|
|
376
|
+
return 'orchestration';
|
|
377
|
+
}
|
|
378
|
+
getToolApproval(name) {
|
|
379
|
+
const alwaysApprove = new Set(['memory_read']);
|
|
380
|
+
const risky = new Set(['execute_command', 'write_file', 'edit_file', 'task']);
|
|
381
|
+
if (alwaysApprove.has(name))
|
|
382
|
+
return false;
|
|
383
|
+
if (risky.has(name))
|
|
384
|
+
return true;
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
getToolRisk(name) {
|
|
388
|
+
const critical = new Set(['execute_command']);
|
|
389
|
+
const high = new Set(['write_file', 'edit_file', 'task']);
|
|
390
|
+
const low = new Set(['read_file', 'list_directory', 'search_file', 'search_content', 'memory_read', 'web_search', 'web_fetch']);
|
|
391
|
+
if (critical.has(name))
|
|
392
|
+
return 'critical';
|
|
393
|
+
if (high.has(name))
|
|
394
|
+
return 'high';
|
|
395
|
+
if (low.has(name))
|
|
396
|
+
return 'low';
|
|
397
|
+
return 'medium';
|
|
398
|
+
}
|
|
399
|
+
async createModelClient(config, cliModel, noInput) {
|
|
400
|
+
const rawModelId = cliModel || config.core.defaultModel;
|
|
401
|
+
const configModel = this.configManager.getModelConfig(rawModelId);
|
|
402
|
+
if (!configModel) {
|
|
403
|
+
// Check if it might be an Ollama model (user can specify any ollama model name)
|
|
404
|
+
if (this.authManager.hasCredentials('ollama') || process.env.OLLAMA_HOST) {
|
|
405
|
+
const creds = this.authManager.getCredentials('ollama');
|
|
406
|
+
return new ModelClient({
|
|
407
|
+
provider: 'ollama',
|
|
408
|
+
baseUrl: creds?.baseUrl || 'http://localhost:11434',
|
|
409
|
+
model: rawModelId,
|
|
410
|
+
maxTokens: config.core.maxTokens,
|
|
411
|
+
temperature: config.core.temperature,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
throw new NovaError(`Model "${rawModelId}" not found in configuration. Use "nova model list" to see available models.`, 'CONFIG_ERROR');
|
|
415
|
+
}
|
|
416
|
+
// Extract model ID from provider/model format or resolve alias
|
|
417
|
+
let actualModelId;
|
|
418
|
+
if (rawModelId.includes('/')) {
|
|
419
|
+
// For provider/model format, use the model part directly
|
|
420
|
+
actualModelId = rawModelId.split('/')[1];
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
// Resolve alias (e.g., "glm-cloud" → "glm-5")
|
|
424
|
+
actualModelId = this.resolveModelAlias(rawModelId);
|
|
425
|
+
}
|
|
426
|
+
const providerType = configModel.provider.type;
|
|
427
|
+
const providerName = configModel.provider.name;
|
|
428
|
+
let creds = this.authManager.getCredentials(providerName);
|
|
429
|
+
// For ollama and ollama-cloud, apiKey is handled differently
|
|
430
|
+
if (providerType !== 'ollama' && providerType !== 'ollama-cloud' && !creds?.apiKey) {
|
|
431
|
+
// In non-interactive mode, fail fast with actionable error
|
|
432
|
+
if (noInput) {
|
|
433
|
+
const envKey = providerType === 'anthropic' ? 'ANTHROPIC_API_KEY'
|
|
434
|
+
: providerType === 'openai' ? 'OPENAI_API_KEY'
|
|
435
|
+
: `${providerName.toUpperCase().replace(/-/g, '_')}_API_KEY`;
|
|
436
|
+
throw new NovaError(`Missing API key for "${providerName}".\n\n` +
|
|
437
|
+
`Solutions:\n` +
|
|
438
|
+
` 1. Set environment variable: export ${envKey}=<your-key>\n` +
|
|
439
|
+
` 2. Or run: nova auth set ${providerName} --key <your-key>\n` +
|
|
440
|
+
` 3. Or use a local model: nova -m ollama/llama3.2`, 'AUTH_ERROR');
|
|
441
|
+
}
|
|
442
|
+
// Interactive prompt for API key
|
|
443
|
+
const apiKey = await this.promptForApiKey(providerName, providerType);
|
|
444
|
+
if (apiKey) {
|
|
445
|
+
await this.authManager.setCredentials({ provider: providerName, apiKey });
|
|
446
|
+
creds = { apiKey };
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
// User cancelled - fallback to ollama if available
|
|
450
|
+
console.log('\x1b[33m Falling back to local Ollama...\x1b[0m');
|
|
451
|
+
return this.createOllamaClient(config, 'llama3.2');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return new ModelClient({
|
|
455
|
+
provider: providerType,
|
|
456
|
+
apiKey: creds?.apiKey,
|
|
457
|
+
baseUrl: creds?.baseUrl || configModel.provider.baseUrl,
|
|
458
|
+
model: actualModelId,
|
|
459
|
+
maxTokens: config.core.maxTokens,
|
|
460
|
+
temperature: config.core.temperature,
|
|
461
|
+
organizationId: creds?.organizationId,
|
|
462
|
+
codingPlanPlatform: configModel.provider.codingPlanPlatform,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
/** Create an Ollama client as fallback */
|
|
466
|
+
createOllamaClient(config, model) {
|
|
467
|
+
const creds = this.authManager.getCredentials('ollama');
|
|
468
|
+
return new ModelClient({
|
|
469
|
+
provider: 'ollama',
|
|
470
|
+
baseUrl: creds?.baseUrl || process.env.OLLAMA_HOST || 'http://localhost:11434',
|
|
471
|
+
model,
|
|
472
|
+
maxTokens: config.core.maxTokens,
|
|
473
|
+
temperature: config.core.temperature,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
/** Prompt user for API key interactively */
|
|
477
|
+
async promptForApiKey(providerName, providerType) {
|
|
478
|
+
const readline = await import('node:readline');
|
|
479
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
480
|
+
const envKey = providerType === 'anthropic' ? 'ANTHROPIC_API_KEY'
|
|
481
|
+
: providerType === 'openai' ? 'OPENAI_API_KEY'
|
|
482
|
+
: providerType === 'google' ? 'GOOGLE_API_KEY'
|
|
483
|
+
: providerType === 'deepseek' ? 'DEEPSEEK_API_KEY'
|
|
484
|
+
: `${providerName.toUpperCase()}_API_KEY`;
|
|
485
|
+
console.log('');
|
|
486
|
+
console.log(`\x1b[33m ⚠ No API key found for "${providerName}"\x1b[0m`);
|
|
487
|
+
console.log(`\x1b[90m You can also set it via: export ${envKey}=<your-key>\x1b[0m`);
|
|
488
|
+
console.log('');
|
|
489
|
+
const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
|
|
490
|
+
try {
|
|
491
|
+
const answer = await question(` Enter ${providerName} API key (or press Enter to skip): `);
|
|
492
|
+
rl.close();
|
|
493
|
+
return answer.trim() || null;
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
rl.close();
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/** Resolve a model alias (e.g., "glm-cloud" → "glm-5", "cloud" → "deepseek-v3.2") */
|
|
501
|
+
resolveModelAlias(modelId) {
|
|
502
|
+
const aliases = this.configManager.getConfig().models.aliases;
|
|
503
|
+
return aliases?.[modelId] || modelId;
|
|
504
|
+
}
|
|
505
|
+
async runSinglePrompt(prompt, cwd, modelClient, config, minimal = false, thinking) {
|
|
506
|
+
const session = this.sessionManager.create({
|
|
507
|
+
workingDirectory: cwd,
|
|
508
|
+
model: config.core.defaultModel,
|
|
509
|
+
streaming: true,
|
|
510
|
+
});
|
|
511
|
+
// Get model config to check for built-in search capability
|
|
512
|
+
const modelConfigResult = this.configManager.getModelConfig(config.core.defaultModel);
|
|
513
|
+
const { buildSystemPrompt } = await import('../../../core/src/context/defaultSystemPrompt.js');
|
|
514
|
+
const chalk = await import('chalk');
|
|
515
|
+
const systemPrompt = buildSystemPrompt({
|
|
516
|
+
workingDirectory: cwd,
|
|
517
|
+
model: modelClient.getModel(),
|
|
518
|
+
approvalMode: config.core.defaultApprovalMode,
|
|
519
|
+
minimal,
|
|
520
|
+
supportsBuiltinSearch: modelConfigResult?.model?.supportsBuiltinSearch,
|
|
521
|
+
});
|
|
522
|
+
const agentLoop = new AgentLoop({
|
|
523
|
+
modelClient,
|
|
524
|
+
sessionManager: this.sessionManager,
|
|
525
|
+
toolRegistry: this.toolRegistry,
|
|
526
|
+
systemPrompt,
|
|
527
|
+
contextCompressor: this.contextCompressor,
|
|
528
|
+
maxContextTokens: config.core.maxTokens * 8 || 128000,
|
|
529
|
+
thinking,
|
|
530
|
+
onTextDelta: (text) => {
|
|
531
|
+
process.stdout.write(text);
|
|
532
|
+
},
|
|
533
|
+
onToolStart: (name) => {
|
|
534
|
+
const time = new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
535
|
+
process.stdout.write(`\n${chalk.default.cyan(` [${time}] `)}${chalk.default.white.bold(name)}\n`);
|
|
536
|
+
},
|
|
537
|
+
onToolComplete: (name, _id, result) => {
|
|
538
|
+
if (result.isError) {
|
|
539
|
+
process.stdout.write(`${chalk.default.red(' [error]')}\n`);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
process.stdout.write(`${chalk.default.gray(' [done]')}\n`);
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
onApprovalRequired: async (request) => {
|
|
546
|
+
return { requestId: request.id, approved: true };
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
const result = await agentLoop.runStream(session.id, prompt);
|
|
550
|
+
const totalTokens = result.totalInputTokens + result.totalOutputTokens;
|
|
551
|
+
console.log(chalk.default.gray(`\n---\n${result.turnsCompleted} turn${result.turnsCompleted > 1 ? 's' : ''} | ${totalTokens} tokens (${result.totalInputTokens} in / ${result.totalOutputTokens} out)`));
|
|
552
|
+
}
|
|
553
|
+
async handleConfigCommand(args) {
|
|
554
|
+
if (args.subcommand === 'show') {
|
|
555
|
+
const config = this.configManager.getConfig();
|
|
556
|
+
console.log(JSON.stringify(config, null, 2));
|
|
557
|
+
}
|
|
558
|
+
else if (args.subcommand === 'edit') {
|
|
559
|
+
const editor = process.env.EDITOR || process.env.VISUAL || 'code';
|
|
560
|
+
const configPath = path.join(os.homedir(), '.nova', 'config.yaml');
|
|
561
|
+
console.log(`Opening config: ${configPath}`);
|
|
562
|
+
const { execFile } = await import('node:child_process');
|
|
563
|
+
const { promisify } = await import('node:util');
|
|
564
|
+
await promisify(execFile)(editor, [configPath]);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
console.log('Usage: nova config [show|edit]');
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
async handleAuthCommand(args) {
|
|
571
|
+
if (args.subcommand === 'set') {
|
|
572
|
+
const provider = args.provider;
|
|
573
|
+
if (!provider) {
|
|
574
|
+
// Enhanced error with actionable suggestions
|
|
575
|
+
console.error('');
|
|
576
|
+
console.error('\x1b[31m╭──────────────────────────────────────────────────────────────────╮\x1b[0m');
|
|
577
|
+
console.error('\x1b[31m│\x1b[0m Error: Missing required argument <provider> \x1b[31m│\x1b[0m');
|
|
578
|
+
console.error('\x1b[31m│\x1b[0m Command: nova auth set \x1b[31m│\x1b[0m');
|
|
579
|
+
console.error('\x1b[31m╰──────────────────────────────────────────────────────────────────╯\x1b[0m');
|
|
580
|
+
console.error('');
|
|
581
|
+
console.error(' Correct Usage:');
|
|
582
|
+
console.error(' \x1b[36mnova auth set <provider> [--key <api-key>] [--base-url <url>]\x1b[0m');
|
|
583
|
+
console.error('');
|
|
584
|
+
console.error(' Built-in providers:');
|
|
585
|
+
console.error(' \x1b[90m• anthropic \x1b[0m - Claude (claude-3.5-sonnet, etc.)');
|
|
586
|
+
console.error(' \x1b[90m• openai \x1b[0m - GPT (gpt-4o, gpt-4-turbo, etc.)');
|
|
587
|
+
console.error(' \x1b[90m• google \x1b[0m - Gemini (gemini-1.5-pro, etc.)');
|
|
588
|
+
console.error(' \x1b[90m• deepseek \x1b[0m - DeepSeek (deepseek-v3, etc.)');
|
|
589
|
+
console.error(' \x1b[90m• ollama \x1b[0m - Local models (llama3.2, etc.)');
|
|
590
|
+
console.error('');
|
|
591
|
+
console.error(' Examples:');
|
|
592
|
+
console.error(' \x1b[90mnova auth set anthropic --key sk-ant-xxx\x1b[0m');
|
|
593
|
+
console.error(' \x1b[90mnova auth set openai --key sk-xxx\x1b[0m');
|
|
594
|
+
console.error(' \x1b[90mnova auth set ollama --base-url http://localhost:11434\x1b[0m');
|
|
595
|
+
console.error(' \x1b[90mnova auth set my-provider --base-url https://api.example.com/v1 --key xxx\x1b[0m');
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const readline = await import('node:readline');
|
|
599
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
600
|
+
const askQuestion = (prompt) => {
|
|
601
|
+
return new Promise((resolve) => {
|
|
602
|
+
rl.question(prompt, (answer) => resolve(answer.trim()));
|
|
603
|
+
});
|
|
604
|
+
};
|
|
605
|
+
try {
|
|
606
|
+
const providerType = this.getProviderType(provider);
|
|
607
|
+
const isOllama = providerType === 'ollama';
|
|
608
|
+
const isCustom = BASE_URL_PROVIDERS.has(provider) && !isOllama;
|
|
609
|
+
// API Key (skip for ollama)
|
|
610
|
+
let apiKey = args.apiKey || '';
|
|
611
|
+
if (!apiKey && !isOllama && REQUIRES_API_KEY.has(providerType)) {
|
|
612
|
+
apiKey = await askQuestion(`Enter API key for ${provider}: `);
|
|
613
|
+
}
|
|
614
|
+
// Base URL (optional for API providers, important for ollama/custom)
|
|
615
|
+
let baseUrl = args.baseUrl || '';
|
|
616
|
+
if (!baseUrl && isOllama) {
|
|
617
|
+
const defaultUrl = 'http://localhost:11434';
|
|
618
|
+
baseUrl = await askQuestion(`Ollama base URL [${defaultUrl}]: `);
|
|
619
|
+
if (!baseUrl)
|
|
620
|
+
baseUrl = defaultUrl;
|
|
621
|
+
}
|
|
622
|
+
else if (!baseUrl && args.provider && !['anthropic', 'openai'].includes(args.provider)) {
|
|
623
|
+
// For custom/other providers, ask about baseUrl
|
|
624
|
+
const config = this.configManager.getConfig();
|
|
625
|
+
const providerConfig = config.models.providers[args.provider];
|
|
626
|
+
if (providerConfig?.baseUrl) {
|
|
627
|
+
// Already has a default baseUrl in config
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
baseUrl = await askQuestion(`Base URL [skip to use default]: `);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
await this.authManager.setCredentials({
|
|
634
|
+
provider,
|
|
635
|
+
apiKey: apiKey || 'no-key-required',
|
|
636
|
+
baseUrl: baseUrl || undefined,
|
|
637
|
+
});
|
|
638
|
+
console.log(`Credentials saved for \x1b[32m${provider}\x1b[0m (type: ${providerType})`);
|
|
639
|
+
}
|
|
640
|
+
finally {
|
|
641
|
+
rl.close();
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
else if (args.subcommand === 'remove') {
|
|
645
|
+
const provider = args.provider;
|
|
646
|
+
if (!provider) {
|
|
647
|
+
console.error('Usage: nova auth remove <provider>');
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const removed = await this.authManager.removeCredentials(provider);
|
|
651
|
+
console.log(removed ? `Credentials removed for ${provider}` : `No credentials found for ${provider}`);
|
|
652
|
+
}
|
|
653
|
+
else if (args.subcommand === 'status') {
|
|
654
|
+
const config = this.configManager.getConfig();
|
|
655
|
+
const providerNames = Object.keys(config.models.providers);
|
|
656
|
+
const extraProviders = this.authManager.listProviders().filter((p) => !providerNames.includes(p));
|
|
657
|
+
console.log('\x1b[1mConfigured Providers:\x1b[0m');
|
|
658
|
+
for (const p of providerNames) {
|
|
659
|
+
const has = this.authManager.hasCredentials(p);
|
|
660
|
+
const providerConfig = config.models.providers[p];
|
|
661
|
+
const typeLabel = providerConfig.type === 'custom' ? `custom (${providerConfig.baseUrl || 'no baseUrl'})` : providerConfig.type;
|
|
662
|
+
const status = has ? '\x1b[32mconfigured\x1b[0m' : '\x1b[31mnot configured\x1b[0m';
|
|
663
|
+
console.log(` ${p.padEnd(14)} ${status.padEnd(20)} [${typeLabel}]`);
|
|
664
|
+
}
|
|
665
|
+
for (const p of extraProviders) {
|
|
666
|
+
const creds = this.authManager.getCredentials(p);
|
|
667
|
+
console.log(` ${p.padEnd(14)} \x1b[33mcustom\x1b[0m [baseUrl: ${creds?.baseUrl || 'not set'}]`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
console.log('Usage: nova auth [set|remove|status]');
|
|
672
|
+
console.log('');
|
|
673
|
+
console.log(' nova auth set <provider> [--key <api-key>] [--base-url <url>]');
|
|
674
|
+
console.log(' nova auth remove <provider>');
|
|
675
|
+
console.log(' nova auth status');
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async handleModelCommand(args) {
|
|
679
|
+
if (args.subcommand === 'list') {
|
|
680
|
+
const config = this.configManager.getConfig();
|
|
681
|
+
const currentDefault = config.core.defaultModel;
|
|
682
|
+
// Test all providers connection status
|
|
683
|
+
console.log('\x1b[90mTesting model connectivity...\x1b[0m\n');
|
|
684
|
+
const tester = this.modelConnectionTester || new ModelConnectionTester(this.authManager, this.configManager);
|
|
685
|
+
const providerStatuses = await tester.testAllProviders();
|
|
686
|
+
// JSON output for Agent consumers
|
|
687
|
+
if (args.json) {
|
|
688
|
+
const output = {
|
|
689
|
+
currentDefault,
|
|
690
|
+
providers: providerStatuses.map(status => ({
|
|
691
|
+
name: status.provider,
|
|
692
|
+
type: status.type,
|
|
693
|
+
status: status.status,
|
|
694
|
+
hasCredentials: status.hasCredentials,
|
|
695
|
+
message: status.message,
|
|
696
|
+
latency: status.latency,
|
|
697
|
+
models: status.models.map(m => ({
|
|
698
|
+
id: m.model,
|
|
699
|
+
status: m.status,
|
|
700
|
+
message: m.message,
|
|
701
|
+
latency: m.latency
|
|
702
|
+
}))
|
|
703
|
+
}))
|
|
704
|
+
};
|
|
705
|
+
console.log(JSON.stringify(output, null, 2));
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
// Human-readable output with enhanced status display
|
|
709
|
+
console.log('\x1b[1mAvailable Models:\x1b[0m\n');
|
|
710
|
+
// Sort providers: available > configured > unconfigured > unavailable
|
|
711
|
+
const sortedStatuses = providerStatuses.sort((a, b) => {
|
|
712
|
+
const order = { available: 0, configured: 1, partial: 2, unconfigured: 3, unavailable: 4, error: 5 };
|
|
713
|
+
return (order[a.status] ?? 99) - (order[b.status] ?? 99);
|
|
714
|
+
});
|
|
715
|
+
for (const status of sortedStatuses) {
|
|
716
|
+
const providerConfig = config.models.providers[status.provider];
|
|
717
|
+
if (!providerConfig)
|
|
718
|
+
continue;
|
|
719
|
+
// Status icons and colors
|
|
720
|
+
const statusConfig = {
|
|
721
|
+
available: { icon: '\x1b[32m✓\x1b[0m', color: '\x1b[1m', desc: '' },
|
|
722
|
+
configured: { icon: '\x1b[32m*\x1b[0m', color: '\x1b[1m', desc: '' },
|
|
723
|
+
partial: { icon: '\x1b[33m~\x1b[0m', color: '\x1b[1m', desc: ' (partial)' },
|
|
724
|
+
unconfigured: { icon: '\x1b[90m○\x1b[0m', color: '\x1b[90m', desc: ' (not configured)' },
|
|
725
|
+
unavailable: { icon: '\x1b[31m✗\x1b[0m', color: '\x1b[90m', desc: ' (unavailable)' },
|
|
726
|
+
error: { icon: '\x1b[31m!\x1b[0m', color: '\x1b[90m', desc: ' (error)' },
|
|
727
|
+
};
|
|
728
|
+
const sc = statusConfig[status.status] || statusConfig.unconfigured;
|
|
729
|
+
const typeLabels = {
|
|
730
|
+
anthropic: 'Anthropic Claude',
|
|
731
|
+
openai: 'OpenAI',
|
|
732
|
+
azure: 'Azure OpenAI',
|
|
733
|
+
ollama: 'Ollama (Local)',
|
|
734
|
+
'ollama-cloud': 'Ollama (Cloud)',
|
|
735
|
+
custom: 'Custom',
|
|
736
|
+
qwen: 'Qwen (DashScope)',
|
|
737
|
+
glm: 'GLM (Zhipu AI)',
|
|
738
|
+
moonshot: 'Moonshot',
|
|
739
|
+
baichuan: 'Baichuan',
|
|
740
|
+
minimax: 'MiniMax',
|
|
741
|
+
yi: 'Yi (01.AI)',
|
|
742
|
+
siliconflow: 'SiliconFlow',
|
|
743
|
+
groq: 'Groq (Ultra-fast)',
|
|
744
|
+
mistral: 'Mistral AI',
|
|
745
|
+
together: 'Together AI',
|
|
746
|
+
perplexity: 'Perplexity',
|
|
747
|
+
};
|
|
748
|
+
const providerLabel = typeLabels[providerConfig.type] || providerConfig.type;
|
|
749
|
+
const latencyStr = status.latency ? ` \x1b[90m(${status.latency}ms)\x1b[0m` : '';
|
|
750
|
+
console.log(`${sc.color}${sc.icon} ${status.provider}\x1b[0m -- ${providerLabel}${sc.desc}${latencyStr}`);
|
|
751
|
+
// Show models with their connection status
|
|
752
|
+
const models = Object.entries(providerConfig.models);
|
|
753
|
+
for (const [modelId, modelConfig] of models) {
|
|
754
|
+
const mc = modelConfig;
|
|
755
|
+
const modelStatus = status.models.find(m => m.model === modelId);
|
|
756
|
+
const isDefault = modelId === currentDefault || modelId === config.models.aliases?.[currentDefault];
|
|
757
|
+
const defaultMarker = isDefault ? ' \x1b[32m(default)\x1b[0m' : '';
|
|
758
|
+
const alias = Object.entries(config.models.aliases || {})
|
|
759
|
+
.find(([, v]) => v === modelId)?.[0];
|
|
760
|
+
const aliasStr = alias ? ` \x1b[90malias: ${alias}\x1b[0m` : '';
|
|
761
|
+
// Model status indicator
|
|
762
|
+
let modelStatusIcon = ' ';
|
|
763
|
+
if (modelStatus) {
|
|
764
|
+
if (modelStatus.status === 'available')
|
|
765
|
+
modelStatusIcon = '\x1b[32m ✓\x1b[0m ';
|
|
766
|
+
else if (modelStatus.status === 'configured')
|
|
767
|
+
modelStatusIcon = '\x1b[32m *\x1b[0m ';
|
|
768
|
+
else if (modelStatus.status === 'unconfigured')
|
|
769
|
+
modelStatusIcon = '\x1b[90m ○\x1b[0m ';
|
|
770
|
+
else if (modelStatus.status === 'unavailable')
|
|
771
|
+
modelStatusIcon = '\x1b[31m ✗\x1b[0m ';
|
|
772
|
+
}
|
|
773
|
+
const features = [];
|
|
774
|
+
if (mc.supportsVision)
|
|
775
|
+
features.push('vision');
|
|
776
|
+
if (mc.supportsTools)
|
|
777
|
+
features.push('tools');
|
|
778
|
+
if (mc.supportsThinking)
|
|
779
|
+
features.push('thinking');
|
|
780
|
+
const featureStr = features.length > 0 ? ` [${features.join(', ')}]` : '';
|
|
781
|
+
const costStr = mc.inputCostPerMToken
|
|
782
|
+
? ` ($${mc.inputCostPerMToken}/${mc.outputCostPerMToken} per 1M tok)`
|
|
783
|
+
: ' (local)';
|
|
784
|
+
// Dim unconfigured/unavailable models
|
|
785
|
+
const dimStart = status.status === 'unconfigured' || status.status === 'unavailable' || status.status === 'error' ? '\x1b[90m' : '';
|
|
786
|
+
const dimEnd = status.status === 'unconfigured' || status.status === 'unavailable' || status.status === 'error' ? '\x1b[0m' : '';
|
|
787
|
+
console.log(`${dimStart}${modelStatusIcon}${modelId}${defaultMarker}${featureStr}${costStr}${aliasStr}${dimEnd}`);
|
|
788
|
+
}
|
|
789
|
+
console.log('');
|
|
790
|
+
}
|
|
791
|
+
console.log('\x1b[32m✓\x1b[0m = connected \x1b[32m*\x1b[0m = configured \x1b[33m~\x1b[0m = partial \x1b[90m○\x1b[0m = not configured \x1b[31m✗\x1b[0m = unavailable');
|
|
792
|
+
console.log('\n\x1b[90mUsage: nova -m <model-id> or nova -m <alias>\x1b[0m');
|
|
793
|
+
console.log('\x1b[90mConfigure: nova auth set <provider> --key <api-key>\x1b[0m');
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
console.log('Usage: nova model list');
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
/** Determine provider type from provider name or config */
|
|
800
|
+
getProviderType(provider) {
|
|
801
|
+
const config = this.configManager.getConfig();
|
|
802
|
+
const providerConfig = config.models.providers[provider];
|
|
803
|
+
if (providerConfig)
|
|
804
|
+
return providerConfig.type;
|
|
805
|
+
// Check auth for custom providers
|
|
806
|
+
if (this.authManager.hasCredentials(provider))
|
|
807
|
+
return 'custom';
|
|
808
|
+
return 'custom';
|
|
809
|
+
}
|
|
810
|
+
/** Auto-discover Ollama models and register them dynamically */
|
|
811
|
+
async autoDiscoverOllamaModels() {
|
|
812
|
+
try {
|
|
813
|
+
const ollamaCreds = this.authManager.getCredentials('ollama');
|
|
814
|
+
const baseUrl = ollamaCreds?.baseUrl || process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
815
|
+
const manager = new OllamaManager(baseUrl);
|
|
816
|
+
if (!(await manager.ping()))
|
|
817
|
+
return; // Ollama not running, skip silently
|
|
818
|
+
const models = await manager.listModels();
|
|
819
|
+
let registered = 0;
|
|
820
|
+
for (const model of models) {
|
|
821
|
+
const wasNew = this.configManager.registerModel('ollama', model.name, manager.toModelConfig(model));
|
|
822
|
+
if (wasNew)
|
|
823
|
+
registered++;
|
|
824
|
+
}
|
|
825
|
+
if (registered > 0) {
|
|
826
|
+
// Also register aliases for newly discovered models
|
|
827
|
+
// (silent, no output - this runs on every startup)
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
catch {
|
|
831
|
+
// Ollama discovery is best-effort, never block startup
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
/** Show connection status summary on startup */
|
|
835
|
+
async showConnectionStatus(config) {
|
|
836
|
+
const statuses = await this.modelConnectionTester.testAllProviders();
|
|
837
|
+
const connected = statuses.filter(s => s.status === 'configured');
|
|
838
|
+
const partial = statuses.filter(s => s.status === 'partial');
|
|
839
|
+
const unconfigured = statuses.filter(s => s.status === 'unconfigured');
|
|
840
|
+
const unavailable = statuses.filter(s => s.status === 'unavailable');
|
|
841
|
+
const errors = statuses.filter(s => s.status === 'error');
|
|
842
|
+
// Quick summary line
|
|
843
|
+
const parts = [];
|
|
844
|
+
if (connected.length > 0) {
|
|
845
|
+
parts.push(`\x1b[32m${connected.length} connected\x1b[0m`);
|
|
846
|
+
}
|
|
847
|
+
if (partial.length > 0) {
|
|
848
|
+
parts.push(`\x1b[36m${partial.length} partial\x1b[0m`);
|
|
849
|
+
}
|
|
850
|
+
if (partial.length > 0) {
|
|
851
|
+
parts.push(`\x1b[33m${partial.length} partial\x1b[0m`);
|
|
852
|
+
}
|
|
853
|
+
if (unconfigured.length > 0) {
|
|
854
|
+
parts.push(`\x1b[90m${unconfigured.length} unconfigured\x1b[0m`);
|
|
855
|
+
}
|
|
856
|
+
if (unavailable.length > 0) {
|
|
857
|
+
parts.push(`\x1b[33m${unavailable.length} unavailable\x1b[0m`);
|
|
858
|
+
}
|
|
859
|
+
if (errors.length > 0) {
|
|
860
|
+
parts.push(`\x1b[31m${errors.length} error\x1b[0m`);
|
|
861
|
+
}
|
|
862
|
+
if (parts.length > 0) {
|
|
863
|
+
console.log(`\n\x1b[90m Model Status: ${parts.join(' | ')}\x1b[0m`);
|
|
864
|
+
}
|
|
865
|
+
// Show connected providers with latency
|
|
866
|
+
for (const status of connected) {
|
|
867
|
+
const latencyStr = status.latency ? ` (${status.latency}ms)` : '';
|
|
868
|
+
console.log(`\x1b[32m ✓ ${status.provider}${latencyStr}\x1b[0m`);
|
|
869
|
+
}
|
|
870
|
+
// Show warnings for unavailable providers
|
|
871
|
+
for (const status of unavailable) {
|
|
872
|
+
console.log(`\x1b[33m ⚠ ${status.provider}: ${status.message}\x1b[0m`);
|
|
873
|
+
}
|
|
874
|
+
// Show errors
|
|
875
|
+
for (const status of errors) {
|
|
876
|
+
console.log(`\x1b[31m ✗ ${status.provider}: ${status.message}\x1b[0m`);
|
|
877
|
+
}
|
|
878
|
+
// Show unconfigured hint if any
|
|
879
|
+
if (unconfigured.length > 0 && connected.length === 0) {
|
|
880
|
+
console.log(`\x1b[90m Run "nova auth set <provider> --key <api-key>" to configure\x1b[0m`);
|
|
881
|
+
}
|
|
882
|
+
console.log('');
|
|
883
|
+
}
|
|
884
|
+
// ==================== Provider Command ====================
|
|
885
|
+
async handleProviderCommand(args) {
|
|
886
|
+
if (args.subcommand === 'list') {
|
|
887
|
+
await this.handleModelCommand({ ...args, command: 'model', subcommand: 'list' });
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
if (args.subcommand === 'add') {
|
|
891
|
+
const baseUrl = args.baseUrl;
|
|
892
|
+
const apiKey = args.apiKey;
|
|
893
|
+
if (!baseUrl) {
|
|
894
|
+
// Enhanced error with actionable suggestions
|
|
895
|
+
console.error('');
|
|
896
|
+
console.error('\x1b[31m╭──────────────────────────────────────────────────────────────────╮\x1b[0m');
|
|
897
|
+
console.error('\x1b[31m│\x1b[0m Error: Missing required argument <base-url> \x1b[31m│\x1b[0m');
|
|
898
|
+
console.error('\x1b[31m│\x1b[0m Command: nova provider add \x1b[31m│\x1b[0m');
|
|
899
|
+
console.error('\x1b[31m╰──────────────────────────────────────────────────────────────────╯\x1b[0m');
|
|
900
|
+
console.error('');
|
|
901
|
+
console.error(' Correct Usage:');
|
|
902
|
+
console.error(' \x1b[36mnova provider add <base-url> <api-key> [-m <model>] [-n <name>]\x1b[0m');
|
|
903
|
+
console.error('');
|
|
904
|
+
console.error(' Flags:');
|
|
905
|
+
console.error(' \x1b[90m-m, --model <id>\x1b[0m Default model ID (default: auto-detected)');
|
|
906
|
+
console.error(' \x1b[90m-n, --name <name>\x1b[0m Provider name (auto-generated from URL if omitted)');
|
|
907
|
+
console.error(' \x1b[90m-k, --key <key>\x1b[0m API key (alternative to positional arg)');
|
|
908
|
+
console.error(' \x1b[90m-u, --base-url <url>\x1b[0m Base URL (alternative to positional arg)');
|
|
909
|
+
console.error('');
|
|
910
|
+
console.error(' Examples:');
|
|
911
|
+
console.error(' \x1b[90mnova provider add https://api.example.com sk-xxx\x1b[0m');
|
|
912
|
+
console.error(' \x1b[90mnova provider add https://api.example.com sk-xxx -m gpt-4\x1b[0m');
|
|
913
|
+
console.error(' \x1b[90mnova provider add -u https://api.example.com -k sk-xxx -n my-provider\x1b[0m');
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
// Auto-generate provider name from URL if not provided
|
|
917
|
+
let providerName = args.provider;
|
|
918
|
+
if (!providerName) {
|
|
919
|
+
try {
|
|
920
|
+
const url = new URL(baseUrl);
|
|
921
|
+
// Extract name from hostname: api.example.com -> example
|
|
922
|
+
const parts = url.hostname.split('.');
|
|
923
|
+
providerName = parts.length >= 2 ? parts[parts.length - 2] : parts[0];
|
|
924
|
+
}
|
|
925
|
+
catch {
|
|
926
|
+
providerName = 'custom-api';
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
const type = args.providerType || 'custom';
|
|
930
|
+
const key = apiKey || 'no-key-required';
|
|
931
|
+
// Probe available models from the API
|
|
932
|
+
console.log(`\x1b[90mProbing ${baseUrl} for available models...\x1b[0m`);
|
|
933
|
+
let availableModels = [];
|
|
934
|
+
let defaultModel = args.defaultModel || '';
|
|
935
|
+
try {
|
|
936
|
+
const { OpenAICompatibleProvider } = await import('../../../core/src/model/providers/OpenAICompatibleProvider.js');
|
|
937
|
+
const probe = new (class extends OpenAICompatibleProvider {
|
|
938
|
+
constructor() {
|
|
939
|
+
super({ apiKey: key, baseUrl, model: 'probe' });
|
|
940
|
+
}
|
|
941
|
+
get name() { return 'probe'; }
|
|
942
|
+
})();
|
|
943
|
+
availableModels = await probe.listModels();
|
|
944
|
+
}
|
|
945
|
+
catch (err) {
|
|
946
|
+
console.error(`\x1b[33mWarning: Could not probe models - ${err.message}\x1b[0m`);
|
|
947
|
+
}
|
|
948
|
+
// Determine default model
|
|
949
|
+
if (!defaultModel && availableModels.length > 0) {
|
|
950
|
+
// Prefer models with common names
|
|
951
|
+
const preferred = ['gpt-4', 'gpt-4o', 'gpt-3.5-turbo', 'claude-3', 'llama', 'qwen', 'glm'];
|
|
952
|
+
for (const p of preferred) {
|
|
953
|
+
const found = availableModels.find(m => m.toLowerCase().includes(p));
|
|
954
|
+
if (found) {
|
|
955
|
+
defaultModel = found;
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// Fall back to first model
|
|
960
|
+
if (!defaultModel) {
|
|
961
|
+
defaultModel = availableModels[0];
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
if (!defaultModel) {
|
|
965
|
+
defaultModel = 'default';
|
|
966
|
+
}
|
|
967
|
+
// Save credentials
|
|
968
|
+
await this.authManager.setCredentials({
|
|
969
|
+
provider: providerName,
|
|
970
|
+
apiKey: key,
|
|
971
|
+
baseUrl,
|
|
972
|
+
});
|
|
973
|
+
// Register provider in config if not already known
|
|
974
|
+
const config = this.configManager.getConfig();
|
|
975
|
+
const existingProvider = config.models.providers[providerName];
|
|
976
|
+
if (!existingProvider) {
|
|
977
|
+
// New provider: create with model
|
|
978
|
+
this.configManager.registerProvider(providerName, {
|
|
979
|
+
type: type,
|
|
980
|
+
baseUrl,
|
|
981
|
+
models: {
|
|
982
|
+
[defaultModel]: {
|
|
983
|
+
name: defaultModel,
|
|
984
|
+
maxContextTokens: 128000,
|
|
985
|
+
maxOutputTokens: 8192,
|
|
986
|
+
supportsVision: false,
|
|
987
|
+
supportsTools: true,
|
|
988
|
+
supportsStreaming: true,
|
|
989
|
+
supportsThinking: false,
|
|
990
|
+
},
|
|
991
|
+
},
|
|
992
|
+
defaultModel,
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
else if (!existingProvider.models[defaultModel]) {
|
|
996
|
+
// Provider exists but model doesn't: add model
|
|
997
|
+
existingProvider.models[defaultModel] = {
|
|
998
|
+
name: defaultModel,
|
|
999
|
+
maxContextTokens: 128000,
|
|
1000
|
+
maxOutputTokens: 8192,
|
|
1001
|
+
supportsVision: false,
|
|
1002
|
+
supportsTools: true,
|
|
1003
|
+
supportsStreaming: true,
|
|
1004
|
+
supportsThinking: false,
|
|
1005
|
+
};
|
|
1006
|
+
existingProvider.defaultModel = defaultModel;
|
|
1007
|
+
}
|
|
1008
|
+
// Set as default model (format: provider/model)
|
|
1009
|
+
const fullModelId = `${providerName}/${defaultModel}`;
|
|
1010
|
+
config.core.defaultModel = fullModelId;
|
|
1011
|
+
// Save configuration to disk
|
|
1012
|
+
await this.configManager.save(config);
|
|
1013
|
+
console.log(`\x1b[32m✓ Provider "${providerName}" added\x1b[0m`);
|
|
1014
|
+
console.log(` Base URL: ${baseUrl}`);
|
|
1015
|
+
if (availableModels.length > 0) {
|
|
1016
|
+
console.log(` Available models (${availableModels.length}):`);
|
|
1017
|
+
// Show first 10 models, indicate if more
|
|
1018
|
+
const shown = availableModels.slice(0, 10);
|
|
1019
|
+
for (const m of shown) {
|
|
1020
|
+
const marker = m === defaultModel ? ' \x1b[33m← selected\x1b[0m' : '';
|
|
1021
|
+
console.log(` - ${m}${marker}`);
|
|
1022
|
+
}
|
|
1023
|
+
if (availableModels.length > 10) {
|
|
1024
|
+
console.log(` ... and ${availableModels.length - 10} more`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
else {
|
|
1028
|
+
console.log(` Model: ${fullModelId}`);
|
|
1029
|
+
}
|
|
1030
|
+
console.log('');
|
|
1031
|
+
console.log(`\x1b[36mDefault set to ${fullModelId}. Run \x1b[33mnova\x1b[36m to start.\x1b[0m`);
|
|
1032
|
+
console.log(`\x1b[90mTo change model: nova -m ${providerName}/<model-id>\x1b[0m`);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
if (args.subcommand === 'add-model') {
|
|
1036
|
+
const providerName = args.provider;
|
|
1037
|
+
if (!providerName) {
|
|
1038
|
+
console.error('Usage: nova provider add-model <provider> --model-id <id> --model-name <name> [--max-context <n>] [--max-output <n>] [--features vision,tools,streaming] [--cost-in <n>] [--cost-out <n>]');
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
const modelId = args.ollamaModel;
|
|
1042
|
+
if (!modelId) {
|
|
1043
|
+
console.error('Error: --model-id is required');
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const features = (args.features || '').split(',').map((f) => f.trim()).filter(Boolean);
|
|
1047
|
+
const modelConfig = {
|
|
1048
|
+
name: args.modelName || modelId,
|
|
1049
|
+
maxContextTokens: args.maxContext || 128000,
|
|
1050
|
+
maxOutputTokens: args.maxOutput || 8192,
|
|
1051
|
+
supportsVision: features.includes('vision'),
|
|
1052
|
+
supportsTools: features.includes('tools'),
|
|
1053
|
+
supportsStreaming: features.includes('streaming') || true,
|
|
1054
|
+
supportsThinking: features.includes('thinking'),
|
|
1055
|
+
};
|
|
1056
|
+
if (args.costIn)
|
|
1057
|
+
modelConfig.inputCostPerMToken = args.costIn;
|
|
1058
|
+
if (args.costOut)
|
|
1059
|
+
modelConfig.outputCostPerMToken = args.costOut;
|
|
1060
|
+
this.configManager.registerModel(providerName, modelId, modelConfig);
|
|
1061
|
+
// Save configuration to disk
|
|
1062
|
+
await this.configManager.save(this.configManager.getConfig());
|
|
1063
|
+
console.log(`\x1b[32mModel "${modelId}" added to provider "${providerName}"\x1b[0m`);
|
|
1064
|
+
console.log(` Use: nova -m ${modelId}`);
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (args.subcommand === 'remove') {
|
|
1068
|
+
const providerName = args.provider;
|
|
1069
|
+
if (!providerName) {
|
|
1070
|
+
console.error('Usage: nova provider remove <name>');
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
await this.authManager.removeCredentials(providerName);
|
|
1074
|
+
console.log(`Provider "${providerName}" removed`);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
console.log('Usage: nova provider [add|add-model|remove|list]');
|
|
1078
|
+
console.log('');
|
|
1079
|
+
console.log(' nova provider add <url> <key> [-m <model>] [-n <name>]');
|
|
1080
|
+
console.log(' nova provider add-model <provider> --model-id <id> --model-name <name> [--features ...]');
|
|
1081
|
+
console.log(' nova provider remove <name>');
|
|
1082
|
+
console.log(' nova provider list');
|
|
1083
|
+
console.log('');
|
|
1084
|
+
console.log('Examples:');
|
|
1085
|
+
console.log(' nova provider add https://api.example.com sk-xxx');
|
|
1086
|
+
console.log(' nova provider add https://api.example.com sk-xxx -m gpt-4');
|
|
1087
|
+
console.log(' nova provider add https://api.example.com sk-xxx -m gpt-4 -n my-api');
|
|
1088
|
+
}
|
|
1089
|
+
// ==================== Coding Plan Command ====================
|
|
1090
|
+
async handleCodingPlanCommand(args) {
|
|
1091
|
+
if (args.subcommand === 'list' || !args.subcommand) {
|
|
1092
|
+
console.log('\x1b[1mSupported Coding Plan Platforms:\x1b[0m\n');
|
|
1093
|
+
const platforms = [
|
|
1094
|
+
{ name: 'alibaba', display: 'Alibaba Cloud (阿里云百炼)', models: 'qwen3.5-plus, qwen3-coder, glm-5', price: 'Lite ¥40/mo, Pro ¥200/mo' },
|
|
1095
|
+
{ name: 'tencent', display: 'Tencent Cloud (腾讯云)', models: 'hy-2.0-instruct, glm-5, kimi-k2.5', price: 'Lite ¥7.9/mo, Pro ¥39.9/mo' },
|
|
1096
|
+
{ name: 'volcengine', display: 'Volcengine (火山引擎)', models: 'doubao-seed-code, deepseek-v3.2, glm-4.7', price: 'Lite ¥9.9/mo, Pro ¥49.9/mo' },
|
|
1097
|
+
{ name: 'baidu', display: 'Baidu Qianfan (百度千帆)', models: 'glm-5, kimi-k2.5, ernie-4.5', price: '¥39.9/mo' },
|
|
1098
|
+
{ name: 'kimi', display: 'Kimi Code', models: 'kimi-k2, kimi-k2.5', price: '¥49/mo' },
|
|
1099
|
+
{ name: 'zhipu', display: 'Zhipu AI (智谱)', models: 'glm-4.7, glm-5', price: '¥49/mo' },
|
|
1100
|
+
{ name: 'minimax', display: 'MiniMax', models: 'minimax-2.7, abab6.5s-chat', price: '¥29/mo' },
|
|
1101
|
+
];
|
|
1102
|
+
for (const p of platforms) {
|
|
1103
|
+
console.log(` \x1b[36m${p.name}\x1b[0m - ${p.display}`);
|
|
1104
|
+
console.log(` Models: ${p.models}`);
|
|
1105
|
+
console.log(` Price: ${p.price}`);
|
|
1106
|
+
console.log('');
|
|
1107
|
+
}
|
|
1108
|
+
console.log('\x1b[90m Get your API key from the platform console, then configure:\x1b[0m');
|
|
1109
|
+
console.log('\x1b[90m nova coding-plan add <platform> --key <your-api-key>\x1b[0m');
|
|
1110
|
+
console.log('');
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
if (args.subcommand === 'add') {
|
|
1114
|
+
const platform = args.provider; // reuse provider arg for platform name
|
|
1115
|
+
if (!platform) {
|
|
1116
|
+
console.error('Usage: nova coding-plan add <platform> --key <api-key>');
|
|
1117
|
+
console.error('Platforms: alibaba, tencent, volcengine, baidu, kimi, zhipu, minimax');
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const validPlatforms = ['alibaba', 'tencent', 'volcengine', 'baidu', 'kimi', 'zhipu', 'minimax', 'custom'];
|
|
1121
|
+
if (!validPlatforms.includes(platform)) {
|
|
1122
|
+
console.error(`Invalid platform: ${platform}`);
|
|
1123
|
+
console.error(`Valid platforms: ${validPlatforms.join(', ')}`);
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
const apiKey = args.apiKey;
|
|
1127
|
+
if (!apiKey) {
|
|
1128
|
+
console.error('Usage: nova coding-plan add <platform> --key <api-key>');
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
// Register the coding-plan provider with models
|
|
1132
|
+
const providerName = `coding-plan-${platform}`;
|
|
1133
|
+
// Platform-specific model configurations
|
|
1134
|
+
const platformModels = {
|
|
1135
|
+
alibaba: {
|
|
1136
|
+
'qwen3.5-plus': { name: 'Qwen 3.5 Plus', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1137
|
+
'qwen3-coder': { name: 'Qwen 3 Coder', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1138
|
+
'glm-5': { name: 'GLM-5', maxContextTokens: 128000, supportsTools: true, supportsStreaming: true, supportsThinking: true },
|
|
1139
|
+
'minimax-m2.5': { name: 'MiniMax M2.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1140
|
+
'kimi-k2.5': { name: 'Kimi K2.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1141
|
+
},
|
|
1142
|
+
tencent: {
|
|
1143
|
+
'hy-2.0-instruct': { name: 'Hunyuan 2.0', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1144
|
+
'glm-5': { name: 'GLM-5', maxContextTokens: 128000, supportsTools: true, supportsStreaming: true, supportsThinking: true },
|
|
1145
|
+
'kimi-k2.5': { name: 'Kimi K2.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1146
|
+
'minimax-m2.5': { name: 'MiniMax M2.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1147
|
+
},
|
|
1148
|
+
volcengine: {
|
|
1149
|
+
'doubao-seed-code': { name: 'Doubao Seed Code', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1150
|
+
'deepseek-v3.2': { name: 'DeepSeek V3.2', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true, supportsThinking: true },
|
|
1151
|
+
'glm-4.7': { name: 'GLM-4.7', maxContextTokens: 128000, supportsTools: true, supportsStreaming: true },
|
|
1152
|
+
'kimi-k2': { name: 'Kimi K2', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1153
|
+
},
|
|
1154
|
+
baidu: {
|
|
1155
|
+
'glm-5': { name: 'GLM-5', maxContextTokens: 128000, supportsTools: true, supportsStreaming: true, supportsThinking: true },
|
|
1156
|
+
'minimax-m2.5': { name: 'MiniMax M2.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1157
|
+
'kimi-k2.5': { name: 'Kimi K2.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1158
|
+
'ernie-4.5': { name: 'ERNIE 4.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1159
|
+
},
|
|
1160
|
+
kimi: {
|
|
1161
|
+
'kimi-k2': { name: 'Kimi K2', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1162
|
+
'kimi-k2.5': { name: 'Kimi K2.5', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1163
|
+
},
|
|
1164
|
+
zhipu: {
|
|
1165
|
+
'glm-4.7': { name: 'GLM-4.7', maxContextTokens: 128000, supportsTools: true, supportsStreaming: true },
|
|
1166
|
+
'glm-5': { name: 'GLM-5', maxContextTokens: 128000, supportsTools: true, supportsStreaming: true, supportsThinking: true },
|
|
1167
|
+
},
|
|
1168
|
+
minimax: {
|
|
1169
|
+
'minimax-2.7': { name: 'MiniMax 2.7', maxContextTokens: 131072, supportsTools: true, supportsStreaming: true },
|
|
1170
|
+
'abab6.5s-chat': { name: 'abab6.5s Chat', maxContextTokens: 32768, supportsTools: true, supportsStreaming: true },
|
|
1171
|
+
},
|
|
1172
|
+
custom: {},
|
|
1173
|
+
};
|
|
1174
|
+
const defaultModels = {
|
|
1175
|
+
alibaba: 'qwen3-coder',
|
|
1176
|
+
tencent: 'glm-5',
|
|
1177
|
+
volcengine: 'doubao-seed-code',
|
|
1178
|
+
baidu: 'glm-5',
|
|
1179
|
+
kimi: 'kimi-k2.5',
|
|
1180
|
+
zhipu: 'glm-5',
|
|
1181
|
+
minimax: 'minimax-2.7',
|
|
1182
|
+
custom: 'glm-5',
|
|
1183
|
+
};
|
|
1184
|
+
this.configManager.registerProvider(providerName, {
|
|
1185
|
+
type: 'coding-plan',
|
|
1186
|
+
codingPlanPlatform: platform,
|
|
1187
|
+
models: platformModels[platform] || {},
|
|
1188
|
+
defaultModel: defaultModels[platform] || 'glm-5',
|
|
1189
|
+
});
|
|
1190
|
+
// Save configuration to disk
|
|
1191
|
+
await this.configManager.save(this.configManager.getConfig());
|
|
1192
|
+
// Save API key
|
|
1193
|
+
await this.authManager.setCredentials({ provider: providerName, apiKey });
|
|
1194
|
+
console.log(`\x1b[32m✓ Coding Plan provider "${platform}" added\x1b[0m`);
|
|
1195
|
+
console.log(` Provider name: ${providerName}`);
|
|
1196
|
+
console.log(` Default model: ${defaultModels[platform] || 'glm-5'}`);
|
|
1197
|
+
console.log(` Available models: ${Object.keys(platformModels[platform] || {}).join(', ') || '(custom)'}`);
|
|
1198
|
+
console.log(` Use: nova -m ${providerName}/${defaultModels[platform] || 'glm-5'}`);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
console.log('Usage: nova coding-plan [list|add]');
|
|
1202
|
+
console.log('');
|
|
1203
|
+
console.log(' nova coding-plan list List supported platforms');
|
|
1204
|
+
console.log(' nova coding-plan add <platform> --key <api-key> Add a Coding Plan provider');
|
|
1205
|
+
}
|
|
1206
|
+
// ==================== Ollama Command ====================
|
|
1207
|
+
async handleOllamaCommand(args) {
|
|
1208
|
+
const ollamaCreds = this.authManager.getCredentials('ollama');
|
|
1209
|
+
const baseUrl = args.ollamaHost || ollamaCreds?.baseUrl || 'http://localhost:11434';
|
|
1210
|
+
const manager = new OllamaManager(baseUrl);
|
|
1211
|
+
if (args.subcommand === 'status' || !args.subcommand) {
|
|
1212
|
+
const isRunning = await manager.ping();
|
|
1213
|
+
if (isRunning) {
|
|
1214
|
+
try {
|
|
1215
|
+
const version = await manager.version();
|
|
1216
|
+
console.log(`\x1b[32mOllama is running\x1b[0m (v${version})`);
|
|
1217
|
+
console.log(` Host: ${baseUrl}`);
|
|
1218
|
+
}
|
|
1219
|
+
catch {
|
|
1220
|
+
console.log(`\x1b[32mOllama is running\x1b[0m`);
|
|
1221
|
+
console.log(` Host: ${baseUrl}`);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
else {
|
|
1225
|
+
console.log(`\x1b[31mOllama is not running\x1b[0m`);
|
|
1226
|
+
console.log('');
|
|
1227
|
+
console.log(` Start Ollama:`);
|
|
1228
|
+
console.log(` ollama serve`);
|
|
1229
|
+
console.log('');
|
|
1230
|
+
console.log(` Install Ollama:`);
|
|
1231
|
+
console.log(` https://ollama.com`);
|
|
1232
|
+
console.log('');
|
|
1233
|
+
console.log(` Configure custom host:`);
|
|
1234
|
+
console.log(` nova auth set ollama --base-url http://your-host:11434`);
|
|
1235
|
+
}
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (args.subcommand === 'list') {
|
|
1239
|
+
if (!(await manager.ping())) {
|
|
1240
|
+
console.log(`\x1b[31mOllama is not running\x1b[0m at ${baseUrl}`);
|
|
1241
|
+
console.log('Start Ollama first: ollama serve');
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
const models = await manager.listModels();
|
|
1245
|
+
if (models.length === 0) {
|
|
1246
|
+
console.log('No models installed. Pull one with: nova ollama pull <model-name>');
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
console.log(`\x1b[1mInstalled Ollama Models:\x1b[0m\n`);
|
|
1250
|
+
for (const m of models) {
|
|
1251
|
+
const sizeGB = (m.size / 1024 / 1024 / 1024).toFixed(1);
|
|
1252
|
+
const family = m.details?.family || '';
|
|
1253
|
+
const params = m.details?.parameter_size || '';
|
|
1254
|
+
console.log(` \x1b[1m${m.name}\x1b[0m`);
|
|
1255
|
+
console.log(` ${family} ${params} ${sizeGB} GB`);
|
|
1256
|
+
}
|
|
1257
|
+
console.log(`\nTotal: ${models.length} model(s)`);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
if (args.subcommand === 'pull') {
|
|
1261
|
+
const modelName = args.ollamaModel;
|
|
1262
|
+
if (!modelName) {
|
|
1263
|
+
console.error('Usage: nova ollama pull <model-name>');
|
|
1264
|
+
console.error('Example: nova ollama pull llama3.1');
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
if (!(await manager.ping())) {
|
|
1268
|
+
console.log(`\x1b[31mOllama is not running\x1b[0m at ${baseUrl}`);
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
console.log(`Pulling ${modelName}...`);
|
|
1272
|
+
try {
|
|
1273
|
+
await manager.pullModel(modelName, (progress) => {
|
|
1274
|
+
if (progress.total && progress.completed) {
|
|
1275
|
+
const pct = ((progress.completed / progress.total) * 100).toFixed(0);
|
|
1276
|
+
process.stdout.write(`\r ${progress.status} ${pct}%`);
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
process.stdout.write(`\r ${progress.status}`);
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
console.log('\n\x1b[32mPull complete!\x1b[0m');
|
|
1283
|
+
console.log(` Use: nova -m ${modelName}`);
|
|
1284
|
+
}
|
|
1285
|
+
catch (err) {
|
|
1286
|
+
console.log(`\n\x1b[31mPull failed:\x1b[0m ${err.message}`);
|
|
1287
|
+
}
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
if (args.subcommand === 'rm') {
|
|
1291
|
+
const modelName = args.ollamaModel;
|
|
1292
|
+
if (!modelName) {
|
|
1293
|
+
console.error('Usage: nova ollama rm <model-name>');
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
try {
|
|
1297
|
+
await manager.deleteModel(modelName);
|
|
1298
|
+
console.log(`\x1b[32mModel "${modelName}" deleted\x1b[0m`);
|
|
1299
|
+
}
|
|
1300
|
+
catch (err) {
|
|
1301
|
+
console.log(`\x1b[31mDelete failed:\x1b[0m ${err.message}`);
|
|
1302
|
+
}
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
if (args.subcommand === 'info') {
|
|
1306
|
+
const modelName = args.ollamaModel;
|
|
1307
|
+
if (!modelName) {
|
|
1308
|
+
console.error('Usage: nova ollama info <model-name>');
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
try {
|
|
1312
|
+
const info = await manager.showModel(modelName);
|
|
1313
|
+
console.log(`\x1b[1m${modelName}\x1b[0m`);
|
|
1314
|
+
if (info.details) {
|
|
1315
|
+
console.log(` Family: ${info.details.family}`);
|
|
1316
|
+
console.log(` Parameter Size: ${info.details.parameter_size}`);
|
|
1317
|
+
console.log(` Quantization: ${info.details.quantization_level}`);
|
|
1318
|
+
console.log(` Format: ${info.details.format}`);
|
|
1319
|
+
}
|
|
1320
|
+
if (info.license)
|
|
1321
|
+
console.log(` License: ${info.license}`);
|
|
1322
|
+
}
|
|
1323
|
+
catch (err) {
|
|
1324
|
+
console.log(`\x1b[31mFailed to show model info:\x1b[0m ${err.message}`);
|
|
1325
|
+
}
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (args.subcommand === 'run') {
|
|
1329
|
+
const modelName = args.ollamaModel;
|
|
1330
|
+
if (!modelName) {
|
|
1331
|
+
console.error('Usage: nova ollama run <model-name>');
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
// Save ollama host if custom
|
|
1335
|
+
if (args.ollamaHost) {
|
|
1336
|
+
await this.authManager.setCredentials({
|
|
1337
|
+
provider: 'ollama',
|
|
1338
|
+
apiKey: 'ollama',
|
|
1339
|
+
baseUrl: args.ollamaHost,
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
// Re-run with this model selected
|
|
1343
|
+
process.argv = ['nova', '-m', modelName, ...process.argv.slice(process.argv.indexOf('run') + 2)];
|
|
1344
|
+
// Just switch to model selection
|
|
1345
|
+
const newArgs = parseCliArgs(['-m', modelName]);
|
|
1346
|
+
const config = this.configManager.getConfig();
|
|
1347
|
+
try {
|
|
1348
|
+
const modelClient = await this.createModelClient(config, modelName);
|
|
1349
|
+
const cwd = process.cwd();
|
|
1350
|
+
const repl = new InkBasedRepl({
|
|
1351
|
+
modelClient,
|
|
1352
|
+
sessionManager: this.sessionManager,
|
|
1353
|
+
toolRegistry: this.toolRegistry,
|
|
1354
|
+
approvalManager: this.approvalManager,
|
|
1355
|
+
authManager: this.authManager,
|
|
1356
|
+
config,
|
|
1357
|
+
configManager: this.configManager,
|
|
1358
|
+
cwd,
|
|
1359
|
+
contextCompressor: this.contextCompressor,
|
|
1360
|
+
mcpManager: this.mcpManager,
|
|
1361
|
+
skillRegistry: this.skillRegistry,
|
|
1362
|
+
});
|
|
1363
|
+
await repl.start();
|
|
1364
|
+
}
|
|
1365
|
+
catch (err) {
|
|
1366
|
+
console.error(`\x1b[31mFailed to start with model "${modelName}":\x1b[0m ${err.message}`);
|
|
1367
|
+
}
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if (args.subcommand === 'cloud') {
|
|
1371
|
+
await this.handleOllamaCloudList();
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
console.log('Usage: nova ollama [status|list|pull|rm|info|run|cloud]');
|
|
1375
|
+
console.log('');
|
|
1376
|
+
console.log(' nova ollama status Show Ollama server status');
|
|
1377
|
+
console.log(' nova ollama list List installed models');
|
|
1378
|
+
console.log(' nova ollama pull <model> Pull a model from Ollama Hub');
|
|
1379
|
+
console.log(' nova ollama rm <model> Delete a local model');
|
|
1380
|
+
console.log(' nova ollama info <model> Show model details');
|
|
1381
|
+
console.log(' nova ollama run <model> Start REPL with model');
|
|
1382
|
+
console.log(' nova ollama cloud List Ollama Cloud models');
|
|
1383
|
+
}
|
|
1384
|
+
/** List available Ollama Cloud models */
|
|
1385
|
+
async handleOllamaCloudList() {
|
|
1386
|
+
const creds = this.authManager.getCredentials('ollama-cloud');
|
|
1387
|
+
const apiKey = creds?.apiKey || process.env.OLLAMA_API_KEY;
|
|
1388
|
+
if (!apiKey) {
|
|
1389
|
+
console.log('\x1b[33mNo Ollama Cloud API key configured.\x1b[0m');
|
|
1390
|
+
console.log('');
|
|
1391
|
+
console.log('Set your API key:');
|
|
1392
|
+
console.log(' nova auth set ollama-cloud --key <your-api-key>');
|
|
1393
|
+
console.log(' or export OLLAMA_API_KEY=<your-api-key>');
|
|
1394
|
+
console.log('');
|
|
1395
|
+
console.log('Get an API key at: https://ollama.com/settings/keys');
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
try {
|
|
1399
|
+
const baseUrl = creds?.baseUrl || 'https://ollama.com';
|
|
1400
|
+
const res = await fetch(`${baseUrl}/api/tags`, {
|
|
1401
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
1402
|
+
signal: AbortSignal.timeout(15000),
|
|
1403
|
+
});
|
|
1404
|
+
if (!res.ok) {
|
|
1405
|
+
console.log(`\x1b[31mFailed to fetch cloud models: ${res.status} ${res.statusText}\x1b[0m`);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
const data = await res.json();
|
|
1409
|
+
const models = data.models || [];
|
|
1410
|
+
if (models.length === 0) {
|
|
1411
|
+
console.log('No models available on Ollama Cloud.');
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
console.log(`\x1b[1mOllama Cloud Models (${models.length}):\x1b[0m\n`);
|
|
1415
|
+
for (const m of models) {
|
|
1416
|
+
const sizeGB = m.size > 0 ? (m.size / 1024 / 1024 / 1024).toFixed(1) + ' GB' : '';
|
|
1417
|
+
const params = m.details?.parameter_size || '';
|
|
1418
|
+
console.log(` \x1b[1m${m.name}\x1b[0m`);
|
|
1419
|
+
if (params || sizeGB) {
|
|
1420
|
+
console.log(` ${params}${params && sizeGB ? ' ' : ''}${sizeGB}`);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
console.log(`\nUse: nova -m deepseek-v3.2 "your prompt"`);
|
|
1424
|
+
console.log('Set API key: nova auth set ollama-cloud');
|
|
1425
|
+
}
|
|
1426
|
+
catch (err) {
|
|
1427
|
+
console.log(`\x1b[31mFailed to connect to Ollama Cloud:\x1b[0m ${err.message}`);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
// ==================== MCP Command ====================
|
|
1431
|
+
async handleMcpCommand(args) {
|
|
1432
|
+
// Initialize MCP only (no model needed for status command)
|
|
1433
|
+
if (!this.mcpManager) {
|
|
1434
|
+
this.mcpManager = new McpManager();
|
|
1435
|
+
const config = this.configManager.getConfig();
|
|
1436
|
+
if (config.mcp) {
|
|
1437
|
+
for (const [name, serverConfig] of Object.entries(config.mcp)) {
|
|
1438
|
+
if (serverConfig.enabled !== false) {
|
|
1439
|
+
try {
|
|
1440
|
+
await this.mcpManager.connect({ name, ...serverConfig });
|
|
1441
|
+
}
|
|
1442
|
+
catch {
|
|
1443
|
+
// Ignore connection errors for status display
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
if (args.subcommand === 'status' || !args.subcommand) {
|
|
1450
|
+
const statuses = this.mcpManager.listServers();
|
|
1451
|
+
if (statuses.length === 0) {
|
|
1452
|
+
console.log('No MCP servers configured.');
|
|
1453
|
+
console.log('Add servers to ~/.nova/config.yaml under "mcp:".');
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
console.log('\x1b[1mMCP Server Status:\x1b[0m\n');
|
|
1457
|
+
for (const s of statuses) {
|
|
1458
|
+
const icon = s.connected ? '\x1b[32m●\x1b[0m' : '\x1b[31m●\x1b[0m';
|
|
1459
|
+
const status = s.connected ? '\x1b[32mconnected\x1b[0m' : '\x1b[31mdisconnected\x1b[0m';
|
|
1460
|
+
console.log(` ${icon} ${s.name.padEnd(20)} ${status}`);
|
|
1461
|
+
if (s.connected) {
|
|
1462
|
+
console.log(` Tools: ${s.toolCount} Resources: ${s.resourceCount}`);
|
|
1463
|
+
}
|
|
1464
|
+
if (s.lastError) {
|
|
1465
|
+
console.log(` Error: \x1b[31m${s.lastError}\x1b[0m`);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
if (args.subcommand === 'list') {
|
|
1471
|
+
// Alias for status
|
|
1472
|
+
await this.handleMcpCommand({ ...args, subcommand: 'status' });
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
console.log('Usage: nova mcp [status|list]');
|
|
1476
|
+
console.log('');
|
|
1477
|
+
console.log(' nova mcp status Show all MCP server connections');
|
|
1478
|
+
console.log(' nova mcp list Alias for status');
|
|
1479
|
+
console.log('');
|
|
1480
|
+
console.log('Configure MCP servers in ~/.nova/config.yaml:');
|
|
1481
|
+
console.log('');
|
|
1482
|
+
console.log(' mcp:');
|
|
1483
|
+
console.log(' filesystem:');
|
|
1484
|
+
console.log(' command: npx');
|
|
1485
|
+
console.log(' args: [-y, "@modelcontextprotocol/server-filesystem", /path]');
|
|
1486
|
+
console.log(' github:');
|
|
1487
|
+
console.log(' command: npx');
|
|
1488
|
+
console.log(' args: [-y, "@modelcontextprotocol/server-github"]');
|
|
1489
|
+
console.log(' env:');
|
|
1490
|
+
console.log(' GITHUB_TOKEN: your-token');
|
|
1491
|
+
console.log('');
|
|
1492
|
+
console.log('Popular MCP servers (install via npm):');
|
|
1493
|
+
console.log(' @modelcontextprotocol/server-filesystem — file system');
|
|
1494
|
+
console.log(' @modelcontextprotocol/server-github — GitHub API');
|
|
1495
|
+
console.log(' @modelcontextprotocol/server-brave-search — web search');
|
|
1496
|
+
console.log(' @modelcontextprotocol/server-sqlite — SQLite');
|
|
1497
|
+
console.log(' @modelcontextprotocol/server-postgres — PostgreSQL');
|
|
1498
|
+
console.log(' @modelscope/mcp-server — ModelScope (魔搭)');
|
|
1499
|
+
}
|
|
1500
|
+
// ==================== Skills Command ====================
|
|
1501
|
+
async handleSkillsCommand(args) {
|
|
1502
|
+
if (!this.skillRegistry) {
|
|
1503
|
+
// Initialize just the skill registry
|
|
1504
|
+
const skillsDir = path.join(os.homedir(), '.nova', 'skills');
|
|
1505
|
+
this.skillRegistry = new SkillRegistry(skillsDir);
|
|
1506
|
+
await this.skillRegistry.initialize();
|
|
1507
|
+
}
|
|
1508
|
+
if (args.subcommand === 'list' || !args.subcommand) {
|
|
1509
|
+
const skills = await this.skillRegistry.list();
|
|
1510
|
+
if (skills.length === 0) {
|
|
1511
|
+
console.log('No skills found.');
|
|
1512
|
+
console.log('');
|
|
1513
|
+
console.log('Skills directory: ~/.nova/skills/');
|
|
1514
|
+
console.log('Each skill is a directory containing a SKILL.md file.');
|
|
1515
|
+
console.log('');
|
|
1516
|
+
console.log('Example skill structure:');
|
|
1517
|
+
console.log(' ~/.nova/skills/');
|
|
1518
|
+
console.log(' my-skill/');
|
|
1519
|
+
console.log(' SKILL.md (skill instructions & metadata)');
|
|
1520
|
+
console.log(' scripts/ (optional scripts)');
|
|
1521
|
+
console.log('');
|
|
1522
|
+
console.log('To install skills from superpowers repository:');
|
|
1523
|
+
console.log(' nova skills install obra/superpowers');
|
|
1524
|
+
console.log(' nova skills install superpowers');
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
console.log('\x1b[1mAvailable Skills:\x1b[0m\n');
|
|
1528
|
+
for (const skill of skills) {
|
|
1529
|
+
const m = skill.metadata;
|
|
1530
|
+
const auto = m.autoGenerated ? ' \x1b[90m[auto]\x1b[0m' : '';
|
|
1531
|
+
const tags = m.tags.length > 0 ? ` \x1b[90m(${m.tags.join(', ')})\x1b[0m` : '';
|
|
1532
|
+
console.log(` \x1b[36m${m.name.padEnd(22)}\x1b[0m ${m.description}${auto}${tags}`);
|
|
1533
|
+
}
|
|
1534
|
+
console.log(`\nTotal: ${skills.length} skill${skills.length !== 1 ? 's' : ''}`);
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
if (args.subcommand === 'install') {
|
|
1538
|
+
const source = args.provider || args.source || 'gitee:anderson2/superpowers';
|
|
1539
|
+
const force = args.force === true;
|
|
1540
|
+
console.log(`Installing skills from ${source}...`);
|
|
1541
|
+
try {
|
|
1542
|
+
const installer = new SkillInstaller();
|
|
1543
|
+
const installed = await installer.install({
|
|
1544
|
+
source,
|
|
1545
|
+
force,
|
|
1546
|
+
});
|
|
1547
|
+
if (installed.length === 0) {
|
|
1548
|
+
console.log('\x1b[33mNo skills installed (already exist). Use --force to overwrite.\x1b[0m');
|
|
1549
|
+
}
|
|
1550
|
+
else {
|
|
1551
|
+
console.log(`\x1b[32mSuccessfully installed ${installed.length} skill${installed.length !== 1 ? 's' : ''}:\x1b[0m`);
|
|
1552
|
+
for (const skill of installed) {
|
|
1553
|
+
console.log(` \x1b[36m✓ ${skill.name}\x1b[0m`);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
catch (error) {
|
|
1558
|
+
console.error(`\x1b[31mFailed to install skills:\x1b[0m ${error.message}`);
|
|
1559
|
+
console.error('');
|
|
1560
|
+
console.error('Supported formats:');
|
|
1561
|
+
console.error(' nova skills install obra/superpowers');
|
|
1562
|
+
console.error(' nova skills install https://github.com/owner/repo');
|
|
1563
|
+
console.error(' nova skills install owner/repo');
|
|
1564
|
+
console.error('');
|
|
1565
|
+
console.error('Options:');
|
|
1566
|
+
console.error(' --force Overwrite existing skills');
|
|
1567
|
+
console.error(' --source GitHub repository (default: obra/superpowers)');
|
|
1568
|
+
}
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
console.log('Usage: nova skills [list|install]');
|
|
1572
|
+
console.log('');
|
|
1573
|
+
console.log(' nova skills list List installed skills');
|
|
1574
|
+
console.log(' nova skills install [source] Install skills from GitHub/Gitee');
|
|
1575
|
+
console.log('');
|
|
1576
|
+
console.log('Examples:');
|
|
1577
|
+
console.log(' nova skills install Install from gitee:anderson2/superpowers');
|
|
1578
|
+
console.log(' nova skills install anderson2/superpowers');
|
|
1579
|
+
console.log(' nova skills install gitee:anderson2/superpowers');
|
|
1580
|
+
console.log(' nova skills install https://gitee.com/anderson2/superpowers.git');
|
|
1581
|
+
console.log(' nova skills install --force Overwrite existing skills');
|
|
1582
|
+
console.log('');
|
|
1583
|
+
console.log('Supported formats:');
|
|
1584
|
+
console.log(' owner/repo - GitHub shorthand');
|
|
1585
|
+
console.log(' gitee:owner/repo - Gitee shorthand');
|
|
1586
|
+
console.log(' https://... - Full Git URL');
|
|
1587
|
+
console.log('');
|
|
1588
|
+
console.log('Popular repositories:');
|
|
1589
|
+
console.log(' gitee:anderson2/superpowers - Agentic skills framework (default)');
|
|
1590
|
+
console.log(' obra/superpowers - GitHub mirror');
|
|
1591
|
+
}
|
|
1592
|
+
printHelp() {
|
|
1593
|
+
const B = {
|
|
1594
|
+
brand: '\x1b[38;5;93m',
|
|
1595
|
+
brandLight: '\x1b[38;5;141m',
|
|
1596
|
+
primary: '\x1b[1m',
|
|
1597
|
+
muted: '\x1b[90m',
|
|
1598
|
+
info: '\x1b[36m',
|
|
1599
|
+
success: '\x1b[32m',
|
|
1600
|
+
warning: '\x1b[33m',
|
|
1601
|
+
reset: '\x1b[0m',
|
|
1602
|
+
};
|
|
1603
|
+
const BOX = {
|
|
1604
|
+
tl: '╭', tr: '╮', bl: '╰', br: '╯',
|
|
1605
|
+
h: '─', v: '│', ht: '├', htr: '┤',
|
|
1606
|
+
hThick: '━', diamond: '◆', arrow: '→',
|
|
1607
|
+
};
|
|
1608
|
+
const termCols = process.stdout.columns || 80;
|
|
1609
|
+
const w = Math.min(termCols - 4, 76);
|
|
1610
|
+
const hr = B.muted + BOX.h.repeat(w) + B.reset;
|
|
1611
|
+
const hrThick = B.brand + BOX.hThick.repeat(w) + B.reset;
|
|
1612
|
+
const cmd = (name, desc) => ` ${B.info}${name.padEnd(20)}${B.reset} ${B.muted}${desc}${B.reset}`;
|
|
1613
|
+
console.log('');
|
|
1614
|
+
console.log(B.brand + BOX.tl + hrThick + BOX.tr + B.reset);
|
|
1615
|
+
// Compact header
|
|
1616
|
+
const header = ' ' + B.brand + 'NOVA' + B.reset + B.brandLight + ' CLI' + B.reset + ' · AI-powered terminal assistant';
|
|
1617
|
+
const headerPad = ' '.repeat(Math.max(0, w - 45));
|
|
1618
|
+
console.log(B.brand + BOX.v + B.reset + header + headerPad + B.brand + BOX.v + B.reset);
|
|
1619
|
+
console.log(B.brand + BOX.bl + hrThick + BOX.br + B.reset);
|
|
1620
|
+
console.log('');
|
|
1621
|
+
// Commands section
|
|
1622
|
+
console.log(B.brand + BOX.diamond + B.reset + ' ' + B.primary + 'COMMANDS' + B.reset);
|
|
1623
|
+
console.log('');
|
|
1624
|
+
console.log(cmd('(default)', 'Start interactive REPL session'));
|
|
1625
|
+
console.log(cmd('-p, --prompt', 'Run a single prompt and exit'));
|
|
1626
|
+
console.log(cmd('-c, --continue', 'Continue the most recent session'));
|
|
1627
|
+
console.log(cmd('-r, --resume', 'Interactively pick a session to resume'));
|
|
1628
|
+
console.log(cmd('model list', 'List all available models'));
|
|
1629
|
+
console.log(cmd('config show/edit', 'Show or edit configuration'));
|
|
1630
|
+
console.log(cmd('auth set <provider>', 'Configure API key'));
|
|
1631
|
+
console.log(cmd('coding-plan list', 'List Coding Plan platforms'));
|
|
1632
|
+
console.log(cmd('coding-plan add', 'Add Coding Plan provider'));
|
|
1633
|
+
console.log(cmd('mcp status', 'Show MCP server connections'));
|
|
1634
|
+
console.log(cmd('skills list', 'List installed skills'));
|
|
1635
|
+
console.log(cmd('skills install', 'Install skills from GitHub/Gitee'));
|
|
1636
|
+
console.log(cmd('version', 'Show version'));
|
|
1637
|
+
console.log('');
|
|
1638
|
+
// Options section
|
|
1639
|
+
console.log(B.brand + BOX.diamond + B.reset + ' ' + B.primary + 'OPTIONS' + B.reset);
|
|
1640
|
+
console.log('');
|
|
1641
|
+
console.log(cmd('-m, --model', 'Model to use'));
|
|
1642
|
+
console.log(cmd('-d, --directory', 'Working directory'));
|
|
1643
|
+
console.log(cmd('--approval-mode', 'yolo | plan | ask | smart'));
|
|
1644
|
+
console.log(cmd('--max-turns', 'Maximum conversation turns'));
|
|
1645
|
+
console.log(cmd('--no-stream', 'Disable streaming output'));
|
|
1646
|
+
console.log(cmd('--no-mcp', 'Disable MCP server connections'));
|
|
1647
|
+
console.log(cmd('--thinking <mode>', 'Control thinking mode: enabled|disabled|auto'));
|
|
1648
|
+
console.log('');
|
|
1649
|
+
// Providers
|
|
1650
|
+
console.log(B.brand + BOX.diamond + B.reset + ' ' + B.primary + 'PROVIDERS' + B.reset);
|
|
1651
|
+
console.log('');
|
|
1652
|
+
console.log(B.muted + ' anthropic, openai, google, deepseek, qwen, glm, moonshot,' + B.reset);
|
|
1653
|
+
console.log(B.muted + ' baichuan, yi, siliconflow, groq, mistral, together, perplexity,' + B.reset);
|
|
1654
|
+
console.log(B.muted + ' ollama (local), ollama-cloud, coding-plan (国内平台), <custom>' + B.reset);
|
|
1655
|
+
console.log('');
|
|
1656
|
+
// Coding Plan
|
|
1657
|
+
console.log(B.brand + BOX.diamond + B.reset + ' ' + B.primary + 'CODING PLAN' + B.reset);
|
|
1658
|
+
console.log('');
|
|
1659
|
+
console.log(B.muted + ' alibaba (阿里云), tencent (腾讯云), volcengine (火山引擎),' + B.reset);
|
|
1660
|
+
console.log(B.muted + ' baidu (百度千帆), kimi, zhipu (智谱), minimax' + B.reset);
|
|
1661
|
+
console.log('');
|
|
1662
|
+
// Examples
|
|
1663
|
+
console.log(B.brand + BOX.diamond + B.reset + ' ' + B.primary + 'EXAMPLES' + B.reset);
|
|
1664
|
+
console.log('');
|
|
1665
|
+
console.log(B.muted + ' nova # Start interactive session' + B.reset);
|
|
1666
|
+
console.log(B.muted + ' nova -c # Continue last session' + B.reset);
|
|
1667
|
+
console.log(B.muted + ' nova -p "Explain this code" # Single prompt' + B.reset);
|
|
1668
|
+
console.log(B.muted + ' nova -m gpt-4o # Use GPT-4o' + B.reset);
|
|
1669
|
+
console.log(B.muted + ' nova coding-plan add alibaba --key <key> # Add Coding Plan' + B.reset);
|
|
1670
|
+
console.log('');
|
|
1671
|
+
}
|
|
1672
|
+
/** Check if this is a first-time setup (no configured providers) */
|
|
1673
|
+
async isFirstTimeSetup(config) {
|
|
1674
|
+
// Check if any provider has credentials
|
|
1675
|
+
const providerNames = Object.keys(config.models.providers);
|
|
1676
|
+
for (const providerName of providerNames) {
|
|
1677
|
+
if (this.authManager.hasCredentials(providerName)) {
|
|
1678
|
+
return false; // Has at least one configured provider
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
// Also check if Ollama is running locally
|
|
1682
|
+
const ollamaCreds = this.authManager.getCredentials('ollama');
|
|
1683
|
+
const baseUrl = ollamaCreds?.baseUrl || process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
1684
|
+
const manager = new OllamaManager(baseUrl);
|
|
1685
|
+
if (await manager.ping()) {
|
|
1686
|
+
return false; // Ollama is available
|
|
1687
|
+
}
|
|
1688
|
+
return true; // No providers configured and Ollama not running
|
|
1689
|
+
}
|
|
1690
|
+
/** First-time setup wizard */
|
|
1691
|
+
async runFirstTimeSetupWizard(config) {
|
|
1692
|
+
const readline = await import('node:readline');
|
|
1693
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1694
|
+
const askQuestion = (prompt) => {
|
|
1695
|
+
return new Promise((resolve) => {
|
|
1696
|
+
rl.question(prompt, (answer) => resolve(answer.trim()));
|
|
1697
|
+
});
|
|
1698
|
+
};
|
|
1699
|
+
const B = {
|
|
1700
|
+
brand: '\x1b[38;5;93m',
|
|
1701
|
+
brandLight: '\x1b[38;5;141m',
|
|
1702
|
+
primary: '\x1b[1m',
|
|
1703
|
+
muted: '\x1b[90m',
|
|
1704
|
+
info: '\x1b[36m',
|
|
1705
|
+
success: '\x1b[32m',
|
|
1706
|
+
warning: '\x1b[33m',
|
|
1707
|
+
reset: '\x1b[0m',
|
|
1708
|
+
};
|
|
1709
|
+
// Welcome banner
|
|
1710
|
+
console.log('');
|
|
1711
|
+
console.log(`${B.brand}╔══════════════════════════════════════════════════════════════╗${B.reset}`);
|
|
1712
|
+
console.log(`${B.brand}║${B.reset} ${B.brand}║${B.reset}`);
|
|
1713
|
+
console.log(`${B.brand}║${B.reset} ${B.primary}Welcome to NOVA CLI!${B.reset} ${B.brand}║${B.reset}`);
|
|
1714
|
+
console.log(`${B.brand}║${B.reset} ${B.muted}Your AI-powered terminal assistant${B.reset} ${B.brand}║${B.reset}`);
|
|
1715
|
+
console.log(`${B.brand}║${B.reset} ${B.brand}║${B.reset}`);
|
|
1716
|
+
console.log(`${B.brand}╚══════════════════════════════════════════════════════════════╝${B.reset}`);
|
|
1717
|
+
console.log('');
|
|
1718
|
+
console.log(`${B.muted}It looks like this is your first time using Nova CLI.${B.reset}`);
|
|
1719
|
+
console.log(`${B.muted}Let's set up your AI model provider:${B.reset}`);
|
|
1720
|
+
console.log('');
|
|
1721
|
+
// Show available options
|
|
1722
|
+
console.log(`${B.primary}Choose your AI provider:${B.reset}`);
|
|
1723
|
+
console.log('');
|
|
1724
|
+
console.log(` ${B.info}1.${B.reset} ${B.primary}Ollama (Local)${B.reset} - ${B.muted}Run models locally, free, no API key needed${B.reset}`);
|
|
1725
|
+
console.log(` ${B.info}2.${B.reset} ${B.primary}Ollama Cloud${B.reset} - ${B.muted}Cloud-hosted models, requires API key${B.reset}`);
|
|
1726
|
+
console.log(` ${B.info}3.${B.reset} ${B.primary}Coding Plan${B.reset} - ${B.muted}国内平台 (阿里云/腾讯云/智谱等), 固定月费${B.reset}`);
|
|
1727
|
+
console.log(` ${B.info}4.${B.reset} ${B.primary}Custom Provider${B.reset} - ${B.muted}Enter your own API endpoint${B.reset}`);
|
|
1728
|
+
console.log(` ${B.info}5.${B.reset} ${B.muted}Skip setup${B.reset} - ${B.muted}Configure later with nova auth set${B.reset}`);
|
|
1729
|
+
console.log('');
|
|
1730
|
+
try {
|
|
1731
|
+
const choice = await askQuestion(` Select [1-5]: `);
|
|
1732
|
+
switch (choice) {
|
|
1733
|
+
case '1': {
|
|
1734
|
+
// Ollama Local
|
|
1735
|
+
console.log('');
|
|
1736
|
+
console.log(`${B.muted}Checking for Ollama...${B.reset}`);
|
|
1737
|
+
const manager = new OllamaManager('http://localhost:11434');
|
|
1738
|
+
if (await manager.ping()) {
|
|
1739
|
+
console.log(`${B.success}✓ Ollama is running!${B.reset}`);
|
|
1740
|
+
const models = await manager.listModels();
|
|
1741
|
+
if (models.length > 0) {
|
|
1742
|
+
console.log(`${B.muted}Available models:${B.reset}`);
|
|
1743
|
+
for (const m of models.slice(0, 5)) {
|
|
1744
|
+
console.log(` - ${m.name}`);
|
|
1745
|
+
}
|
|
1746
|
+
const defaultModel = models[0].name;
|
|
1747
|
+
config.core.defaultModel = defaultModel;
|
|
1748
|
+
await this.configManager.save(config);
|
|
1749
|
+
console.log('');
|
|
1750
|
+
console.log(`${B.success}✓ Setup complete!${B.reset}`);
|
|
1751
|
+
console.log(`${B.muted}Default model: ${defaultModel}${B.reset}`);
|
|
1752
|
+
console.log(`${B.muted}Run 'nova' to start chatting!${B.reset}`);
|
|
1753
|
+
rl.close();
|
|
1754
|
+
return true;
|
|
1755
|
+
}
|
|
1756
|
+
else {
|
|
1757
|
+
console.log(`${B.warning}No models installed.${B.reset}`);
|
|
1758
|
+
console.log(`${B.muted}Pull a model with: ollama pull llama3.2${B.reset}`);
|
|
1759
|
+
console.log(`${B.muted}Then run: nova${B.reset}`);
|
|
1760
|
+
rl.close();
|
|
1761
|
+
return false;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
else {
|
|
1765
|
+
console.log(`${B.warning}Ollama is not running.${B.reset}`);
|
|
1766
|
+
console.log('');
|
|
1767
|
+
console.log(`${B.muted}To use local models:${B.reset}`);
|
|
1768
|
+
console.log(` 1. Install Ollama: ${B.info}https://ollama.com${B.reset}`);
|
|
1769
|
+
console.log(` 2. Start Ollama: ${B.primary}ollama serve${B.reset}`);
|
|
1770
|
+
console.log(` 3. Pull a model: ${B.primary}ollama pull llama3.2${B.reset}`);
|
|
1771
|
+
console.log(` 4. Run Nova: ${B.primary}nova${B.reset}`);
|
|
1772
|
+
console.log('');
|
|
1773
|
+
console.log(`${B.muted}Or choose option 2 to use Ollama Cloud instead.${B.reset}`);
|
|
1774
|
+
rl.close();
|
|
1775
|
+
return false;
|
|
1776
|
+
}
|
|
1777
|
+
break;
|
|
1778
|
+
}
|
|
1779
|
+
case '2': {
|
|
1780
|
+
// Ollama Cloud
|
|
1781
|
+
console.log('');
|
|
1782
|
+
console.log(`${B.primary}Configure Ollama Cloud${B.reset}`);
|
|
1783
|
+
console.log(`${B.muted}Get your API key at: ${B.info}https://ollama.com/settings/keys${B.reset}`);
|
|
1784
|
+
console.log('');
|
|
1785
|
+
const apiKey = await askQuestion(` Enter API key: `);
|
|
1786
|
+
if (!apiKey) {
|
|
1787
|
+
console.log(`${B.warning}API key is required.${B.reset}`);
|
|
1788
|
+
rl.close();
|
|
1789
|
+
return false;
|
|
1790
|
+
}
|
|
1791
|
+
// Save credentials
|
|
1792
|
+
await this.authManager.setCredentials({
|
|
1793
|
+
provider: 'ollama-cloud',
|
|
1794
|
+
apiKey,
|
|
1795
|
+
baseUrl: 'https://ollama.com',
|
|
1796
|
+
});
|
|
1797
|
+
// Register provider if not exists
|
|
1798
|
+
if (!config.models.providers['ollama-cloud']) {
|
|
1799
|
+
this.configManager.registerProvider('ollama-cloud', {
|
|
1800
|
+
type: 'ollama-cloud',
|
|
1801
|
+
baseUrl: 'https://ollama.com',
|
|
1802
|
+
models: {
|
|
1803
|
+
'glm-5': {
|
|
1804
|
+
name: 'GLM-5',
|
|
1805
|
+
maxContextTokens: 128000,
|
|
1806
|
+
maxOutputTokens: 8192,
|
|
1807
|
+
supportsTools: true,
|
|
1808
|
+
supportsStreaming: true,
|
|
1809
|
+
supportsThinking: true,
|
|
1810
|
+
supportsVision: true,
|
|
1811
|
+
},
|
|
1812
|
+
'deepseek-v3.2': {
|
|
1813
|
+
name: 'DeepSeek V3.2',
|
|
1814
|
+
maxContextTokens: 131072,
|
|
1815
|
+
maxOutputTokens: 8192,
|
|
1816
|
+
supportsTools: true,
|
|
1817
|
+
supportsStreaming: true,
|
|
1818
|
+
supportsThinking: true,
|
|
1819
|
+
supportsVision: true,
|
|
1820
|
+
},
|
|
1821
|
+
'qwen3-coder': {
|
|
1822
|
+
name: 'Qwen 3 Coder',
|
|
1823
|
+
maxContextTokens: 131072,
|
|
1824
|
+
maxOutputTokens: 8192,
|
|
1825
|
+
supportsTools: true,
|
|
1826
|
+
supportsStreaming: true,
|
|
1827
|
+
supportsVision: false,
|
|
1828
|
+
supportsThinking: true,
|
|
1829
|
+
},
|
|
1830
|
+
},
|
|
1831
|
+
defaultModel: 'glm-5',
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
config.core.defaultModel = 'ollama-cloud/glm-5';
|
|
1835
|
+
await this.configManager.save(config);
|
|
1836
|
+
console.log('');
|
|
1837
|
+
console.log(`${B.success}✓ Setup complete!${B.reset}`);
|
|
1838
|
+
console.log(`${B.muted}Default model: glm-5${B.reset}`);
|
|
1839
|
+
console.log(`${B.muted}Run 'nova' to start chatting!${B.reset}`);
|
|
1840
|
+
rl.close();
|
|
1841
|
+
return true;
|
|
1842
|
+
}
|
|
1843
|
+
case '3': {
|
|
1844
|
+
// Coding Plan (国内平台)
|
|
1845
|
+
console.log('');
|
|
1846
|
+
await this.handleCodingPlanCommand({ ...{}, command: 'coding-plan', subcommand: 'list' });
|
|
1847
|
+
console.log('');
|
|
1848
|
+
const platform = await askQuestion(` Enter platform name (e.g., alibaba, tencent, zhipu): `);
|
|
1849
|
+
if (!platform) {
|
|
1850
|
+
console.log(`${B.warning}Platform name is required.${B.reset}`);
|
|
1851
|
+
rl.close();
|
|
1852
|
+
return false;
|
|
1853
|
+
}
|
|
1854
|
+
const validPlatforms = ['alibaba', 'tencent', 'volcengine', 'baidu', 'kimi', 'zhipu', 'minimax'];
|
|
1855
|
+
if (!validPlatforms.includes(platform)) {
|
|
1856
|
+
console.log(`${B.warning}Invalid platform. Valid: ${validPlatforms.join(', ')}${B.reset}`);
|
|
1857
|
+
rl.close();
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1860
|
+
const apiKey = await askQuestion(` Enter API key: `);
|
|
1861
|
+
if (!apiKey) {
|
|
1862
|
+
console.log(`${B.warning}API key is required.${B.reset}`);
|
|
1863
|
+
rl.close();
|
|
1864
|
+
return false;
|
|
1865
|
+
}
|
|
1866
|
+
// Use existing coding-plan add logic
|
|
1867
|
+
await this.handleCodingPlanCommand({
|
|
1868
|
+
...{},
|
|
1869
|
+
command: 'coding-plan',
|
|
1870
|
+
subcommand: 'add',
|
|
1871
|
+
provider: platform,
|
|
1872
|
+
apiKey,
|
|
1873
|
+
});
|
|
1874
|
+
rl.close();
|
|
1875
|
+
return true;
|
|
1876
|
+
}
|
|
1877
|
+
case '4': {
|
|
1878
|
+
// Custom Provider
|
|
1879
|
+
console.log('');
|
|
1880
|
+
console.log(`${B.primary}Configure Custom Provider${B.reset}`);
|
|
1881
|
+
console.log('');
|
|
1882
|
+
const baseUrl = await askQuestion(` Base URL (e.g., https://api.example.com/v1): `);
|
|
1883
|
+
if (!baseUrl) {
|
|
1884
|
+
console.log(`${B.warning}Base URL is required.${B.reset}`);
|
|
1885
|
+
rl.close();
|
|
1886
|
+
return false;
|
|
1887
|
+
}
|
|
1888
|
+
const apiKey = await askQuestion(` API key (press Enter if not required): `);
|
|
1889
|
+
const modelName = await askQuestion(` Default model name (press Enter for 'default'): `);
|
|
1890
|
+
// Use existing provider add logic
|
|
1891
|
+
await this.handleProviderCommand({
|
|
1892
|
+
...{},
|
|
1893
|
+
command: 'provider',
|
|
1894
|
+
subcommand: 'add',
|
|
1895
|
+
baseUrl,
|
|
1896
|
+
apiKey: apiKey || 'no-key-required',
|
|
1897
|
+
defaultModel: modelName || 'default',
|
|
1898
|
+
});
|
|
1899
|
+
rl.close();
|
|
1900
|
+
return true;
|
|
1901
|
+
}
|
|
1902
|
+
case '5': {
|
|
1903
|
+
// Skip
|
|
1904
|
+
console.log('');
|
|
1905
|
+
console.log(`${B.muted}Setup skipped.${B.reset}`);
|
|
1906
|
+
console.log('');
|
|
1907
|
+
console.log(`${B.muted}To configure later:${B.reset}`);
|
|
1908
|
+
console.log(` ${B.info}nova auth set <provider> --key <api-key>${B.reset}`);
|
|
1909
|
+
console.log(` ${B.info}nova model list${B.reset} ${B.muted}- see available models${B.reset}`);
|
|
1910
|
+
console.log(` ${B.info}nova --help${B.reset} ${B.muted}- show all commands${B.reset}`);
|
|
1911
|
+
rl.close();
|
|
1912
|
+
return false;
|
|
1913
|
+
}
|
|
1914
|
+
default: {
|
|
1915
|
+
console.log(`${B.warning}Invalid choice. Run 'nova' again to restart setup.${B.reset}`);
|
|
1916
|
+
rl.close();
|
|
1917
|
+
return false;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
catch (err) {
|
|
1922
|
+
rl.close();
|
|
1923
|
+
return false;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
/** Interactive picker: list recent sessions for -r / --resume */
|
|
1927
|
+
async pickSessionInteractive() {
|
|
1928
|
+
const sessions = this.sessionManager.listPersistedSessions(20);
|
|
1929
|
+
if (sessions.length === 0) {
|
|
1930
|
+
console.log('\x1b[33m No previous sessions found.\x1b[0m');
|
|
1931
|
+
return undefined;
|
|
1932
|
+
}
|
|
1933
|
+
console.log('\n\x1b[1m Recent Sessions\x1b[0m\n');
|
|
1934
|
+
sessions.forEach((s, idx) => {
|
|
1935
|
+
const date = new Date(s.updatedAt).toLocaleString();
|
|
1936
|
+
const id = s.id.slice(0, 8);
|
|
1937
|
+
const turns = s.turnCount;
|
|
1938
|
+
const tokens = (s.totalInputTokens + s.totalOutputTokens).toLocaleString();
|
|
1939
|
+
const title = (s.title || 'New session').slice(0, 60);
|
|
1940
|
+
const model = s.config.model?.split('/').pop() || '';
|
|
1941
|
+
console.log(` \x1b[36m${String(idx + 1).padStart(2)}.\x1b[0m ${title}\n` +
|
|
1942
|
+
` \x1b[90m${id} ${date} ${turns} turns ${tokens} tok ${model}\x1b[0m`);
|
|
1943
|
+
});
|
|
1944
|
+
console.log('');
|
|
1945
|
+
const readline = await import('node:readline');
|
|
1946
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1947
|
+
return new Promise((resolve) => {
|
|
1948
|
+
rl.question(' Select session (1-' + sessions.length + ', or Enter to start fresh): ', (answer) => {
|
|
1949
|
+
rl.close();
|
|
1950
|
+
const n = parseInt(answer.trim(), 10);
|
|
1951
|
+
if (!isNaN(n) && n >= 1 && n <= sessions.length) {
|
|
1952
|
+
const selected = sessions[n - 1];
|
|
1953
|
+
const session = this.sessionManager.loadFromDisk(selected.id);
|
|
1954
|
+
if (session) {
|
|
1955
|
+
console.log(`\x1b[36m Restoring session: ${selected.id.slice(0, 8)} — ${selected.title}\x1b[0m`);
|
|
1956
|
+
resolve(session.id);
|
|
1957
|
+
}
|
|
1958
|
+
else {
|
|
1959
|
+
console.log('\x1b[33m Could not load session. Starting fresh.\x1b[0m');
|
|
1960
|
+
resolve(undefined);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
else {
|
|
1964
|
+
resolve(undefined);
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
// Entry point
|
|
1971
|
+
if (import.meta.url === `file://${process.argv[1].replace(/\\/g, '/')}`) {
|
|
1972
|
+
const app = new NovaApp();
|
|
1973
|
+
app.run().catch((err) => {
|
|
1974
|
+
console.error('Fatal error:', err);
|
|
1975
|
+
process.exit(1);
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
//# sourceMappingURL=NovaApp.js.map
|