centaurus-cli 2.5.2 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/dist/cli-adapter.d.ts +6 -0
  2. package/dist/cli-adapter.d.ts.map +1 -1
  3. package/dist/cli-adapter.js +215 -29
  4. package/dist/cli-adapter.js.map +1 -1
  5. package/dist/commands/CommandParser.js +2 -2
  6. package/dist/commands/CommandParser.js.map +1 -1
  7. package/dist/config/defaultConfig.js +1 -1
  8. package/dist/config/defaultConfig.js.map +1 -1
  9. package/dist/config/models.d.ts +50 -5
  10. package/dist/config/models.d.ts.map +1 -1
  11. package/dist/config/models.js +144 -20
  12. package/dist/config/models.js.map +1 -1
  13. package/dist/config/types.d.ts +3 -0
  14. package/dist/config/types.d.ts.map +1 -1
  15. package/dist/config/types.js +3 -1
  16. package/dist/config/types.js.map +1 -1
  17. package/dist/index.js +9 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/services/ai-service-client.d.ts +8 -2
  20. package/dist/services/ai-service-client.d.ts.map +1 -1
  21. package/dist/services/ai-service-client.js +2 -1
  22. package/dist/services/ai-service-client.js.map +1 -1
  23. package/dist/services/environment-context-injector.d.ts +69 -0
  24. package/dist/services/environment-context-injector.d.ts.map +1 -0
  25. package/dist/services/environment-context-injector.js +198 -0
  26. package/dist/services/environment-context-injector.js.map +1 -0
  27. package/dist/tools/ToolRegistry.d.ts.map +1 -1
  28. package/dist/tools/ToolRegistry.js +120 -26
  29. package/dist/tools/ToolRegistry.js.map +1 -1
  30. package/dist/tools/command.d.ts.map +1 -1
  31. package/dist/tools/command.js +30 -6
  32. package/dist/tools/command.js.map +1 -1
  33. package/dist/tools/file-ops.d.ts.map +1 -1
  34. package/dist/tools/file-ops.js +70 -23
  35. package/dist/tools/file-ops.js.map +1 -1
  36. package/dist/tools/find-files.d.ts +47 -0
  37. package/dist/tools/find-files.d.ts.map +1 -0
  38. package/dist/tools/find-files.js +190 -0
  39. package/dist/tools/find-files.js.map +1 -0
  40. package/dist/tools/get-diff.d.ts +45 -0
  41. package/dist/tools/get-diff.d.ts.map +1 -0
  42. package/dist/tools/get-diff.js +138 -0
  43. package/dist/tools/get-diff.js.map +1 -0
  44. package/dist/tools/grep-search.d.ts +85 -0
  45. package/dist/tools/grep-search.d.ts.map +1 -0
  46. package/dist/tools/grep-search.js +497 -0
  47. package/dist/tools/grep-search.js.map +1 -0
  48. package/dist/tools/inspect-symbol.d.ts +27 -0
  49. package/dist/tools/inspect-symbol.d.ts.map +1 -0
  50. package/dist/tools/inspect-symbol.js +259 -0
  51. package/dist/tools/inspect-symbol.js.map +1 -0
  52. package/dist/tools/validation.d.ts +49 -0
  53. package/dist/tools/validation.d.ts.map +1 -0
  54. package/dist/tools/validation.js +125 -0
  55. package/dist/tools/validation.js.map +1 -0
  56. package/dist/types/index.d.ts +13 -0
  57. package/dist/types/index.d.ts.map +1 -1
  58. package/dist/ui/components/App.d.ts +3 -0
  59. package/dist/ui/components/App.d.ts.map +1 -1
  60. package/dist/ui/components/App.js +272 -72
  61. package/dist/ui/components/App.js.map +1 -1
  62. package/dist/ui/components/ContextWindowIndicator.d.ts +8 -0
  63. package/dist/ui/components/ContextWindowIndicator.d.ts.map +1 -0
  64. package/dist/ui/components/ContextWindowIndicator.js +34 -0
  65. package/dist/ui/components/ContextWindowIndicator.js.map +1 -0
  66. package/dist/ui/components/FontRecommendation.d.ts +1 -0
  67. package/dist/ui/components/FontRecommendation.d.ts.map +1 -0
  68. package/dist/ui/components/FontRecommendation.js +1 -0
  69. package/dist/ui/components/FontRecommendation.js.map +1 -0
  70. package/dist/ui/components/InputBox.d.ts +2 -0
  71. package/dist/ui/components/InputBox.d.ts.map +1 -1
  72. package/dist/ui/components/InputBox.js +9 -4
  73. package/dist/ui/components/InputBox.js.map +1 -1
  74. package/dist/ui/components/MarkdownRenderer.d.ts.map +1 -1
  75. package/dist/ui/components/MarkdownRenderer.js +34 -9
  76. package/dist/ui/components/MarkdownRenderer.js.map +1 -1
  77. package/dist/ui/components/MessageDisplay.d.ts.map +1 -1
  78. package/dist/ui/components/MessageDisplay.js +9 -2
  79. package/dist/ui/components/MessageDisplay.js.map +1 -1
  80. package/dist/ui/components/StreamingMessageDisplay.d.ts.map +1 -1
  81. package/dist/ui/components/StreamingMessageDisplay.js +11 -3
  82. package/dist/ui/components/StreamingMessageDisplay.js.map +1 -1
  83. package/dist/ui/components/ThinkingDisplay.d.ts +12 -0
  84. package/dist/ui/components/ThinkingDisplay.d.ts.map +1 -0
  85. package/dist/ui/components/ThinkingDisplay.js +41 -0
  86. package/dist/ui/components/ThinkingDisplay.js.map +1 -0
  87. package/dist/utils/markdown-parser.d.ts +4 -0
  88. package/dist/utils/markdown-parser.d.ts.map +1 -1
  89. package/dist/utils/markdown-parser.js +15 -1
  90. package/dist/utils/markdown-parser.js.map +1 -1
  91. package/dist/utils/version-checker.d.ts.map +1 -1
  92. package/dist/utils/version-checker.js +3 -31
  93. package/dist/utils/version-checker.js.map +1 -1
  94. package/package.json +6 -5
  95. package/README.md +0 -501
  96. package/dist/ai/provider-factory.d.ts +0 -6
  97. package/dist/ai/provider-factory.d.ts.map +0 -1
  98. package/dist/ai/provider-factory.js +0 -27
  99. package/dist/ai/provider-factory.js.map +0 -1
  100. package/dist/ai/providers/base.d.ts +0 -25
  101. package/dist/ai/providers/base.d.ts.map +0 -1
  102. package/dist/ai/providers/base.js +0 -9
  103. package/dist/ai/providers/base.js.map +0 -1
  104. package/dist/ai/providers/gemini.d.ts +0 -34
  105. package/dist/ai/providers/gemini.d.ts.map +0 -1
  106. package/dist/ai/providers/gemini.js +0 -146
  107. package/dist/ai/providers/gemini.js.map +0 -1
  108. package/dist/commands/view-duplication-logs.d.ts +0 -5
  109. package/dist/commands/view-duplication-logs.d.ts.map +0 -1
  110. package/dist/commands/view-duplication-logs.js +0 -14
  111. package/dist/commands/view-duplication-logs.js.map +0 -1
  112. package/dist/context/__tests__/command-detector.test.d.ts +0 -14
  113. package/dist/context/__tests__/command-detector.test.d.ts.map +0 -1
  114. package/dist/context/__tests__/command-detector.test.js +0 -318
  115. package/dist/context/__tests__/command-detector.test.js.map +0 -1
  116. package/dist/context/__tests__/context-manager.test.d.ts +0 -16
  117. package/dist/context/__tests__/context-manager.test.d.ts.map +0 -1
  118. package/dist/context/__tests__/context-manager.test.js +0 -375
  119. package/dist/context/__tests__/context-manager.test.js.map +0 -1
  120. package/dist/context/__tests__/error-handling.test.d.ts +0 -15
  121. package/dist/context/__tests__/error-handling.test.d.ts.map +0 -1
  122. package/dist/context/__tests__/error-handling.test.js +0 -447
  123. package/dist/context/__tests__/error-handling.test.js.map +0 -1
  124. package/dist/context/handlers/__tests__/docker-handler.test.d.ts +0 -13
  125. package/dist/context/handlers/__tests__/docker-handler.test.d.ts.map +0 -1
  126. package/dist/context/handlers/__tests__/docker-handler.test.js +0 -285
  127. package/dist/context/handlers/__tests__/docker-handler.test.js.map +0 -1
  128. package/dist/context/handlers/__tests__/ssh-handler.test.d.ts +0 -13
  129. package/dist/context/handlers/__tests__/ssh-handler.test.d.ts.map +0 -1
  130. package/dist/context/handlers/__tests__/ssh-handler.test.js +0 -251
  131. package/dist/context/handlers/__tests__/ssh-handler.test.js.map +0 -1
  132. package/dist/context/handlers/__tests__/wsl-handler.test.d.ts +0 -7
  133. package/dist/context/handlers/__tests__/wsl-handler.test.d.ts.map +0 -1
  134. package/dist/context/handlers/__tests__/wsl-handler.test.js +0 -331
  135. package/dist/context/handlers/__tests__/wsl-handler.test.js.map +0 -1
  136. package/dist/index-custom.d.ts +0 -3
  137. package/dist/index-custom.d.ts.map +0 -1
  138. package/dist/index-custom.js +0 -65
  139. package/dist/index-custom.js.map +0 -1
  140. package/dist/prompts/system-prompt.d.ts +0 -47
  141. package/dist/prompts/system-prompt.d.ts.map +0 -1
  142. package/dist/prompts/system-prompt.js +0 -377
  143. package/dist/prompts/system-prompt.js.map +0 -1
  144. package/dist/providers/GoogleProvider.d.ts +0 -26
  145. package/dist/providers/GoogleProvider.d.ts.map +0 -1
  146. package/dist/providers/GoogleProvider.js +0 -313
  147. package/dist/providers/GoogleProvider.js.map +0 -1
  148. package/dist/providers/Provider.d.ts +0 -114
  149. package/dist/providers/Provider.d.ts.map +0 -1
  150. package/dist/providers/Provider.js +0 -44
  151. package/dist/providers/Provider.js.map +0 -1
  152. package/dist/services/__tests__/ai-context-injector.test.d.ts +0 -15
  153. package/dist/services/__tests__/ai-context-injector.test.d.ts.map +0 -1
  154. package/dist/services/__tests__/ai-context-injector.test.js +0 -326
  155. package/dist/services/__tests__/ai-context-injector.test.js.map +0 -1
  156. package/dist/src/context/types.js +0 -27
  157. package/dist/src/services/ai-context-injector.js +0 -96
  158. package/dist/src/services/ai-service-client.js +0 -270
  159. package/dist/src/services/api-client.js +0 -349
  160. package/dist/src/tools/types.js +0 -1
  161. package/dist/src/types/index.js +0 -1
  162. package/dist/test/context/types.js +0 -27
  163. package/dist/test/services/__tests__/ai-context-injector.test.js +0 -325
  164. package/dist/test/services/ai-context-injector.js +0 -96
  165. package/dist/test/services/ai-service-client.js +0 -270
  166. package/dist/test/services/api-client.js +0 -349
  167. package/dist/test/tools/types.js +0 -1
  168. package/dist/test/types/index.js +0 -1
  169. package/dist/test-ai-context-injector.js +0 -97
  170. package/dist/tests/automated-verification.d.ts +0 -27
  171. package/dist/tests/automated-verification.d.ts.map +0 -1
  172. package/dist/tests/automated-verification.js +0 -359
  173. package/dist/tests/automated-verification.js.map +0 -1
  174. package/dist/tests/integration-tests.d.ts +0 -50
  175. package/dist/tests/integration-tests.d.ts.map +0 -1
  176. package/dist/tests/integration-tests.js +0 -648
  177. package/dist/tests/integration-tests.js.map +0 -1
  178. package/dist/tools/file-ops-test.d.ts +0 -6
  179. package/dist/tools/file-ops-test.d.ts.map +0 -1
  180. package/dist/tools/file-ops-test.js +0 -197
  181. package/dist/tools/file-ops-test.js.map +0 -1
  182. package/dist/ui/DisplayHistory.d.ts +0 -53
  183. package/dist/ui/DisplayHistory.d.ts.map +0 -1
  184. package/dist/ui/DisplayHistory.js +0 -82
  185. package/dist/ui/DisplayHistory.js.map +0 -1
  186. package/dist/ui/clack-ui.d.ts +0 -83
  187. package/dist/ui/clack-ui.d.ts.map +0 -1
  188. package/dist/ui/clack-ui.js +0 -304
  189. package/dist/ui/clack-ui.js.map +0 -1
  190. package/dist/ui/components/DisplayItemRenderer.d.ts +0 -18
  191. package/dist/ui/components/DisplayItemRenderer.d.ts.map +0 -1
  192. package/dist/ui/components/DisplayItemRenderer.js +0 -53
  193. package/dist/ui/components/DisplayItemRenderer.js.map +0 -1
  194. package/dist/ui/components/DynamicMessage.d.ts +0 -13
  195. package/dist/ui/components/DynamicMessage.d.ts.map +0 -1
  196. package/dist/ui/components/DynamicMessage.js +0 -27
  197. package/dist/ui/components/DynamicMessage.js.map +0 -1
  198. package/dist/ui/components/FileViewerScreen.d.ts +0 -14
  199. package/dist/ui/components/FileViewerScreen.d.ts.map +0 -1
  200. package/dist/ui/components/FileViewerScreen.js +0 -74
  201. package/dist/ui/components/FileViewerScreen.js.map +0 -1
  202. package/dist/ui/components/ScrollableContent.d.ts +0 -7
  203. package/dist/ui/components/ScrollableContent.d.ts.map +0 -1
  204. package/dist/ui/components/ScrollableContent.js +0 -6
  205. package/dist/ui/components/ScrollableContent.js.map +0 -1
  206. package/dist/ui/components/ScrollableMessageList.d.ts +0 -10
  207. package/dist/ui/components/ScrollableMessageList.d.ts.map +0 -1
  208. package/dist/ui/components/ScrollableMessageList.js +0 -133
  209. package/dist/ui/components/ScrollableMessageList.js.map +0 -1
  210. package/dist/ui/components/ScrollableScreen.d.ts +0 -9
  211. package/dist/ui/components/ScrollableScreen.d.ts.map +0 -1
  212. package/dist/ui/components/ScrollableScreen.js +0 -22
  213. package/dist/ui/components/ScrollableScreen.js.map +0 -1
  214. package/dist/ui/components/StaticMessageHistory.d.ts +0 -14
  215. package/dist/ui/components/StaticMessageHistory.d.ts.map +0 -1
  216. package/dist/ui/components/StaticMessageHistory.js +0 -19
  217. package/dist/ui/components/StaticMessageHistory.js.map +0 -1
  218. package/dist/ui/components/code-block.d.ts +0 -10
  219. package/dist/ui/components/code-block.d.ts.map +0 -1
  220. package/dist/ui/components/code-block.js +0 -74
  221. package/dist/ui/components/code-block.js.map +0 -1
  222. package/dist/ui/components/confirm-prompt.d.ts +0 -12
  223. package/dist/ui/components/confirm-prompt.d.ts.map +0 -1
  224. package/dist/ui/components/confirm-prompt.js +0 -104
  225. package/dist/ui/components/confirm-prompt.js.map +0 -1
  226. package/dist/ui/components/diff-viewer.d.ts +0 -9
  227. package/dist/ui/components/diff-viewer.d.ts.map +0 -1
  228. package/dist/ui/components/diff-viewer.js +0 -57
  229. package/dist/ui/components/diff-viewer.js.map +0 -1
  230. package/dist/ui/components/input-box.d.ts +0 -18
  231. package/dist/ui/components/input-box.d.ts.map +0 -1
  232. package/dist/ui/components/input-box.js +0 -157
  233. package/dist/ui/components/input-box.js.map +0 -1
  234. package/dist/ui/components/keyboard-help.d.ts +0 -7
  235. package/dist/ui/components/keyboard-help.d.ts.map +0 -1
  236. package/dist/ui/components/keyboard-help.js +0 -43
  237. package/dist/ui/components/keyboard-help.js.map +0 -1
  238. package/dist/ui/components/loading-indicator.d.ts +0 -3
  239. package/dist/ui/components/loading-indicator.d.ts.map +0 -1
  240. package/dist/ui/components/loading-indicator.js +0 -42
  241. package/dist/ui/components/loading-indicator.js.map +0 -1
  242. package/dist/ui/components/message-display.d.ts +0 -7
  243. package/dist/ui/components/message-display.d.ts.map +0 -1
  244. package/dist/ui/components/message-display.js +0 -104
  245. package/dist/ui/components/message-display.js.map +0 -1
  246. package/dist/ui/components/misc.d.ts +0 -28
  247. package/dist/ui/components/misc.d.ts.map +0 -1
  248. package/dist/ui/components/misc.js +0 -128
  249. package/dist/ui/components/misc.js.map +0 -1
  250. package/dist/ui/components/select-prompt.d.ts +0 -13
  251. package/dist/ui/components/select-prompt.d.ts.map +0 -1
  252. package/dist/ui/components/select-prompt.js +0 -42
  253. package/dist/ui/components/select-prompt.js.map +0 -1
  254. package/dist/ui/components/status-bar.d.ts +0 -11
  255. package/dist/ui/components/status-bar.d.ts.map +0 -1
  256. package/dist/ui/components/status-bar.js +0 -47
  257. package/dist/ui/components/status-bar.js.map +0 -1
  258. package/dist/ui/components/tool-execution.d.ts +0 -3
  259. package/dist/ui/components/tool-execution.d.ts.map +0 -1
  260. package/dist/ui/components/tool-execution.js +0 -374
  261. package/dist/ui/components/tool-execution.js.map +0 -1
  262. package/dist/ui/components/tool-result.d.ts +0 -11
  263. package/dist/ui/components/tool-result.d.ts.map +0 -1
  264. package/dist/ui/components/tool-result.js +0 -58
  265. package/dist/ui/components/tool-result.js.map +0 -1
  266. package/dist/ui/components/welcome-banner.d.ts +0 -3
  267. package/dist/ui/components/welcome-banner.d.ts.map +0 -1
  268. package/dist/ui/components/welcome-banner.js +0 -46
  269. package/dist/ui/components/welcome-banner.js.map +0 -1
  270. package/dist/ui/hooks/useDisplayHistory.d.ts +0 -13
  271. package/dist/ui/hooks/useDisplayHistory.d.ts.map +0 -1
  272. package/dist/ui/hooks/useDisplayHistory.js +0 -45
  273. package/dist/ui/hooks/useDisplayHistory.js.map +0 -1
  274. package/dist/ui/render-engine.d.ts +0 -69
  275. package/dist/ui/render-engine.d.ts.map +0 -1
  276. package/dist/ui/render-engine.js +0 -197
  277. package/dist/ui/render-engine.js.map +0 -1
  278. package/dist/ui/terminal/TerminalRenderer.d.ts +0 -84
  279. package/dist/ui/terminal/TerminalRenderer.d.ts.map +0 -1
  280. package/dist/ui/terminal/TerminalRenderer.js +0 -154
  281. package/dist/ui/terminal/TerminalRenderer.js.map +0 -1
  282. package/dist/ui/terminal/TerminalUI.d.ts +0 -139
  283. package/dist/ui/terminal/TerminalUI.d.ts.map +0 -1
  284. package/dist/ui/terminal/TerminalUI.js +0 -430
  285. package/dist/ui/terminal/TerminalUI.js.map +0 -1
  286. package/dist/ui/terminal/VirtualChatBuffer.d.ts +0 -32
  287. package/dist/ui/terminal/VirtualChatBuffer.d.ts.map +0 -1
  288. package/dist/ui/terminal/VirtualChatBuffer.js +0 -37
  289. package/dist/ui/terminal/VirtualChatBuffer.js.map +0 -1
  290. package/dist/ui/terminal-kit-base.d.ts +0 -117
  291. package/dist/ui/terminal-kit-base.d.ts.map +0 -1
  292. package/dist/ui/terminal-kit-base.js +0 -188
  293. package/dist/ui/terminal-kit-base.js.map +0 -1
  294. package/dist/ui/utils/duplication-detector.d.ts +0 -32
  295. package/dist/ui/utils/duplication-detector.d.ts.map +0 -1
  296. package/dist/ui/utils/duplication-detector.js +0 -227
  297. package/dist/ui/utils/duplication-detector.js.map +0 -1
  298. package/dist/ui/utils/duplication-logger.d.ts +0 -21
  299. package/dist/ui/utils/duplication-logger.d.ts.map +0 -1
  300. package/dist/ui/utils/duplication-logger.js +0 -85
  301. package/dist/ui/utils/duplication-logger.js.map +0 -1
  302. package/dist/ui/utils/terminal-scanner.d.ts +0 -19
  303. package/dist/ui/utils/terminal-scanner.d.ts.map +0 -1
  304. package/dist/ui/utils/terminal-scanner.js +0 -217
  305. package/dist/ui/utils/terminal-scanner.js.map +0 -1
  306. package/dist/version.d.ts +0 -2
  307. package/dist/version.d.ts.map +0 -1
  308. package/dist/version.js +0 -3
  309. package/dist/version.js.map +0 -1
  310. package/scripts/generate-version.js +0 -25
