@within-7/minto 0.1.7 → 0.3.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 (601) hide show
  1. package/cli.js +155 -37
  2. package/dist/Tool.js +38 -0
  3. package/dist/Tool.js.map +3 -3
  4. package/dist/commands/agents/AgentsCommand.js +73 -49
  5. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  6. package/dist/commands/agents/constants.js +1 -1
  7. package/dist/commands/agents/constants.js.map +1 -1
  8. package/dist/commands/agents/index.js +1 -1
  9. package/dist/commands/bug.js +74 -7
  10. package/dist/commands/bug.js.map +3 -3
  11. package/dist/commands/clear.js +3 -0
  12. package/dist/commands/clear.js.map +2 -2
  13. package/dist/commands/compact.js +37 -0
  14. package/dist/commands/compact.js.map +2 -2
  15. package/dist/commands/context.js +85 -0
  16. package/dist/commands/context.js.map +7 -0
  17. package/dist/commands/ctx_viz.js +18 -10
  18. package/dist/commands/ctx_viz.js.map +2 -2
  19. package/dist/commands/doctor.js +158 -12
  20. package/dist/commands/doctor.js.map +2 -2
  21. package/dist/commands/export.js +157 -0
  22. package/dist/commands/export.js.map +7 -0
  23. package/dist/commands/mcp-interactive.js +28 -18
  24. package/dist/commands/mcp-interactive.js.map +2 -2
  25. package/dist/commands/model.js +9 -7
  26. package/dist/commands/model.js.map +2 -2
  27. package/dist/commands/permissions.js +87 -0
  28. package/dist/commands/permissions.js.map +7 -0
  29. package/dist/commands/plugin/AddMarketplaceForm.js +3 -2
  30. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  31. package/dist/commands/plugin/ConfirmDialog.js +2 -1
  32. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  33. package/dist/commands/plugin/ErrorView.js +2 -1
  34. package/dist/commands/plugin/ErrorView.js.map +2 -2
  35. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +5 -4
  36. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  37. package/dist/commands/plugin/InstalledPluginsManager.js +5 -4
  38. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  39. package/dist/commands/plugin/MainMenu.js +2 -1
  40. package/dist/commands/plugin/MainMenu.js.map +2 -2
  41. package/dist/commands/plugin/MarketplaceManager.js +5 -4
  42. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  43. package/dist/commands/plugin/MarketplaceSelector.js +4 -3
  44. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  45. package/dist/commands/plugin/PlaceholderScreen.js +3 -2
  46. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  47. package/dist/commands/plugin/PluginBrowser.js +6 -5
  48. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  49. package/dist/commands/plugin/PluginDetailsInstall.js +5 -4
  50. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  51. package/dist/commands/plugin/PluginDetailsManage.js +4 -3
  52. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  53. package/dist/commands/plugin.js +16 -15
  54. package/dist/commands/plugin.js.map +2 -2
  55. package/dist/commands/quit.js +3 -1
  56. package/dist/commands/quit.js.map +2 -2
  57. package/dist/commands/sandbox.js +105 -0
  58. package/dist/commands/sandbox.js.map +7 -0
  59. package/dist/commands/setup.js +2 -1
  60. package/dist/commands/setup.js.map +2 -2
  61. package/dist/commands/status.js +59 -0
  62. package/dist/commands/status.js.map +7 -0
  63. package/dist/commands/tasks.js +108 -0
  64. package/dist/commands/tasks.js.map +7 -0
  65. package/dist/commands/todos.js +123 -0
  66. package/dist/commands/todos.js.map +7 -0
  67. package/dist/commands/undo.js +245 -0
  68. package/dist/commands/undo.js.map +7 -0
  69. package/dist/commands.js +22 -2
  70. package/dist/commands.js.map +2 -2
  71. package/dist/components/AgentThinkingBlock.js +10 -18
  72. package/dist/components/AgentThinkingBlock.js.map +2 -2
  73. package/dist/components/AsciiLogo.js +7 -8
  74. package/dist/components/AsciiLogo.js.map +2 -2
  75. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
  76. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  77. package/dist/components/AskUserQuestionDialog/QuestionView.js +2 -1
  78. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  79. package/dist/components/BackgroundTasksPanel.js +78 -29
  80. package/dist/components/BackgroundTasksPanel.js.map +2 -2
  81. package/dist/components/BashStreamingProgress.js +24 -0
  82. package/dist/components/BashStreamingProgress.js.map +7 -0
  83. package/dist/components/CollapsibleHint.js +15 -0
  84. package/dist/components/CollapsibleHint.js.map +7 -0
  85. package/dist/components/Config.js +3 -2
  86. package/dist/components/Config.js.map +2 -2
  87. package/dist/components/ConsoleOAuthFlow.js +2 -1
  88. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  89. package/dist/components/Cost.js +2 -1
  90. package/dist/components/Cost.js.map +2 -2
  91. package/dist/components/FileEditToolUpdatedMessage.js +1 -1
  92. package/dist/components/FileEditToolUpdatedMessage.js.map +2 -2
  93. package/dist/components/HeaderBar.js +13 -8
  94. package/dist/components/HeaderBar.js.map +2 -2
  95. package/dist/components/HistorySearchOverlay.js +4 -3
  96. package/dist/components/HistorySearchOverlay.js.map +2 -2
  97. package/dist/components/HotkeyHelpPanel.js +134 -0
  98. package/dist/components/HotkeyHelpPanel.js.map +7 -0
  99. package/dist/components/InvalidConfigDialog.js +2 -1
  100. package/dist/components/InvalidConfigDialog.js.map +2 -2
  101. package/dist/components/Logo.js +24 -68
  102. package/dist/components/Logo.js.map +2 -2
  103. package/dist/components/MCPServerApprovalDialog.js +2 -1
  104. package/dist/components/MCPServerApprovalDialog.js.map +2 -2
  105. package/dist/components/MCPServerDialogCopy.js +2 -1
  106. package/dist/components/MCPServerDialogCopy.js.map +2 -2
  107. package/dist/components/MCPServerMultiselectDialog.js +2 -1
  108. package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
  109. package/dist/components/Message.js +23 -7
  110. package/dist/components/Message.js.map +3 -3
  111. package/dist/components/MessageSelector.js +4 -3
  112. package/dist/components/MessageSelector.js.map +2 -2
  113. package/dist/components/ModeIndicator.js +2 -1
  114. package/dist/components/ModeIndicator.js.map +2 -2
  115. package/dist/components/ModelConfig.js +20 -6
  116. package/dist/components/ModelConfig.js.map +2 -2
  117. package/dist/components/ModelListManager.js +7 -6
  118. package/dist/components/ModelListManager.js.map +2 -2
  119. package/dist/components/ModelSelector/ModelSelector.js +27 -14
  120. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  121. package/dist/components/Onboarding.js +22 -16
  122. package/dist/components/Onboarding.js.map +2 -2
  123. package/dist/components/OperationSummary.js +130 -0
  124. package/dist/components/OperationSummary.js.map +7 -0
  125. package/dist/components/ProgressBar.js +74 -0
  126. package/dist/components/ProgressBar.js.map +7 -0
  127. package/dist/components/PromptInput.js +210 -87
  128. package/dist/components/PromptInput.js.map +2 -2
  129. package/dist/components/RequestStatusIndicator.js +194 -0
  130. package/dist/components/RequestStatusIndicator.js.map +7 -0
  131. package/dist/components/SensitiveFileWarning.js +31 -0
  132. package/dist/components/SensitiveFileWarning.js.map +7 -0
  133. package/dist/components/Spinner.js +141 -27
  134. package/dist/components/Spinner.js.map +2 -2
  135. package/dist/components/SpinnerSymbol.js +21 -27
  136. package/dist/components/SpinnerSymbol.js.map +2 -2
  137. package/dist/components/StreamingBashOutput.js +9 -8
  138. package/dist/components/StreamingBashOutput.js.map +2 -2
  139. package/dist/components/StructuredDiff.js +6 -8
  140. package/dist/components/StructuredDiff.js.map +2 -2
  141. package/dist/components/SubagentBlock.js +5 -3
  142. package/dist/components/SubagentBlock.js.map +2 -2
  143. package/dist/components/SubagentProgress.js +17 -15
  144. package/dist/components/SubagentProgress.js.map +2 -2
  145. package/dist/components/TaskCard.js +30 -24
  146. package/dist/components/TaskCard.js.map +2 -2
  147. package/dist/components/TextInput.js +9 -1
  148. package/dist/components/TextInput.js.map +2 -2
  149. package/dist/components/TodoChangeBlock.js +1 -1
  150. package/dist/components/TodoChangeBlock.js.map +2 -2
  151. package/dist/components/TodoPanel.js +140 -31
  152. package/dist/components/TodoPanel.js.map +3 -3
  153. package/dist/components/TokenCounter.js +74 -0
  154. package/dist/components/TokenCounter.js.map +7 -0
  155. package/dist/components/TokenWarning.js +2 -1
  156. package/dist/components/TokenWarning.js.map +2 -2
  157. package/dist/components/ToolUseLoader.js +2 -2
  158. package/dist/components/ToolUseLoader.js.map +2 -2
  159. package/dist/components/TreeConnector.js +26 -0
  160. package/dist/components/TreeConnector.js.map +7 -0
  161. package/dist/components/TrustDialog.js +2 -1
  162. package/dist/components/TrustDialog.js.map +2 -2
  163. package/dist/components/TurnCompletionIndicator.js +18 -0
  164. package/dist/components/TurnCompletionIndicator.js.map +7 -0
  165. package/dist/components/binary-feedback/BinaryFeedbackView.js +2 -1
  166. package/dist/components/binary-feedback/BinaryFeedbackView.js.map +2 -2
  167. package/dist/components/messages/AssistantTextMessage.js +20 -9
  168. package/dist/components/messages/AssistantTextMessage.js.map +2 -2
  169. package/dist/components/messages/AssistantThinkingMessage.js +18 -3
  170. package/dist/components/messages/AssistantThinkingMessage.js.map +2 -2
  171. package/dist/components/messages/AssistantToolUseMessage.js +17 -10
  172. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  173. package/dist/components/messages/GroupRenderer.js +54 -0
  174. package/dist/components/messages/GroupRenderer.js.map +7 -0
  175. package/dist/components/messages/NestedTasksPreview.js +24 -0
  176. package/dist/components/messages/NestedTasksPreview.js.map +7 -0
  177. package/dist/components/messages/ParallelTasksGroupView.js +93 -0
  178. package/dist/components/messages/ParallelTasksGroupView.js.map +7 -0
  179. package/dist/components/messages/TaskInModuleView.js +218 -0
  180. package/dist/components/messages/TaskInModuleView.js.map +7 -0
  181. package/dist/components/messages/TaskOutputContent.js +56 -0
  182. package/dist/components/messages/TaskOutputContent.js.map +7 -0
  183. package/dist/components/messages/UserPromptMessage.js +2 -2
  184. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  185. package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js +2 -3
  186. package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js.map +2 -2
  187. package/dist/components/permissions/FallbackPermissionRequest.js +4 -4
  188. package/dist/components/permissions/FallbackPermissionRequest.js.map +2 -2
  189. package/dist/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.js +4 -4
  190. package/dist/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.js.map +2 -2
  191. package/dist/constants/colors.js +120 -54
  192. package/dist/constants/colors.js.map +2 -2
  193. package/dist/constants/formatRules.js +102 -0
  194. package/dist/constants/formatRules.js.map +7 -0
  195. package/dist/constants/prompts.js +12 -34
  196. package/dist/constants/prompts.js.map +2 -2
  197. package/dist/constants/symbols.js +64 -6
  198. package/dist/constants/symbols.js.map +2 -2
  199. package/dist/constants/timing.js +5 -0
  200. package/dist/constants/timing.js.map +2 -2
  201. package/dist/constants/toolInputExamples.js +84 -0
  202. package/dist/constants/toolInputExamples.js.map +7 -0
  203. package/dist/core/backupManager.js +321 -0
  204. package/dist/core/backupManager.js.map +7 -0
  205. package/dist/core/config/defaults.js +84 -0
  206. package/dist/core/config/defaults.js.map +7 -0
  207. package/dist/core/config/index.js +111 -0
  208. package/dist/core/config/index.js.map +7 -0
  209. package/dist/core/config/loader.js +221 -0
  210. package/dist/core/config/loader.js.map +7 -0
  211. package/dist/core/config/migrations.js +128 -0
  212. package/dist/core/config/migrations.js.map +7 -0
  213. package/dist/core/config/schema.js +178 -0
  214. package/dist/core/config/schema.js.map +7 -0
  215. package/dist/core/costTracker.js +129 -0
  216. package/dist/core/costTracker.js.map +7 -0
  217. package/dist/core/gitAutoCommit.js +287 -0
  218. package/dist/core/gitAutoCommit.js.map +7 -0
  219. package/dist/core/index.js +8 -0
  220. package/dist/core/index.js.map +7 -0
  221. package/dist/core/operationTracker.js +212 -0
  222. package/dist/core/operationTracker.js.map +7 -0
  223. package/dist/core/permissions/auditLog.js +204 -0
  224. package/dist/core/permissions/auditLog.js.map +7 -0
  225. package/dist/core/permissions/engine/index.js +3 -0
  226. package/dist/core/permissions/engine/index.js.map +7 -0
  227. package/dist/core/permissions/engine/permissionEngine.js +106 -0
  228. package/dist/core/permissions/engine/permissionEngine.js.map +7 -0
  229. package/dist/core/permissions/engine/types.js +1 -0
  230. package/dist/core/permissions/engine/types.js.map +7 -0
  231. package/dist/core/permissions/index.js +84 -0
  232. package/dist/core/permissions/index.js.map +7 -0
  233. package/dist/core/permissions/ruleEngine.js +259 -0
  234. package/dist/core/permissions/ruleEngine.js.map +7 -0
  235. package/dist/core/permissions/rules/allowedToolsRule.js +62 -0
  236. package/dist/core/permissions/rules/allowedToolsRule.js.map +7 -0
  237. package/dist/core/permissions/rules/autoEscalationRule.js +296 -0
  238. package/dist/core/permissions/rules/autoEscalationRule.js.map +7 -0
  239. package/dist/core/permissions/rules/index.js +46 -0
  240. package/dist/core/permissions/rules/index.js.map +7 -0
  241. package/dist/core/permissions/rules/planModeRule.js +55 -0
  242. package/dist/core/permissions/rules/planModeRule.js.map +7 -0
  243. package/dist/core/permissions/rules/projectBoundaryRule.js +173 -0
  244. package/dist/core/permissions/rules/projectBoundaryRule.js.map +7 -0
  245. package/dist/core/permissions/rules/safeModeRule.js +65 -0
  246. package/dist/core/permissions/rules/safeModeRule.js.map +7 -0
  247. package/dist/core/permissions/rules/sensitivePathsRule.js +345 -0
  248. package/dist/core/permissions/rules/sensitivePathsRule.js.map +7 -0
  249. package/dist/core/permissions/types.js +127 -0
  250. package/dist/core/permissions/types.js.map +7 -0
  251. package/dist/core/tokenStats.js +9 -0
  252. package/dist/core/tokenStats.js.map +7 -0
  253. package/dist/core/tokenStatsManager.js +331 -0
  254. package/dist/core/tokenStatsManager.js.map +7 -0
  255. package/dist/core/tools/executor.js +143 -0
  256. package/dist/core/tools/executor.js.map +7 -0
  257. package/dist/core/tools/index.js +15 -0
  258. package/dist/core/tools/index.js.map +7 -0
  259. package/dist/core/tools/registry.js +183 -0
  260. package/dist/core/tools/registry.js.map +7 -0
  261. package/dist/core/tools/types.js +1 -0
  262. package/dist/core/tools/types.js.map +7 -0
  263. package/dist/cost-tracker.js +23 -15
  264. package/dist/cost-tracker.js.map +2 -2
  265. package/dist/entrypoints/cli.js +158 -130
  266. package/dist/entrypoints/cli.js.map +2 -2
  267. package/dist/entrypoints/mcp.js +12 -4
  268. package/dist/entrypoints/mcp.js.map +2 -2
  269. package/dist/history.js +14 -3
  270. package/dist/history.js.map +2 -2
  271. package/dist/hooks/useAgentTokenStats.js +72 -0
  272. package/dist/hooks/useAgentTokenStats.js.map +7 -0
  273. package/dist/hooks/useAgentTranscripts.js +140 -0
  274. package/dist/hooks/useAgentTranscripts.js.map +7 -0
  275. package/dist/hooks/useAnimationSync.js +53 -0
  276. package/dist/hooks/useAnimationSync.js.map +7 -0
  277. package/dist/hooks/useArrowKeyHistory.js +4 -2
  278. package/dist/hooks/useArrowKeyHistory.js.map +2 -2
  279. package/dist/hooks/useCanUseTool.js +3 -1
  280. package/dist/hooks/useCanUseTool.js.map +2 -2
  281. package/dist/hooks/useExitOnCtrlCD.js +9 -5
  282. package/dist/hooks/useExitOnCtrlCD.js.map +2 -2
  283. package/dist/hooks/useHookStatus.js +40 -0
  284. package/dist/hooks/useHookStatus.js.map +7 -0
  285. package/dist/hooks/useLogMessages.js +29 -2
  286. package/dist/hooks/useLogMessages.js.map +2 -2
  287. package/dist/hooks/useMessageGroups.js +43 -0
  288. package/dist/hooks/useMessageGroups.js.map +7 -0
  289. package/dist/hooks/useTerminalSize.js +62 -6
  290. package/dist/hooks/useTerminalSize.js.map +2 -2
  291. package/dist/hooks/useUnifiedCompletion.js +69 -0
  292. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  293. package/dist/i18n/index.js +109 -0
  294. package/dist/i18n/index.js.map +7 -0
  295. package/dist/i18n/locales/en.js +348 -0
  296. package/dist/i18n/locales/en.js.map +7 -0
  297. package/dist/i18n/locales/index.js +7 -0
  298. package/dist/i18n/locales/index.js.map +7 -0
  299. package/dist/i18n/locales/zh-CN.js +348 -0
  300. package/dist/i18n/locales/zh-CN.js.map +7 -0
  301. package/dist/i18n/types.js +8 -0
  302. package/dist/i18n/types.js.map +7 -0
  303. package/dist/permissions.js +28 -1
  304. package/dist/permissions.js.map +2 -2
  305. package/dist/query.js +253 -21
  306. package/dist/query.js.map +3 -3
  307. package/dist/screens/REPL.js +523 -194
  308. package/dist/screens/REPL.js.map +3 -3
  309. package/dist/services/adapters/chatCompletions.js +3 -1
  310. package/dist/services/adapters/chatCompletions.js.map +2 -2
  311. package/dist/services/adapters/messageNormalizer.js +354 -0
  312. package/dist/services/adapters/messageNormalizer.js.map +7 -0
  313. package/dist/services/adapters/responsesAPI.js +6 -3
  314. package/dist/services/adapters/responsesAPI.js.map +2 -2
  315. package/dist/services/checkpointManager.js +386 -0
  316. package/dist/services/checkpointManager.js.map +7 -0
  317. package/dist/services/claude.js +192 -14
  318. package/dist/services/claude.js.map +3 -3
  319. package/dist/services/compressionService.js +50 -1
  320. package/dist/services/compressionService.js.map +2 -2
  321. package/dist/services/contextMonitor.js +162 -0
  322. package/dist/services/contextMonitor.js.map +7 -0
  323. package/dist/services/customCommands.js +60 -41
  324. package/dist/services/customCommands.js.map +2 -2
  325. package/dist/services/hookExecutor.js +173 -1
  326. package/dist/services/hookExecutor.js.map +2 -2
  327. package/dist/services/intelligentCompactor.js +281 -0
  328. package/dist/services/intelligentCompactor.js.map +7 -0
  329. package/dist/services/lspConfig.js +109 -0
  330. package/dist/services/lspConfig.js.map +7 -0
  331. package/dist/services/mcpClient.js +338 -43
  332. package/dist/services/mcpClient.js.map +2 -2
  333. package/dist/services/modelOrchestrator.js +310 -0
  334. package/dist/services/modelOrchestrator.js.map +7 -0
  335. package/dist/services/openai.js +8 -1
  336. package/dist/services/openai.js.map +2 -2
  337. package/dist/services/outputStyles.js +138 -0
  338. package/dist/services/outputStyles.js.map +7 -0
  339. package/dist/services/plugins/index.js +5 -0
  340. package/dist/services/plugins/index.js.map +7 -0
  341. package/dist/services/plugins/lspServers.js +188 -0
  342. package/dist/services/plugins/lspServers.js.map +7 -0
  343. package/dist/services/plugins/pluginRuntime.js +229 -0
  344. package/dist/services/plugins/pluginRuntime.js.map +7 -0
  345. package/dist/services/plugins/pluginValidation.js +219 -0
  346. package/dist/services/plugins/pluginValidation.js.map +7 -0
  347. package/dist/services/plugins/skillMarketplace.js +556 -0
  348. package/dist/services/plugins/skillMarketplace.js.map +7 -0
  349. package/dist/services/responseStateManager.js +37 -3
  350. package/dist/services/responseStateManager.js.map +2 -2
  351. package/dist/services/sandbox/filesystemBoundary.js +341 -0
  352. package/dist/services/sandbox/filesystemBoundary.js.map +7 -0
  353. package/dist/services/sandbox/index.js +14 -0
  354. package/dist/services/sandbox/index.js.map +7 -0
  355. package/dist/services/sandbox/networkProxy.js +293 -0
  356. package/dist/services/sandbox/networkProxy.js.map +7 -0
  357. package/dist/services/sandbox/sandboxController.js +574 -0
  358. package/dist/services/sandbox/sandboxController.js.map +7 -0
  359. package/dist/services/sandbox/types.js +50 -0
  360. package/dist/services/sandbox/types.js.map +7 -0
  361. package/dist/services/sessionMemory.js +266 -0
  362. package/dist/services/sessionMemory.js.map +7 -0
  363. package/dist/services/taskRouter.js +324 -0
  364. package/dist/services/taskRouter.js.map +7 -0
  365. package/dist/tools/ArchitectTool/ArchitectTool.js +7 -1
  366. package/dist/tools/ArchitectTool/ArchitectTool.js.map +2 -2
  367. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +6 -2
  368. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  369. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +2 -1
  370. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  371. package/dist/tools/BaseTool.js +72 -0
  372. package/dist/tools/BaseTool.js.map +7 -0
  373. package/dist/tools/BashOutputTool/BashOutputToolResultMessage.js +3 -0
  374. package/dist/tools/BashOutputTool/BashOutputToolResultMessage.js.map +2 -2
  375. package/dist/tools/BashTool/BashTool.js +79 -3
  376. package/dist/tools/BashTool/BashTool.js.map +2 -2
  377. package/dist/tools/BashTool/BashToolResultMessage.js +3 -0
  378. package/dist/tools/BashTool/BashToolResultMessage.js.map +2 -2
  379. package/dist/tools/BashTool/OutputLine.js +54 -0
  380. package/dist/tools/BashTool/OutputLine.js.map +2 -2
  381. package/dist/tools/BashTool/prompt.js +336 -3
  382. package/dist/tools/BashTool/prompt.js.map +2 -2
  383. package/dist/tools/FileEditTool/FileEditTool.js +29 -4
  384. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  385. package/dist/tools/FileEditTool/prompt.js +6 -3
  386. package/dist/tools/FileEditTool/prompt.js.map +2 -2
  387. package/dist/tools/FileWriteTool/FileWriteTool.js +5 -5
  388. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  389. package/dist/tools/FileWriteTool/prompt.js +4 -2
  390. package/dist/tools/FileWriteTool/prompt.js.map +2 -2
  391. package/dist/tools/GlobTool/GlobTool.js +4 -2
  392. package/dist/tools/GlobTool/GlobTool.js.map +2 -2
  393. package/dist/tools/GrepTool/GrepTool.js +36 -7
  394. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  395. package/dist/tools/KillShellTool/KillShellToolResultMessage.js +3 -0
  396. package/dist/tools/KillShellTool/KillShellToolResultMessage.js.map +2 -2
  397. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js +109 -0
  398. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +7 -0
  399. package/dist/tools/ListMcpResourcesTool/prompt.js +19 -0
  400. package/dist/tools/ListMcpResourcesTool/prompt.js.map +7 -0
  401. package/dist/tools/LspTool/LspTool.js +664 -0
  402. package/dist/tools/LspTool/LspTool.js.map +7 -0
  403. package/dist/tools/LspTool/prompt.js +27 -0
  404. package/dist/tools/LspTool/prompt.js.map +7 -0
  405. package/dist/tools/MCPTool/MCPTool.js +9 -1
  406. package/dist/tools/MCPTool/MCPTool.js.map +2 -2
  407. package/dist/tools/MemoryReadTool/MemoryReadTool.js +19 -6
  408. package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
  409. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +6 -6
  410. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
  411. package/dist/tools/MultiEditTool/MultiEditTool.js +19 -2
  412. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  413. package/dist/tools/MultiEditTool/prompt.js +5 -3
  414. package/dist/tools/MultiEditTool/prompt.js.map +2 -2
  415. package/dist/tools/NotebookEditTool/NotebookEditTool.js +7 -2
  416. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  417. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
  418. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +75 -0
  419. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +7 -0
  420. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +109 -0
  421. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +7 -0
  422. package/dist/tools/PlanModeTool/prompt.js +94 -0
  423. package/dist/tools/PlanModeTool/prompt.js.map +7 -0
  424. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js +130 -0
  425. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +7 -0
  426. package/dist/tools/ReadMcpResourceTool/prompt.js +17 -0
  427. package/dist/tools/ReadMcpResourceTool/prompt.js.map +7 -0
  428. package/dist/tools/SkillTool/SkillTool.js +10 -4
  429. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  430. package/dist/tools/SkillTool/prompt.js +1 -1
  431. package/dist/tools/SkillTool/prompt.js.map +1 -1
  432. package/dist/tools/SlashCommandTool/SlashCommandTool.js +260 -0
  433. package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +7 -0
  434. package/dist/tools/SlashCommandTool/prompt.js +35 -0
  435. package/dist/tools/SlashCommandTool/prompt.js.map +7 -0
  436. package/dist/tools/TaskOutputTool/TaskOutputTool.js +190 -0
  437. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +7 -0
  438. package/dist/tools/TaskOutputTool/prompt.js +15 -0
  439. package/dist/tools/TaskOutputTool/prompt.js.map +7 -0
  440. package/dist/tools/TaskTool/TaskTool.js +310 -104
  441. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  442. package/dist/tools/TaskTool/prompt.js.map +2 -2
  443. package/dist/tools/TodoWriteTool/TodoWriteTool.js +42 -77
  444. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
  445. package/dist/tools/URLFetcherTool/URLFetcherTool.js +4 -1
  446. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
  447. package/dist/tools/URLFetcherTool/cache.js +55 -8
  448. package/dist/tools/URLFetcherTool/cache.js.map +2 -2
  449. package/dist/tools.js +31 -2
  450. package/dist/tools.js.map +2 -2
  451. package/dist/types/hooks.js +4 -0
  452. package/dist/types/hooks.js.map +2 -2
  453. package/dist/types/marketplace.js.map +2 -2
  454. package/dist/types/messageGroup.js +36 -0
  455. package/dist/types/messageGroup.js.map +7 -0
  456. package/dist/types/plugin.js.map +2 -2
  457. package/dist/types/thinking.js +1 -0
  458. package/dist/types/thinking.js.map +7 -0
  459. package/dist/utils/BackgroundShellManager.js +136 -39
  460. package/dist/utils/BackgroundShellManager.js.map +2 -2
  461. package/dist/utils/CircuitBreaker.js +242 -0
  462. package/dist/utils/CircuitBreaker.js.map +7 -0
  463. package/dist/utils/MessageBatchBuffer.js +102 -0
  464. package/dist/utils/MessageBatchBuffer.js.map +7 -0
  465. package/dist/utils/PersistentShell.js +151 -1
  466. package/dist/utils/PersistentShell.js.map +2 -2
  467. package/dist/utils/agentLoader.js +1 -23
  468. package/dist/utils/agentLoader.js.map +2 -2
  469. package/dist/utils/agentTranscripts.js +641 -0
  470. package/dist/utils/agentTranscripts.js.map +7 -0
  471. package/dist/utils/animationManager.js +213 -0
  472. package/dist/utils/animationManager.js.map +7 -0
  473. package/dist/utils/animationSync.js +110 -0
  474. package/dist/utils/animationSync.js.map +7 -0
  475. package/dist/utils/ask.js +2 -0
  476. package/dist/utils/ask.js.map +2 -2
  477. package/dist/utils/asyncFile.js +215 -0
  478. package/dist/utils/asyncFile.js.map +7 -0
  479. package/dist/utils/backgroundAgentManager.js +231 -0
  480. package/dist/utils/backgroundAgentManager.js.map +7 -0
  481. package/dist/utils/config.js +108 -10
  482. package/dist/utils/config.js.map +2 -2
  483. package/dist/utils/conversationRecovery.js +19 -0
  484. package/dist/utils/conversationRecovery.js.map +2 -2
  485. package/dist/utils/credentials/CredentialStore.js +1 -0
  486. package/dist/utils/credentials/CredentialStore.js.map +7 -0
  487. package/dist/utils/credentials/EncryptedFileStore.js +157 -0
  488. package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
  489. package/dist/utils/credentials/index.js +37 -0
  490. package/dist/utils/credentials/index.js.map +7 -0
  491. package/dist/utils/credentials/migration.js +82 -0
  492. package/dist/utils/credentials/migration.js.map +7 -0
  493. package/dist/utils/exit.js +73 -0
  494. package/dist/utils/exit.js.map +7 -0
  495. package/dist/utils/format.js +73 -5
  496. package/dist/utils/format.js.map +2 -2
  497. package/dist/utils/generators.js +76 -6
  498. package/dist/utils/generators.js.map +2 -2
  499. package/dist/utils/globalErrorHandler.js +149 -0
  500. package/dist/utils/globalErrorHandler.js.map +7 -0
  501. package/dist/utils/groupHandlers/index.js +8 -0
  502. package/dist/utils/groupHandlers/index.js.map +7 -0
  503. package/dist/utils/groupHandlers/parallelTasksHandler.js +140 -0
  504. package/dist/utils/groupHandlers/parallelTasksHandler.js.map +7 -0
  505. package/dist/utils/groupHandlers/taskHandler.js +104 -0
  506. package/dist/utils/groupHandlers/taskHandler.js.map +7 -0
  507. package/dist/utils/groupHandlers/types.js +1 -0
  508. package/dist/utils/groupHandlers/types.js.map +7 -0
  509. package/dist/utils/logRotation.js +224 -0
  510. package/dist/utils/logRotation.js.map +7 -0
  511. package/dist/utils/markdown.js +13 -1
  512. package/dist/utils/markdown.js.map +2 -2
  513. package/dist/utils/marketplaceManager.js +3 -5
  514. package/dist/utils/marketplaceManager.js.map +2 -2
  515. package/dist/utils/memSafety.js +264 -0
  516. package/dist/utils/memSafety.js.map +7 -0
  517. package/dist/utils/messageGroupManager.js +274 -0
  518. package/dist/utils/messageGroupManager.js.map +7 -0
  519. package/dist/utils/messages.js +13 -4
  520. package/dist/utils/messages.js.map +2 -2
  521. package/dist/utils/model.js +119 -15
  522. package/dist/utils/model.js.map +3 -3
  523. package/dist/utils/permissions/filesystem.js +162 -6
  524. package/dist/utils/permissions/filesystem.js.map +2 -2
  525. package/dist/utils/plan/planMode.js +143 -0
  526. package/dist/utils/plan/planMode.js.map +7 -0
  527. package/dist/utils/pluginLoader.js +17 -21
  528. package/dist/utils/pluginLoader.js.map +2 -2
  529. package/dist/utils/ripgrep.js +55 -2
  530. package/dist/utils/ripgrep.js.map +2 -2
  531. package/dist/utils/safePath.js +132 -0
  532. package/dist/utils/safePath.js.map +7 -0
  533. package/dist/utils/sanitizeInput.js +32 -0
  534. package/dist/utils/sanitizeInput.js.map +7 -0
  535. package/dist/utils/secureKeyStorage.js +312 -0
  536. package/dist/utils/secureKeyStorage.js.map +7 -0
  537. package/dist/utils/sensitiveFiles.js +125 -0
  538. package/dist/utils/sensitiveFiles.js.map +7 -0
  539. package/dist/utils/session/sessionPlugins.js +67 -0
  540. package/dist/utils/session/sessionPlugins.js.map +7 -0
  541. package/dist/utils/taskDisplayUtils.js +257 -0
  542. package/dist/utils/taskDisplayUtils.js.map +7 -0
  543. package/dist/utils/teamConfig.js +2 -1
  544. package/dist/utils/teamConfig.js.map +2 -2
  545. package/dist/utils/theme.js +6 -6
  546. package/dist/utils/theme.js.map +1 -1
  547. package/dist/utils/todoStorage.js +92 -2
  548. package/dist/utils/todoStorage.js.map +2 -2
  549. package/dist/utils/toolRiskClassification.js +207 -0
  550. package/dist/utils/toolRiskClassification.js.map +7 -0
  551. package/dist/utils/toolTimeout.js +136 -0
  552. package/dist/utils/toolTimeout.js.map +7 -0
  553. package/dist/utils/tooling/safeRender.js +116 -0
  554. package/dist/utils/tooling/safeRender.js.map +7 -0
  555. package/dist/utils/userFriendlyError.js +346 -0
  556. package/dist/utils/userFriendlyError.js.map +7 -0
  557. package/dist/utils/vendor/ripgrep/arm64-darwin/rg +0 -0
  558. package/dist/version.js +2 -2
  559. package/dist/version.js.map +1 -1
  560. package/package.json +17 -5
  561. package/scripts/postinstall.js +128 -38
  562. package/dist/commands/agents.js +0 -2086
  563. package/dist/commands/agents.js.map +0 -7
  564. package/dist/commands/build.js +0 -74
  565. package/dist/commands/build.js.map +0 -7
  566. package/dist/commands/compression.js +0 -57
  567. package/dist/commands/compression.js.map +0 -7
  568. package/dist/commands/listen.js +0 -37
  569. package/dist/commands/listen.js.map +0 -7
  570. package/dist/commands/login.js +0 -37
  571. package/dist/commands/login.js.map +0 -7
  572. package/dist/commands/logout.js +0 -33
  573. package/dist/commands/logout.js.map +0 -7
  574. package/dist/commands/mcp.js +0 -40
  575. package/dist/commands/mcp.js.map +0 -7
  576. package/dist/commands/mcp_refresh.js +0 -40
  577. package/dist/commands/mcp_refresh.js.map +0 -7
  578. package/dist/commands/modelstatus.js +0 -21
  579. package/dist/commands/modelstatus.js.map +0 -7
  580. package/dist/commands/onboarding.js +0 -36
  581. package/dist/commands/onboarding.js.map +0 -7
  582. package/dist/commands/plugin-interactive.js +0 -446
  583. package/dist/commands/plugin-interactive.js.map +0 -7
  584. package/dist/commands/pr_comments.js +0 -61
  585. package/dist/commands/pr_comments.js.map +0 -7
  586. package/dist/commands/release-notes.js +0 -30
  587. package/dist/commands/release-notes.js.map +0 -7
  588. package/dist/commands/review.js +0 -51
  589. package/dist/commands/review.js.map +0 -7
  590. package/dist/components/Bug.js +0 -147
  591. package/dist/components/Bug.js.map +0 -7
  592. package/dist/components/ModelSelector.js +0 -2062
  593. package/dist/components/ModelSelector.js.map +0 -7
  594. package/dist/components/ModelStatusDisplay.js +0 -87
  595. package/dist/components/ModelStatusDisplay.js.map +0 -7
  596. package/dist/entrypoints/cli-wrapper.js +0 -61
  597. package/dist/entrypoints/cli-wrapper.js.map +0 -7
  598. package/dist/hooks/useCancelRequest.js +0 -28
  599. package/dist/hooks/useCancelRequest.js.map +0 -7
  600. package/dist/screens/Doctor.js +0 -22
  601. package/dist/screens/Doctor.js.map +0 -7
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/components/SensitiveFileWarning.tsx"],
4
+ "sourcesContent": ["/**\n * SensitiveFileWarning Component\n *\n * Displays an informational warning when operating on files that may contain\n * sensitive information (API keys, credentials, private keys, etc.).\n *\n * This warning is INFORMATIONAL only - it does not block operations.\n * Designed to give non-technical users awareness without interrupting workflow.\n */\n\nimport { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { basename } from 'path'\nimport { SEMANTIC_COLORS } from '../constants/colors'\nimport { getSensitiveFileReason } from '../utils/sensitiveFiles'\n\ninterface Props {\n /** The path to the sensitive file */\n filePath: string\n /** The operation being performed on the file */\n operation: 'read' | 'write' | 'delete'\n}\n\n/**\n * Maps operation types to human-readable Chinese text\n */\nconst operationText: Record<Props['operation'], string> = {\n read: '\u8BFB\u53D6',\n write: '\u4FEE\u6539',\n delete: '\u5220\u9664',\n}\n\n/**\n * SensitiveFileWarning displays an informational warning box when\n * accessing files that may contain sensitive information.\n *\n * @example\n * ```tsx\n * <SensitiveFileWarning filePath=\"/path/to/.env\" operation=\"write\" />\n * ```\n */\nexport function SensitiveFileWarning({ filePath, operation }: Props) {\n const fileName = basename(filePath)\n const reason = getSensitiveFileReason(filePath)\n\n return (\n <Box\n flexDirection=\"column\"\n paddingX={1}\n paddingY={1}\n borderStyle=\"round\"\n borderColor={SEMANTIC_COLORS.accent}\n >\n <Text bold color={SEMANTIC_COLORS.accent}>\n \uD83D\uDD12 \u654F\u611F\u6587\u4EF6\u8B66\u544A\n </Text>\n <Text color={SEMANTIC_COLORS.secondary}>\n \u6B63\u5728{operationText[operation]}\u654F\u611F\u6587\u4EF6: <Text bold>{fileName}</Text>\n </Text>\n <Text color={SEMANTIC_COLORS.muted}>\n {reason || '\u6B64\u6587\u4EF6\u53EF\u80FD\u5305\u542B\u5BC6\u94A5\u3001\u51ED\u8BC1\u6216\u5176\u4ED6\u654F\u611F\u4FE1\u606F\u3002'}\n </Text>\n </Box>\n )\n}\n"],
5
+ "mappings": "AAUA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAChC,SAAS,8BAA8B;AAYvC,MAAM,gBAAoD;AAAA,EACxD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;AAWO,SAAS,qBAAqB,EAAE,UAAU,UAAU,GAAU;AACnE,QAAM,WAAW,SAAS,QAAQ;AAClC,QAAM,SAAS,uBAAuB,QAAQ;AAE9C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAc;AAAA,MACd,UAAU;AAAA,MACV,UAAU;AAAA,MACV,aAAY;AAAA,MACZ,aAAa,gBAAgB;AAAA;AAAA,IAE7B,oCAAC,QAAK,MAAI,MAAC,OAAO,gBAAgB,UAAQ,gDAE1C;AAAA,IACA,oCAAC,QAAK,OAAO,gBAAgB,aAAW,gBACnC,cAAc,SAAS,GAAE,8BAAM,oCAAC,QAAK,MAAI,QAAE,QAAS,CACzD;AAAA,IACA,oCAAC,QAAK,OAAO,gBAAgB,SAC1B,UAAU,0HACb;AAAA,EACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -1,10 +1,54 @@
1
1
  import { Box, Text } from "ink";
