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.
Files changed (649) hide show
  1. package/README.md +84 -0
  2. package/bin/nova +38 -0
  3. package/bin/nova.js +11 -0
  4. package/dist/commands/SmartCompletion.d.ts +71 -0
  5. package/dist/commands/SmartCompletion.d.ts.map +1 -0
  6. package/dist/commands/SmartCompletion.js +377 -0
  7. package/dist/commands/SmartCompletion.js.map +1 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +5 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/packages/cli/src/commands/SmartCompletion.d.ts +71 -0
  13. package/dist/packages/cli/src/commands/SmartCompletion.d.ts.map +1 -0
  14. package/dist/packages/cli/src/commands/SmartCompletion.js +377 -0
  15. package/dist/packages/cli/src/commands/SmartCompletion.js.map +1 -0
  16. package/dist/packages/cli/src/index.d.ts +2 -0
  17. package/dist/packages/cli/src/index.d.ts.map +1 -0
  18. package/dist/packages/cli/src/index.js +5 -0
  19. package/dist/packages/cli/src/index.js.map +1 -0
  20. package/dist/packages/cli/src/startup/IFlowRepl.d.ts +50 -0
  21. package/dist/packages/cli/src/startup/IFlowRepl.d.ts.map +1 -0
  22. package/dist/packages/cli/src/startup/IFlowRepl.js +178 -0
  23. package/dist/packages/cli/src/startup/IFlowRepl.js.map +1 -0
  24. package/dist/packages/cli/src/startup/InkBasedRepl.d.ts +151 -0
  25. package/dist/packages/cli/src/startup/InkBasedRepl.d.ts.map +1 -0
  26. package/dist/packages/cli/src/startup/InkBasedRepl.js +1415 -0
  27. package/dist/packages/cli/src/startup/InkBasedRepl.js.map +1 -0
  28. package/dist/packages/cli/src/startup/InteractiveRepl.d.ts +141 -0
  29. package/dist/packages/cli/src/startup/InteractiveRepl.d.ts.map +1 -0
  30. package/dist/packages/cli/src/startup/InteractiveRepl.js +2561 -0
  31. package/dist/packages/cli/src/startup/InteractiveRepl.js.map +1 -0
  32. package/dist/packages/cli/src/startup/NovaApp.d.ts +57 -0
  33. package/dist/packages/cli/src/startup/NovaApp.d.ts.map +1 -0
  34. package/dist/packages/cli/src/startup/NovaApp.js +1978 -0
  35. package/dist/packages/cli/src/startup/NovaApp.js.map +1 -0
  36. package/dist/packages/cli/src/startup/index.d.ts +5 -0
  37. package/dist/packages/cli/src/startup/index.d.ts.map +1 -0
  38. package/dist/packages/cli/src/startup/index.js +4 -0
  39. package/dist/packages/cli/src/startup/index.js.map +1 -0
  40. package/dist/packages/cli/src/startup/parseArgs.d.ts +47 -0
  41. package/dist/packages/cli/src/startup/parseArgs.d.ts.map +1 -0
  42. package/dist/packages/cli/src/startup/parseArgs.js +262 -0
  43. package/dist/packages/cli/src/startup/parseArgs.js.map +1 -0
  44. package/dist/packages/cli/src/ui/IFlowDropdown.d.ts +63 -0
  45. package/dist/packages/cli/src/ui/IFlowDropdown.d.ts.map +1 -0
  46. package/dist/packages/cli/src/ui/IFlowDropdown.js +362 -0
  47. package/dist/packages/cli/src/ui/IFlowDropdown.js.map +1 -0
  48. package/dist/packages/cli/src/ui/ModernReplUI.d.ts +55 -0
  49. package/dist/packages/cli/src/ui/ModernReplUI.d.ts.map +1 -0
  50. package/dist/packages/cli/src/ui/ModernReplUI.js +207 -0
  51. package/dist/packages/cli/src/ui/ModernReplUI.js.map +1 -0
  52. package/dist/packages/cli/src/ui/SimpleSelector2.d.ts +28 -0
  53. package/dist/packages/cli/src/ui/SimpleSelector2.d.ts.map +1 -0
  54. package/dist/packages/cli/src/ui/SimpleSelector2.js +181 -0
  55. package/dist/packages/cli/src/ui/SimpleSelector2.js.map +1 -0
  56. package/dist/packages/cli/src/ui/components/ActiveCursor.d.ts +128 -0
  57. package/dist/packages/cli/src/ui/components/ActiveCursor.d.ts.map +1 -0
  58. package/dist/packages/cli/src/ui/components/ActiveCursor.js +273 -0
  59. package/dist/packages/cli/src/ui/components/ActiveCursor.js.map +1 -0
  60. package/dist/packages/cli/src/ui/components/ConfirmDialog.d.ts +51 -0
  61. package/dist/packages/cli/src/ui/components/ConfirmDialog.d.ts.map +1 -0
  62. package/dist/packages/cli/src/ui/components/ConfirmDialog.js +147 -0
  63. package/dist/packages/cli/src/ui/components/ConfirmDialog.js.map +1 -0
  64. package/dist/packages/cli/src/ui/components/ErrorPanel.d.ts +33 -0
  65. package/dist/packages/cli/src/ui/components/ErrorPanel.d.ts.map +1 -0
  66. package/dist/packages/cli/src/ui/components/ErrorPanel.js +309 -0
  67. package/dist/packages/cli/src/ui/components/ErrorPanel.js.map +1 -0
  68. package/dist/packages/cli/src/ui/components/InkAppRunner.d.ts +18 -0
  69. package/dist/packages/cli/src/ui/components/InkAppRunner.d.ts.map +1 -0
  70. package/dist/packages/cli/src/ui/components/InkAppRunner.js +33 -0
  71. package/dist/packages/cli/src/ui/components/InkAppRunner.js.map +1 -0
  72. package/dist/packages/cli/src/ui/components/InkComponents.d.ts +126 -0
  73. package/dist/packages/cli/src/ui/components/InkComponents.d.ts.map +1 -0
  74. package/dist/packages/cli/src/ui/components/InkComponents.js +216 -0
  75. package/dist/packages/cli/src/ui/components/InkComponents.js.map +1 -0
  76. package/dist/packages/cli/src/ui/components/NovaInkApp.d.ts +11 -0
  77. package/dist/packages/cli/src/ui/components/NovaInkApp.d.ts.map +1 -0
  78. package/dist/packages/cli/src/ui/components/NovaInkApp.js +148 -0
  79. package/dist/packages/cli/src/ui/components/NovaInkApp.js.map +1 -0
  80. package/dist/packages/cli/src/ui/components/ProgressBar.d.ts +65 -0
  81. package/dist/packages/cli/src/ui/components/ProgressBar.d.ts.map +1 -0
  82. package/dist/packages/cli/src/ui/components/ProgressBar.js +135 -0
  83. package/dist/packages/cli/src/ui/components/ProgressBar.js.map +1 -0
  84. package/dist/packages/cli/src/ui/components/ProgressIndicator.d.ts +41 -0
  85. package/dist/packages/cli/src/ui/components/ProgressIndicator.d.ts.map +1 -0
  86. package/dist/packages/cli/src/ui/components/ProgressIndicator.js +235 -0
  87. package/dist/packages/cli/src/ui/components/ProgressIndicator.js.map +1 -0
  88. package/dist/packages/cli/src/ui/components/QuickActions.d.ts +36 -0
  89. package/dist/packages/cli/src/ui/components/QuickActions.d.ts.map +1 -0
  90. package/dist/packages/cli/src/ui/components/QuickActions.js +328 -0
  91. package/dist/packages/cli/src/ui/components/QuickActions.js.map +1 -0
  92. package/dist/packages/cli/src/ui/components/SimpleErrorPanel.d.ts +16 -0
  93. package/dist/packages/cli/src/ui/components/SimpleErrorPanel.d.ts.map +1 -0
  94. package/dist/packages/cli/src/ui/components/SimpleErrorPanel.js +193 -0
  95. package/dist/packages/cli/src/ui/components/SimpleErrorPanel.js.map +1 -0
  96. package/dist/packages/cli/src/ui/components/StatusBar.d.ts +30 -0
  97. package/dist/packages/cli/src/ui/components/StatusBar.d.ts.map +1 -0
  98. package/dist/packages/cli/src/ui/components/StatusBar.js +154 -0
  99. package/dist/packages/cli/src/ui/components/StatusBar.js.map +1 -0
  100. package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.d.ts +109 -0
  101. package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.d.ts.map +1 -0
  102. package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.js +335 -0
  103. package/dist/packages/cli/src/ui/components/ThinkingBlockRenderer.js.map +1 -0
  104. package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.d.ts +59 -0
  105. package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.d.ts.map +1 -0
  106. package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.js +172 -0
  107. package/dist/packages/cli/src/ui/components/ThinkingContentDisplay.js.map +1 -0
  108. package/dist/packages/cli/src/ui/components/TodoProgressPanel.d.ts +91 -0
  109. package/dist/packages/cli/src/ui/components/TodoProgressPanel.d.ts.map +1 -0
  110. package/dist/packages/cli/src/ui/components/TodoProgressPanel.js +284 -0
  111. package/dist/packages/cli/src/ui/components/TodoProgressPanel.js.map +1 -0
  112. package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.d.ts +89 -0
  113. package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.d.ts.map +1 -0
  114. package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.js +246 -0
  115. package/dist/packages/cli/src/ui/components/ToolCallStatusDisplay.js.map +1 -0
  116. package/dist/packages/cli/src/ui/components/UserMessageHighlight.d.ts +48 -0
  117. package/dist/packages/cli/src/ui/components/UserMessageHighlight.d.ts.map +1 -0
  118. package/dist/packages/cli/src/ui/components/UserMessageHighlight.js +196 -0
  119. package/dist/packages/cli/src/ui/components/UserMessageHighlight.js.map +1 -0
  120. package/dist/packages/cli/src/ui/components/index.d.ts +17 -0
  121. package/dist/packages/cli/src/ui/components/index.d.ts.map +1 -0
  122. package/dist/packages/cli/src/ui/components/index.js +18 -0
  123. package/dist/packages/cli/src/ui/components/index.js.map +1 -0
  124. package/dist/packages/cli/src/ui/ink-prototype.d.ts +3 -0
  125. package/dist/packages/cli/src/ui/ink-prototype.d.ts.map +1 -0
  126. package/dist/packages/cli/src/ui/ink-prototype.js +160 -0
  127. package/dist/packages/cli/src/ui/ink-prototype.js.map +1 -0
  128. package/dist/packages/cli/src/utils/CliUI.d.ts +163 -0
  129. package/dist/packages/cli/src/utils/CliUI.d.ts.map +1 -0
  130. package/dist/packages/cli/src/utils/CliUI.js +292 -0
  131. package/dist/packages/cli/src/utils/CliUI.js.map +1 -0
  132. package/dist/packages/cli/src/utils/CompletionHelper.d.ts +112 -0
  133. package/dist/packages/cli/src/utils/CompletionHelper.d.ts.map +1 -0
  134. package/dist/packages/cli/src/utils/CompletionHelper.js +304 -0
  135. package/dist/packages/cli/src/utils/CompletionHelper.js.map +1 -0
  136. package/dist/packages/cli/src/utils/EnhancedCompleter.d.ts +107 -0
  137. package/dist/packages/cli/src/utils/EnhancedCompleter.d.ts.map +1 -0
  138. package/dist/packages/cli/src/utils/EnhancedCompleter.js +428 -0
  139. package/dist/packages/cli/src/utils/EnhancedCompleter.js.map +1 -0
  140. package/dist/packages/cli/src/utils/ErrorEnhancer.d.ts +103 -0
  141. package/dist/packages/cli/src/utils/ErrorEnhancer.d.ts.map +1 -0
  142. package/dist/packages/cli/src/utils/ErrorEnhancer.js +350 -0
  143. package/dist/packages/cli/src/utils/ErrorEnhancer.js.map +1 -0
  144. package/dist/packages/cli/src/utils/OutputFormatter.d.ts +65 -0
  145. package/dist/packages/cli/src/utils/OutputFormatter.d.ts.map +1 -0
  146. package/dist/packages/cli/src/utils/OutputFormatter.js +145 -0
  147. package/dist/packages/cli/src/utils/OutputFormatter.js.map +1 -0
  148. package/dist/packages/cli/src/utils/index.d.ts +5 -0
  149. package/dist/packages/cli/src/utils/index.d.ts.map +1 -0
  150. package/dist/packages/cli/src/utils/index.js +8 -0
  151. package/dist/packages/cli/src/utils/index.js.map +1 -0
  152. package/dist/packages/cli/tsconfig.tsbuildinfo +1 -0
  153. package/dist/packages/core/src/agents/AgentOrchestrator.d.ts +147 -0
  154. package/dist/packages/core/src/agents/AgentOrchestrator.d.ts.map +1 -0
  155. package/dist/packages/core/src/agents/AgentOrchestrator.js +358 -0
  156. package/dist/packages/core/src/agents/AgentOrchestrator.js.map +1 -0
  157. package/dist/packages/core/src/agents/index.d.ts +3 -0
  158. package/dist/packages/core/src/agents/index.d.ts.map +1 -0
  159. package/dist/packages/core/src/agents/index.js +5 -0
  160. package/dist/packages/core/src/agents/index.js.map +1 -0
  161. package/dist/packages/core/src/analysis/ProjectAnalyzer.d.ts +95 -0
  162. package/dist/packages/core/src/analysis/ProjectAnalyzer.d.ts.map +1 -0
  163. package/dist/packages/core/src/analysis/ProjectAnalyzer.js +656 -0
  164. package/dist/packages/core/src/analysis/ProjectAnalyzer.js.map +1 -0
  165. package/dist/packages/core/src/audit/AuditLogger.d.ts +140 -0
  166. package/dist/packages/core/src/audit/AuditLogger.d.ts.map +1 -0
  167. package/dist/packages/core/src/audit/AuditLogger.js +357 -0
  168. package/dist/packages/core/src/audit/AuditLogger.js.map +1 -0
  169. package/dist/packages/core/src/audit/index.d.ts +3 -0
  170. package/dist/packages/core/src/audit/index.d.ts.map +1 -0
  171. package/dist/packages/core/src/audit/index.js +5 -0
  172. package/dist/packages/core/src/audit/index.js.map +1 -0
  173. package/dist/packages/core/src/auth/AuthManager.d.ts +38 -0
  174. package/dist/packages/core/src/auth/AuthManager.d.ts.map +1 -0
  175. package/dist/packages/core/src/auth/AuthManager.js +120 -0
  176. package/dist/packages/core/src/auth/AuthManager.js.map +1 -0
  177. package/dist/packages/core/src/auth/index.d.ts +3 -0
  178. package/dist/packages/core/src/auth/index.d.ts.map +1 -0
  179. package/dist/packages/core/src/auth/index.js +2 -0
  180. package/dist/packages/core/src/auth/index.js.map +1 -0
  181. package/dist/packages/core/src/config/ConfigManager.d.ts +47 -0
  182. package/dist/packages/core/src/config/ConfigManager.d.ts.map +1 -0
  183. package/dist/packages/core/src/config/ConfigManager.js +1197 -0
  184. package/dist/packages/core/src/config/ConfigManager.js.map +1 -0
  185. package/dist/packages/core/src/config/index.d.ts +2 -0
  186. package/dist/packages/core/src/config/index.d.ts.map +1 -0
  187. package/dist/packages/core/src/config/index.js +2 -0
  188. package/dist/packages/core/src/config/index.js.map +1 -0
  189. package/dist/packages/core/src/context/ContextBuilder.d.ts +39 -0
  190. package/dist/packages/core/src/context/ContextBuilder.d.ts.map +1 -0
  191. package/dist/packages/core/src/context/ContextBuilder.js +132 -0
  192. package/dist/packages/core/src/context/ContextBuilder.js.map +1 -0
  193. package/dist/packages/core/src/context/ContextCompressor.d.ts +147 -0
  194. package/dist/packages/core/src/context/ContextCompressor.d.ts.map +1 -0
  195. package/dist/packages/core/src/context/ContextCompressor.js +451 -0
  196. package/dist/packages/core/src/context/ContextCompressor.js.map +1 -0
  197. package/dist/packages/core/src/context/LayeredMemoryManager.d.ts +160 -0
  198. package/dist/packages/core/src/context/LayeredMemoryManager.d.ts.map +1 -0
  199. package/dist/packages/core/src/context/LayeredMemoryManager.js +505 -0
  200. package/dist/packages/core/src/context/LayeredMemoryManager.js.map +1 -0
  201. package/dist/packages/core/src/context/MemoryDiscovery.d.ts +33 -0
  202. package/dist/packages/core/src/context/MemoryDiscovery.d.ts.map +1 -0
  203. package/dist/packages/core/src/context/MemoryDiscovery.js +146 -0
  204. package/dist/packages/core/src/context/MemoryDiscovery.js.map +1 -0
  205. package/dist/packages/core/src/context/defaultSystemPrompt.d.ts +12 -0
  206. package/dist/packages/core/src/context/defaultSystemPrompt.d.ts.map +1 -0
  207. package/dist/packages/core/src/context/defaultSystemPrompt.js +32 -0
  208. package/dist/packages/core/src/context/defaultSystemPrompt.js.map +1 -0
  209. package/dist/packages/core/src/context/index.d.ts +9 -0
  210. package/dist/packages/core/src/context/index.d.ts.map +1 -0
  211. package/dist/packages/core/src/context/index.js +5 -0
  212. package/dist/packages/core/src/context/index.js.map +1 -0
  213. package/dist/packages/core/src/extensions/SkillGenerator.d.ts +77 -0
  214. package/dist/packages/core/src/extensions/SkillGenerator.d.ts.map +1 -0
  215. package/dist/packages/core/src/extensions/SkillGenerator.js +323 -0
  216. package/dist/packages/core/src/extensions/SkillGenerator.js.map +1 -0
  217. package/dist/packages/core/src/extensions/SkillInstaller.d.ts +74 -0
  218. package/dist/packages/core/src/extensions/SkillInstaller.d.ts.map +1 -0
  219. package/dist/packages/core/src/extensions/SkillInstaller.js +216 -0
  220. package/dist/packages/core/src/extensions/SkillInstaller.js.map +1 -0
  221. package/dist/packages/core/src/extensions/SkillRegistry.d.ts +99 -0
  222. package/dist/packages/core/src/extensions/SkillRegistry.d.ts.map +1 -0
  223. package/dist/packages/core/src/extensions/SkillRegistry.js +263 -0
  224. package/dist/packages/core/src/extensions/SkillRegistry.js.map +1 -0
  225. package/dist/packages/core/src/extensions/SkillValidator.d.ts +51 -0
  226. package/dist/packages/core/src/extensions/SkillValidator.d.ts.map +1 -0
  227. package/dist/packages/core/src/extensions/SkillValidator.js +465 -0
  228. package/dist/packages/core/src/extensions/SkillValidator.js.map +1 -0
  229. package/dist/packages/core/src/extensions/index.d.ts +11 -0
  230. package/dist/packages/core/src/extensions/index.d.ts.map +1 -0
  231. package/dist/packages/core/src/extensions/index.js +8 -0
  232. package/dist/packages/core/src/extensions/index.js.map +1 -0
  233. package/dist/packages/core/src/index.d.ts +14 -0
  234. package/dist/packages/core/src/index.d.ts.map +1 -0
  235. package/dist/packages/core/src/index.js +30 -0
  236. package/dist/packages/core/src/index.js.map +1 -0
  237. package/dist/packages/core/src/mcp/McpManager.d.ts +94 -0
  238. package/dist/packages/core/src/mcp/McpManager.d.ts.map +1 -0
  239. package/dist/packages/core/src/mcp/McpManager.js +494 -0
  240. package/dist/packages/core/src/mcp/McpManager.js.map +1 -0
  241. package/dist/packages/core/src/mcp/index.d.ts +2 -0
  242. package/dist/packages/core/src/mcp/index.d.ts.map +1 -0
  243. package/dist/packages/core/src/mcp/index.js +3 -0
  244. package/dist/packages/core/src/mcp/index.js.map +1 -0
  245. package/dist/packages/core/src/model/ModelClient.d.ts +40 -0
  246. package/dist/packages/core/src/model/ModelClient.d.ts.map +1 -0
  247. package/dist/packages/core/src/model/ModelClient.js +163 -0
  248. package/dist/packages/core/src/model/ModelClient.js.map +1 -0
  249. package/dist/packages/core/src/model/ModelConnectionTester.d.ts +58 -0
  250. package/dist/packages/core/src/model/ModelConnectionTester.d.ts.map +1 -0
  251. package/dist/packages/core/src/model/ModelConnectionTester.js +311 -0
  252. package/dist/packages/core/src/model/ModelConnectionTester.js.map +1 -0
  253. package/dist/packages/core/src/model/ModelValidator.d.ts +39 -0
  254. package/dist/packages/core/src/model/ModelValidator.d.ts.map +1 -0
  255. package/dist/packages/core/src/model/ModelValidator.js +296 -0
  256. package/dist/packages/core/src/model/ModelValidator.js.map +1 -0
  257. package/dist/packages/core/src/model/index.d.ts +7 -0
  258. package/dist/packages/core/src/model/index.d.ts.map +1 -0
  259. package/dist/packages/core/src/model/index.js +5 -0
  260. package/dist/packages/core/src/model/index.js.map +1 -0
  261. package/dist/packages/core/src/model/providers/AnthropicProvider.d.ts +25 -0
  262. package/dist/packages/core/src/model/providers/AnthropicProvider.d.ts.map +1 -0
  263. package/dist/packages/core/src/model/providers/AnthropicProvider.js +258 -0
  264. package/dist/packages/core/src/model/providers/AnthropicProvider.js.map +1 -0
  265. package/dist/packages/core/src/model/providers/CodingPlanProvider.d.ts +66 -0
  266. package/dist/packages/core/src/model/providers/CodingPlanProvider.d.ts.map +1 -0
  267. package/dist/packages/core/src/model/providers/CodingPlanProvider.js +161 -0
  268. package/dist/packages/core/src/model/providers/CodingPlanProvider.js.map +1 -0
  269. package/dist/packages/core/src/model/providers/OllamaCloudProvider.d.ts +38 -0
  270. package/dist/packages/core/src/model/providers/OllamaCloudProvider.d.ts.map +1 -0
  271. package/dist/packages/core/src/model/providers/OllamaCloudProvider.js +365 -0
  272. package/dist/packages/core/src/model/providers/OllamaCloudProvider.js.map +1 -0
  273. package/dist/packages/core/src/model/providers/OllamaManager.d.ts +72 -0
  274. package/dist/packages/core/src/model/providers/OllamaManager.d.ts.map +1 -0
  275. package/dist/packages/core/src/model/providers/OllamaManager.js +144 -0
  276. package/dist/packages/core/src/model/providers/OllamaManager.js.map +1 -0
  277. package/dist/packages/core/src/model/providers/OllamaProvider.d.ts +23 -0
  278. package/dist/packages/core/src/model/providers/OllamaProvider.d.ts.map +1 -0
  279. package/dist/packages/core/src/model/providers/OllamaProvider.js +56 -0
  280. package/dist/packages/core/src/model/providers/OllamaProvider.js.map +1 -0
  281. package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.d.ts +53 -0
  282. package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
  283. package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.js +383 -0
  284. package/dist/packages/core/src/model/providers/OpenAICompatibleProvider.js.map +1 -0
  285. package/dist/packages/core/src/model/providers/OpenAIProvider.d.ts +12 -0
  286. package/dist/packages/core/src/model/providers/OpenAIProvider.d.ts.map +1 -0
  287. package/dist/packages/core/src/model/providers/OpenAIProvider.js +19 -0
  288. package/dist/packages/core/src/model/providers/OpenAIProvider.js.map +1 -0
  289. package/dist/packages/core/src/model/providers/index.d.ts +13 -0
  290. package/dist/packages/core/src/model/providers/index.d.ts.map +1 -0
  291. package/dist/packages/core/src/model/providers/index.js +7 -0
  292. package/dist/packages/core/src/model/providers/index.js.map +1 -0
  293. package/dist/packages/core/src/model/types.d.ts +81 -0
  294. package/dist/packages/core/src/model/types.d.ts.map +1 -0
  295. package/dist/packages/core/src/model/types.js +12 -0
  296. package/dist/packages/core/src/model/types.js.map +1 -0
  297. package/dist/packages/core/src/security/ApprovalManager.d.ts +56 -0
  298. package/dist/packages/core/src/security/ApprovalManager.d.ts.map +1 -0
  299. package/dist/packages/core/src/security/ApprovalManager.js +122 -0
  300. package/dist/packages/core/src/security/ApprovalManager.js.map +1 -0
  301. package/dist/packages/core/src/security/FileFilter.d.ts +47 -0
  302. package/dist/packages/core/src/security/FileFilter.d.ts.map +1 -0
  303. package/dist/packages/core/src/security/FileFilter.js +111 -0
  304. package/dist/packages/core/src/security/FileFilter.js.map +1 -0
  305. package/dist/packages/core/src/security/HookExecutor.d.ts +37 -0
  306. package/dist/packages/core/src/security/HookExecutor.d.ts.map +1 -0
  307. package/dist/packages/core/src/security/HookExecutor.js +142 -0
  308. package/dist/packages/core/src/security/HookExecutor.js.map +1 -0
  309. package/dist/packages/core/src/security/SandboxExecutor.d.ts +90 -0
  310. package/dist/packages/core/src/security/SandboxExecutor.d.ts.map +1 -0
  311. package/dist/packages/core/src/security/SandboxExecutor.js +345 -0
  312. package/dist/packages/core/src/security/SandboxExecutor.js.map +1 -0
  313. package/dist/packages/core/src/security/index.d.ts +8 -0
  314. package/dist/packages/core/src/security/index.d.ts.map +1 -0
  315. package/dist/packages/core/src/security/index.js +6 -0
  316. package/dist/packages/core/src/security/index.js.map +1 -0
  317. package/dist/packages/core/src/session/AgentLoop.d.ts +75 -0
  318. package/dist/packages/core/src/session/AgentLoop.d.ts.map +1 -0
  319. package/dist/packages/core/src/session/AgentLoop.js +381 -0
  320. package/dist/packages/core/src/session/AgentLoop.js.map +1 -0
  321. package/dist/packages/core/src/session/SessionManager.d.ts +96 -0
  322. package/dist/packages/core/src/session/SessionManager.d.ts.map +1 -0
  323. package/dist/packages/core/src/session/SessionManager.js +390 -0
  324. package/dist/packages/core/src/session/SessionManager.js.map +1 -0
  325. package/dist/packages/core/src/session/index.d.ts +4 -0
  326. package/dist/packages/core/src/session/index.d.ts.map +1 -0
  327. package/dist/packages/core/src/session/index.js +3 -0
  328. package/dist/packages/core/src/session/index.js.map +1 -0
  329. package/dist/packages/core/src/telemetry/Telemetry.d.ts +25 -0
  330. package/dist/packages/core/src/telemetry/Telemetry.d.ts.map +1 -0
  331. package/dist/packages/core/src/telemetry/Telemetry.js +73 -0
  332. package/dist/packages/core/src/telemetry/Telemetry.js.map +1 -0
  333. package/dist/packages/core/src/telemetry/TelemetryService.d.ts +152 -0
  334. package/dist/packages/core/src/telemetry/TelemetryService.d.ts.map +1 -0
  335. package/dist/packages/core/src/telemetry/TelemetryService.js +410 -0
  336. package/dist/packages/core/src/telemetry/TelemetryService.js.map +1 -0
  337. package/dist/packages/core/src/telemetry/index.d.ts +5 -0
  338. package/dist/packages/core/src/telemetry/index.d.ts.map +1 -0
  339. package/dist/packages/core/src/telemetry/index.js +3 -0
  340. package/dist/packages/core/src/telemetry/index.js.map +1 -0
  341. package/dist/packages/core/src/testing/AutoFixer.d.ts +98 -0
  342. package/dist/packages/core/src/testing/AutoFixer.d.ts.map +1 -0
  343. package/dist/packages/core/src/testing/AutoFixer.js +262 -0
  344. package/dist/packages/core/src/testing/AutoFixer.js.map +1 -0
  345. package/dist/packages/core/src/testing/ErrorAnalyzer.d.ts +74 -0
  346. package/dist/packages/core/src/testing/ErrorAnalyzer.d.ts.map +1 -0
  347. package/dist/packages/core/src/testing/ErrorAnalyzer.js +407 -0
  348. package/dist/packages/core/src/testing/ErrorAnalyzer.js.map +1 -0
  349. package/dist/packages/core/src/testing/TestRunner.d.ts +57 -0
  350. package/dist/packages/core/src/testing/TestRunner.d.ts.map +1 -0
  351. package/dist/packages/core/src/testing/TestRunner.js +205 -0
  352. package/dist/packages/core/src/testing/TestRunner.js.map +1 -0
  353. package/dist/packages/core/src/testing/agent-cli-tests.d.ts +2 -0
  354. package/dist/packages/core/src/testing/agent-cli-tests.d.ts.map +1 -0
  355. package/dist/packages/core/src/testing/agent-cli-tests.js +493 -0
  356. package/dist/packages/core/src/testing/agent-cli-tests.js.map +1 -0
  357. package/dist/packages/core/src/testing/index.d.ts +7 -0
  358. package/dist/packages/core/src/testing/index.d.ts.map +1 -0
  359. package/dist/packages/core/src/testing/index.js +8 -0
  360. package/dist/packages/core/src/testing/index.js.map +1 -0
  361. package/dist/packages/core/src/tools/ToolRegistry.d.ts +72 -0
  362. package/dist/packages/core/src/tools/ToolRegistry.d.ts.map +1 -0
  363. package/dist/packages/core/src/tools/ToolRegistry.js +208 -0
  364. package/dist/packages/core/src/tools/ToolRegistry.js.map +1 -0
  365. package/dist/packages/core/src/tools/impl/EditFileTool.d.ts +3 -0
  366. package/dist/packages/core/src/tools/impl/EditFileTool.d.ts.map +1 -0
  367. package/dist/packages/core/src/tools/impl/EditFileTool.js +76 -0
  368. package/dist/packages/core/src/tools/impl/EditFileTool.js.map +1 -0
  369. package/dist/packages/core/src/tools/impl/FileProcessor.d.ts +85 -0
  370. package/dist/packages/core/src/tools/impl/FileProcessor.d.ts.map +1 -0
  371. package/dist/packages/core/src/tools/impl/FileProcessor.js +512 -0
  372. package/dist/packages/core/src/tools/impl/FileProcessor.js.map +1 -0
  373. package/dist/packages/core/src/tools/impl/FileProcessorTool.d.ts +151 -0
  374. package/dist/packages/core/src/tools/impl/FileProcessorTool.d.ts.map +1 -0
  375. package/dist/packages/core/src/tools/impl/FileProcessorTool.js +100 -0
  376. package/dist/packages/core/src/tools/impl/FileProcessorTool.js.map +1 -0
  377. package/dist/packages/core/src/tools/impl/ImageProcessorTool.d.ts +43 -0
  378. package/dist/packages/core/src/tools/impl/ImageProcessorTool.d.ts.map +1 -0
  379. package/dist/packages/core/src/tools/impl/ImageProcessorTool.js +104 -0
  380. package/dist/packages/core/src/tools/impl/ImageProcessorTool.js.map +1 -0
  381. package/dist/packages/core/src/tools/impl/ListDirectoryTool.d.ts +3 -0
  382. package/dist/packages/core/src/tools/impl/ListDirectoryTool.d.ts.map +1 -0
  383. package/dist/packages/core/src/tools/impl/ListDirectoryTool.js +120 -0
  384. package/dist/packages/core/src/tools/impl/ListDirectoryTool.js.map +1 -0
  385. package/dist/packages/core/src/tools/impl/MemoryTool.d.ts +4 -0
  386. package/dist/packages/core/src/tools/impl/MemoryTool.d.ts.map +1 -0
  387. package/dist/packages/core/src/tools/impl/MemoryTool.js +80 -0
  388. package/dist/packages/core/src/tools/impl/MemoryTool.js.map +1 -0
  389. package/dist/packages/core/src/tools/impl/ReadFileTool.d.ts +3 -0
  390. package/dist/packages/core/src/tools/impl/ReadFileTool.d.ts.map +1 -0
  391. package/dist/packages/core/src/tools/impl/ReadFileTool.js +223 -0
  392. package/dist/packages/core/src/tools/impl/ReadFileTool.js.map +1 -0
  393. package/dist/packages/core/src/tools/impl/SearchContentTool.d.ts +3 -0
  394. package/dist/packages/core/src/tools/impl/SearchContentTool.d.ts.map +1 -0
  395. package/dist/packages/core/src/tools/impl/SearchContentTool.js +65 -0
  396. package/dist/packages/core/src/tools/impl/SearchContentTool.js.map +1 -0
  397. package/dist/packages/core/src/tools/impl/SearchFileTool.d.ts +3 -0
  398. package/dist/packages/core/src/tools/impl/SearchFileTool.d.ts.map +1 -0
  399. package/dist/packages/core/src/tools/impl/SearchFileTool.js +48 -0
  400. package/dist/packages/core/src/tools/impl/SearchFileTool.js.map +1 -0
  401. package/dist/packages/core/src/tools/impl/ShellTool.d.ts +3 -0
  402. package/dist/packages/core/src/tools/impl/ShellTool.d.ts.map +1 -0
  403. package/dist/packages/core/src/tools/impl/ShellTool.js +92 -0
  404. package/dist/packages/core/src/tools/impl/ShellTool.js.map +1 -0
  405. package/dist/packages/core/src/tools/impl/TaskTool.d.ts +51 -0
  406. package/dist/packages/core/src/tools/impl/TaskTool.d.ts.map +1 -0
  407. package/dist/packages/core/src/tools/impl/TaskTool.js +172 -0
  408. package/dist/packages/core/src/tools/impl/TaskTool.js.map +1 -0
  409. package/dist/packages/core/src/tools/impl/TodoTool.d.ts +31 -0
  410. package/dist/packages/core/src/tools/impl/TodoTool.d.ts.map +1 -0
  411. package/dist/packages/core/src/tools/impl/TodoTool.js +102 -0
  412. package/dist/packages/core/src/tools/impl/TodoTool.js.map +1 -0
  413. package/dist/packages/core/src/tools/impl/WebFetchTool.d.ts +3 -0
  414. package/dist/packages/core/src/tools/impl/WebFetchTool.d.ts.map +1 -0
  415. package/dist/packages/core/src/tools/impl/WebFetchTool.js +77 -0
  416. package/dist/packages/core/src/tools/impl/WebFetchTool.js.map +1 -0
  417. package/dist/packages/core/src/tools/impl/WebSearchTool.d.ts +3 -0
  418. package/dist/packages/core/src/tools/impl/WebSearchTool.d.ts.map +1 -0
  419. package/dist/packages/core/src/tools/impl/WebSearchTool.js +67 -0
  420. package/dist/packages/core/src/tools/impl/WebSearchTool.js.map +1 -0
  421. package/dist/packages/core/src/tools/impl/WriteFileTool.d.ts +3 -0
  422. package/dist/packages/core/src/tools/impl/WriteFileTool.d.ts.map +1 -0
  423. package/dist/packages/core/src/tools/impl/WriteFileTool.js +41 -0
  424. package/dist/packages/core/src/tools/impl/WriteFileTool.js.map +1 -0
  425. package/dist/packages/core/src/tools/impl/index.d.ts +14 -0
  426. package/dist/packages/core/src/tools/impl/index.d.ts.map +1 -0
  427. package/dist/packages/core/src/tools/impl/index.js +21 -0
  428. package/dist/packages/core/src/tools/impl/index.js.map +1 -0
  429. package/dist/packages/core/src/tools/index.d.ts +16 -0
  430. package/dist/packages/core/src/tools/index.d.ts.map +1 -0
  431. package/dist/packages/core/src/tools/index.js +21 -0
  432. package/dist/packages/core/src/tools/index.js.map +1 -0
  433. package/dist/packages/core/src/tools/schemas/execution.d.ts +41 -0
  434. package/dist/packages/core/src/tools/schemas/execution.d.ts.map +1 -0
  435. package/dist/packages/core/src/tools/schemas/execution.js +42 -0
  436. package/dist/packages/core/src/tools/schemas/execution.js.map +1 -0
  437. package/dist/packages/core/src/tools/schemas/file.d.ts +113 -0
  438. package/dist/packages/core/src/tools/schemas/file.d.ts.map +1 -0
  439. package/dist/packages/core/src/tools/schemas/file.js +116 -0
  440. package/dist/packages/core/src/tools/schemas/file.js.map +1 -0
  441. package/dist/packages/core/src/tools/schemas/fileProcessorSchema.d.ts +278 -0
  442. package/dist/packages/core/src/tools/schemas/fileProcessorSchema.d.ts.map +1 -0
  443. package/dist/packages/core/src/tools/schemas/fileProcessorSchema.js +61 -0
  444. package/dist/packages/core/src/tools/schemas/fileProcessorSchema.js.map +1 -0
  445. package/dist/packages/core/src/tools/schemas/index.d.ts +8 -0
  446. package/dist/packages/core/src/tools/schemas/index.d.ts.map +1 -0
  447. package/dist/packages/core/src/tools/schemas/index.js +11 -0
  448. package/dist/packages/core/src/tools/schemas/index.js.map +1 -0
  449. package/dist/packages/core/src/tools/schemas/memory.d.ts +50 -0
  450. package/dist/packages/core/src/tools/schemas/memory.d.ts.map +1 -0
  451. package/dist/packages/core/src/tools/schemas/memory.js +51 -0
  452. package/dist/packages/core/src/tools/schemas/memory.js.map +1 -0
  453. package/dist/packages/core/src/tools/schemas/orchestration.d.ts +41 -0
  454. package/dist/packages/core/src/tools/schemas/orchestration.d.ts.map +1 -0
  455. package/dist/packages/core/src/tools/schemas/orchestration.js +44 -0
  456. package/dist/packages/core/src/tools/schemas/orchestration.js.map +1 -0
  457. package/dist/packages/core/src/tools/schemas/search.d.ts +111 -0
  458. package/dist/packages/core/src/tools/schemas/search.d.ts.map +1 -0
  459. package/dist/packages/core/src/tools/schemas/search.js +110 -0
  460. package/dist/packages/core/src/tools/schemas/search.js.map +1 -0
  461. package/dist/packages/core/src/tools/schemas/todo.d.ts +29 -0
  462. package/dist/packages/core/src/tools/schemas/todo.d.ts.map +1 -0
  463. package/dist/packages/core/src/tools/schemas/todo.js +32 -0
  464. package/dist/packages/core/src/tools/schemas/todo.js.map +1 -0
  465. package/dist/packages/core/src/tools/schemas/web.d.ts +84 -0
  466. package/dist/packages/core/src/tools/schemas/web.d.ts.map +1 -0
  467. package/dist/packages/core/src/tools/schemas/web.js +85 -0
  468. package/dist/packages/core/src/tools/schemas/web.js.map +1 -0
  469. package/dist/packages/core/src/types/config.d.ts +212 -0
  470. package/dist/packages/core/src/types/config.d.ts.map +1 -0
  471. package/dist/packages/core/src/types/config.js +5 -0
  472. package/dist/packages/core/src/types/config.js.map +1 -0
  473. package/dist/packages/core/src/types/errors.d.ts +92 -0
  474. package/dist/packages/core/src/types/errors.d.ts.map +1 -0
  475. package/dist/packages/core/src/types/errors.js +172 -0
  476. package/dist/packages/core/src/types/errors.js.map +1 -0
  477. package/dist/packages/core/src/types/index.d.ts +5 -0
  478. package/dist/packages/core/src/types/index.d.ts.map +1 -0
  479. package/dist/packages/core/src/types/index.js +8 -0
  480. package/dist/packages/core/src/types/index.js.map +1 -0
  481. package/dist/packages/core/src/types/session.d.ts +141 -0
  482. package/dist/packages/core/src/types/session.d.ts.map +1 -0
  483. package/dist/packages/core/src/types/session.js +16 -0
  484. package/dist/packages/core/src/types/session.js.map +1 -0
  485. package/dist/packages/core/src/types/tools.d.ts +126 -0
  486. package/dist/packages/core/src/types/tools.d.ts.map +1 -0
  487. package/dist/packages/core/src/types/tools.js +26 -0
  488. package/dist/packages/core/src/types/tools.js.map +1 -0
  489. package/dist/packages/core/src/utils/CheckpointManager.d.ts +100 -0
  490. package/dist/packages/core/src/utils/CheckpointManager.d.ts.map +1 -0
  491. package/dist/packages/core/src/utils/CheckpointManager.js +255 -0
  492. package/dist/packages/core/src/utils/CheckpointManager.js.map +1 -0
  493. package/dist/packages/core/src/utils/Logger.d.ts +29 -0
  494. package/dist/packages/core/src/utils/Logger.d.ts.map +1 -0
  495. package/dist/packages/core/src/utils/Logger.js +77 -0
  496. package/dist/packages/core/src/utils/Logger.js.map +1 -0
  497. package/dist/packages/core/src/utils/RetryManager.d.ts +125 -0
  498. package/dist/packages/core/src/utils/RetryManager.d.ts.map +1 -0
  499. package/dist/packages/core/src/utils/RetryManager.js +348 -0
  500. package/dist/packages/core/src/utils/RetryManager.js.map +1 -0
  501. package/dist/packages/core/src/utils/TokenCounter.d.ts +73 -0
  502. package/dist/packages/core/src/utils/TokenCounter.d.ts.map +1 -0
  503. package/dist/packages/core/src/utils/TokenCounter.js +338 -0
  504. package/dist/packages/core/src/utils/TokenCounter.js.map +1 -0
  505. package/dist/packages/core/src/utils/VectorMemoryStore.d.ts +110 -0
  506. package/dist/packages/core/src/utils/VectorMemoryStore.d.ts.map +1 -0
  507. package/dist/packages/core/src/utils/VectorMemoryStore.js +320 -0
  508. package/dist/packages/core/src/utils/VectorMemoryStore.js.map +1 -0
  509. package/dist/packages/core/src/utils/helpers.d.ts +24 -0
  510. package/dist/packages/core/src/utils/helpers.d.ts.map +1 -0
  511. package/dist/packages/core/src/utils/helpers.js +77 -0
  512. package/dist/packages/core/src/utils/helpers.js.map +1 -0
  513. package/dist/packages/core/src/utils/index.d.ts +11 -0
  514. package/dist/packages/core/src/utils/index.d.ts.map +1 -0
  515. package/dist/packages/core/src/utils/index.js +7 -0
  516. package/dist/packages/core/src/utils/index.js.map +1 -0
  517. package/dist/startup/IFlowRepl.d.ts +50 -0
  518. package/dist/startup/IFlowRepl.d.ts.map +1 -0
  519. package/dist/startup/IFlowRepl.js +178 -0
  520. package/dist/startup/IFlowRepl.js.map +1 -0
  521. package/dist/startup/InkBasedRepl.d.ts +151 -0
  522. package/dist/startup/InkBasedRepl.d.ts.map +1 -0
  523. package/dist/startup/InkBasedRepl.js +1415 -0
  524. package/dist/startup/InkBasedRepl.js.map +1 -0
  525. package/dist/startup/InteractiveRepl.d.ts +141 -0
  526. package/dist/startup/InteractiveRepl.d.ts.map +1 -0
  527. package/dist/startup/InteractiveRepl.js +2561 -0
  528. package/dist/startup/InteractiveRepl.js.map +1 -0
  529. package/dist/startup/NovaApp.d.ts +57 -0
  530. package/dist/startup/NovaApp.d.ts.map +1 -0
  531. package/dist/startup/NovaApp.js +1978 -0
  532. package/dist/startup/NovaApp.js.map +1 -0
  533. package/dist/startup/index.d.ts +5 -0
  534. package/dist/startup/index.d.ts.map +1 -0
  535. package/dist/startup/index.js +4 -0
  536. package/dist/startup/index.js.map +1 -0
  537. package/dist/startup/parseArgs.d.ts +47 -0
  538. package/dist/startup/parseArgs.d.ts.map +1 -0
  539. package/dist/startup/parseArgs.js +262 -0
  540. package/dist/startup/parseArgs.js.map +1 -0
  541. package/dist/ui/IFlowDropdown.d.ts +63 -0
  542. package/dist/ui/IFlowDropdown.d.ts.map +1 -0
  543. package/dist/ui/IFlowDropdown.js +362 -0
  544. package/dist/ui/IFlowDropdown.js.map +1 -0
  545. package/dist/ui/ModernReplUI.d.ts +55 -0
  546. package/dist/ui/ModernReplUI.d.ts.map +1 -0
  547. package/dist/ui/ModernReplUI.js +207 -0
  548. package/dist/ui/ModernReplUI.js.map +1 -0
  549. package/dist/ui/SimpleSelector2.d.ts +28 -0
  550. package/dist/ui/SimpleSelector2.d.ts.map +1 -0
  551. package/dist/ui/SimpleSelector2.js +181 -0
  552. package/dist/ui/SimpleSelector2.js.map +1 -0
  553. package/dist/ui/components/ActiveCursor.d.ts +128 -0
  554. package/dist/ui/components/ActiveCursor.d.ts.map +1 -0
  555. package/dist/ui/components/ActiveCursor.js +273 -0
  556. package/dist/ui/components/ActiveCursor.js.map +1 -0
  557. package/dist/ui/components/ConfirmDialog.d.ts +51 -0
  558. package/dist/ui/components/ConfirmDialog.d.ts.map +1 -0
  559. package/dist/ui/components/ConfirmDialog.js +147 -0
  560. package/dist/ui/components/ConfirmDialog.js.map +1 -0
  561. package/dist/ui/components/ErrorPanel.d.ts +33 -0
  562. package/dist/ui/components/ErrorPanel.d.ts.map +1 -0
  563. package/dist/ui/components/ErrorPanel.js +309 -0
  564. package/dist/ui/components/ErrorPanel.js.map +1 -0
  565. package/dist/ui/components/InkAppRunner.d.ts +18 -0
  566. package/dist/ui/components/InkAppRunner.d.ts.map +1 -0
  567. package/dist/ui/components/InkAppRunner.js +33 -0
  568. package/dist/ui/components/InkAppRunner.js.map +1 -0
  569. package/dist/ui/components/InkComponents.d.ts +126 -0
  570. package/dist/ui/components/InkComponents.d.ts.map +1 -0
  571. package/dist/ui/components/InkComponents.js +216 -0
  572. package/dist/ui/components/InkComponents.js.map +1 -0
  573. package/dist/ui/components/NovaInkApp.d.ts +11 -0
  574. package/dist/ui/components/NovaInkApp.d.ts.map +1 -0
  575. package/dist/ui/components/NovaInkApp.js +148 -0
  576. package/dist/ui/components/NovaInkApp.js.map +1 -0
  577. package/dist/ui/components/ProgressBar.d.ts +65 -0
  578. package/dist/ui/components/ProgressBar.d.ts.map +1 -0
  579. package/dist/ui/components/ProgressBar.js +135 -0
  580. package/dist/ui/components/ProgressBar.js.map +1 -0
  581. package/dist/ui/components/ProgressIndicator.d.ts +41 -0
  582. package/dist/ui/components/ProgressIndicator.d.ts.map +1 -0
  583. package/dist/ui/components/ProgressIndicator.js +235 -0
  584. package/dist/ui/components/ProgressIndicator.js.map +1 -0
  585. package/dist/ui/components/QuickActions.d.ts +36 -0
  586. package/dist/ui/components/QuickActions.d.ts.map +1 -0
  587. package/dist/ui/components/QuickActions.js +328 -0
  588. package/dist/ui/components/QuickActions.js.map +1 -0
  589. package/dist/ui/components/SimpleErrorPanel.d.ts +16 -0
  590. package/dist/ui/components/SimpleErrorPanel.d.ts.map +1 -0
  591. package/dist/ui/components/SimpleErrorPanel.js +193 -0
  592. package/dist/ui/components/SimpleErrorPanel.js.map +1 -0
  593. package/dist/ui/components/StatusBar.d.ts +30 -0
  594. package/dist/ui/components/StatusBar.d.ts.map +1 -0
  595. package/dist/ui/components/StatusBar.js +154 -0
  596. package/dist/ui/components/StatusBar.js.map +1 -0
  597. package/dist/ui/components/ThinkingBlockRenderer.d.ts +109 -0
  598. package/dist/ui/components/ThinkingBlockRenderer.d.ts.map +1 -0
  599. package/dist/ui/components/ThinkingBlockRenderer.js +335 -0
  600. package/dist/ui/components/ThinkingBlockRenderer.js.map +1 -0
  601. package/dist/ui/components/ThinkingContentDisplay.d.ts +59 -0
  602. package/dist/ui/components/ThinkingContentDisplay.d.ts.map +1 -0
  603. package/dist/ui/components/ThinkingContentDisplay.js +172 -0
  604. package/dist/ui/components/ThinkingContentDisplay.js.map +1 -0
  605. package/dist/ui/components/TodoProgressPanel.d.ts +91 -0
  606. package/dist/ui/components/TodoProgressPanel.d.ts.map +1 -0
  607. package/dist/ui/components/TodoProgressPanel.js +284 -0
  608. package/dist/ui/components/TodoProgressPanel.js.map +1 -0
  609. package/dist/ui/components/ToolCallStatusDisplay.d.ts +89 -0
  610. package/dist/ui/components/ToolCallStatusDisplay.d.ts.map +1 -0
  611. package/dist/ui/components/ToolCallStatusDisplay.js +246 -0
  612. package/dist/ui/components/ToolCallStatusDisplay.js.map +1 -0
  613. package/dist/ui/components/UserMessageHighlight.d.ts +48 -0
  614. package/dist/ui/components/UserMessageHighlight.d.ts.map +1 -0
  615. package/dist/ui/components/UserMessageHighlight.js +196 -0
  616. package/dist/ui/components/UserMessageHighlight.js.map +1 -0
  617. package/dist/ui/components/index.d.ts +17 -0
  618. package/dist/ui/components/index.d.ts.map +1 -0
  619. package/dist/ui/components/index.js +18 -0
  620. package/dist/ui/components/index.js.map +1 -0
  621. package/dist/ui/ink-prototype.d.ts +3 -0
  622. package/dist/ui/ink-prototype.d.ts.map +1 -0
  623. package/dist/ui/ink-prototype.js +160 -0
  624. package/dist/ui/ink-prototype.js.map +1 -0
  625. package/dist/utils/CliUI.d.ts +163 -0
  626. package/dist/utils/CliUI.d.ts.map +1 -0
  627. package/dist/utils/CliUI.js +292 -0
  628. package/dist/utils/CliUI.js.map +1 -0
  629. package/dist/utils/CompletionHelper.d.ts +112 -0
  630. package/dist/utils/CompletionHelper.d.ts.map +1 -0
  631. package/dist/utils/CompletionHelper.js +304 -0
  632. package/dist/utils/CompletionHelper.js.map +1 -0
  633. package/dist/utils/EnhancedCompleter.d.ts +107 -0
  634. package/dist/utils/EnhancedCompleter.d.ts.map +1 -0
  635. package/dist/utils/EnhancedCompleter.js +428 -0
  636. package/dist/utils/EnhancedCompleter.js.map +1 -0
  637. package/dist/utils/ErrorEnhancer.d.ts +103 -0
  638. package/dist/utils/ErrorEnhancer.d.ts.map +1 -0
  639. package/dist/utils/ErrorEnhancer.js +350 -0
  640. package/dist/utils/ErrorEnhancer.js.map +1 -0
  641. package/dist/utils/OutputFormatter.d.ts +65 -0
  642. package/dist/utils/OutputFormatter.d.ts.map +1 -0
  643. package/dist/utils/OutputFormatter.js +145 -0
  644. package/dist/utils/OutputFormatter.js.map +1 -0
  645. package/dist/utils/index.d.ts +5 -0
  646. package/dist/utils/index.d.ts.map +1 -0
  647. package/dist/utils/index.js +8 -0
  648. package/dist/utils/index.js.map +1 -0
  649. 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