@@ -1,6 +1,7 @@
1
1
  import React, { useState, useCallback } from 'react';
2
2
  import { Box, Text, useApp, useInput, Static } from 'ink';
3
3
  import { spawn } from 'child_process';
4
+ import * as fs from 'fs';
4
5
  import { WelcomeBanner } from './WelcomeBanner.js';
5
6
  import { InputBox } from './InputBox.js';
6
7
  import { MessageDisplay } from './MessageDisplay.js';
@@ -49,13 +50,43 @@ const ApprovalSection = React.memo(({ approvalRequest, onApprove }) => {
49
50
  // Only re-render if the approval request message changes
50
51
  return prevProps.approvalRequest.message === nextProps.approvalRequest.message;
51
52
  });
52
- export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceived, onResponseStream, onPickerSetup, onPickerSelection, onToolExecutionUpdate, onToolApprovalRequest, onToolStreamingOutput, onPlanModeChange, onPlanApprovalRequest, onCommandModeChange, onToggleCommandMode, onCwdChange, onSubshellContextChange, onPasswordRequest }) => {
53
+ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceived, onResponseStream, onThoughtStream, onThoughtComplete, onPickerSetup, onPickerSelection, onToolExecutionUpdate, onToolApprovalRequest, onToolStreamingOutput, onPlanModeChange, onPlanApprovalRequest, onCommandModeChange, onToggleCommandMode, onCwdChange, onModelChange, onSubshellContextChange, onPasswordRequest }) => {
53
54
  const { exit } = useApp();
54
55
  const autoAcceptRef = React.useRef(false);
55
56
  // Helper to clear screen
56
57
  const clearScreen = useCallback(() => {
57
58
  process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
58
59
  }, []);
60
+ // Helper to get max tokens for a model
61
+ const getMaxTokensForModel = useCallback((model) => {
62
+ // Import dynamically to avoid circular dependencies
63
+ try {
64
+ const { getModelContextWindow } = require('../../config/models.js');
65
+ return getModelContextWindow(model);
66
+ }
67
+ catch (error) {
68
+ // Fallback to defaults if config loading fails
69
+ if (model.includes('gemini-3'))
70
+ return 2000000;
71
+ if (model.includes('gemini-2.5'))
72
+ return 2000000;
73
+ if (model.includes('kimi'))
74
+ return 128000;
75
+ return 2000000;
76
+ }
77
+ }, []);
78
+ // Helper to estimate tokens (rough approximation: 1 token ≈ 4 characters)
79
+ const estimateTokens = useCallback((text) => {
80
+ return Math.ceil(text.length / 4);
81
+ }, []);
82
+ // Helper to calculate total tokens from messages
83
+ const calculateTotalTokens = useCallback((messages) => {
84
+ let total = 0;
85
+ for (const msg of messages) {
86
+ total += estimateTokens(msg.content);
87
+ }
88
+ return total;
89
+ }, [estimateTokens]);
59
90
  const [state, setState] = useState({
60
91
  screen: 'chat',
61
92
  messageHistory: [],
@@ -63,7 +94,7 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
63
94
  autoAcceptMode: false,
64
95
  isLoading: false,
65
96
  loadingMessage: '',
66
- currentModel: initialModel || 'gemini-2.0-flash-exp',
97
+ currentModel: initialModel || 'gemini-2.5-flash',
67
98
  planMode: initialPlanMode || false,
68
99
  commandMode: false,
69
100
  currentWorkingDirectory: process.cwd(),
@@ -72,6 +103,9 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
72
103
  subshellContext: undefined,
73
104
  versionInfo: undefined,
74
105
  checkingVersion: true,
106
+ isAiWorking: false,
107
+ currentTokens: 0,
108
+ maxTokens: getMaxTokensForModel(initialModel || 'gemini-2.5-flash'),
75
109
  pickerOptions: undefined,
76
110
  approvalRequest: undefined,
77
111
  planApprovalRequest: undefined,
@@ -98,6 +132,20 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
98
132
  React.useEffect(() => {
99
133
  autoAcceptRef.current = state.autoAcceptMode;
100
134
  }, [state.autoAcceptMode]);
135
+ // Update token count whenever messages change
136
+ React.useEffect(() => {
137
+ const allMessages = [...state.messageHistory];
138
+ if (state.currentMessage) {
139
+ allMessages.push(state.currentMessage);
140
+ }
141
+ const totalTokens = calculateTotalTokens(allMessages);
142
+ if (totalTokens !== state.currentTokens) {
143
+ setState(prev => ({
144
+ ...prev,
145
+ currentTokens: totalTokens
146
+ }));
147
+ }
148
+ }, [state.messageHistory, state.currentMessage, calculateTotalTokens]);
101
149
  // Track if we're currently streaming
102
150
  const isStreamingRef = React.useRef(false);
103
151
  // Set up callback to receive streaming chunks - only once on mount
@@ -105,33 +153,131 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
105
153
  onResponseStream((chunk) => {
106
154
  isStreamingRef.current = true; // Mark that we're streaming
107
155
  setState(prev => {
108
- // If we don't have a current assistant message, create one
156
+ // If we have a current assistant message (possibly created by thoughts), append to it
157
+ if (prev.currentMessage && prev.currentMessage.role === 'assistant') {
158
+ return {
159
+ ...prev,
160
+ currentMessage: {
161
+ ...prev.currentMessage,
162
+ content: prev.currentMessage.content + chunk
163
+ },
164
+ isAiWorking: true // Keep AI working state active
165
+ };
166
+ }
167
+ // Otherwise create a new message
168
+ const now = new Date();
169
+ const newMessage = {
170
+ id: `assistant-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
171
+ role: 'assistant',
172
+ content: chunk,
173
+ timestamp: now,
174
+ shouldStream: true // AI responses should stream
175
+ };
176
+ return {
177
+ ...prev,
178
+ currentMessage: newMessage,
179
+ isLoading: false,
180
+ isAiWorking: true // AI is actively working
181
+ };
182
+ });
183
+ });
184
+ }, [onResponseStream]);
185
+ // Set up callback to receive thought chunks
186
+ React.useEffect(() => {
187
+ onThoughtStream((thought) => {
188
+ // Debug logging to file
189
+ try {
190
+ fs.appendFileSync('cli_frontend_logs.txt', `[${new Date().toISOString()}] [App] Received thought: ${thought.substring(0, 100)}\n`);
191
+ }
192
+ catch (e) {
193
+ // Ignore logging errors
194
+ }
195
+ setState(prev => {
196
+ // If we don't have a current assistant message, create one for the thoughts
109
197
  if (!prev.currentMessage || prev.currentMessage.role !== 'assistant') {
198
+ try {
199
+ fs.appendFileSync('cli_frontend_logs.txt', `[${new Date().toISOString()}] [App] Creating new assistant message for thoughts\n`);
200
+ }
201
+ catch (e) {
202
+ // Ignore logging errors
203
+ }
110
204
  const now = new Date();
205
+ const newLines = thought.split('\n').filter(line => line.trim());
206
+ const last3Lines = newLines.slice(-3);
111
207
  const newMessage = {
112
208
  id: `assistant-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
113
209
  role: 'assistant',
114
- content: chunk,
210
+ content: '', // Empty content initially, will be filled when text arrives
115
211
  timestamp: now,
116
- shouldStream: true // AI responses should stream
212
+ shouldStream: true,
213
+ thoughts: last3Lines
117
214
  };
118
215
  return {
119
216
  ...prev,
120
217
  currentMessage: newMessage,
121
- isLoading: false
218
+ isLoading: false,
219
+ isAiWorking: true
122
220
  };
123
221
  }
124
- // Otherwise append chunk to existing message
222
+ // Split thought into lines and keep only last 3
223
+ const newLines = thought.split('\n').filter(line => line.trim());
224
+ const existingThoughts = prev.currentMessage.thoughts || [];
225
+ const allLines = [...existingThoughts, ...newLines];
226
+ const last3Lines = allLines.slice(-3);
227
+ try {
228
+ fs.appendFileSync('cli_frontend_logs.txt', `[${new Date().toISOString()}] [App] Updating thoughts: ${JSON.stringify(last3Lines)}\n`);
229
+ }
230
+ catch (e) {
231
+ // Ignore logging errors
232
+ }
125
233
  return {
126
234
  ...prev,
127
235
  currentMessage: {
128
236
  ...prev.currentMessage,
129
- content: prev.currentMessage.content + chunk
237
+ thoughts: last3Lines,
238
+ thinkingDuration: undefined // Clear duration while thinking
130
239
  }
131
240
  };
132
241
  });
133
242
  });
134
- }, [onResponseStream]);
243
+ }, [onThoughtStream]);
244
+ // Set up callback for when thinking completes
245
+ React.useEffect(() => {
246
+ onThoughtComplete((durationSeconds) => {
247
+ try {
248
+ fs.appendFileSync('cli_frontend_logs.txt', `[${new Date().toISOString()}] [App] onThoughtComplete called with ${durationSeconds}s\n`);
249
+ }
250
+ catch (e) {
251
+ // Ignore
252
+ }
253
+ setState(prev => {
254
+ // Only update if we have a current assistant message
255
+ if (!prev.currentMessage || prev.currentMessage.role !== 'assistant') {
256
+ try {
257
+ fs.appendFileSync('cli_frontend_logs.txt', `[${new Date().toISOString()}] [App] No current assistant message for thought completion\n`);
258
+ }
259
+ catch (e) {
260
+ // Ignore
261
+ }
262
+ return prev;
263
+ }
264
+ try {
265
+ fs.appendFileSync('cli_frontend_logs.txt', `[${new Date().toISOString()}] [App] Setting thinkingDuration to ${durationSeconds}s on message ${prev.currentMessage.id}\n`);
266
+ }
267
+ catch (e) {
268
+ // Ignore
269
+ }
270
+ return {
271
+ ...prev,
272
+ currentMessage: {
273
+ ...prev.currentMessage,
274
+ thoughts: undefined, // Clear thoughts
275
+ thinkingDuration: durationSeconds // Set duration
276
+ }
277
+ };
278
+ });
279
+ });
280
+ }, [onThoughtComplete]);
135
281
  // Set up callback to receive complete responses (for slash commands and non-streaming responses)