2
2
  import * as React from "react";
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useRef, useMemo } from "react";
4
4
  import { getTheme } from "../utils/theme.js";
5
5
  import { sample } from "lodash-es";
6
6
  import { getSessionState } from "../utils/sessionState.js";
7
+ import { useUnifiedAnimation } from "../utils/animationManager.js";
8
+ import { formatNumber, formatTokenUsage } from "../utils/format.js";
9
+ import { BRAND_GRADIENT, SEMANTIC_COLORS } from "../constants/colors.js";
7
10
  const CHARACTERS = process.platform === "darwin" ? ["\xB7", "\u2722", "\u2733", "\u2217", "\u273B", "\u273D"] : ["\xB7", "\u2722", "*", "\u2217", "\u273B", "\u273D"];
11
+ function createDefaultState() {
12
+ return { phase: "thinking" };
13
+ }
14
+ const streamingStates = /* @__PURE__ */ new Map([
15
+ ["__main__", createDefaultState()]
16
+ ]);
17
+ const agentContextStack = ["__main__"];
18
+ function getCurrentAgentContext() {
19
+ return agentContextStack[agentContextStack.length - 1] ?? "__main__";
20
+ }
21
+ function pushAgentContext(agentId) {
22
+ agentContextStack.push(agentId);
23
+ if (!streamingStates.has(agentId)) {
24
+ streamingStates.set(agentId, createDefaultState());
25
+ }
26
+ }
27
+ function popAgentContext() {
28
+ if (agentContextStack.length > 1) {
29
+ return agentContextStack.pop();
30
+ }
31
+ return void 0;
32
+ }
33
+ function setStreamingState(state, agentId) {
34
+ const targetId = agentId ?? getCurrentAgentContext();
35
+ const current = streamingStates.get(targetId) ?? createDefaultState();
36
+ streamingStates.set(targetId, { ...current, ...state });
37
+ }
38
+ function resetStreamingState(agentId) {
39
+ const targetId = agentId ?? getCurrentAgentContext();
40
+ streamingStates.set(targetId, createDefaultState());
41
+ }
42
+ function getStreamingState(agentId) {
43
+ const targetId = agentId ?? getCurrentAgentContext();
44
+ return streamingStates.get(targetId) ?? createDefaultState();
45
+ }
46
+ function getMainStreamingState() {
47
+ return streamingStates.get("__main__") ?? createDefaultState();
48
+ }
49
+ function cleanupAgentStreamingState(agentId) {
50
+ streamingStates.delete(agentId);
51
+ }
8
52
  const MESSAGES = [
9
53
  "Accomplishing",
10
54
  "Actioning",
@@ -63,39 +107,109 @@ const MESSAGES = [
63
107
  "Vibing",
64
108
  "Working"
65
109
  ];
66
- function Spinner() {
110
+ function Spinner({ isActive = true }) {
67
111
  const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()];
68
- const [frame, setFrame] = useState(0);
69
- const [elapsedTime, setElapsedTime] = useState(0);
70
112
  const message = useRef(sample(MESSAGES));
71
113
  const startTime = useRef(Date.now());
72
- useEffect(() => {
73
- const timer = setInterval(() => {
74
- setFrame((f) => (f + 1) % frames.length);
75
- }, 120);
76
- return () => clearInterval(timer);
77
- }, [frames.length]);
78
- useEffect(() => {
79
- const timer = setInterval(() => {
80
- setElapsedTime(Math.floor((Date.now() - startTime.current) / 1e3));
81
- }, 1e3);
82
- return () => clearInterval(timer);
83
- }, []);
84
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React.createElement(Box, { flexWrap: "nowrap", height: 1, width: 2 }, /* @__PURE__ */ React.createElement(Text, { color: getTheme().minto }, frames[frame])), /* @__PURE__ */ React.createElement(Text, { color: getTheme().minto }, message.current, "\u2026 "), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "(", elapsedTime, "s \xB7 ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "esc"), " to interrupt)"), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "\xB7 ", getSessionState("currentError")));
114
+ const theme = getTheme();
115
+ const { spinnerFrame, elapsedTime } = useUnifiedAnimation({
116
+ enabled: isActive,
117
+ startTime: startTime.current,
118
+ spinnerFrameCount: frames.length,
119
+ componentId: "main-spinner"
120
+ });
121
+ const streamState = useMemo(() => getMainStreamingState(), [spinnerFrame]);
122
+ const getPhaseDisplay = () => {
123
+ switch (streamState.phase) {
124
+ case "deep_thinking": {
125
+ const elapsed = streamState.thinkingStartTime ? Math.floor((Date.now() - streamState.thinkingStartTime) / 1e3) : 0;
126
+ const charInfo = streamState.tokenCount ? `${formatNumber(streamState.tokenCount)} chars` : "";
127
+ if (streamState.thinkingMaxTokens && streamState.tokenCount) {
128
+ return `Deep thinking (${elapsed}s \xB7 ${charInfo} / ${formatNumber(streamState.thinkingMaxTokens)})`;
129
+ }
130
+ return `Deep thinking (${elapsed}s${charInfo ? " \xB7 " + charInfo : ""})`;
131
+ }
132
+ case "retrying":
133
+ return streamState.retryCount && streamState.maxRetries ? `Retrying (${streamState.retryCount}/${streamState.maxRetries})${streamState.errorName ? ` \xB7 ${streamState.errorName}` : ""}` : streamState.errorName ? `Retrying \xB7 ${streamState.errorName}` : "Retrying...";
134
+ case "permission":
135
+ return "Waiting for permission...";
136
+ case "compacting":
137
+ return "Compacting context...";
138
+ case "concurrent":
139
+ return streamState.concurrentCount ? `Running ${streamState.concurrentCount} concurrent tasks...` : "Running concurrent tasks...";
140
+ case "tool_use":
141
+ return streamState.toolName ? `Using ${streamState.toolName}` : "Executing tool";
142
+ case "generating":
143
+ return "Receiving";
144
+ case "waiting":
145
+ return "Waiting for response";
146
+ case "thinking":
147
+ default:
148
+ return message.current;
149
+ }
150
+ };
151
+ const phaseColors = {
152
+ thinking: BRAND_GRADIENT.MIDDLE,
153
+ // 粉紫 - 思考中
154
+ deep_thinking: BRAND_GRADIENT.START,
155
+ // 紫蓝 - 深度思考
156
+ generating: SEMANTIC_COLORS.success,
157
+ // 绿色 - 生成中
158
+ tool_use: SEMANTIC_COLORS.running,
159
+ // 粉紫 - 工具执行
160
+ waiting: SEMANTIC_COLORS.dim,
161
+ // 灰色 - 等待
162
+ retrying: SEMANTIC_COLORS.error,
163
+ // 红色 - 重试
164
+ permission: SEMANTIC_COLORS.info,
165
+ // 蓝色 - 权限
166
+ compacting: SEMANTIC_COLORS.info,
167
+ // 蓝色 - 压缩
168
+ concurrent: SEMANTIC_COLORS.success
169
+ // 绿色 - 并发
170
+ };
171
+ const getTokenDisplay = () => {
172
+ if (streamState.inputTokens || streamState.outputTokens) {
173
+ return formatTokenUsage(streamState.inputTokens, streamState.outputTokens);
174
+ }
175
+ if (streamState.receivedChars && streamState.receivedChars > 0) {
176
+ const approxOutputTokens = Math.round(streamState.receivedChars / 3);
177
+ if (streamState.sentChars && streamState.sentChars > 0) {
178
+ const approxInputTokens = Math.round(streamState.sentChars / 3);
179
+ return `\u2191 ~${formatNumber(approxInputTokens)} \xB7 \u2193 ~${formatNumber(approxOutputTokens)}`;
180
+ }
181
+ return `\u2193 ~${formatNumber(approxOutputTokens)}`;
182
+ }
183
+ return "";
184
+ };
185
+ const tokenUsage = getTokenDisplay();
186
+ const thinkingDuration = streamState.thinkingDurationMs ? Math.floor(streamState.thinkingDurationMs / 1e3) : null;
187
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React.createElement(Box, { flexWrap: "nowrap", height: 1, width: 2 }, /* @__PURE__ */ React.createElement(Text, { color: phaseColors[streamState.phase] }, frames[spinnerFrame])), /* @__PURE__ */ React.createElement(Text, { color: phaseColors[streamState.phase] }, getPhaseDisplay(), "\u2026 "), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "(", /* @__PURE__ */ React.createElement(Text, { bold: true }, "esc esc"), " to cancel \xB7 ", elapsedTime, "s", tokenUsage && /* @__PURE__ */ React.createElement(Text, null, " \xB7 ", tokenUsage), thinkingDuration !== null && thinkingDuration > 0 && /* @__PURE__ */ React.createElement(Text, null, " \xB7 thinking ", thinkingDuration, "s"), ")"), getSessionState("currentError") && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "\xB7 ", getSessionState("currentError")));
85
188
  }