136
282
  React.useEffect(() => {
137
283
  onResponseReceived((message) => {
@@ -148,7 +294,8 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
148
294
  return {
149
295
  ...prev,
150
296
  currentMessage: null,
151
- isLoading: false
297
+ isLoading: false,
298
+ isAiWorking: false // AI finished working
152
299
  };
153
300
  }
154
301
  // Create complete message with full content
@@ -161,12 +308,14 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
161
308
  ...prev,
162
309
  messageHistory: [...prev.messageHistory, completeMessage],
163
310
  currentMessage: null,
164
- isLoading: false
311
+ isLoading: false,
312
+ isAiWorking: false // AI finished working
165
313
  };
166
314
  }
167
315
  return {
168
316
  ...prev,
169
- isLoading: false
317
+ isLoading: false,
318
+ isAiWorking: false // AI finished working
170
319
  };
171
320
  });
172
321
  return;
@@ -187,7 +336,8 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
187
336
  ...prev,
188
337
  messageHistory: [...prev.messageHistory, outputMessage],
189
338
  currentMessage: null,
190
- isLoading: false
339
+ isLoading: false,
340
+ isAiWorking: false // AI finished working
191
341
  };
192
342
  }
193
343
  // For non-streaming responses (like slash commands), display the complete message
@@ -205,14 +355,16 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
205
355
  ...prev,
206
356
  messageHistory: [...prev.messageHistory, prev.currentMessage],
207
357
  currentMessage: newMessage,
208
- isLoading: false
358
+ isLoading: false,
359
+ isAiWorking: false // AI finished working
209
360
  };
210
361
  }
211
362
  // Otherwise just set as current
212
363
  return {
213
364
  ...prev,
214
365
  currentMessage: newMessage,
215
- isLoading: false
366
+ isLoading: false,
367
+ isAiWorking: false // AI finished working
216
368
  };
217
369
  });
218
370
  });
@@ -244,36 +396,47 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
244
396
  timestamp: new Date(),
245
397
  toolExecution: { ...update }
246
398
  };
247
- // If current message is a tool with same name and executing, update it
399
+ // If current message is a tool with same name and executing, update it in place
248
400
  if (prev.currentMessage?.role === 'tool' &&
249
401
  prev.currentMessage.toolExecution?.toolName === update.toolName &&
250
402
  prev.currentMessage.toolExecution?.status === 'executing' &&
251
403
  update.status !== 'executing') {
252
- // Move completed tool to history
404
+ // Tool completed - update in place and keep as current
253
405
  return {
254
406
  ...prev,
255
- messageHistory: [...prev.messageHistory, { ...prev.currentMessage, toolExecution: { ...update } }],
256
- currentMessage: null
407
+ currentMessage: { ...prev.currentMessage, toolExecution: { ...update } },
408
+ isAiWorking: true // Keep AI working state active (more tool calls or response may follow)
257
409
  };
258
410
  }