86
- function SimpleSpinner() {
189
+ function SimpleSpinner({
190
+ isActive = true,
191
+ label
192
+ }) {
87
193
  const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()];
88
- const [frame, setFrame] = useState(0);
89
- useEffect(() => {
90
- const timer = setInterval(() => {
91
- setFrame((f) => (f + 1) % frames.length);
92
- }, 120);
93
- return () => clearInterval(timer);
94
- }, [frames.length]);
95
- return /* @__PURE__ */ React.createElement(Box, { flexWrap: "nowrap", height: 1, width: 2 }, /* @__PURE__ */ React.createElement(Text, { color: getTheme().minto }, frames[frame]));
194
+ const startTime = useRef(Date.now());
195
+ const { spinnerFrame } = useUnifiedAnimation({
196
+ enabled: isActive,
197
+ startTime: startTime.current,
198
+ spinnerFrameCount: frames.length,
199
+ componentId: "simple-spinner"
200
+ });
201
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Box, { flexWrap: "nowrap", height: 1, width: 2 }, /* @__PURE__ */ React.createElement(Text, { color: BRAND_GRADIENT.MIDDLE }, frames[spinnerFrame])), label && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", label));
96
202
  }
97
203
  export {
98
204
  SimpleSpinner,
99
- Spinner
205
+ Spinner,
206
+ cleanupAgentStreamingState,
207
+ getCurrentAgentContext,
208
+ getMainStreamingState,
209
+ getStreamingState,
210
+ popAgentContext,
211
+ pushAgentContext,
212
+ resetStreamingState,
213
+ setStreamingState
100
214
  };