259
- // If starting new tool execution, move current to history if exists
411
+ // If starting new tool execution
260
412
  if (update.status === 'executing') {
261
- // If there's a current assistant message being streamed, move it to history first
262
- const newHistory = prev.currentMessage && prev.currentMessage.role === 'assistant'
263
- ? [...prev.messageHistory, prev.currentMessage]
264
- : prev.messageHistory;
413
+ // IMPORTANT: Always preserve current message by moving it to history first
414
+ let newHistory = prev.messageHistory;
415
+ if (prev.currentMessage) {
416
+ // Move ANY current message to history before showing tool
417
+ newHistory = [...prev.messageHistory, prev.currentMessage];
418
+ }
265
419
  return {
266
420
  ...prev,
267
421
  messageHistory: newHistory,
268
422
  currentMessage: toolMessage,
269
- isLoading: false // Clear loading state when tool starts
423
+ isLoading: false,
424
+ isAiWorking: true
270
425
  };
271
426
  }
272
- // For completed tools without a current executing one, add directly to history
427
+ // For completed tools without a current executing one
428
+ // IMPORTANT: Preserve current message before adding tool to history
429
+ let newHistory = prev.messageHistory;
430
+ // If there's a current message, move it to history first
431
+ if (prev.currentMessage) {
432
+ newHistory = [...prev.messageHistory, prev.currentMessage];
433
+ }
434
+ // Add tool message to history
273
435
  return {
274
436
  ...prev,
275
- messageHistory: [...prev.messageHistory, toolMessage],
276
- currentMessage: null
437
+ messageHistory: [...newHistory, toolMessage],
438
+ currentMessage: null,
439
+ isAiWorking: true // Keep AI working state active
277
440
  };