101
215
  //# sourceMappingURL=Spinner.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/Spinner.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { useEffect, useRef, useState } from 'react'\nimport { getTheme } from '@utils/theme'\nimport { sample } from 'lodash-es'\nimport { getSessionState } from '@utils/sessionState'\n// NB: The third character in this string is an emoji that\n// renders on Windows consoles with a green background\nconst CHARACTERS =\n process.platform === 'darwin'\n ? ['\u00B7', '\u2722', '\u2733', '\u2217', '\u273B', '\u273D']\n : ['\u00B7', '\u2722', '*', '\u2217', '\u273B', '\u273D']\n\nconst MESSAGES = [\n 'Accomplishing',\n 'Actioning',\n 'Actualizing',\n 'Baking',\n 'Brewing',\n 'Calculating',\n 'Cerebrating',\n 'Churning',\n 'Coding',\n 'Coalescing',\n 'Cogitating',\n 'Computing',\n 'Conjuring',\n 'Considering',\n 'Cooking',\n 'Crafting',\n 'Creating',\n 'Crunching',\n 'Deliberating',\n 'Determining',\n 'Doing',\n 'Effecting',\n 'Finagling',\n 'Forging',\n 'Forming',\n 'Generating',\n 'Hatching',\n 'Herding',\n 'Honking',\n 'Hustling',\n 'Ideating',\n 'Inferring',\n 'Manifesting',\n 'Marinating',\n 'Moseying',\n 'Mulling',\n 'Mustering',\n 'Musing',\n 'Noodling',\n 'Percolating',\n 'Pondering',\n 'Processing',\n 'Puttering',\n 'Reticulating',\n 'Ruminating',\n 'Schlepping',\n 'Shucking',\n 'Simmering',\n 'Smooshing',\n 'Spinning',\n 'Stewing',\n 'Synthesizing',\n 'Thinking',\n 'Transmuting',\n 'Vibing',\n 'Working',\n]\n\nexport function Spinner(): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const [frame, setFrame] = useState(0)\n const [elapsedTime, setElapsedTime] = useState(0)\n const message = useRef(sample(MESSAGES))\n const startTime = useRef(Date.now())\n\n useEffect(() => {\n const timer = setInterval(() => {\n setFrame(f => (f + 1) % frames.length)\n }, 120)\n\n return () => clearInterval(timer)\n }, [frames.length])\n\n useEffect(() => {\n const timer = setInterval(() => {\n setElapsedTime(Math.floor((Date.now() - startTime.current) / 1000))\n }, 1000)\n\n return () => clearInterval(timer)\n }, [])\n\n return (\n <Box flexDirection=\"row\" marginTop={1}>\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={getTheme().minto}>{frames[frame]}</Text>\n </Box>\n <Text color={getTheme().minto}>{message.current}\u2026 </Text>\n <Text color={getTheme().secondaryText}>\n ({elapsedTime}s \u00B7 <Text bold>esc</Text> to interrupt)\n </Text>\n <Text color={getTheme().secondaryText}>\n \u00B7 {getSessionState('currentError')}\n </Text>\n </Box>\n )\n}\n\nexport function SimpleSpinner(): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const [frame, setFrame] = useState(0)\n\n useEffect(() => {\n const timer = setInterval(() => {\n setFrame(f => (f + 1) % frames.length)\n }, 120)\n\n return () => clearInterval(timer)\n }, [frames.length])\n\n return (\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={getTheme().minto}>{frames[frame]}</Text>\n </Box>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { useEffect, useRef, useState, useMemo } from 'react'\nimport { getTheme } from '@utils/theme'\nimport { sample } from 'lodash-es'\nimport { getSessionState, setSessionState } from '@utils/sessionState'\nimport { useUnifiedAnimation } from '@utils/animationManager'\nimport { formatNumber, formatTokenUsage } from '@utils/format'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\n\n// NB: The third character in this string is an emoji that\n// renders on Windows consoles with a green background\nconst CHARACTERS =\n process.platform === 'darwin'\n ? ['\u00B7', '\u2722', '\u2733', '\u2217', '\u273B', '\u273D']\n : ['\u00B7', '\u2722', '*', '\u2217', '\u273B', '\u273D']\n\n// Streaming phases for better UX feedback\nexport type StreamingPhase =\n | 'thinking' // Initial thinking phase\n | 'generating' // Generating response content\n | 'tool_use' // Executing a tool\n | 'waiting' // Waiting for API response\n | 'deep_thinking' // Extended thinking mode (Claude thinking blocks)\n | 'retrying' // API retry in progress\n | 'permission' // Waiting for user permission\n | 'compacting' // Compacting conversation context\n | 'concurrent' // Running concurrent tasks\n\n// Streaming state type definition\nexport interface StreamingStateData {\n phase: StreamingPhase\n toolName?: string\n tokenCount?: number\n chunkCount?: number\n // New fields for enhanced feedback\n retryCount?: number\n maxRetries?: number\n errorName?: string\n concurrentCount?: number\n thinkingMaxTokens?: number\n // Token usage from API (real-time streaming)\n inputTokens?: number\n outputTokens?: number\n // Character counts for approximate token estimation\n sentChars?: number // Characters sent to API (input)\n receivedChars?: number // Characters received from API (output)\n // Thinking timing\n thinkingStartTime?: number\n thinkingDurationMs?: number\n}\n\n// Default state factory\nfunction createDefaultState(): StreamingStateData {\n return { phase: 'thinking' }\n}\n\n// Store streaming state per agent (agentId -> state)\n// '__main__' is used for the main/root agent\nconst streamingStates: Map<string, StreamingStateData> = new Map([\n ['__main__', createDefaultState()],\n])\n\n// Current active agent context (stack to support nesting)\nconst agentContextStack: string[] = ['__main__']\n\n/**\n * Get the current active agent ID\n */\nexport function getCurrentAgentContext(): string {\n return agentContextStack[agentContextStack.length - 1] ?? '__main__'\n}\n\n/**\n * Push a new agent context (when entering a subagent)\n */\nexport function pushAgentContext(agentId: string): void {\n agentContextStack.push(agentId)\n if (!streamingStates.has(agentId)) {\n streamingStates.set(agentId, createDefaultState())\n }\n}\n\n/**\n * Pop the current agent context (when exiting a subagent)\n */\nexport function popAgentContext(): string | undefined {\n if (agentContextStack.length > 1) {\n return agentContextStack.pop()\n }\n return undefined\n}\n\n/**\n * Set streaming state for a specific agent (or current context if not specified)\n */\nexport function setStreamingState(\n state: Partial<StreamingStateData>,\n agentId?: string,\n): void {\n const targetId = agentId ?? getCurrentAgentContext()\n const current = streamingStates.get(targetId) ?? createDefaultState()\n streamingStates.set(targetId, { ...current, ...state })\n}\n\n/**\n * Reset streaming state for a specific agent (or current context if not specified)\n */\nexport function resetStreamingState(agentId?: string): void {\n const targetId = agentId ?? getCurrentAgentContext()\n streamingStates.set(targetId, createDefaultState())\n}\n\n/**\n * Get streaming state for a specific agent (or current context if not specified)\n */\nexport function getStreamingState(agentId?: string): StreamingStateData {\n const targetId = agentId ?? getCurrentAgentContext()\n return streamingStates.get(targetId) ?? createDefaultState()\n}\n\n/**\n * Get streaming state for the main/root agent (used by main Spinner)\n */\nexport function getMainStreamingState(): StreamingStateData {\n return streamingStates.get('__main__') ?? createDefaultState()\n}\n\n/**\n * Clean up streaming state for a completed agent\n */\nexport function cleanupAgentStreamingState(agentId: string): void {\n streamingStates.delete(agentId)\n}\n\nconst MESSAGES = [\n 'Accomplishing',\n 'Actioning',\n 'Actualizing',\n 'Baking',\n 'Brewing',\n 'Calculating',\n 'Cerebrating',\n 'Churning',\n 'Coding',\n 'Coalescing',\n 'Cogitating',\n 'Computing',\n 'Conjuring',\n 'Considering',\n 'Cooking',\n 'Crafting',\n 'Creating',\n 'Crunching',\n 'Deliberating',\n 'Determining',\n 'Doing',\n 'Effecting',\n 'Finagling',\n 'Forging',\n 'Forming',\n 'Generating',\n 'Hatching',\n 'Herding',\n 'Honking',\n 'Hustling',\n 'Ideating',\n 'Inferring',\n 'Manifesting',\n 'Marinating',\n 'Moseying',\n 'Mulling',\n 'Mustering',\n 'Musing',\n 'Noodling',\n 'Percolating',\n 'Pondering',\n 'Processing',\n 'Puttering',\n 'Reticulating',\n 'Ruminating',\n 'Schlepping',\n 'Shucking',\n 'Simmering',\n 'Smooshing',\n 'Spinning',\n 'Stewing',\n 'Synthesizing',\n 'Thinking',\n 'Transmuting',\n 'Vibing',\n 'Working',\n]\n\ninterface SpinnerProps {\n /** Whether the spinner should be active (animating) */\n isActive?: boolean\n}\n\nexport function Spinner({ isActive = true }: SpinnerProps): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const message = useRef(sample(MESSAGES))\n const startTime = useRef(Date.now())\n const theme = getTheme()\n\n // Use unified animation manager instead of separate setInterval timers\n const { spinnerFrame, elapsedTime } = useUnifiedAnimation({\n enabled: isActive,\n startTime: startTime.current,\n spinnerFrameCount: frames.length,\n componentId: 'main-spinner',\n })\n\n // Get main streaming state on each render (updates when spinnerFrame changes)\n // Use getMainStreamingState to avoid subagent interference\n const streamState = useMemo(() => getMainStreamingState(), [spinnerFrame])\n\n // Get phase-specific display\n const getPhaseDisplay = () => {\n switch (streamState.phase) {\n case 'deep_thinking': {\n // Show elapsed time and char count for thinking phase\n const elapsed = streamState.thinkingStartTime\n ? Math.floor((Date.now() - streamState.thinkingStartTime) / 1000)\n : 0\n const charInfo = streamState.tokenCount\n ? `${formatNumber(streamState.tokenCount)} chars`\n : ''\n\n if (streamState.thinkingMaxTokens && streamState.tokenCount) {\n return `Deep thinking (${elapsed}s \u00B7 ${charInfo} / ${formatNumber(streamState.thinkingMaxTokens)})`\n }\n return `Deep thinking (${elapsed}s${charInfo ? ' \u00B7 ' + charInfo : ''})`\n }\n\n case 'retrying':\n return streamState.retryCount && streamState.maxRetries\n ? `Retrying (${streamState.retryCount}/${streamState.maxRetries})${streamState.errorName ? ` \u00B7 ${streamState.errorName}` : ''}`\n : streamState.errorName\n ? `Retrying \u00B7 ${streamState.errorName}`\n : 'Retrying...'\n\n case 'permission':\n return 'Waiting for permission...'\n\n case 'compacting':\n return 'Compacting context...'\n\n case 'concurrent':\n return streamState.concurrentCount\n ? `Running ${streamState.concurrentCount} concurrent tasks...`\n : 'Running concurrent tasks...'\n\n case 'tool_use':\n return streamState.toolName\n ? `Using ${streamState.toolName}`\n : 'Executing tool'\n\n case 'generating':\n return 'Receiving'\n\n case 'waiting':\n return 'Waiting for response'\n\n case 'thinking':\n default:\n return message.current\n }\n }\n\n // \u4F7F\u7528\u8BED\u4E49\u989C\u8272\u7CFB\u7EDF\uFF0C\u54C1\u724C\u8272\u7528\u4E8E\u601D\u8003\uFF0C\u72B6\u6001\u8272\u7528\u4E8E\u5404\u9636\u6BB5\n const phaseColors: Record<StreamingPhase, string> = {\n thinking: BRAND_GRADIENT.MIDDLE, // \u7C89\u7D2B - \u601D\u8003\u4E2D\n deep_thinking: BRAND_GRADIENT.START, // \u7D2B\u84DD - \u6DF1\u5EA6\u601D\u8003\n generating: SEMANTIC_COLORS.success, // \u7EFF\u8272 - \u751F\u6210\u4E2D\n tool_use: SEMANTIC_COLORS.running, // \u7C89\u7D2B - \u5DE5\u5177\u6267\u884C\n waiting: SEMANTIC_COLORS.dim, // \u7070\u8272 - \u7B49\u5F85\n retrying: SEMANTIC_COLORS.error, // \u7EA2\u8272 - \u91CD\u8BD5\n permission: SEMANTIC_COLORS.info, // \u84DD\u8272 - \u6743\u9650\n compacting: SEMANTIC_COLORS.info, // \u84DD\u8272 - \u538B\u7F29\n concurrent: SEMANTIC_COLORS.success, // \u7EFF\u8272 - \u5E76\u53D1\n }\n\n // Get token usage from streaming state (API values or approximate from chars)\n const getTokenDisplay = () => {\n // Prefer API-provided token counts - always show bidirectional if available\n if (streamState.inputTokens || streamState.outputTokens) {\n return formatTokenUsage(streamState.inputTokens, streamState.outputTokens)\n }\n // Fallback: approximate from received characters (~3 chars per token for mixed content)\n if (streamState.receivedChars && streamState.receivedChars > 0) {\n const approxOutputTokens = Math.round(streamState.receivedChars / 3)\n // If we have sentChars, show both directions\n if (streamState.sentChars && streamState.sentChars > 0) {\n const approxInputTokens = Math.round(streamState.sentChars / 3)\n return `\u2191 ~${formatNumber(approxInputTokens)} \u00B7 \u2193 ~${formatNumber(approxOutputTokens)}`\n }\n return `\u2193 ~${formatNumber(approxOutputTokens)}`\n }\n return ''\n }\n\n const tokenUsage = getTokenDisplay()\n\n // Get thinking duration if available\n const thinkingDuration = streamState.thinkingDurationMs\n ? Math.floor(streamState.thinkingDurationMs / 1000)\n : null\n\n return (\n <Box flexDirection=\"row\" marginTop={1}>\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={phaseColors[streamState.phase]}>\n {frames[spinnerFrame]}\n </Text>\n </Box>\n <Text color={phaseColors[streamState.phase]}>{getPhaseDisplay()}\u2026 </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n (<Text bold>esc esc</Text> to cancel \u00B7 {elapsedTime}s\n {tokenUsage && <Text> \u00B7 {tokenUsage}</Text>}\n {thinkingDuration !== null && thinkingDuration > 0 && (\n <Text> \u00B7 thinking {thinkingDuration}s</Text>\n )}\n )\n </Text>\n {getSessionState('currentError') && (\n <Text color={SEMANTIC_COLORS.dim}>\n \u00B7 {getSessionState('currentError')}\n </Text>\n )}\n </Box>\n )\n}\n\ninterface SimpleSpinnerProps {\n /** Whether the spinner should be active (animating) */\n isActive?: boolean\n /** Optional label to display next to spinner */\n label?: string\n}\n\nexport function SimpleSpinner({\n isActive = true,\n label,\n}: SimpleSpinnerProps): React.ReactNode {\n const frames = [...CHARACTERS, ...[...CHARACTERS].reverse()]\n const startTime = useRef(Date.now())\n\n // Use unified animation manager\n const { spinnerFrame } = useUnifiedAnimation({\n enabled: isActive,\n startTime: startTime.current,\n spinnerFrameCount: frames.length,\n componentId: 'simple-spinner',\n })\n\n return (\n <Box flexDirection=\"row\">\n <Box flexWrap=\"nowrap\" height={1} width={2}>\n <Text color={BRAND_GRADIENT.MIDDLE}>{frames[spinnerFrame]}</Text>\n </Box>\n {label && <Text color={SEMANTIC_COLORS.dim}> {label}</Text>}\n </Box>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,27 +1,25 @@
1
- import React, { useState, useEffect } from "react";
1
+ import React, { useRef } from "react";
2
2
  import { Text } from "ink";
3
3
  import { SYMBOLS } from "../constants/symbols.js";
4
4
  import { getAgentColor } from "../constants/colors.js";
5
5
  import { getTheme } from "../utils/theme.js";
6
+ import { useUnifiedAnimation } from "../utils/animationManager.js";
6
7
  function SpinnerSymbol({
7
8
  isRunning,
8
9
  color,
9
- symbol = SYMBOLS.TASK_INDICATOR,
10
- interval = 500
10
+ symbol = SYMBOLS.TASK_INDICATOR
11
11
  }) {
12
12
  const theme = getTheme();
13
13
  const baseColor = getAgentColor(color, theme);
14
- const [isBright, setIsBright] = useState(true);
15
- useEffect(() => {
16
- if (!isRunning) {
17
- setIsBright(true);
18
- return;
19
- }
20
- const timer = setInterval(() => {
21
- setIsBright((prev) => !prev);
22
- }, interval);
23
- return () => clearInterval(timer);
24
- }, [isRunning, interval]);
14
+ const startTimeRef = useRef(Date.now());
15
+ const { dotVisible } = useUnifiedAnimation({
16
+ enabled: isRunning,
17
+ startTime: startTimeRef.current,
18
+ spinnerFrameCount: 2,
19
+ // Just for alternation
20
+ componentId: "spinner-symbol"
21
+ });
22
+ const isBright = isRunning ? dotVisible : true;
25
23
  const displayColor = isRunning && !isBright ? dimColor(baseColor) : baseColor;
26
24
  return /* @__PURE__ */ React.createElement(Text, { color: displayColor }, symbol);
27
25
  }
@@ -51,23 +49,19 @@ function getBlinkingSymbols() {
51
49
  }
52
50
  function SpinnerSymbolAlternative({
53
51
  isRunning,
54
- color,
55
- interval = 500
52
+ color
56
53
  }) {
57
54
  const theme = getTheme();
58
55
  const displayColor = getAgentColor(color, theme);
56
+ const startTimeRef = useRef(Date.now());
59
57
  const symbols = getBlinkingSymbols();
60
- const [frameIndex, setFrameIndex] = useState(0);
61
- useEffect(() => {
62
- if (!isRunning) {
63
- setFrameIndex(0);
64
- return;
65
- }
66
- const timer = setInterval(() => {
67
- setFrameIndex((prev) => (prev + 1) % symbols.length);
68
- }, interval);
69
- return () => clearInterval(timer);
70
- }, [isRunning, interval, symbols.length]);
58
+ const { spinnerFrame } = useUnifiedAnimation({
59
+ enabled: isRunning,
60
+ startTime: startTimeRef.current,
61
+ spinnerFrameCount: symbols.length,
62
+ componentId: "spinner-symbol-alt"
63
+ });
64
+ const frameIndex = isRunning ? spinnerFrame : 0;
71
65
  return /* @__PURE__ */ React.createElement(Text, { color: displayColor }, symbols[frameIndex]);
72
66
  }