278
441
  });
279
442
  });
@@ -303,22 +466,27 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
303
466
  resolve: (approved) => {
304
467
  // Clear screen and return to chat
305
468
  clearScreen();
306
- // Add approval result to chat history
307
- const resultMessage = {
308
- id: `approval-result-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
309
- role: 'system',
310
- content: approved
311
- ? `✓ ${request.operationType === 'write_file' ? 'File write' : request.operationType === 'edit_file' ? 'File edit' : 'Command'} approved`
312
- : `✗ ${request.operationType === 'write_file' ? 'File write' : request.operationType === 'edit_file' ? 'File edit' : 'Command'} declined`,
313
- timestamp: new Date()
314
- };
315
469
  // Switch back to chat and clear approval request
316
- setState(prev => ({
317
- ...prev,
318
- screen: 'chat',
319
- approvalRequest: undefined,
320
- messageHistory: [...prev.messageHistory, resultMessage]
321
- }));
470
+ setState(prev => {
471
+ const nextState = {
472
+ ...prev,
473
+ screen: 'chat',
474
+ approvalRequest: undefined,
475
+ };
476
+ if (!approved) {
477
+ const resultMessage = {
478
+ id: `approval-result-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
479
+ role: 'system',
480
+ content: `✗ ${request.operationType === 'write_file' ? 'File write' : request.operationType === 'edit_file' ? 'File edit' : 'Command'} declined`,
481
+ timestamp: new Date()
482
+ };
483
+ return {
484
+ ...nextState,
485
+ messageHistory: [...prev.messageHistory, resultMessage]
486
+ };
487
+ }
488
+ return nextState;
489
+ });
322
490
  resolve(approved);