73
67
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/SpinnerSymbol.tsx"],
4
- "sourcesContent": ["import React, { useState, useEffect } from 'react'\nimport { Text } from 'ink'\nimport { SYMBOLS } from '@constants/symbols'\nimport { getAgentColor } from '@constants/colors'\nimport { getTheme } from '@utils/theme'\nimport type { AgentColor } from '@constants/colors'\n\ninterface SpinnerSymbolProps {\n /** \u662F\u5426\u6B63\u5728\u8FD0\u884C\uFF08\u63A7\u5236\u95EA\u70C1\uFF09 */\n isRunning: boolean\n /** Agent \u989C\u8272 */\n color?: AgentColor\n /** \u81EA\u5B9A\u4E49\u7B26\u53F7\uFF08\u9ED8\u8BA4\u4F7F\u7528 TASK_INDICATOR \u23FA\uFF09 */\n symbol?: string\n /** \u95EA\u70C1\u95F4\u9694\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 500ms\uFF09 */\n interval?: number\n}\n\n/**\n * \u95EA\u70C1\u7684\u4EFB\u52A1\u6307\u793A\u5668\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u4EFB\u52A1\u6267\u884C\u4E2D\uFF1A\u23FA \u7B26\u53F7\u4EE5\u6307\u5B9A\u989C\u8272\u95EA\u70C1\uFF08\u4EAE \u2192 \u6697\u4EA4\u66FF\uFF09\n * - \u4EFB\u52A1\u5B8C\u6210\u540E\uFF1A\u23FA \u7B26\u53F7\u505C\u6B62\u95EA\u70C1\uFF0C\u4FDD\u6301\u56FA\u5B9A\u989C\u8272\n * - \u652F\u6301\u81EA\u5B9A\u4E49\u989C\u8272\u548C\u95EA\u70C1\u9891\u7387\n *\n * \u5B9E\u73B0\u539F\u7406\uFF1A\n * \u4F7F\u7528 setInterval \u6BCF 500ms \u5207\u6362\u4E00\u6B21\u4E0D\u900F\u660E\u5EA6\uFF08\u901A\u8FC7\u989C\u8272\u4EAE\u5EA6\u8C03\u6574\u6A21\u62DF\uFF09\n */\nexport function SpinnerSymbol({\n isRunning,\n color,\n symbol = SYMBOLS.TASK_INDICATOR,\n interval = 500,\n}: SpinnerSymbolProps) {\n const theme = getTheme()\n const baseColor = getAgentColor(color, theme)\n\n // \u95EA\u70C1\u72B6\u6001\uFF1Atrue = \u4EAE\uFF0Cfalse = \u6697\n const [isBright, setIsBright] = useState(true)\n\n useEffect(() => {\n // \u53EA\u6709\u5728\u8FD0\u884C\u65F6\u624D\u95EA\u70C1\n if (!isRunning) {\n setIsBright(true) // \u505C\u6B62\u65F6\u4FDD\u6301\u4EAE\u72B6\u6001\n return\n }\n\n // \u8BBE\u7F6E\u95EA\u70C1\u5B9A\u65F6\u5668\n const timer = setInterval(() => {\n setIsBright(prev => !prev)\n }, interval)\n\n // \u6E05\u7406\u5B9A\u65F6\u5668\n return () => clearInterval(timer)\n }, [isRunning, interval])\n\n // \u6839\u636E\u95EA\u70C1\u72B6\u6001\u8C03\u6574\u989C\u8272\u4EAE\u5EA6\n const displayColor = isRunning && !isBright ? dimColor(baseColor) : baseColor\n\n return <Text color={displayColor}>{symbol}</Text>\n}\n\n/**\n * \u964D\u4F4E\u989C\u8272\u4EAE\u5EA6\uFF08\u6A21\u62DF\u6697\u72B6\u6001\uFF09\n *\n * @param color - Hex \u989C\u8272\u503C\uFF08\u5982 #FF5555\uFF09\n * @returns \u964D\u4F4E\u4EAE\u5EA6\u540E\u7684 Hex \u989C\u8272\u503C\n */\nfunction dimColor(color: string): string {\n // \u79FB\u9664 # \u524D\u7F00\n const hex = color.replace('#', '')\n\n // \u8F6C\u6362\u4E3A RGB\n const r = parseInt(hex.substring(0, 2), 16)\n const g = parseInt(hex.substring(2, 4), 16)\n const b = parseInt(hex.substring(4, 6), 16)\n\n // \u964D\u4F4E\u4EAE\u5EA6\u5230 40%\n const dimFactor = 0.4\n const dimR = Math.floor(r * dimFactor)\n const dimG = Math.floor(g * dimFactor)\n const dimB = Math.floor(b * dimFactor)\n\n // \u8F6C\u6362\u56DE Hex\n const toHex = (n: number) => n.toString(16).padStart(2, '0')\n return `#${toHex(dimR)}${toHex(dimG)}${toHex(dimB)}`\n}\n\n/**\n * \u83B7\u53D6\u95EA\u70C1\u5E27\u7B26\u53F7\uFF08\u5907\u9009\u65B9\u6848\uFF1A\u4F7F\u7528\u4E0D\u540C\u7B26\u53F7\u6A21\u62DF\u95EA\u70C1\uFF09\n *\n * \u5982\u679C\u7EC8\u7AEF\u4E0D\u652F\u6301\u989C\u8272\u8C03\u6574\uFF0C\u53EF\u4EE5\u4F7F\u7528\u4E0D\u540C\u7B26\u53F7\u4EA4\u66FF\u663E\u793A\n */\nexport function getBlinkingSymbols(): string[] {\n return [\n '\u23FA', // \u5B9E\u5FC3\u5706\n '\u25C9', // \u5927\u5706\u70B9\n '\u25CF', // \u4E2D\u5706\u70B9\n '\u25CB', // \u7A7A\u5FC3\u5706\n ]\n}\n\n/**\n * \u95EA\u70C1\u7684\u4EFB\u52A1\u6307\u793A\u5668\uFF08\u5907\u9009\u65B9\u6848\uFF1A\u7B26\u53F7\u4EA4\u66FF\uFF09\n *\n * \u4F7F\u7528\u4E0D\u540C\u7B26\u53F7\u6A21\u62DF\u95EA\u70C1\u6548\u679C\uFF0C\u9002\u7528\u4E8E\u4E0D\u652F\u6301\u989C\u8272\u8C03\u6574\u7684\u7EC8\u7AEF\n */\nexport function SpinnerSymbolAlternative({\n isRunning,\n color,\n interval = 500,\n}: Omit<SpinnerSymbolProps, 'symbol'>) {\n const theme = getTheme()\n const displayColor = getAgentColor(color, theme)\n\n const symbols = getBlinkingSymbols()\n const [frameIndex, setFrameIndex] = useState(0)\n\n useEffect(() => {\n if (!isRunning) {\n setFrameIndex(0) // \u505C\u6B62\u65F6\u4F7F\u7528\u7B2C\u4E00\u5E27\n return\n }\n\n const timer = setInterval(() => {\n setFrameIndex(prev => (prev + 1) % symbols.length)\n }, interval)\n\n return () => clearInterval(timer)\n }, [isRunning, interval, symbols.length])\n\n return <Text color={displayColor}>{symbols[frameIndex]}</Text>\n}\n"],
5
- "mappings": "AAAA,OAAO,SAAS,UAAU,iBAAiB;AAC3C,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AAyBlB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,SAAS,QAAQ;AAAA,EACjB,WAAW;AACb,GAAuB;AACrB,QAAM,QAAQ,SAAS;AACvB,QAAM,YAAY,cAAc,OAAO,KAAK;AAG5C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,IAAI;AAE7C,YAAU,MAAM;AAEd,QAAI,CAAC,WAAW;AACd,kBAAY,IAAI;AAChB;AAAA,IACF;AAGA,UAAM,QAAQ,YAAY,MAAM;AAC9B,kBAAY,UAAQ,CAAC,IAAI;AAAA,IAC3B,GAAG,QAAQ;AAGX,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,WAAW,QAAQ,CAAC;AAGxB,QAAM,eAAe,aAAa,CAAC,WAAW,SAAS,SAAS,IAAI;AAEpE,SAAO,oCAAC,QAAK,OAAO,gBAAe,MAAO;AAC5C;AAQA,SAAS,SAAS,OAAuB;AAEvC,QAAM,MAAM,MAAM,QAAQ,KAAK,EAAE;AAGjC,QAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAC1C,QAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAC1C,QAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAG1C,QAAM,YAAY;AAClB,QAAM,OAAO,KAAK,MAAM,IAAI,SAAS;AACrC,QAAM,OAAO,KAAK,MAAM,IAAI,SAAS;AACrC,QAAM,OAAO,KAAK,MAAM,IAAI,SAAS;AAGrC,QAAM,QAAQ,CAAC,MAAc,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC3D,SAAO,IAAI,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;AACpD;AAOO,SAAS,qBAA+B;AAC7C,SAAO;AAAA,IACL;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,EACF;AACF;AAOO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAuC;AACrC,QAAM,QAAQ,SAAS;AACvB,QAAM,eAAe,cAAc,OAAO,KAAK;AAE/C,QAAM,UAAU,mBAAmB;AACnC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,CAAC;AAE9C,YAAU,MAAM;AACd,QAAI,CAAC,WAAW;AACd,oBAAc,CAAC;AACf;AAAA,IACF;AAEA,UAAM,QAAQ,YAAY,MAAM;AAC9B,oBAAc,WAAS,OAAO,KAAK,QAAQ,MAAM;AAAA,IACnD,GAAG,QAAQ;AAEX,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,WAAW,UAAU,QAAQ,MAAM,CAAC;AAExC,SAAO,oCAAC,QAAK,OAAO,gBAAe,QAAQ,UAAU,CAAE;AACzD;",
4
+ "sourcesContent": ["import React, { useRef, useMemo } from 'react'\nimport { Text } from 'ink'\nimport { SYMBOLS } from '@constants/symbols'\nimport { getAgentColor } from '@constants/colors'\nimport { getTheme } from '@utils/theme'\nimport { useUnifiedAnimation } from '@utils/animationManager'\nimport type { AgentColor } from '@constants/colors'\n\ninterface SpinnerSymbolProps {\n /** \u662F\u5426\u6B63\u5728\u8FD0\u884C\uFF08\u63A7\u5236\u95EA\u70C1\uFF09 */\n isRunning: boolean\n /** Agent \u989C\u8272 */\n color?: AgentColor\n /** \u81EA\u5B9A\u4E49\u7B26\u53F7\uFF08\u9ED8\u8BA4\u4F7F\u7528 TASK_INDICATOR \u23FA\uFF09 */\n symbol?: string\n /** \u95EA\u70C1\u95F4\u9694\uFF08\u6BEB\u79D2\uFF0C\u9ED8\u8BA4 500ms\uFF09- \u4E0D\u518D\u4F7F\u7528\uFF0C\u4FDD\u7559\u7528\u4E8E\u5411\u540E\u517C\u5BB9 */\n interval?: number\n}\n\n/**\n * \u95EA\u70C1\u7684\u4EFB\u52A1\u6307\u793A\u5668\u7EC4\u4EF6\n *\n * \u529F\u80FD\uFF1A\n * - \u4EFB\u52A1\u6267\u884C\u4E2D\uFF1A\u23FA \u7B26\u53F7\u4EE5\u6307\u5B9A\u989C\u8272\u95EA\u70C1\uFF08\u4EAE \u2192 \u6697\u4EA4\u66FF\uFF09\n * - \u4EFB\u52A1\u5B8C\u6210\u540E\uFF1A\u23FA \u7B26\u53F7\u505C\u6B62\u95EA\u70C1\uFF0C\u4FDD\u6301\u56FA\u5B9A\u989C\u8272\n * - \u652F\u6301\u81EA\u5B9A\u4E49\u989C\u8272\u548C\u95EA\u70C1\u9891\u7387\n *\n * \u5B9E\u73B0\u539F\u7406\uFF1A\n * \u4F7F\u7528\u7EDF\u4E00\u52A8\u753B\u7BA1\u7406\u5668\u4EE3\u66FF setInterval\uFF0C\u51CF\u5C11\u5C4F\u5E55\u95EA\u70C1\n */\nexport function SpinnerSymbol({\n isRunning,\n color,\n symbol = SYMBOLS.TASK_INDICATOR,\n}: SpinnerSymbolProps) {\n const theme = getTheme()\n const baseColor = getAgentColor(color, theme)\n const startTimeRef = useRef(Date.now())\n\n // Use unified animation manager with 2 frames (bright/dim)\n const { dotVisible } = useUnifiedAnimation({\n enabled: isRunning,\n startTime: startTimeRef.current,\n spinnerFrameCount: 2, // Just for alternation\n componentId: 'spinner-symbol',\n })\n\n // dotVisible alternates at 600ms, perfect for blink effect\n const isBright = isRunning ? dotVisible : true\n\n // \u6839\u636E\u95EA\u70C1\u72B6\u6001\u8C03\u6574\u989C\u8272\u4EAE\u5EA6\n const displayColor = isRunning && !isBright ? dimColor(baseColor) : baseColor\n\n return <Text color={displayColor}>{symbol}</Text>\n}\n\n/**\n * \u964D\u4F4E\u989C\u8272\u4EAE\u5EA6\uFF08\u6A21\u62DF\u6697\u72B6\u6001\uFF09\n *\n * @param color - Hex \u989C\u8272\u503C\uFF08\u5982 #FF5555\uFF09\n * @returns \u964D\u4F4E\u4EAE\u5EA6\u540E\u7684 Hex \u989C\u8272\u503C\n */\nfunction dimColor(color: string): string {\n // \u79FB\u9664 # \u524D\u7F00\n const hex = color.replace('#', '')\n\n // \u8F6C\u6362\u4E3A RGB\n const r = parseInt(hex.substring(0, 2), 16)\n const g = parseInt(hex.substring(2, 4), 16)\n const b = parseInt(hex.substring(4, 6), 16)\n\n // \u964D\u4F4E\u4EAE\u5EA6\u5230 40%\n const dimFactor = 0.4\n const dimR = Math.floor(r * dimFactor)\n const dimG = Math.floor(g * dimFactor)\n const dimB = Math.floor(b * dimFactor)\n\n // \u8F6C\u6362\u56DE Hex\n const toHex = (n: number) => n.toString(16).padStart(2, '0')\n return `#${toHex(dimR)}${toHex(dimG)}${toHex(dimB)}`\n}\n\n/**\n * \u83B7\u53D6\u95EA\u70C1\u5E27\u7B26\u53F7\uFF08\u5907\u9009\u65B9\u6848\uFF1A\u4F7F\u7528\u4E0D\u540C\u7B26\u53F7\u6A21\u62DF\u95EA\u70C1\uFF09\n *\n * \u5982\u679C\u7EC8\u7AEF\u4E0D\u652F\u6301\u989C\u8272\u8C03\u6574\uFF0C\u53EF\u4EE5\u4F7F\u7528\u4E0D\u540C\u7B26\u53F7\u4EA4\u66FF\u663E\u793A\n */\nexport function getBlinkingSymbols(): string[] {\n return [\n '\u23FA', // \u5B9E\u5FC3\u5706\n '\u25C9', // \u5927\u5706\u70B9\n '\u25CF', // \u4E2D\u5706\u70B9\n '\u25CB', // \u7A7A\u5FC3\u5706\n ]\n}\n\n/**\n * \u95EA\u70C1\u7684\u4EFB\u52A1\u6307\u793A\u5668\uFF08\u5907\u9009\u65B9\u6848\uFF1A\u7B26\u53F7\u4EA4\u66FF\uFF09\n *\n * \u4F7F\u7528\u4E0D\u540C\u7B26\u53F7\u6A21\u62DF\u95EA\u70C1\u6548\u679C\uFF0C\u9002\u7528\u4E8E\u4E0D\u652F\u6301\u989C\u8272\u8C03\u6574\u7684\u7EC8\u7AEF\n * \u4F7F\u7528\u7EDF\u4E00\u52A8\u753B\u7BA1\u7406\u5668\u4EE3\u66FF setInterval\n */\nexport function SpinnerSymbolAlternative({\n isRunning,\n color,\n}: Omit<SpinnerSymbolProps, 'symbol'>) {\n const theme = getTheme()\n const displayColor = getAgentColor(color, theme)\n const startTimeRef = useRef(Date.now())\n\n const symbols = getBlinkingSymbols()\n\n // Use unified animation manager\n const { spinnerFrame } = useUnifiedAnimation({\n enabled: isRunning,\n startTime: startTimeRef.current,\n spinnerFrameCount: symbols.length,\n componentId: 'spinner-symbol-alt',\n })\n\n const frameIndex = isRunning ? spinnerFrame : 0\n\n return <Text color={displayColor}>{symbols[frameIndex]}</Text>\n}\n"],
5
+ "mappings": "AAAA,OAAO,SAAS,cAAuB;AACvC,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,2BAA2B;AAyB7B,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,SAAS,QAAQ;AACnB,GAAuB;AACrB,QAAM,QAAQ,SAAS;AACvB,QAAM,YAAY,cAAc,OAAO,KAAK;AAC5C,QAAM,eAAe,OAAO,KAAK,IAAI,CAAC;AAGtC,QAAM,EAAE,WAAW,IAAI,oBAAoB;AAAA,IACzC,SAAS;AAAA,IACT,WAAW,aAAa;AAAA,IACxB,mBAAmB;AAAA;AAAA,IACnB,aAAa;AAAA,EACf,CAAC;AAGD,QAAM,WAAW,YAAY,aAAa;AAG1C,QAAM,eAAe,aAAa,CAAC,WAAW,SAAS,SAAS,IAAI;AAEpE,SAAO,oCAAC,QAAK,OAAO,gBAAe,MAAO;AAC5C;AAQA,SAAS,SAAS,OAAuB;AAEvC,QAAM,MAAM,MAAM,QAAQ,KAAK,EAAE;AAGjC,QAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAC1C,QAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAC1C,QAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAG1C,QAAM,YAAY;AAClB,QAAM,OAAO,KAAK,MAAM,IAAI,SAAS;AACrC,QAAM,OAAO,KAAK,MAAM,IAAI,SAAS;AACrC,QAAM,OAAO,KAAK,MAAM,IAAI,SAAS;AAGrC,QAAM,QAAQ,CAAC,MAAc,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC3D,SAAO,IAAI,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;AACpD;AAOO,SAAS,qBAA+B;AAC7C,SAAO;AAAA,IACL;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,EACF;AACF;AAQO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AACF,GAAuC;AACrC,QAAM,QAAQ,SAAS;AACvB,QAAM,eAAe,cAAc,OAAO,KAAK;AAC/C,QAAM,eAAe,OAAO,KAAK,IAAI,CAAC;AAEtC,QAAM,UAAU,mBAAmB;AAGnC,QAAM,EAAE,aAAa,IAAI,oBAAoB;AAAA,IAC3C,SAAS;AAAA,IACT,WAAW,aAAa;AAAA,IACxB,mBAAmB,QAAQ;AAAA,IAC3B,aAAa;AAAA,EACf,CAAC;AAED,QAAM,aAAa,YAAY,eAAe;AAE9C,SAAO,oCAAC,QAAK,OAAO,gBAAe,QAAQ,UAAU,CAAE;AACzD;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import { useState, useEffect, useRef } from "react";
4
4
  import { getTheme } from "../utils/theme.js";