323
491
  }
324
492
  },
@@ -338,21 +506,26 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
338
506
  operationType: request.operationType,
339
507
  operationDetails: request.operationDetails,
340
508
  resolve: (approved) => {
341
- // Add approval result to chat history
342
- const resultMessage = {
343
- id: `approval-result-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
344
- role: 'system',
345
- content: approved
346
- ? `✓ Command approved`
347
- : `✗ Command declined`,
348
- timestamp: new Date()
349
- };
350
- // Clear approval request and add result to history
351
- setState(prev => ({
352
- ...prev,
353
- approvalRequest: undefined,
354
- messageHistory: [...prev.messageHistory, resultMessage]
355
- }));
509
+ // Clear approval request
510
+ setState(prev => {
511
+ const nextState = {
512
+ ...prev,
513
+ approvalRequest: undefined,
514
+ };
515
+ if (!approved) {
516
+ const resultMessage = {
517
+ id: `approval-result-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
518
+ role: 'system',
519
+ content: `✗ Command declined`,
520
+ timestamp: new Date()
521
+ };
522
+ return {
523
+ ...nextState,
524
+ messageHistory: [...prev.messageHistory, resultMessage]
525
+ };
526
+ }
527
+ return nextState;
528
+ });
356
529
  resolve(approved);
357
530
  }
358
531
  },
@@ -412,6 +585,15 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
412
585
  }));
413
586
  });
414
587
  }, [onCwdChange]);
588
+ // Set up callback to receive model changes
589
+ React.useEffect(() => {
590
+ onModelChange((modelName) => {
591
+ setState(prev => ({
592
+ ...prev,
593
+ currentModel: modelName
594
+ }));
595
+ });
596
+ }, [onModelChange]);
415
597
  // Set up callback to receive subshell context changes
416
598
  React.useEffect(() => {
417
599
  onSubshellContextChange((context) => {
@@ -493,10 +675,11 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
493
675
  // Ctrl+C handling - double press to exit
494
676
  if (key.ctrl && input === 'c') {
495
677
  // If AI is loading/executing, first Ctrl+C cancels the operation
496
- if (state.isLoading) {
678
+ if (state.isLoading || state.isAiWorking) {
497
679
  setState(prev => ({
498
680
  ...prev,
499
681
  isLoading: false,
682
+ isAiWorking: false,
500
683
  currentMessage: null,
501
684
  showExitWarning: true
502
685
  }));
@@ -567,7 +750,8 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
567
750
  ...prev,
568
751
  messageHistory: [],
569
752
  currentMessage: null,
570
- isLoading: false
753
+ isLoading: false,
754
+ currentTokens: 0
571
755
  }));
572
756
  // Still call onMessage to clear backend history
573
757
  try {
@@ -597,6 +781,7 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
597
781
  messageHistory: [...newHistory, commandMessage],
598
782
  currentMessage: null,
599
783
  isLoading: false,
784
+ isAiWorking: false, // Command mode doesn't use AI
600
785
  };
601
786
  });
602
787
  }
@@ -620,6 +805,7 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
620
805
  currentMessage: null,
621
806
  isLoading: true,
622
807
  loadingMessage: 'Processing your request...',
808
+ isAiWorking: true // AI is starting to work
623
809
  };
624
810
  });
625
811
  }
@@ -629,6 +815,7 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
629
815
  ...prev,
630
816
  isLoading: true,
631
817
  loadingMessage: 'Processing command...',
818
+ isAiWorking: true // AI is starting to work
632
819
  }));
633
820
  }
634
821
  // Add to command history (avoid duplicates of the last command)
@@ -658,14 +845,16 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
658
845
  ...prev,
659
846
  messageHistory: newHistory,
660
847
  currentMessage: null,
661
- isLoading: false
848
+ isLoading: false,
849
+ isAiWorking: false // AI finished working
662
850
  };
663
851
  });
664
852
  }
665
853
  finally {
666
854
  setState(prev => ({
667
855
  ...prev,
668
- isLoading: false
856
+ isLoading: false,
857
+ isAiWorking: false // AI finished working
669
858
  }));
670
859
  }
671
860
  }, [onMessage]);
@@ -675,18 +864,16 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
675
864
  autoAcceptMode: !prev.autoAcceptMode
676
865
  }));
677
866
  }, []);
678
- // Check if there are any executing tools to avoid double spinners
679
- const hasExecutingTools = React.useMemo(() => state.currentMessage?.role === 'tool' && state.currentMessage?.toolExecution?.status === 'executing', [state.currentMessage]);
680
867
  return (React.createElement(Box, { flexDirection: "column" },
681
868
  state.screen === 'chat' && (React.createElement(React.Fragment, null,
682
869
  React.createElement(MessageList, { history: state.messageHistory, current: state.currentMessage, showBanner: true }),
683
- state.isLoading && !hasExecutingTools && React.createElement(LoadingIndicator, { key: "loading-indicator" }),
870
+ state.isAiWorking && React.createElement(LoadingIndicator, { key: "loading-indicator" }),
684
871
  state.approvalRequest ? (React.createElement(ApprovalSection, { key: `approval-${state.approvalRequest.message}`, approvalRequest: state.approvalRequest, onApprove: (approved) => {
685
872
  const resolve = state.approvalRequest?.resolve;
686
873
  if (resolve) {
687
874
  resolve(approved);
688
875
  }
689
- } })) : (React.createElement(InputBox, { key: "input-box", onSubmit: handleSubmit, autoAcceptMode: state.autoAcceptMode, model: state.currentModel, planMode: state.planMode, commandMode: state.commandMode, currentWorkingDirectory: state.currentWorkingDirectory, commandHistory: state.commandHistory, onToggleAutoAccept: handleToggleAutoAccept, onToggleCommandMode: onToggleCommandMode, isActive: true, subshellContext: state.subshellContext })),
876
+ } })) : (React.createElement(InputBox, { key: "input-box", onSubmit: handleSubmit, autoAcceptMode: state.autoAcceptMode, model: state.currentModel, planMode: state.planMode, commandMode: state.commandMode, currentWorkingDirectory: state.currentWorkingDirectory, commandHistory: state.commandHistory, onToggleAutoAccept: handleToggleAutoAccept, onToggleCommandMode: onToggleCommandMode, isActive: true, subshellContext: state.subshellContext, currentTokens: state.currentTokens, maxTokens: state.maxTokens })),
690
877
  state.showExitWarning && (React.createElement(Box, { marginTop: 1 },
691
878
  React.createElement(Text, { color: "#ffaa00", bold: true }, "\u26A0\uFE0F Press Ctrl+C again to exit"))))),
692
879
  state.screen === 'approval' && state.approvalRequest && (React.createElement(ApprovalSection, { key: `approval-${state.approvalRequest.message}`, approvalRequest: state.approvalRequest, onApprove: (approved) => {
@@ -700,8 +887,13 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
700
887
  setState(prev => ({ ...prev, screen: 'chat', isLoading: true }));
701
888
  try {
702
889
  await onPickerSelection(value, state.pickerOptions.type);
703
- // Update currentModel (picker is always for model selection now)
704
- setState(prev => ({ ...prev, currentModel: value, isLoading: false }));
890
+ // Model name will be updated via onModelChange callback
891
+ // Just update loading state here
892
+ setState(prev => ({
893
+ ...prev,
894
+ isLoading: false,
895
+ isAiWorking: false
896
+ }));
705
897
  }
706
898
  catch (error) {
707
899
  setState(prev => {
@@ -715,7 +907,8 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
715
907
  ...prev,
716
908
  messageHistory: [...prev.messageHistory, errorMessage],
717
909
  currentMessage: null,
718
- isLoading: false
910
+ isLoading: false,
911
+ isAiWorking: false
719
912
  };
720
913
  });
721
914
  }
@@ -741,7 +934,8 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
741
934
  ...prev,
742
935
  screen: 'chat',
743
936
  planApprovalRequest: undefined,
744
- isLoading: true
937
+ isLoading: true,
938
+ isAiWorking: true
745
939
  }));
746
940
  }, onNo: () => {
747
941
  const resolve = state.planApprovalRequest?.resolve;
@@ -779,8 +973,14 @@ export const App = ({ onMessage, initialModel, initialPlanMode, onResponseReceiv
779
973
  });
780
974
  installProcess.on('close', (code) => {
781
975
  if (code === 0) {
782
- console.log('\n✅ Update complete!\n');
783
- console.log('Please run "centaurus" again to start the updated version.\n');
976
+ console.log('\n✅ Update complete! Restarting Centaurus CLI...\n');
977
+ // Restart the CLI
978
+ const restartProcess = spawn('centaurus', [], {
979
+ stdio: 'inherit',
980
+ shell: true,
981
+ detached: true
982
+ });
983
+ restartProcess.unref();
784
984
  process.exit(0);
785
985
  }
786
986
  else {