5
5
  import { BackgroundShellManager } from "../utils/BackgroundShellManager.js";
6
+ import { useUnifiedAnimation } from "../utils/animationManager.js";
6
7
  const MAX_SUMMARY_LINES = 5;
7
8
  const MAX_DETAIL_LINES = 50;
8
9
  function StreamingBashOutput({
@@ -13,11 +14,17 @@ function StreamingBashOutput({
13
14
  }) {
14
15
  const [outputLines, setOutputLines] = useState([]);
15
16
  const [hiddenLineCount, setHiddenLineCount] = useState(0);
16
- const [elapsedTime, setElapsedTime] = useState(0);
17
17
  const [isComplete, setIsComplete] = useState(false);
18
18
  const [showDetail, setShowDetail] = useState(false);
19
- const startTime = useRef(Date.now());
19
+ const startTimeRef = useRef(Date.now());
20
20
  const theme = getTheme();
21
+ const { elapsedTime } = useUnifiedAnimation({
22
+ enabled: !isComplete,
23
+ startTime: startTimeRef.current,
24
+ spinnerFrameCount: 1,
25
+ // Not used but required
26
+ componentId: `streaming-bash-${shellId}`
27
+ });
21
28
  useInput((input, key) => {
22
29
  if (key.ctrl && input === "b") {
23
30
  onMoveToBackground();
@@ -25,12 +32,6 @@ function StreamingBashOutput({
25
32
  setShowDetail((prev) => !prev);
26
33
  }
27
34
  });
28
- useEffect(() => {
29
- const interval = setInterval(() => {
30
- setElapsedTime(Math.floor((Date.now() - startTime.current) / 1e3));
31
- }, 1e3);
32
- return () => clearInterval(interval);
33
- }, []);
34
35
  useEffect(() => {
35
36
  const manager = BackgroundShellManager.getInstance();
36
37
  const interval = setInterval(() => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/StreamingBashOutput.tsx"],
4
- "sourcesContent": ["import { Box, Text, useInput } from 'ink'\nimport * as React from 'react'\nimport { useState, useEffect, useRef } from 'react'\nimport { getTheme } from '@utils/theme'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\n\ntype Props = {\n shellId: string\n command: string\n onMoveToBackground: () => void\n onComplete: (stdout: string, stderr: string, exitCode: number) => void\n}\n\nconst MAX_SUMMARY_LINES = 5 // Show only first few lines in summary mode\nconst MAX_DETAIL_LINES = 50 // Show more lines in detail mode\n\nexport function StreamingBashOutput({\n shellId,\n command,\n onMoveToBackground,\n onComplete,\n}: Props) {\n const [outputLines, setOutputLines] = useState<string[]>([])\n const [hiddenLineCount, setHiddenLineCount] = useState(0)\n const [elapsedTime, setElapsedTime] = useState(0)\n const [isComplete, setIsComplete] = useState(false)\n const [showDetail, setShowDetail] = useState(false) // Ctrl+O toggle\n const startTime = useRef(Date.now())\n const theme = getTheme()\n\n // Listen for keyboard shortcuts\n useInput((input, key) => {\n if (key.ctrl && input === 'b') {\n onMoveToBackground()\n } else if (key.ctrl && input === 'o') {\n setShowDetail(prev => !prev)\n }\n })\n\n // Update elapsed time\n useEffect(() => {\n const interval = setInterval(() => {\n setElapsedTime(Math.floor((Date.now() - startTime.current) / 1000))\n }, 1000)\n\n return () => clearInterval(interval)\n }, [])\n\n // Poll for output from background shell\n useEffect(() => {\n const manager = BackgroundShellManager.getInstance()\n\n const interval = setInterval(() => {\n const shell = manager.get(shellId)\n if (!shell) return\n\n // Get all output\n const allLines = [...shell.stdout, ...shell.stderr].filter(\n line => line.trim().length > 0,\n )\n setOutputLines(allLines)\n\n // Calculate hidden lines based on current mode\n const maxLines = showDetail ? MAX_DETAIL_LINES : MAX_SUMMARY_LINES\n if (allLines.length > maxLines) {\n setHiddenLineCount(allLines.length - maxLines)\n } else {\n setHiddenLineCount(0)\n }\n\n // Check if complete\n if (shell.status !== 'running' && !isComplete) {\n setIsComplete(true)\n clearInterval(interval)\n\n // Clean up the shell after getting final output\n const stdout = shell.stdout.join('\\n')\n const stderr = shell.stderr.join('\\n')\n const exitCode = shell.exitCode ?? 0\n\n // Remove from manager after a short delay\n setTimeout(() => {\n manager.remove(shellId)\n }, 1000)\n\n onComplete(stdout, stderr, exitCode)\n }\n }, 100)\n\n return () => clearInterval(interval)\n }, [shellId, isComplete, onComplete, showDetail])\n\n const maxLines = showDetail ? MAX_DETAIL_LINES : MAX_SUMMARY_LINES\n const visibleLines = outputLines.slice(0, maxLines) // Show first N lines, not last N\n\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text color={theme.bashBorder}>! </Text>\n <Text>{command}</Text>\n </Box>\n\n {visibleLines.length > 0 && (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Text color={theme.secondaryText}>\u23BF </Text>\n {visibleLines.map((line, index) => (\n <Box key={index}>\n <Text>{line}</Text>\n </Box>\n ))}\n </Box>\n )}\n\n {hiddenLineCount > 0 && (\n <Box paddingLeft={2}>\n <Text color={theme.secondaryText}>\n +{hiddenLineCount} more lines ({elapsedTime}s)\n </Text>\n </Box>\n )}\n\n {!isComplete && (\n <Box paddingLeft={2} marginTop={1} flexDirection=\"row\" gap={1}>\n <Text color={theme.secondaryText}>\n ctrl+o {showDetail ? 'hide details' : 'show details'}\n </Text>\n <Text color={theme.secondaryText}>\u00B7</Text>\n <Text color={theme.warning}>ctrl+b run in background</Text>\n </Box>\n )}\n </Box>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Box, Text, useInput } from 'ink'\nimport * as React from 'react'\nimport { useState, useEffect, useRef } from 'react'\nimport { getTheme } from '@utils/theme'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport { useUnifiedAnimation } from '@utils/animationManager'\n\ntype Props = {\n shellId: string\n command: string\n onMoveToBackground: () => void\n onComplete: (stdout: string, stderr: string, exitCode: number) => void\n}\n\nconst MAX_SUMMARY_LINES = 5 // Show only first few lines in summary mode\nconst MAX_DETAIL_LINES = 50 // Show more lines in detail mode\n\nexport function StreamingBashOutput({\n shellId,\n command,\n onMoveToBackground,\n onComplete,\n}: Props) {\n const [outputLines, setOutputLines] = useState<string[]>([])\n const [hiddenLineCount, setHiddenLineCount] = useState(0)\n const [isComplete, setIsComplete] = useState(false)\n const [showDetail, setShowDetail] = useState(false) // Ctrl+O toggle\n const startTimeRef = useRef(Date.now())\n const theme = getTheme()\n\n // Use unified animation manager for elapsed time instead of setInterval\n const { elapsedTime } = useUnifiedAnimation({\n enabled: !isComplete,\n startTime: startTimeRef.current,\n spinnerFrameCount: 1, // Not used but required\n componentId: `streaming-bash-${shellId}`,\n })\n\n // Listen for keyboard shortcuts\n useInput((input, key) => {\n if (key.ctrl && input === 'b') {\n onMoveToBackground()\n } else if (key.ctrl && input === 'o') {\n setShowDetail(prev => !prev)\n }\n })\n\n // Poll for output from background shell\n useEffect(() => {\n const manager = BackgroundShellManager.getInstance()\n\n const interval = setInterval(() => {\n const shell = manager.get(shellId)\n if (!shell) return\n\n // Get all output\n const allLines = [...shell.stdout, ...shell.stderr].filter(\n line => line.trim().length > 0,\n )\n setOutputLines(allLines)\n\n // Calculate hidden lines based on current mode\n const maxLines = showDetail ? MAX_DETAIL_LINES : MAX_SUMMARY_LINES\n if (allLines.length > maxLines) {\n setHiddenLineCount(allLines.length - maxLines)\n } else {\n setHiddenLineCount(0)\n }\n\n // Check if complete\n if (shell.status !== 'running' && !isComplete) {\n setIsComplete(true)\n clearInterval(interval)\n\n // Clean up the shell after getting final output\n const stdout = shell.stdout.join('\\n')\n const stderr = shell.stderr.join('\\n')\n const exitCode = shell.exitCode ?? 0\n\n // Remove from manager after a short delay\n setTimeout(() => {\n manager.remove(shellId)\n }, 1000)\n\n onComplete(stdout, stderr, exitCode)\n }\n }, 100)\n\n return () => clearInterval(interval)\n }, [shellId, isComplete, onComplete, showDetail])\n\n const maxLines = showDetail ? MAX_DETAIL_LINES : MAX_SUMMARY_LINES\n const visibleLines = outputLines.slice(0, maxLines) // Show first N lines, not last N\n\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text color={theme.bashBorder}>! </Text>\n <Text>{command}</Text>\n </Box>\n\n {visibleLines.length > 0 && (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Text color={theme.secondaryText}>\u23BF </Text>\n {visibleLines.map((line, index) => (\n <Box key={index}>\n <Text>{line}</Text>\n </Box>\n ))}\n </Box>\n )}\n\n {hiddenLineCount > 0 && (\n <Box paddingLeft={2}>\n <Text color={theme.secondaryText}>\n +{hiddenLineCount} more lines ({elapsedTime}s)\n </Text>\n </Box>\n )}\n\n {!isComplete && (\n <Box paddingLeft={2} marginTop={1} flexDirection=\"row\" gap={1}>\n <Text color={theme.secondaryText}>\n ctrl+o {showDetail ? 'hide details' : 'show details'}\n </Text>\n <Text color={theme.secondaryText}>\u00B7</Text>\n <Text color={theme.warning}>ctrl+b run in background</Text>\n </Box>\n )}\n </Box>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["maxLines"]
7
7
  }
@@ -3,6 +3,7 @@ import * as React from "react";
3
3
  import { getTheme } from "../utils/theme.js";
4
4
  import { useMemo } from "react";
5
5
  import { wrapText } from "../utils/format.js";
6
+ import { SEMANTIC_COLORS } from "../constants/colors.js";
6
7
  function StructuredDiff({
7
8
  patch,
8
9
  dim,
@@ -54,9 +55,8 @@ function formatDiff(lines, startingLineNumber, width, dim, overrideTheme) {
54
55
  ), /* @__PURE__ */ React.createElement(
55
56
  Text,
56
57
  {
57
- color: overrideTheme ? theme.text : void 0,
58
- backgroundColor: dim ? theme.diff.addedDimmed : theme.diff.added,
59
- dimColor: dim
58
+ color: dim ? SEMANTIC_COLORS.dim : overrideTheme ? theme.text : void 0,
59
+ backgroundColor: dim ? theme.diff.addedDimmed : theme.diff.added
60
60
  },
61
61
  line
62
62
  )));
@@ -70,9 +70,8 @@ function formatDiff(lines, startingLineNumber, width, dim, overrideTheme) {
70
70
  ), /* @__PURE__ */ React.createElement(
71
71
  Text,
72
72
  {
73
- color: overrideTheme ? theme.text : void 0,
74
- backgroundColor: dim ? theme.diff.removedDimmed : theme.diff.removed,
75
- dimColor: dim
73
+ color: dim ? SEMANTIC_COLORS.dim : overrideTheme ? theme.text : void 0,
74
+ backgroundColor: dim ? theme.diff.removedDimmed : theme.diff.removed
76
75
  },
77
76
  line
78
77
  )));
@@ -86,8 +85,7 @@ function formatDiff(lines, startingLineNumber, width, dim, overrideTheme) {
86
85
  ), /* @__PURE__ */ React.createElement(
87
86
  Text,
88
87
  {
89
- color: overrideTheme ? theme.text : void 0,
90
- dimColor: dim
88
+ color: dim ? SEMANTIC_COLORS.dim : overrideTheme ? theme.text : void 0
91
89
  },
92
90
  line
93
91
  )));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/StructuredDiff.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { Hunk } from 'diff'\nimport { getTheme, ThemeNames } from '@utils/theme'\nimport { useMemo } from 'react'\nimport { wrapText } from '@utils/format'\n\ntype Props = {\n patch: Hunk\n dim: boolean\n width: number\n overrideTheme?: ThemeNames // custom theme for previews\n key?: React.Key\n}\n\nexport function StructuredDiff({\n patch,\n dim,\n width,\n overrideTheme,\n}: Props): React.ReactNode {\n const diff = useMemo(\n () => formatDiff(patch.lines, patch.oldStart, width, dim, overrideTheme),\n [patch.lines, patch.oldStart, width, dim, overrideTheme],\n )\n\n return diff.map((_, i) => <Box key={i}>{_}</Box>)\n}\n\nfunction formatDiff(\n lines: string[],\n startingLineNumber: number,\n width: number,\n dim: boolean,\n overrideTheme?: ThemeNames,\n): React.ReactNode[] {\n const theme = getTheme(overrideTheme)\n\n const ls = numberDiffLines(\n lines.map(code => {\n if (code.startsWith('+')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'add',\n }\n }\n if (code.startsWith('-')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'remove',\n }\n }\n return { code, i: 0, type: 'nochange' }\n }),\n startingLineNumber,\n )\n\n const maxLineNumber = Math.max(...ls.map(({ i }) => i))\n const maxWidth = maxLineNumber.toString().length\n\n return ls.flatMap(({ type, code, i }) => {\n const wrappedLines = wrapText(code, width - maxWidth)\n return wrappedLines.map((line, lineIndex) => {\n const key = `${type}-${i}-${lineIndex}`\n switch (type) {\n case 'add':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={overrideTheme ? theme.text : undefined}\n backgroundColor={\n dim ? theme.diff.addedDimmed : theme.diff.added\n }\n dimColor={dim}\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n case 'remove':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={overrideTheme ? theme.text : undefined}\n backgroundColor={\n dim ? theme.diff.removedDimmed : theme.diff.removed\n }\n dimColor={dim}\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n case 'nochange':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={overrideTheme ? theme.text : undefined}\n dimColor={dim}\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n }\n })\n })\n}\n\nfunction LineNumber({\n i,\n width,\n}: {\n i: number | undefined\n width: number\n}): React.ReactNode {\n return (\n <Text color={getTheme().secondaryText}>\n {i !== undefined ? i.toString().padStart(width) : ' '.repeat(width)}{' '}\n </Text>\n )\n}\n\nfunction numberDiffLines(\n diff: { code: string; type: string }[],\n startLine: number,\n): { code: string; type: string; i: number }[] {\n let i = startLine\n const result: { code: string; type: string; i: number }[] = []\n const queue = [...diff]\n\n while (queue.length > 0) {\n const { code, type } = queue.shift()!\n const line = {\n code: code,\n type,\n i,\n }\n\n // Update counters based on change type\n switch (type) {\n case 'nochange':\n i++\n result.push(line)\n break\n case 'add':\n i++\n result.push(line)\n break\n case 'remove': {\n result.push(line)\n let numRemoved = 0\n while (queue[0]?.type === 'remove') {\n i++\n const { code, type } = queue.shift()!\n const line = {\n code: code,\n type,\n i,\n }\n result.push(line)\n numRemoved++\n }\n i -= numRemoved\n break\n }\n }\n }\n\n return result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { Hunk } from 'diff'\nimport { getTheme, ThemeNames } from '@utils/theme'\nimport { useMemo } from 'react'\nimport { wrapText } from '@utils/format'\nimport { SEMANTIC_COLORS } from '@constants/colors'\n\ntype Props = {\n patch: Hunk\n dim: boolean\n width: number\n overrideTheme?: ThemeNames // custom theme for previews\n key?: React.Key\n}\n\nexport function StructuredDiff({\n patch,\n dim,\n width,\n overrideTheme,\n}: Props): React.ReactNode {\n const diff = useMemo(\n () => formatDiff(patch.lines, patch.oldStart, width, dim, overrideTheme),\n [patch.lines, patch.oldStart, width, dim, overrideTheme],\n )\n\n return diff.map((_, i) => <Box key={i}>{_}</Box>)\n}\n\nfunction formatDiff(\n lines: string[],\n startingLineNumber: number,\n width: number,\n dim: boolean,\n overrideTheme?: ThemeNames,\n): React.ReactNode[] {\n const theme = getTheme(overrideTheme)\n\n const ls = numberDiffLines(\n lines.map(code => {\n if (code.startsWith('+')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'add',\n }\n }\n if (code.startsWith('-')) {\n return {\n code: ' ' + code.slice(1),\n i: 0,\n type: 'remove',\n }\n }\n return { code, i: 0, type: 'nochange' }\n }),\n startingLineNumber,\n )\n\n const maxLineNumber = Math.max(...ls.map(({ i }) => i))\n const maxWidth = maxLineNumber.toString().length\n\n return ls.flatMap(({ type, code, i }) => {\n const wrappedLines = wrapText(code, width - maxWidth)\n return wrappedLines.map((line, lineIndex) => {\n const key = `${type}-${i}-${lineIndex}`\n switch (type) {\n case 'add':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={\n dim\n ? SEMANTIC_COLORS.dim\n : overrideTheme\n ? theme.text\n : undefined\n }\n backgroundColor={\n dim ? theme.diff.addedDimmed : theme.diff.added\n }\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n case 'remove':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={\n dim\n ? SEMANTIC_COLORS.dim\n : overrideTheme\n ? theme.text\n : undefined\n }\n backgroundColor={\n dim ? theme.diff.removedDimmed : theme.diff.removed\n }\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n case 'nochange':\n return (\n <React.Fragment key={key}>\n <Text>\n <LineNumber\n i={lineIndex === 0 ? i : undefined}\n width={maxWidth}\n />\n <Text\n color={\n dim\n ? SEMANTIC_COLORS.dim\n : overrideTheme\n ? theme.text\n : undefined\n }\n >\n {line}\n </Text>\n </Text>\n </React.Fragment>\n )\n }\n })\n })\n}\n\nfunction LineNumber({\n i,\n width,\n}: {\n i: number | undefined\n width: number\n}): React.ReactNode {\n return (\n <Text color={getTheme().secondaryText}>\n {i !== undefined ? i.toString().padStart(width) : ' '.repeat(width)}{' '}\n </Text>\n )\n}\n\nfunction numberDiffLines(\n diff: { code: string; type: string }[],\n startLine: number,\n): { code: string; type: string; i: number }[] {\n let i = startLine\n const result: { code: string; type: string; i: number }[] = []\n const queue = [...diff]\n\n while (queue.length > 0) {\n const { code, type } = queue.shift()!\n const line = {\n code: code,\n type,\n i,\n }\n\n // Update counters based on change type\n switch (type) {\n case 'nochange':\n i++\n result.push(line)\n break\n case 'add':\n i++\n result.push(line)\n break\n case 'remove': {\n result.push(line)\n let numRemoved = 0\n while (queue[0]?.type === 'remove') {\n i++\n const { code, type } = queue.shift()!\n const line = {\n code: code,\n type,\n i,\n }\n result.push(line)\n numRemoved++\n }\n i -= numRemoved\n break\n }\n }\n }\n\n return result\n}\n"],
5
+ "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AAEvB,SAAS,gBAA4B;AACrC,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAUzB,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,OAAO;AAAA,IACX,MAAM,WAAW,MAAM,OAAO,MAAM,UAAU,OAAO,KAAK,aAAa;AAAA,IACvE,CAAC,MAAM,OAAO,MAAM,UAAU,OAAO,KAAK,aAAa;AAAA,EACzD;AAEA,SAAO,KAAK,IAAI,CAAC,GAAG,MAAM,oCAAC,OAAI,KAAK,KAAI,CAAE,CAAM;AAClD;AAEA,SAAS,WACP,OACA,oBACA,OACA,KACA,eACmB;AACnB,QAAM,QAAQ,SAAS,aAAa;AAEpC,QAAM,KAAK;AAAA,IACT,MAAM,IAAI,UAAQ;AAChB,UAAI,KAAK,WAAW,GAAG,GAAG;AACxB,eAAO;AAAA,UACL,MAAM,MAAM,KAAK,MAAM,CAAC;AAAA,UACxB,GAAG;AAAA,UACH,MAAM;AAAA,QACR;AAAA,MACF;AACA,UAAI,KAAK,WAAW,GAAG,GAAG;AACxB,eAAO;AAAA,UACL,MAAM,MAAM,KAAK,MAAM,CAAC;AAAA,UACxB,GAAG;AAAA,UACH,MAAM;AAAA,QACR;AAAA,MACF;AACA,aAAO,EAAE,MAAM,GAAG,GAAG,MAAM,WAAW;AAAA,IACxC,CAAC;AAAA,IACD;AAAA,EACF;AAEA,QAAM,gBAAgB,KAAK,IAAI,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;AACtD,QAAM,WAAW,cAAc,SAAS,EAAE;AAE1C,SAAO,GAAG,QAAQ,CAAC,EAAE,MAAM,MAAM,EAAE,MAAM;AACvC,UAAM,eAAe,SAAS,MAAM,QAAQ,QAAQ;AACpD,WAAO,aAAa,IAAI,CAAC,MAAM,cAAc;AAC3C,YAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,SAAS;AACrC,cAAQ,MAAM;AAAA,QACZ,KAAK;AACH,iBACE,oCAAC,MAAM,UAAN,EAAe,OACd,oCAAC,YACC;AAAA,YAAC;AAAA;AAAA,cACC,GAAG,cAAc,IAAI,IAAI;AAAA,cACzB,OAAO;AAAA;AAAA,UACT,GACA;AAAA,YAAC;AAAA;AAAA,cACC,OACE,MACI,gBAAgB,MAChB,gBACE,MAAM,OACN;AAAA,cAER,iBACE,MAAM,MAAM,KAAK,cAAc,MAAM,KAAK;AAAA;AAAA,YAG3C;AAAA,UACH,CACF,CACF;AAAA,QAEJ,KAAK;AACH,iBACE,oCAAC,MAAM,UAAN,EAAe,OACd,oCAAC,YACC;AAAA,YAAC;AAAA;AAAA,cACC,GAAG,cAAc,IAAI,IAAI;AAAA,cACzB,OAAO;AAAA;AAAA,UACT,GACA;AAAA,YAAC;AAAA;AAAA,cACC,OACE,MACI,gBAAgB,MAChB,gBACE,MAAM,OACN;AAAA,cAER,iBACE,MAAM,MAAM,KAAK,gBAAgB,MAAM,KAAK;AAAA;AAAA,YAG7C;AAAA,UACH,CACF,CACF;AAAA,QAEJ,KAAK;AACH,iBACE,oCAAC,MAAM,UAAN,EAAe,OACd,oCAAC,YACC;AAAA,YAAC;AAAA;AAAA,cACC,GAAG,cAAc,IAAI,IAAI;AAAA,cACzB,OAAO;AAAA;AAAA,UACT,GACA;AAAA,YAAC;AAAA;AAAA,cACC,OACE,MACI,gBAAgB,MAChB,gBACE,MAAM,OACN;AAAA;AAAA,YAGP;AAAA,UACH,CACF,CACF;AAAA,MAEN;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AACF,GAGoB;AAClB,SACE,oCAAC,QAAK,OAAO,SAAS,EAAE,iBACrB,MAAM,SAAY,EAAE,SAAS,EAAE,SAAS,KAAK,IAAI,IAAI,OAAO,KAAK,GAAG,GACvE;AAEJ;AAEA,SAAS,gBACP,MACA,WAC6C;AAC7C,MAAI,IAAI;AACR,QAAM,SAAsD,CAAC;AAC7D,QAAM,QAAQ,CAAC,GAAG,IAAI;AAEtB,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,EAAE,MAAM,KAAK,IAAI,MAAM,MAAM;AACnC,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH;AACA,eAAO,KAAK,IAAI;AAChB;AAAA,MACF,KAAK;AACH;AACA,eAAO,KAAK,IAAI;AAChB;AAAA,MACF,KAAK,UAAU;AACb,eAAO,KAAK,IAAI;AAChB,YAAI,aAAa;AACjB,eAAO,MAAM,CAAC,GAAG,SAAS,UAAU;AAClC;AACA,gBAAM,EAAE,MAAAA,OAAM,MAAAC,MAAK,IAAI,MAAM,MAAM;AACnC,gBAAMC,QAAO;AAAA,YACX,MAAMF;AAAA,YACN,MAAAC;AAAA,YACA;AAAA,UACF;AACA,iBAAO,KAAKC,KAAI;AAChB;AAAA,QACF;AACA,aAAK;AACL;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;",
6
6
  "names": ["code", "type", "line"]
7
7
  }
@@ -5,6 +5,7 @@ import { getTodoStatusSymbol } from "../constants/symbols.js";
5
5
  import { AgentThinkingBlock } from "./AgentThinkingBlock.js";
6
6
  import { ToolExecutionBlock } from "./ToolExecutionBlock.js";
7
7
  import { AgentResponseBlock } from "./AgentResponseBlock.js";
8
+ import { useAgentTokenStats, formatTokenCount } from "../hooks/useAgentTokenStats.js";
8
9
  function SubagentBlock({
9
10
  subagent,
10
11
  indent = 0,
@@ -13,6 +14,7 @@ function SubagentBlock({
13
14
  const theme = getTheme();
14
15
  const [expanded, setExpanded] = useState(isExpanded);
15
16
  const marginLeft = indent * 2;
17
+ const tokenStats = useAgentTokenStats(subagent.id);
16
18
  const todos = extractSubagentTodos(subagent);
17
19
  const thinking = extractThinkingMessage(subagent);
18
20
  const toolExecutions = extractToolExecutions(subagent);
@@ -20,7 +22,7 @@ function SubagentBlock({
20
22
  const duration = subagent.metrics.endTime ? ((subagent.metrics.endTime - subagent.metrics.startTime) / 1e3).toFixed(
21
23
  1
22
24
  ) : "...";
23
- const tokenCount = subagent.metrics.tokenCount || 0;
25
+ const tokenCount = tokenStats?.grandTotalTokens ?? 0;
24
26
  const statusIndicator = getStatusIndicator(subagent.status);
25
27
  const statusColor = getStatusColor(subagent.status, theme);
26
28
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft, marginTop: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.brand }, "\u{1F916} "), /* @__PURE__ */ React.createElement(Text, null, "Launching subagent: ", /* @__PURE__ */ React.createElement(Text, { bold: true }, subagent.agentType))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u251C\u2500 "), /* @__PURE__ */ React.createElement(Text, null, "Task: ", subagent.taskName)), expanded && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502"), todos && todos.length > 0 && /* @__PURE__ */ React.createElement(SubagentTodosSection, { todos, theme }), thinking && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502 "), /* @__PURE__ */ React.createElement(
@@ -38,7 +40,7 @@ function SubagentBlock({
38
40
  indent: 0,
39
41
  isSubagent: true
40
42
  }
41
- )), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2514\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusIndicator, " ", getStatusLabel(subagent.status)), subagent.status === "completed" && /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " ", "(", duration, "s, ", tokenCount, " tokens)")), !expanded && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "Ctrl+O"), " to expand details")));
43
+ )), /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2502")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: theme.dimmedText }, "\u2514\u2500 "), /* @__PURE__ */ React.createElement(Text, { color: statusColor }, statusIndicator, " ", getStatusLabel(subagent.status)), subagent.status === "completed" && /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, " ", "(", duration, "s, ", formatTokenCount(tokenCount), " tokens)")), !expanded && /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginLeft: 4 }, /* @__PURE__ */ React.createElement(Text, { color: theme.mutedText }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "Ctrl+O"), " to expand details")));
42
44
  }
43
45
  function SubagentTodosSection({
44
46
  todos,
@@ -146,7 +148,7 @@ function getStatusLabel(status) {
146
148
  case "running":
147
149
  return "Running...";
148
150
  case "completed":
149
- return "Completed";
151
+ return "Done";
150
152
  case "error":
151
153
  return "Error";
152
154
  }