@within-7/minto 0.3.10 → 0.4.1

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 (306) hide show
  1. package/dist/Tool.js.map +2 -2
  2. package/dist/commands/agents/AgentsCommand.js +2 -2
  3. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  4. package/dist/commands/ctx_viz.js +1 -1
  5. package/dist/commands/effort.js +87 -0
  6. package/dist/commands/effort.js.map +7 -0
  7. package/dist/commands/export.js +19 -9
  8. package/dist/commands/export.js.map +2 -2
  9. package/dist/commands/ide.js +18 -0
  10. package/dist/commands/ide.js.map +7 -0
  11. package/dist/commands/mcp-interactive.js +14 -8
  12. package/dist/commands/mcp-interactive.js.map +2 -2
  13. package/dist/commands/memory.js +168 -0
  14. package/dist/commands/memory.js.map +7 -0
  15. package/dist/commands/model.js +45 -2
  16. package/dist/commands/model.js.map +2 -2
  17. package/dist/commands/outputStyle.js +64 -0
  18. package/dist/commands/outputStyle.js.map +7 -0
  19. package/dist/commands/plugin/utils.js +33 -1
  20. package/dist/commands/plugin/utils.js.map +2 -2
  21. package/dist/commands/plugin.js +10 -1
  22. package/dist/commands/plugin.js.map +2 -2
  23. package/dist/commands/refreshCommands.js +2 -0
  24. package/dist/commands/refreshCommands.js.map +2 -2
  25. package/dist/commands/review.js +51 -0
  26. package/dist/commands/review.js.map +7 -0
  27. package/dist/commands/terminalSetup.js +6 -0
  28. package/dist/commands/terminalSetup.js.map +2 -2
  29. package/dist/commands/undo.js +8 -0
  30. package/dist/commands/undo.js.map +2 -2
  31. package/dist/commands/vim.js +22 -0
  32. package/dist/commands/vim.js.map +7 -0
  33. package/dist/commands.js +12 -0
  34. package/dist/commands.js.map +2 -2
  35. package/dist/components/HighlightedCode.js +1 -0
  36. package/dist/components/HighlightedCode.js.map +2 -2
  37. package/dist/components/ModelSelector/ModelSelector.js +250 -143
  38. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  39. package/dist/components/PromptInput.js +21 -6
  40. package/dist/components/PromptInput.js.map +2 -2
  41. package/dist/components/PulseLabel.js +44 -0
  42. package/dist/components/PulseLabel.js.map +7 -0
  43. package/dist/components/RequestStatusIndicator.js +1 -1
  44. package/dist/components/RequestStatusIndicator.js.map +1 -1
  45. package/dist/components/Spinner.js +12 -42
  46. package/dist/components/Spinner.js.map +3 -3
  47. package/dist/components/StartupStatus.js +57 -0
  48. package/dist/components/StartupStatus.js.map +7 -0
  49. package/dist/components/SubagentBlock.js +43 -6
  50. package/dist/components/SubagentBlock.js.map +2 -2
  51. package/dist/components/TabbedListView/TabBar.js +13 -8
  52. package/dist/components/TabbedListView/TabBar.js.map +2 -2
  53. package/dist/components/TabbedListView/TabbedListView.js +1 -1
  54. package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
  55. package/dist/components/TodoPanel.js +1 -1
  56. package/dist/components/TodoPanel.js.map +1 -1
  57. package/dist/components/ToolUseLoader.js +5 -0
  58. package/dist/components/ToolUseLoader.js.map +2 -2
  59. package/dist/components/TrustDialog.js +0 -2
  60. package/dist/components/TrustDialog.js.map +2 -2
  61. package/dist/components/messages/TaskInModuleView.js +1 -1
  62. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  63. package/dist/components/messages/TaskToolMessage.js +1 -1
  64. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  65. package/dist/components/messages/UserPromptMessage.js +6 -1
  66. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  67. package/dist/constants/modelCapabilities.js +103 -18
  68. package/dist/constants/modelCapabilities.js.map +2 -2
  69. package/dist/constants/product.js +2 -0
  70. package/dist/constants/product.js.map +2 -2
  71. package/dist/constants/prompts/agentPrompt.js +30 -0
  72. package/dist/constants/prompts/agentPrompt.js.map +7 -0
  73. package/dist/constants/prompts/codeConventions.js +27 -0
  74. package/dist/constants/prompts/codeConventions.js.map +7 -0
  75. package/dist/constants/prompts/doingTasks.js +15 -0
  76. package/dist/constants/prompts/doingTasks.js.map +7 -0
  77. package/dist/constants/prompts/envInfo.js +17 -0
  78. package/dist/constants/prompts/envInfo.js.map +7 -0
  79. package/dist/constants/prompts/executingWithCare.js +17 -0
  80. package/dist/constants/prompts/executingWithCare.js.map +7 -0
  81. package/dist/constants/prompts/identity.js +10 -0
  82. package/dist/constants/prompts/identity.js.map +7 -0
  83. package/dist/constants/prompts/index.js +78 -0
  84. package/dist/constants/prompts/index.js.map +7 -0
  85. package/dist/constants/prompts/taskManagement.js +60 -0
  86. package/dist/constants/prompts/taskManagement.js.map +7 -0
  87. package/dist/constants/prompts/toneAndStyle.js +62 -0
  88. package/dist/constants/prompts/toneAndStyle.js.map +7 -0
  89. package/dist/constants/prompts/toolUsagePolicy.js +38 -0
  90. package/dist/constants/prompts/toolUsagePolicy.js.map +7 -0
  91. package/dist/constants/prompts.js +5 -176
  92. package/dist/constants/prompts.js.map +2 -2
  93. package/dist/constants/providerRegistry.js +235 -0
  94. package/dist/constants/providerRegistry.js.map +7 -0
  95. package/dist/constants/providers.js +35 -0
  96. package/dist/constants/providers.js.map +7 -0
  97. package/dist/context/PermissionContext.js +0 -1
  98. package/dist/context/PermissionContext.js.map +2 -2
  99. package/dist/context.js +87 -31
  100. package/dist/context.js.map +2 -2
  101. package/dist/core/backupHook.js +2 -2
  102. package/dist/core/backupHook.js.map +2 -2
  103. package/dist/core/config/defaults.js +4 -1
  104. package/dist/core/config/defaults.js.map +2 -2
  105. package/dist/core/config/schema.js +7 -1
  106. package/dist/core/config/schema.js.map +2 -2
  107. package/dist/core/costTracker.js +18 -0
  108. package/dist/core/costTracker.js.map +2 -2
  109. package/dist/core/index.js +0 -1
  110. package/dist/core/index.js.map +2 -2
  111. package/dist/core/tokenStatsManager.js +22 -4
  112. package/dist/core/tokenStatsManager.js.map +2 -2
  113. package/dist/entrypoints/cli.js +65 -84
  114. package/dist/entrypoints/cli.js.map +2 -2
  115. package/dist/hooks/useAgentTokenStats.js +1 -1
  116. package/dist/hooks/useAgentTokenStats.js.map +2 -2
  117. package/dist/hooks/useAgentTranscripts.js +2 -1
  118. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  119. package/dist/hooks/useBackgroundShells.js +29 -0
  120. package/dist/hooks/useBackgroundShells.js.map +7 -0
  121. package/dist/hooks/useCanUseTool.js +1 -1
  122. package/dist/hooks/useCanUseTool.js.map +2 -2
  123. package/dist/hooks/useDeferredLoading.js +64 -0
  124. package/dist/hooks/useDeferredLoading.js.map +7 -0
  125. package/dist/hooks/useHookStatus.js +1 -1
  126. package/dist/hooks/useHookStatus.js.map +2 -2
  127. package/dist/hooks/useSessionTracking.js +55 -0
  128. package/dist/hooks/useSessionTracking.js.map +7 -0
  129. package/dist/hooks/useTerminalSize.js +21 -0
  130. package/dist/hooks/useTerminalSize.js.map +2 -2
  131. package/dist/hooks/useTextInput.js +1 -0
  132. package/dist/hooks/useTextInput.js.map +2 -2
  133. package/dist/hooks/useUnifiedCompletion.js +3 -2
  134. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  135. package/dist/i18n/locales/en.js +8 -9
  136. package/dist/i18n/locales/en.js.map +2 -2
  137. package/dist/i18n/locales/zh-CN.js +8 -9
  138. package/dist/i18n/locales/zh-CN.js.map +2 -2
  139. package/dist/i18n/types.js.map +1 -1
  140. package/dist/messages.js +41 -17
  141. package/dist/messages.js.map +2 -2
  142. package/dist/permissions.js +94 -1
  143. package/dist/permissions.js.map +2 -2
  144. package/dist/query.js +27 -19
  145. package/dist/query.js.map +2 -2
  146. package/dist/screens/REPL.js +83 -74
  147. package/dist/screens/REPL.js.map +2 -2
  148. package/dist/services/adapters/responsesAPI.js +6 -0
  149. package/dist/services/adapters/responsesAPI.js.map +2 -2
  150. package/dist/services/agentTeams/index.js +35 -0
  151. package/dist/services/agentTeams/index.js.map +7 -0
  152. package/dist/services/agentTeams/mailbox.js +114 -0
  153. package/dist/services/agentTeams/mailbox.js.map +7 -0
  154. package/dist/services/agentTeams/teamManager.js +149 -0
  155. package/dist/services/agentTeams/teamManager.js.map +7 -0
  156. package/dist/services/agentTeams/teamTaskStore.js +114 -0
  157. package/dist/services/agentTeams/teamTaskStore.js.map +7 -0
  158. package/dist/services/agentTeams/teammateSpawner.js +80 -0
  159. package/dist/services/agentTeams/teammateSpawner.js.map +7 -0
  160. package/dist/services/checkpointManager.js +16 -3
  161. package/dist/services/checkpointManager.js.map +2 -2
  162. package/dist/services/claude.js +19 -1728
  163. package/dist/services/claude.js.map +3 -3
  164. package/dist/services/gpt5ConnectionTest.js +4 -2
  165. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  166. package/dist/services/hookExecutor.js +411 -127
  167. package/dist/services/hookExecutor.js.map +2 -2
  168. package/dist/services/llm/anthropicProvider.js +807 -0
  169. package/dist/services/llm/anthropicProvider.js.map +7 -0
  170. package/dist/services/llm/dispatch.js +218 -0
  171. package/dist/services/llm/dispatch.js.map +7 -0
  172. package/dist/services/llm/index.js +44 -0
  173. package/dist/services/llm/index.js.map +7 -0
  174. package/dist/services/llm/mintoContext.js +69 -0
  175. package/dist/services/llm/mintoContext.js.map +7 -0
  176. package/dist/services/llm/openaiProvider.js +622 -0
  177. package/dist/services/llm/openaiProvider.js.map +7 -0
  178. package/dist/services/llm/types.js +157 -0
  179. package/dist/services/llm/types.js.map +7 -0
  180. package/dist/services/mcpClient.js +183 -33
  181. package/dist/services/mcpClient.js.map +2 -2
  182. package/dist/services/notifier.js +14 -0
  183. package/dist/services/notifier.js.map +2 -2
  184. package/dist/services/oauth.js +4 -2
  185. package/dist/services/oauth.js.map +2 -2
  186. package/dist/services/openai.js +66 -56
  187. package/dist/services/openai.js.map +3 -3
  188. package/dist/services/outputStyles.js +102 -21
  189. package/dist/services/outputStyles.js.map +2 -2
  190. package/dist/services/plugins/skillMarketplace.js +4 -1
  191. package/dist/services/plugins/skillMarketplace.js.map +2 -2
  192. package/dist/services/sentry.js +1 -1
  193. package/dist/services/sentry.js.map +2 -2
  194. package/dist/services/sessionMemory.js +16 -3
  195. package/dist/services/sessionMemory.js.map +2 -2
  196. package/dist/services/systemReminder.js +350 -3
  197. package/dist/services/systemReminder.js.map +2 -2
  198. package/dist/services/taskStore.js +19 -0
  199. package/dist/services/taskStore.js.map +2 -2
  200. package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
  201. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +1 -1
  202. package/dist/tools/BashOutputTool/BashOutputTool.js.map +1 -1
  203. package/dist/tools/BashTool/BashTool.js +28 -0
  204. package/dist/tools/BashTool/BashTool.js.map +2 -2
  205. package/dist/tools/FileEditTool/FileEditTool.js +1 -1
  206. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  207. package/dist/tools/FileReadTool/FileReadTool.js +14 -0
  208. package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
  209. package/dist/tools/FileWriteTool/FileWriteTool.js +3 -1
  210. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  211. package/dist/tools/GlobTool/GlobTool.js.map +1 -1
  212. package/dist/tools/GrepTool/GrepTool.js.map +1 -1
  213. package/dist/tools/KillShellTool/KillShellTool.js.map +1 -1
  214. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
  215. package/dist/tools/LspTool/LspTool.js +11 -2
  216. package/dist/tools/LspTool/LspTool.js.map +2 -2
  217. package/dist/tools/MCPTool/MCPTool.js.map +1 -1
  218. package/dist/tools/MemoryReadTool/MemoryReadTool.js +2 -1
  219. package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
  220. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +2 -1
  221. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
  222. package/dist/tools/MultiEditTool/MultiEditTool.js.map +1 -1
  223. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +1 -1
  224. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +1 -1
  225. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +8 -2
  226. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  227. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +2 -0
  228. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  229. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +1 -1
  230. package/dist/tools/SlashCommandTool/SlashCommandTool.js +174 -18
  231. package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +3 -3
  232. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +1 -1
  233. package/dist/tools/TaskGetTool/TaskGetTool.js.map +1 -1
  234. package/dist/tools/TaskListTool/TaskListTool.js.map +1 -1
  235. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +1 -1
  236. package/dist/tools/TaskStopTool/TaskStopTool.js.map +1 -1
  237. package/dist/tools/TaskTool/TaskTool.js +75 -5
  238. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  239. package/dist/tools/TaskTool/prompt.js +12 -6
  240. package/dist/tools/TaskTool/prompt.js.map +2 -2
  241. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +1 -1
  242. package/dist/tools/ThinkTool/ThinkTool.js.map +1 -1
  243. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +1 -1
  244. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +1 -1
  245. package/dist/tools/WebSearchTool/WebSearchTool.js.map +1 -1
  246. package/dist/tools/WebSearchTool/searchProviders.js +2 -1
  247. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  248. package/dist/tools/lsTool/lsTool.js.map +2 -2
  249. package/dist/tools/lsTool/prompt.js.map +1 -1
  250. package/dist/tools.js +14 -3
  251. package/dist/tools.js.map +2 -2
  252. package/dist/types/PermissionMode.js +21 -1
  253. package/dist/types/PermissionMode.js.map +2 -2
  254. package/dist/types/agentTeams.js +1 -0
  255. package/dist/types/agentTeams.js.map +7 -0
  256. package/dist/types/hooks.js +8 -2
  257. package/dist/types/hooks.js.map +2 -2
  258. package/dist/types/plugin.js +1 -1
  259. package/dist/types/plugin.js.map +2 -2
  260. package/dist/utils/agentLoader.js +25 -3
  261. package/dist/utils/agentLoader.js.map +2 -2
  262. package/dist/utils/animationManager.js +1 -1
  263. package/dist/utils/animationManager.js.map +2 -2
  264. package/dist/utils/ask.js +1 -1
  265. package/dist/utils/async.js +5 -1
  266. package/dist/utils/async.js.map +2 -2
  267. package/dist/utils/autoCompactCore.js +60 -0
  268. package/dist/utils/autoCompactCore.js.map +2 -2
  269. package/dist/utils/config.js +26 -128
  270. package/dist/utils/config.js.map +2 -2
  271. package/dist/utils/configSchema.js +227 -0
  272. package/dist/utils/configSchema.js.map +7 -0
  273. package/dist/utils/debugLogger.js.map +2 -2
  274. package/dist/utils/env.js +4 -3
  275. package/dist/utils/env.js.map +2 -2
  276. package/dist/utils/envConfig.js +34 -0
  277. package/dist/utils/envConfig.js.map +3 -3
  278. package/dist/utils/gpt5.js +146 -0
  279. package/dist/utils/gpt5.js.map +7 -0
  280. package/dist/utils/hookManager.js +374 -140
  281. package/dist/utils/hookManager.js.map +2 -2
  282. package/dist/utils/markdown.js +47 -0
  283. package/dist/utils/markdown.js.map +2 -2
  284. package/dist/utils/memoizeWithTTL.js +25 -0
  285. package/dist/utils/memoizeWithTTL.js.map +7 -0
  286. package/dist/utils/model.js +34 -9
  287. package/dist/utils/model.js.map +2 -2
  288. package/dist/utils/pluginInstaller.js +34 -5
  289. package/dist/utils/pluginInstaller.js.map +2 -2
  290. package/dist/utils/pluginLoader.js +201 -32
  291. package/dist/utils/pluginLoader.js.map +2 -2
  292. package/dist/utils/safeFetch.js +45 -0
  293. package/dist/utils/safeFetch.js.map +7 -0
  294. package/dist/utils/skillLoader.js +59 -6
  295. package/dist/utils/skillLoader.js.map +2 -2
  296. package/dist/utils/streamingState.js +52 -0
  297. package/dist/utils/streamingState.js.map +7 -0
  298. package/dist/utils/style.js +6 -3
  299. package/dist/utils/style.js.map +2 -2
  300. package/dist/utils/teamConfig.js +9 -3
  301. package/dist/utils/teamConfig.js.map +2 -2
  302. package/dist/utils/toolRiskClassification.js +0 -6
  303. package/dist/utils/toolRiskClassification.js.map +2 -2
  304. package/dist/version.js +2 -2
  305. package/dist/version.js.map +1 -1
  306. package/package.json +7 -6
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/TabbedListView/TabbedListView.tsx"],
4
- "sourcesContent": ["/**\n * TabbedListView Component\n *\n * A reusable component matching the /command suggestion UI pattern:\n * - Tab navigation (\u2190/\u2192 or Tab key)\n * - Search input (/ to focus)\n * - Scrollable list with \u25C6 selection indicator\n * - Brand gradient colors for selection\n * - Consistent height control (MAX_VISIBLE = 8)\n * - Status overlay for loading/success/error states (maintains frame)\n * - Back navigation support (onBack)\n *\n * Based on Claude Code CLI's /plugins UI pattern.\n */\n\nimport React, { useState, useCallback, useMemo } from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport { TabBar } from './TabBar'\nimport { SearchInput } from './SearchInput'\nimport { ScrollableList } from './ScrollableList'\nimport { SimpleSpinner } from '@components/Spinner'\nimport { StatusOverlayContent } from '@components/StatusOverlayContent'\nimport { useTerminalSize } from '@hooks/useTerminalSize'\nimport type { TabbedListViewProps, ListItem } from './types'\n\ntype FocusArea = 'tabs' | 'search' | 'list'\n\n// Heights for dynamic visible count calculation\nconst PROMPT_HEIGHT = 5 // PromptInput borders + input + hints\nconst CHROME_HEIGHT = 6 // title + tabs + search + footer + borders\n\nexport function TabbedListView({\n title,\n tabs,\n activeTab,\n onTabChange,\n items,\n searchEnabled = true,\n searchPlaceholder = 'Search...',\n searchQuery = '',\n onSearchChange,\n onSearchSubmit,\n onSelect,\n onClose,\n onBack,\n footerHint,\n isLoading = false,\n loadingText = 'Loading...',\n emptyText = 'No items found',\n groupByCategory = false,\n renderItem,\n statusOverlay,\n isActive = true,\n showItemCount = true,\n statusDismissHint,\n multiSelect,\n selectedIds,\n onSelectionChange,\n onMultiSelect,\n multiSelectActionLabel,\n}: TabbedListViewProps) {\n // Dynamic visible count based on terminal height\n const { rows } = useTerminalSize()\n const visibleCount = Math.min(\n 8,\n Math.max(3, rows - PROMPT_HEIGHT - CHROME_HEIGHT),\n )\n\n // Focus state: which area is currently active\n const [focusArea, setFocusArea] = useState<FocusArea>(\n searchEnabled ? 'search' : 'list',\n )\n\n // Index of focused item in the list\n const [focusedIndex, setFocusedIndex] = useState(0)\n\n // Filter items based on search query\n const filteredItems = useMemo(() => {\n if (!searchQuery.trim()) return items\n\n const query = searchQuery.toLowerCase()\n return items.filter(\n item =>\n item.label.toLowerCase().includes(query) ||\n item.description?.toLowerCase().includes(query) ||\n item.category?.toLowerCase().includes(query),\n )\n }, [items, searchQuery])\n\n // Reset focus index when items change\n React.useEffect(() => {\n setFocusedIndex(0)\n }, [filteredItems.length, activeTab])\n\n // Handle tab cycling\n const cycleTab = useCallback(\n (direction: 'left' | 'right') => {\n const currentIndex = tabs.findIndex(t => t.id === activeTab)\n let newIndex: number\n\n if (direction === 'right') {\n newIndex = (currentIndex + 1) % tabs.length\n } else {\n newIndex = currentIndex - 1 < 0 ? tabs.length - 1 : currentIndex - 1\n }\n\n onTabChange(tabs[newIndex].id)\n },\n [tabs, activeTab, onTabChange],\n )\n\n // Handle item selection\n const handleSelect = useCallback(\n (item: ListItem) => {\n if (onSelect) {\n onSelect(item)\n }\n },\n [onSelect],\n )\n\n // Keyboard input handling\n useInput(\n (input, key) => {\n // Status overlay: success/error \u2192 ESC/Enter closes\n if (statusOverlay && statusOverlay.type !== 'loading') {\n if (key.escape || key.return) {\n onClose()\n }\n return\n }\n\n // Status overlay: loading \u2192 ignore all input\n if (statusOverlay) return\n\n // Escape: onBack if provided, else onClose\n if (key.escape) {\n if (onBack) {\n onBack()\n } else {\n onClose()\n }\n return\n }\n\n // Tab key cycles through tabs\n if (key.tab) {\n cycleTab(key.shift ? 'left' : 'right')\n return\n }\n\n // Left/Right arrows for tab navigation when in tabs or search area\n if (focusArea === 'tabs' || focusArea === 'search') {\n if (key.leftArrow) {\n cycleTab('left')\n return\n }\n if (key.rightArrow) {\n cycleTab('right')\n return\n }\n }\n\n // Handle focus area navigation\n if (key.downArrow) {\n if (focusArea === 'search') {\n setFocusArea('list')\n return\n }\n if (focusArea === 'list' && filteredItems.length > 0) {\n setFocusedIndex(prev =>\n prev < filteredItems.length - 1 ? prev + 1 : prev,\n )\n return\n }\n }\n\n if (key.upArrow) {\n if (focusArea === 'list') {\n if (focusedIndex > 0) {\n setFocusedIndex(prev => prev - 1)\n } else if (searchEnabled) {\n setFocusArea('search')\n }\n return\n }\n }\n\n // Enter in search: submit if onSearchSubmit provided\n if (key.return && focusArea === 'search' && onSearchSubmit) {\n onSearchSubmit(searchQuery)\n return\n }\n\n // Space: toggle selection in multi-select mode\n if (\n input === ' ' &&\n multiSelect &&\n focusArea === 'list' &&\n filteredItems.length > 0\n ) {\n const item = filteredItems[focusedIndex]\n if (item && onSelectionChange) {\n const isCurrentlySelected = selectedIds?.has(item.id) ?? false\n onSelectionChange(item.id, !isCurrentlySelected)\n }\n return\n }\n\n // 'a': toggle select-all / deselect-all in multi-select mode\n if (\n input === 'a' &&\n multiSelect &&\n focusArea === 'list' &&\n filteredItems.length > 0\n ) {\n if (onSelectionChange) {\n const allSelected = filteredItems.every(item =>\n selectedIds?.has(item.id),\n )\n for (const item of filteredItems) {\n onSelectionChange(item.id, !allSelected)\n }\n }\n return\n }\n\n // Enter: batch action if multi-select has selections, otherwise single select\n if (key.return && focusArea === 'list' && filteredItems.length > 0) {\n if (\n multiSelect &&\n selectedIds &&\n selectedIds.size > 0 &&\n onMultiSelect\n ) {\n const selectedItems = filteredItems.filter(item =>\n selectedIds.has(item.id),\n )\n if (selectedItems.length > 0) {\n onMultiSelect(selectedItems)\n return\n }\n }\n handleSelect(filteredItems[focusedIndex])\n return\n }\n\n // '/' to focus search\n if (input === '/' && searchEnabled && focusArea !== 'search') {\n setFocusArea('search')\n return\n }\n },\n { isActive },\n )\n\n // Build footer hint based on current state\n const defaultHint = '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Tab/\u2190\u2192 Switch \u00B7 Esc Close'\n const multiSelectHint =\n '\u2191\u2193 Navigate \u00B7 Space Select \u00B7 a All \u00B7 Enter Action \u00B7 Tab Switch \u00B7 Esc Close'\n const displayHint = statusOverlay\n ? statusOverlay.type === 'loading'\n ? ''\n : statusDismissHint || 'Esc Close'\n : multiSelect\n ? selectedIds && selectedIds.size > 0 && multiSelectActionLabel\n ? `${multiSelectActionLabel} \u00B7 ${footerHint || multiSelectHint}`\n : footerHint || multiSelectHint\n : footerHint || defaultHint\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {/* Header bar with top border - mimics PromptInput separator */}\n <Box\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n flexDirection=\"column\"\n >\n {/* Title with brand color */}\n <Box>\n <Text color={BRAND_GRADIENT.START}>\u25C6</Text>\n <Text bold color={SEMANTIC_COLORS.secondary}>\n {' '}\n {title}\n </Text>\n {showItemCount && !statusOverlay && filteredItems.length > 0 && (\n <Text color={SEMANTIC_COLORS.dim}> ({filteredItems.length})</Text>\n )}\n </Box>\n\n {/* Tab Bar - left aligned */}\n <Box marginTop={1}>\n <TabBar tabs={tabs} activeTab={activeTab} onTabChange={onTabChange} />\n </Box>\n </Box>\n\n {/* Content container with bottom border */}\n <Box\n flexDirection=\"column\"\n borderTop={false}\n borderBottom={true}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n paddingY={1}\n >\n {/* Search Input - hidden during status overlay */}\n {searchEnabled && !statusOverlay && (\n <Box marginBottom={1}>\n <SearchInput\n value={searchQuery}\n onChange={onSearchChange || (() => {})}\n placeholder={searchPlaceholder}\n isActive={focusArea === 'search'}\n />\n </Box>\n )}\n\n {/* Content Area - key forces re-render on tab change */}\n <Box key={`content-${activeTab}`} flexDirection=\"column\">\n {statusOverlay ? (\n <StatusOverlayContent\n type={statusOverlay.type}\n message={statusOverlay.message}\n />\n ) : isLoading ? (\n <Box>\n <SimpleSpinner />\n <Text color={SEMANTIC_COLORS.dim}> {loadingText}</Text>\n </Box>\n ) : filteredItems.length === 0 ? (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}>{emptyText}</Text>\n </Box>\n ) : (\n <ScrollableList\n items={filteredItems}\n focusedIndex={focusedIndex}\n onFocusChange={setFocusedIndex}\n onSelect={handleSelect}\n visibleCount={visibleCount}\n groupByCategory={groupByCategory}\n renderItem={renderItem}\n multiSelect={multiSelect}\n selectedIds={selectedIds}\n />\n )}\n </Box>\n\n {/* Footer Hint - compact style */}\n {displayHint && (\n <Box marginTop={1}>\n <Text color={SEMANTIC_COLORS.muted}>{displayHint}</Text>\n </Box>\n )}\n </Box>\n </Box>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * TabbedListView Component\n *\n * A reusable component matching the /command suggestion UI pattern:\n * - Tab navigation (\u2190/\u2192 or Tab key)\n * - Search input (/ to focus)\n * - Scrollable list with \u25C6 selection indicator\n * - Brand gradient colors for selection\n * - Consistent height control (MAX_VISIBLE = 8)\n * - Status overlay for loading/success/error states (maintains frame)\n * - Back navigation support (onBack)\n *\n * Based on Claude Code CLI's /plugins UI pattern.\n */\n\nimport React, { useState, useCallback, useMemo } from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport { TabBar } from './TabBar'\nimport { SearchInput } from './SearchInput'\nimport { ScrollableList } from './ScrollableList'\nimport { SimpleSpinner } from '@components/Spinner'\nimport { StatusOverlayContent } from '@components/StatusOverlayContent'\nimport { useTerminalSize } from '@hooks/useTerminalSize'\nimport type { TabbedListViewProps, ListItem } from './types'\n\ntype FocusArea = 'tabs' | 'search' | 'list'\n\n// Heights for dynamic visible count calculation\nconst PROMPT_HEIGHT = 5 // PromptInput borders + input + hints\nconst CHROME_HEIGHT = 6 // title + tabs + search + footer + borders\n\nexport function TabbedListView({\n title,\n tabs,\n activeTab,\n onTabChange,\n items,\n searchEnabled = true,\n searchPlaceholder = 'Search...',\n searchQuery = '',\n onSearchChange,\n onSearchSubmit,\n onSelect,\n onClose,\n onBack,\n footerHint,\n isLoading = false,\n loadingText = 'Loading...',\n emptyText = 'No items found',\n groupByCategory = false,\n renderItem,\n statusOverlay,\n isActive = true,\n showItemCount = true,\n statusDismissHint,\n multiSelect,\n selectedIds,\n onSelectionChange,\n onMultiSelect,\n multiSelectActionLabel,\n}: TabbedListViewProps) {\n // Dynamic visible count based on terminal height\n const { rows } = useTerminalSize()\n const visibleCount = Math.min(\n 8,\n Math.max(3, rows - PROMPT_HEIGHT - CHROME_HEIGHT),\n )\n\n // Focus state: which area is currently active\n const [focusArea, setFocusArea] = useState<FocusArea>(\n searchEnabled ? 'search' : 'list',\n )\n\n // Index of focused item in the list\n const [focusedIndex, setFocusedIndex] = useState(0)\n\n // Filter items based on search query\n const filteredItems = useMemo(() => {\n if (!searchQuery.trim()) return items\n\n const query = searchQuery.toLowerCase()\n return items.filter(\n item =>\n item.label.toLowerCase().includes(query) ||\n item.description?.toLowerCase().includes(query) ||\n item.category?.toLowerCase().includes(query),\n )\n }, [items, searchQuery])\n\n // Reset focus index when items change\n React.useEffect(() => {\n setFocusedIndex(0)\n }, [filteredItems.length, activeTab])\n\n // Handle tab cycling\n const cycleTab = useCallback(\n (direction: 'left' | 'right') => {\n const currentIndex = tabs.findIndex(t => t.id === activeTab)\n let newIndex: number\n\n if (direction === 'right') {\n newIndex = (currentIndex + 1) % tabs.length\n } else {\n newIndex = currentIndex - 1 < 0 ? tabs.length - 1 : currentIndex - 1\n }\n\n onTabChange(tabs[newIndex].id)\n },\n [tabs, activeTab, onTabChange],\n )\n\n // Handle item selection\n const handleSelect = useCallback(\n (item: ListItem) => {\n if (onSelect) {\n onSelect(item)\n }\n },\n [onSelect],\n )\n\n // Keyboard input handling\n useInput(\n (input, key) => {\n // Status overlay: success/error \u2192 ESC/Enter closes\n if (statusOverlay && statusOverlay.type !== 'loading') {\n if (key.escape || key.return) {\n onClose()\n }\n return\n }\n\n // Status overlay: loading \u2192 ignore all input\n if (statusOverlay) return\n\n // Escape: onBack if provided, else onClose\n if (key.escape) {\n if (onBack) {\n onBack()\n } else {\n onClose()\n }\n return\n }\n\n // Tab key cycles through tabs\n if (key.tab) {\n cycleTab(key.shift ? 'left' : 'right')\n return\n }\n\n // Left/Right arrows for tab navigation when in tabs or search area\n if (focusArea === 'tabs' || focusArea === 'search') {\n if (key.leftArrow) {\n cycleTab('left')\n return\n }\n if (key.rightArrow) {\n cycleTab('right')\n return\n }\n }\n\n // Handle focus area navigation\n if (key.downArrow) {\n if (focusArea === 'search') {\n setFocusArea('list')\n return\n }\n if (focusArea === 'list' && filteredItems.length > 0) {\n setFocusedIndex(prev =>\n prev < filteredItems.length - 1 ? prev + 1 : prev,\n )\n return\n }\n }\n\n if (key.upArrow) {\n if (focusArea === 'list') {\n if (focusedIndex > 0) {\n setFocusedIndex(prev => prev - 1)\n } else if (searchEnabled) {\n setFocusArea('search')\n }\n return\n }\n }\n\n // Enter in search: submit if onSearchSubmit provided\n if (key.return && focusArea === 'search' && onSearchSubmit) {\n onSearchSubmit(searchQuery)\n return\n }\n\n // Space: toggle selection in multi-select mode\n if (\n input === ' ' &&\n multiSelect &&\n focusArea === 'list' &&\n filteredItems.length > 0\n ) {\n const item = filteredItems[focusedIndex]\n if (item && onSelectionChange) {\n const isCurrentlySelected = selectedIds?.has(item.id) ?? false\n onSelectionChange(item.id, !isCurrentlySelected)\n }\n return\n }\n\n // 'a': toggle select-all / deselect-all in multi-select mode\n if (\n input === 'a' &&\n multiSelect &&\n focusArea === 'list' &&\n filteredItems.length > 0\n ) {\n if (onSelectionChange) {\n const allSelected = filteredItems.every(item =>\n selectedIds?.has(item.id),\n )\n for (const item of filteredItems) {\n onSelectionChange(item.id, !allSelected)\n }\n }\n return\n }\n\n // Enter: batch action if multi-select has selections, otherwise single select\n if (key.return && focusArea === 'list' && filteredItems.length > 0) {\n if (\n multiSelect &&\n selectedIds &&\n selectedIds.size > 0 &&\n onMultiSelect\n ) {\n const selectedItems = filteredItems.filter(item =>\n selectedIds.has(item.id),\n )\n if (selectedItems.length > 0) {\n onMultiSelect(selectedItems)\n return\n }\n }\n handleSelect(filteredItems[focusedIndex])\n return\n }\n\n // '/' to focus search\n if (input === '/' && searchEnabled && focusArea !== 'search') {\n setFocusArea('search')\n return\n }\n },\n { isActive },\n )\n\n // Build footer hint based on current state\n const defaultHint = '\u2191\u2193 Navigate \u00B7 Enter Select \u00B7 Tab/\u2190\u2192 Switch \u00B7 Esc Close'\n const multiSelectHint =\n '\u2191\u2193 Navigate \u00B7 Space Select \u00B7 a All \u00B7 Enter Action \u00B7 Tab Switch \u00B7 Esc Close'\n const displayHint = statusOverlay\n ? statusOverlay.type === 'loading'\n ? ''\n : statusDismissHint || 'Esc Close'\n : multiSelect\n ? selectedIds && selectedIds.size > 0 && multiSelectActionLabel\n ? `${multiSelectActionLabel} \u00B7 ${footerHint || multiSelectHint}`\n : footerHint || multiSelectHint\n : footerHint || defaultHint\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {/* Header bar with top border - mimics PromptInput separator */}\n <Box\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n flexDirection=\"column\"\n >\n {/* Title with brand color */}\n <Box>\n <Text color={BRAND_GRADIENT.START}>\u25C6</Text>\n <Text bold color={SEMANTIC_COLORS.secondary}>\n {' '}\n {title}\n </Text>\n {showItemCount && !statusOverlay && filteredItems.length > 0 && (\n <Text color={SEMANTIC_COLORS.dim}> ({filteredItems.length})</Text>\n )}\n </Box>\n\n {/* Tab Bar - left aligned, flexShrink={0} prevents compression */}\n <Box marginTop={1} flexShrink={0}>\n <TabBar tabs={tabs} activeTab={activeTab} onTabChange={onTabChange} />\n </Box>\n </Box>\n\n {/* Content container with bottom border */}\n <Box\n flexDirection=\"column\"\n borderTop={false}\n borderBottom={true}\n borderLeft={false}\n borderRight={false}\n borderColor={BRAND_GRADIENT.START}\n borderStyle=\"single\"\n width=\"100%\"\n paddingX={1}\n paddingY={1}\n >\n {/* Search Input - hidden during status overlay */}\n {searchEnabled && !statusOverlay && (\n <Box marginBottom={1}>\n <SearchInput\n value={searchQuery}\n onChange={onSearchChange || (() => {})}\n placeholder={searchPlaceholder}\n isActive={focusArea === 'search'}\n />\n </Box>\n )}\n\n {/* Content Area - key forces re-render on tab change */}\n <Box key={`content-${activeTab}`} flexDirection=\"column\">\n {statusOverlay ? (\n <StatusOverlayContent\n type={statusOverlay.type}\n message={statusOverlay.message}\n />\n ) : isLoading ? (\n <Box>\n <SimpleSpinner />\n <Text color={SEMANTIC_COLORS.dim}> {loadingText}</Text>\n </Box>\n ) : filteredItems.length === 0 ? (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}>{emptyText}</Text>\n </Box>\n ) : (\n <ScrollableList\n items={filteredItems}\n focusedIndex={focusedIndex}\n onFocusChange={setFocusedIndex}\n onSelect={handleSelect}\n visibleCount={visibleCount}\n groupByCategory={groupByCategory}\n renderItem={renderItem}\n multiSelect={multiSelect}\n selectedIds={selectedIds}\n />\n )}\n </Box>\n\n {/* Footer Hint - compact style */}\n {displayHint && (\n <Box marginTop={1}>\n <Text color={SEMANTIC_COLORS.muted}>{displayHint}</Text>\n </Box>\n )}\n </Box>\n </Box>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import { getTheme } from "../utils/theme.js";
4
4
  import { SYMBOLS, getTodoStatusSymbol } from "../constants/symbols.js";
5
5
  import { formatNumber, formatTokenUsage } from "../utils/format.js";
6
6
  import { sample } from "lodash-es";
7
- import { getMainStreamingState } from "./Spinner.js";
7
+ import { getMainStreamingState } from "../utils/streamingState.js";
8
8
  import { getSessionState } from "../utils/sessionState.js";
9
9
  import { useUnifiedAnimation } from "../utils/animationManager.js";
10
10
  import {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/TodoPanel.tsx"],
4
- "sourcesContent": ["import React, { useRef, useMemo, memo } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { SYMBOLS, getTodoStatusSymbol } from '@constants/symbols'\nimport { formatNumber, formatTokenUsage } from '@utils/format'\nimport type { TodoItem as TodoItemType } from '@utils/todoStorage'\nimport { sample } from 'lodash-es'\nimport { getMainStreamingState } from './Spinner'\nimport { getSessionState } from '@utils/sessionState'\nimport { useUnifiedAnimation } from '@utils/animationManager'\nimport {\n SEMANTIC_COLORS,\n BRAND_GRADIENT,\n SYMBOL_COLORS,\n} from '@constants/colors'\nimport { getIndent } from '@constants/formatRules'\nimport { t } from '@i18n'\n\n/**\n * Isolated Spinner component to prevent animation updates from re-rendering the entire panel\n * This component handles all animation state internally\n */\ninterface SpinnerLineProps {\n currentTaskActiveForm?: string\n todos: TodoItemType[]\n shouldShowTodos: boolean\n fallbackMessage: string\n}\n\n/**\n * Shimmer effect colors - creates a light sweep animation across text\n * Uses brand gradient colors cycling through: purple-blue \u2192 pink-purple \u2192 coral \u2192 pink-purple \u2192 purple-blue\n */\nconst SHIMMER_COLORS = [\n BRAND_GRADIENT.START, // #667EEA purple-blue\n '#8673D9', // blend\n BRAND_GRADIENT.MIDDLE, // #B668C8 pink-purple\n '#D560A0', // blend\n BRAND_GRADIENT.END, // #F5576C coral\n '#D560A0', // blend back\n BRAND_GRADIENT.MIDDLE, // #B668C8\n '#8673D9', // blend back\n] as const\n\n/**\n * Get shimmer color based on animation frame\n * Creates a smooth color transition effect\n */\nfunction getShimmerColor(frame: number): string {\n return SHIMMER_COLORS[frame % SHIMMER_COLORS.length]!\n}\n\nconst SpinnerLine = memo(function SpinnerLine({\n currentTaskActiveForm,\n todos,\n shouldShowTodos,\n fallbackMessage,\n}: SpinnerLineProps) {\n const theme = getTheme()\n const startTimeRef = useRef(Date.now())\n\n // Animation state is isolated in this component\n // Use 8 frames for shimmer effect (matching SHIMMER_COLORS length)\n const { spinnerFrame, elapsedTime } = useUnifiedAnimation({\n enabled: true,\n startTime: startTimeRef.current,\n spinnerFrameCount: SHIMMER_COLORS.length,\n componentId: 'todo-panel-spinner',\n })\n\n // Get streaming state - only updates when this component re-renders\n // Use main streaming state to avoid subagent interference\n const streamState = useMemo(() => getMainStreamingState(), [spinnerFrame])\n\n // Get current task display\n const getCurrentTaskDisplay = (): string => {\n if (currentTaskActiveForm) {\n return currentTaskActiveForm\n }\n const inProgressTodo = todos.find(t => t.status === 'in_progress')\n if (inProgressTodo?.activeForm) {\n return inProgressTodo.activeForm\n }\n switch (streamState.phase) {\n case 'tool_use':\n return streamState.toolName\n ? `Using ${streamState.toolName}`\n : 'Executing tool'\n case 'generating':\n return 'Generating response'\n case 'waiting':\n return 'Waiting for response'\n case 'thinking':\n default:\n return fallbackMessage || 'Thinking'\n }\n }\n\n // Format time display\n const formatTime = (seconds: number): string => {\n if (seconds < 60) return `${seconds}s`\n const mins = Math.floor(seconds / 60)\n const secs = seconds % 60\n return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`\n }\n\n // Get shimmer color for the main text (light sweep effect)\n const shimmerColor = getShimmerColor(spinnerFrame)\n\n // Spinner icon uses thinking frames\n const spinnerIconFrame = spinnerFrame % SYMBOLS.THINKING_FRAMES.length\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 // Only show output approximation since we don't have input char count in this path\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\" flexWrap=\"nowrap\">\n {/* Spinner icon with shimmer effect */}\n <Text color={shimmerColor}>\n {SYMBOLS.THINKING_FRAMES[spinnerIconFrame]}{' '}\n </Text>\n {/* Main task description with shimmer effect */}\n <Text color={shimmerColor} bold>\n {getCurrentTaskDisplay()}\n </Text>\n <Text color={shimmerColor}>\u2026 </Text>\n {/* Meta info with brand gradient accent */}\n <Text color={BRAND_GRADIENT.START}>\n (<Text color={SEMANTIC_COLORS.secondary}>esc esc</Text> to cancel \u00B7{' '}\n <Text color={SEMANTIC_COLORS.secondary}>{formatTime(elapsedTime)}</Text>\n {tokenUsage && (\n <Text color={SEMANTIC_COLORS.secondary}> \u00B7 {tokenUsage}</Text>\n )}\n {thinkingDuration !== null && thinkingDuration > 0 && (\n <Text color={SEMANTIC_COLORS.secondary}>\n {' '}\n \u00B7 thinking {thinkingDuration}s\n </Text>\n )}\n {shouldShowTodos && (\n <Text color={SEMANTIC_COLORS.secondary}>\n {' '}\n \u00B7 <Text color={SEMANTIC_COLORS.secondary}>ctrl+t</Text> to hide\n </Text>\n )}\n )\n </Text>\n {getSessionState('currentError') && (\n <Text color={SYMBOL_COLORS.error}>\n {' '}\n \u00B7 {getSessionState('currentError')}\n </Text>\n )}\n </Box>\n )\n})\n\n// Spinner messages for fallback when no todos\nconst SPINNER_MESSAGES = [\n 'Thinking',\n 'Processing',\n 'Working',\n 'Computing',\n 'Analyzing',\n 'Generating',\n 'Executing',\n 'Preparing',\n]\n\ninterface TodoPanelProps {\n /** TODO \u5217\u8868 */\n todos: TodoItemType[]\n /** \u662F\u5426\u663E\u793A\u9762\u677F\uFF08\u7531 Ctrl+T \u63A7\u5236\uFF09 */\n isVisible: boolean\n /** \u5F53\u524D\u4E3B\u4EFB\u52A1\u7684 activeForm\uFF08\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\u63CF\u8FF0\uFF09 */\n currentTaskActiveForm?: string\n /** \u8FD0\u884C\u65F6\u95F4\uFF08\u79D2\uFF09 */\n elapsedTime?: number\n /** \u662F\u5426\u6B63\u5728\u52A0\u8F7D\uFF08\u63A7\u5236\u52A8\u753B\uFF09 */\n isLoading?: boolean\n /** \u662F\u5426\u663E\u793A\u4EFB\u52A1\u5217\u8868\u8BE6\u60C5 */\n showTodoList?: boolean\n}\n\n/**\n * \u7EDF\u4E00\u7684 Spinner + TODO \u9762\u677F\u7EC4\u4EF6 - Claude Code CLI \u98CE\u683C\n *\n * \u5F53\u6709 TODO \u65F6\u663E\u793A\uFF1A\n * ```\n * \u273D Fixing async tool description bug\u2026 (esc to interrupt \u00B7 ctrl+t to hide todos \u00B7 2m 52s \u00B7 \u2191 5.1k tokens)\n * \u23BF \u2610 Phase 1.1: Fix async tool description bug\n * \u2610 Phase 1.2: Add memory safety fixes\n * ```\n *\n * \u65E0 TODO \u65F6\u663E\u793A\u666E\u901A Spinner\uFF1A\n * ```\n * \u273D Thinking\u2026 (3s \u00B7 esc to interrupt)\n * ```\n *\n * Performance optimization: Animation is isolated in SpinnerLine component\n * to prevent the entire panel from re-rendering on each animation frame.\n */\nexport const TodoPanel = memo(function TodoPanel({\n todos,\n isVisible,\n currentTaskActiveForm,\n elapsedTime: initialElapsedTime = 0,\n isLoading = true,\n showTodoList = true,\n}: TodoPanelProps) {\n const theme = getTheme()\n const fallbackMessage = useRef(sample(SPINNER_MESSAGES) || 'Thinking')\n\n // \u5224\u65AD\u662F\u5426\u6709\u5F85\u529E\u4E8B\u9879\n const hasTodos = todos.length > 0\n const hasIncompleteTasks = todos.some(t => t.status !== 'completed')\n const shouldShowTodos =\n hasTodos && hasIncompleteTasks && isVisible && showTodoList\n\n // \u5982\u679C\u4E0D\u5728\u52A0\u8F7D\u72B6\u6001\uFF0C\u53EA\u663E\u793A todo \u5217\u8868\uFF08\u5982\u679C\u6709\u672A\u5B8C\u6210\u4EFB\u52A1\uFF09\n if (!isLoading) {\n // \u5373\u4F7F\u4E0D\u5728\u52A0\u8F7D\u4E2D\uFF0C\u5982\u679C\u6709\u672A\u5B8C\u6210\u7684\u4EFB\u52A1\u4E5F\u663E\u793A\u5217\u8868\n if (shouldShowTodos) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {/* \u9759\u6001\u6807\u9898 - \u663E\u793A\u8FD8\u6709\u672A\u5B8C\u6210\u4EFB\u52A1 */}\n <Box flexDirection=\"row\">\n <Text color={SEMANTIC_COLORS.dim}>\u25CB </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {hasIncompleteTasks\n ? t('ui.todo.tasksRemaining', {\n count: todos.filter(todo => todo.status !== 'completed')\n .length,\n })\n : t('ui.todo.allDone')}\n </Text>\n </Box>\n {/* TODO \u5217\u8868 */}\n <Box flexDirection=\"column\">\n {todos.map((todo, idx) => (\n <React.Fragment key={`todo-${idx}-${todo.content.slice(0, 20)}`}>\n <TodoItemClaudeStyle\n todo={todo}\n theme={theme}\n isFirst={idx === 0}\n />\n </React.Fragment>\n ))}\n </Box>\n </Box>\n )\n }\n return null\n }\n\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {/* \u7B2C\u4E00\u884C\uFF1A\u52A8\u753B + \u5F53\u524D\u4EFB\u52A1 + \u7EDF\u8BA1\u4FE1\u606F - \u9694\u79BB\u5728 SpinnerLine \u7EC4\u4EF6\u4E2D */}\n <SpinnerLine\n currentTaskActiveForm={currentTaskActiveForm}\n todos={todos}\n shouldShowTodos={shouldShowTodos}\n fallbackMessage={fallbackMessage.current}\n />\n\n {/* TODO \u5217\u8868 - \u4EC5\u5F53\u53EF\u89C1\u4E14\u6709\u672A\u5B8C\u6210\u4EFB\u52A1\u65F6\u663E\u793A */}\n {shouldShowTodos && <TodoList todos={todos} theme={theme} />}\n </Box>\n )\n})\n\n/**\n * Memoized TODO list to prevent re-rendering when only the spinner updates\n */\ninterface TodoListProps {\n todos: TodoItemType[]\n theme: ReturnType<typeof getTheme>\n}\n\nconst TodoList = memo(function TodoList({ todos, theme }: TodoListProps) {\n return (\n <Box flexDirection=\"column\">\n {todos.map((todo, idx) => (\n <React.Fragment key={`todo-${idx}-${todo.content.slice(0, 20)}`}>\n <TodoItemClaudeStyle todo={todo} theme={theme} isFirst={idx === 0} />\n </React.Fragment>\n ))}\n </Box>\n )\n})\n\n/**\n * Claude Code \u98CE\u683C\u7684 TODO \u9879\n *\n * \u683C\u5F0F\uFF1A\n * ```\n * \u23BF \u2612 \u5DF2\u5B8C\u6210\u4EFB\u52A1 (\u7070\u8272 + \u5220\u9664\u7EBF)\n * \u2610 \u5F85\u5B8C\u6210\u4EFB\u52A1\n * ```\n */\ninterface TodoItemClaudeStyleProps {\n todo: TodoItemType\n theme: ReturnType<typeof getTheme>\n isFirst: boolean\n}\n\n/**\n * Memoized TODO item component with brand-consistent colors\n */\nconst TodoItemClaudeStyle = memo(function TodoItemClaudeStyle({\n todo,\n theme,\n isFirst,\n}: TodoItemClaudeStyleProps) {\n const isCompleted = todo.status === 'completed'\n const isInProgress = todo.status === 'in_progress'\n\n // \u7B26\u53F7\uFF1A\u2610 \u672A\u5B8C\u6210 / \u2612 \u5DF2\u5B8C\u6210\n const symbol = getTodoStatusSymbol(todo.status)\n\n // \u4F7F\u7528\u54C1\u724C\u914D\u8272\uFF1A\n // - \u5DF2\u5B8C\u6210\uFF1Adim \u7070\u8272\n // - \u8FDB\u884C\u4E2D\uFF1A\u54C1\u724C\u4E2D\u95F4\u8272\uFF08\u7C89\u7D2B\uFF09\n // - \u5F85\u5904\u7406\uFF1A\u54C1\u724C\u8D77\u59CB\u8272\uFF08\u7D2B\u84DD\uFF09\n const symbolColor = isCompleted\n ? SEMANTIC_COLORS.dim\n : isInProgress\n ? BRAND_GRADIENT.MIDDLE\n : BRAND_GRADIENT.START\n\n const textColor = isCompleted\n ? SEMANTIC_COLORS.dim\n : SEMANTIC_COLORS.secondary\n\n // \u4F7F\u7528 REPL \u89C4\u8303\u5B9A\u4E49\u7684\u7ED3\u6784\u7B26\u53F7\uFF1A\u7B2C\u4E00\u4E2A\u7528 \" \u23BF \"\uFF0C\u5176\u4ED6\u7528\u7F29\u8FDB\u5BF9\u9F50\n const indent = isFirst\n ? `${getIndent('L1')}${SYMBOLS.CHILD_OUTPUT} `\n : `${getIndent('L1')} `\n\n return (\n <Box flexDirection=\"row\">\n {/* \u7F29\u8FDB - \u4F7F\u7528\u54C1\u724C\u8272 */}\n <Text color={SYMBOL_COLORS.child}>{indent}</Text>\n\n {/* \u72B6\u6001\u7B26\u53F7 - \u4F7F\u7528\u54C1\u724C\u914D\u8272 */}\n <Text color={symbolColor}>{symbol} </Text>\n\n {/* \u4EFB\u52A1\u63CF\u8FF0 */}\n <Text color={textColor} strikethrough={isCompleted}>\n {todo.content}\n </Text>\n </Box>\n )\n})\n"],
4
+ "sourcesContent": ["import React, { useRef, useMemo, memo } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { SYMBOLS, getTodoStatusSymbol } from '@constants/symbols'\nimport { formatNumber, formatTokenUsage } from '@utils/format'\nimport type { TodoItem as TodoItemType } from '@utils/todoStorage'\nimport { sample } from 'lodash-es'\nimport { getMainStreamingState } from '@utils/streamingState'\nimport { getSessionState } from '@utils/sessionState'\nimport { useUnifiedAnimation } from '@utils/animationManager'\nimport {\n SEMANTIC_COLORS,\n BRAND_GRADIENT,\n SYMBOL_COLORS,\n} from '@constants/colors'\nimport { getIndent } from '@constants/formatRules'\nimport { t } from '@i18n'\n\n/**\n * Isolated Spinner component to prevent animation updates from re-rendering the entire panel\n * This component handles all animation state internally\n */\ninterface SpinnerLineProps {\n currentTaskActiveForm?: string\n todos: TodoItemType[]\n shouldShowTodos: boolean\n fallbackMessage: string\n}\n\n/**\n * Shimmer effect colors - creates a light sweep animation across text\n * Uses brand gradient colors cycling through: purple-blue \u2192 pink-purple \u2192 coral \u2192 pink-purple \u2192 purple-blue\n */\nconst SHIMMER_COLORS = [\n BRAND_GRADIENT.START, // #667EEA purple-blue\n '#8673D9', // blend\n BRAND_GRADIENT.MIDDLE, // #B668C8 pink-purple\n '#D560A0', // blend\n BRAND_GRADIENT.END, // #F5576C coral\n '#D560A0', // blend back\n BRAND_GRADIENT.MIDDLE, // #B668C8\n '#8673D9', // blend back\n] as const\n\n/**\n * Get shimmer color based on animation frame\n * Creates a smooth color transition effect\n */\nfunction getShimmerColor(frame: number): string {\n return SHIMMER_COLORS[frame % SHIMMER_COLORS.length]!\n}\n\nconst SpinnerLine = memo(function SpinnerLine({\n currentTaskActiveForm,\n todos,\n shouldShowTodos,\n fallbackMessage,\n}: SpinnerLineProps) {\n const theme = getTheme()\n const startTimeRef = useRef(Date.now())\n\n // Animation state is isolated in this component\n // Use 8 frames for shimmer effect (matching SHIMMER_COLORS length)\n const { spinnerFrame, elapsedTime } = useUnifiedAnimation({\n enabled: true,\n startTime: startTimeRef.current,\n spinnerFrameCount: SHIMMER_COLORS.length,\n componentId: 'todo-panel-spinner',\n })\n\n // Get streaming state - only updates when this component re-renders\n // Use main streaming state to avoid subagent interference\n const streamState = useMemo(() => getMainStreamingState(), [spinnerFrame])\n\n // Get current task display\n const getCurrentTaskDisplay = (): string => {\n if (currentTaskActiveForm) {\n return currentTaskActiveForm\n }\n const inProgressTodo = todos.find(t => t.status === 'in_progress')\n if (inProgressTodo?.activeForm) {\n return inProgressTodo.activeForm\n }\n switch (streamState.phase) {\n case 'tool_use':\n return streamState.toolName\n ? `Using ${streamState.toolName}`\n : 'Executing tool'\n case 'generating':\n return 'Generating response'\n case 'waiting':\n return 'Waiting for response'\n case 'thinking':\n default:\n return fallbackMessage || 'Thinking'\n }\n }\n\n // Format time display\n const formatTime = (seconds: number): string => {\n if (seconds < 60) return `${seconds}s`\n const mins = Math.floor(seconds / 60)\n const secs = seconds % 60\n return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`\n }\n\n // Get shimmer color for the main text (light sweep effect)\n const shimmerColor = getShimmerColor(spinnerFrame)\n\n // Spinner icon uses thinking frames\n const spinnerIconFrame = spinnerFrame % SYMBOLS.THINKING_FRAMES.length\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 // Only show output approximation since we don't have input char count in this path\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\" flexWrap=\"nowrap\">\n {/* Spinner icon with shimmer effect */}\n <Text color={shimmerColor}>\n {SYMBOLS.THINKING_FRAMES[spinnerIconFrame]}{' '}\n </Text>\n {/* Main task description with shimmer effect */}\n <Text color={shimmerColor} bold>\n {getCurrentTaskDisplay()}\n </Text>\n <Text color={shimmerColor}>\u2026 </Text>\n {/* Meta info with brand gradient accent */}\n <Text color={BRAND_GRADIENT.START}>\n (<Text color={SEMANTIC_COLORS.secondary}>esc esc</Text> to cancel \u00B7{' '}\n <Text color={SEMANTIC_COLORS.secondary}>{formatTime(elapsedTime)}</Text>\n {tokenUsage && (\n <Text color={SEMANTIC_COLORS.secondary}> \u00B7 {tokenUsage}</Text>\n )}\n {thinkingDuration !== null && thinkingDuration > 0 && (\n <Text color={SEMANTIC_COLORS.secondary}>\n {' '}\n \u00B7 thinking {thinkingDuration}s\n </Text>\n )}\n {shouldShowTodos && (\n <Text color={SEMANTIC_COLORS.secondary}>\n {' '}\n \u00B7 <Text color={SEMANTIC_COLORS.secondary}>ctrl+t</Text> to hide\n </Text>\n )}\n )\n </Text>\n {getSessionState('currentError') && (\n <Text color={SYMBOL_COLORS.error}>\n {' '}\n \u00B7 {getSessionState('currentError')}\n </Text>\n )}\n </Box>\n )\n})\n\n// Spinner messages for fallback when no todos\nconst SPINNER_MESSAGES = [\n 'Thinking',\n 'Processing',\n 'Working',\n 'Computing',\n 'Analyzing',\n 'Generating',\n 'Executing',\n 'Preparing',\n]\n\ninterface TodoPanelProps {\n /** TODO \u5217\u8868 */\n todos: TodoItemType[]\n /** \u662F\u5426\u663E\u793A\u9762\u677F\uFF08\u7531 Ctrl+T \u63A7\u5236\uFF09 */\n isVisible: boolean\n /** \u5F53\u524D\u4E3B\u4EFB\u52A1\u7684 activeForm\uFF08\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\u63CF\u8FF0\uFF09 */\n currentTaskActiveForm?: string\n /** \u8FD0\u884C\u65F6\u95F4\uFF08\u79D2\uFF09 */\n elapsedTime?: number\n /** \u662F\u5426\u6B63\u5728\u52A0\u8F7D\uFF08\u63A7\u5236\u52A8\u753B\uFF09 */\n isLoading?: boolean\n /** \u662F\u5426\u663E\u793A\u4EFB\u52A1\u5217\u8868\u8BE6\u60C5 */\n showTodoList?: boolean\n}\n\n/**\n * \u7EDF\u4E00\u7684 Spinner + TODO \u9762\u677F\u7EC4\u4EF6 - Claude Code CLI \u98CE\u683C\n *\n * \u5F53\u6709 TODO \u65F6\u663E\u793A\uFF1A\n * ```\n * \u273D Fixing async tool description bug\u2026 (esc to interrupt \u00B7 ctrl+t to hide todos \u00B7 2m 52s \u00B7 \u2191 5.1k tokens)\n * \u23BF \u2610 Phase 1.1: Fix async tool description bug\n * \u2610 Phase 1.2: Add memory safety fixes\n * ```\n *\n * \u65E0 TODO \u65F6\u663E\u793A\u666E\u901A Spinner\uFF1A\n * ```\n * \u273D Thinking\u2026 (3s \u00B7 esc to interrupt)\n * ```\n *\n * Performance optimization: Animation is isolated in SpinnerLine component\n * to prevent the entire panel from re-rendering on each animation frame.\n */\nexport const TodoPanel = memo(function TodoPanel({\n todos,\n isVisible,\n currentTaskActiveForm,\n elapsedTime: initialElapsedTime = 0,\n isLoading = true,\n showTodoList = true,\n}: TodoPanelProps) {\n const theme = getTheme()\n const fallbackMessage = useRef(sample(SPINNER_MESSAGES) || 'Thinking')\n\n // \u5224\u65AD\u662F\u5426\u6709\u5F85\u529E\u4E8B\u9879\n const hasTodos = todos.length > 0\n const hasIncompleteTasks = todos.some(t => t.status !== 'completed')\n const shouldShowTodos =\n hasTodos && hasIncompleteTasks && isVisible && showTodoList\n\n // \u5982\u679C\u4E0D\u5728\u52A0\u8F7D\u72B6\u6001\uFF0C\u53EA\u663E\u793A todo \u5217\u8868\uFF08\u5982\u679C\u6709\u672A\u5B8C\u6210\u4EFB\u52A1\uFF09\n if (!isLoading) {\n // \u5373\u4F7F\u4E0D\u5728\u52A0\u8F7D\u4E2D\uFF0C\u5982\u679C\u6709\u672A\u5B8C\u6210\u7684\u4EFB\u52A1\u4E5F\u663E\u793A\u5217\u8868\n if (shouldShowTodos) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {/* \u9759\u6001\u6807\u9898 - \u663E\u793A\u8FD8\u6709\u672A\u5B8C\u6210\u4EFB\u52A1 */}\n <Box flexDirection=\"row\">\n <Text color={SEMANTIC_COLORS.dim}>\u25CB </Text>\n <Text color={SEMANTIC_COLORS.dim}>\n {hasIncompleteTasks\n ? t('ui.todo.tasksRemaining', {\n count: todos.filter(todo => todo.status !== 'completed')\n .length,\n })\n : t('ui.todo.allDone')}\n </Text>\n </Box>\n {/* TODO \u5217\u8868 */}\n <Box flexDirection=\"column\">\n {todos.map((todo, idx) => (\n <React.Fragment key={`todo-${idx}-${todo.content.slice(0, 20)}`}>\n <TodoItemClaudeStyle\n todo={todo}\n theme={theme}\n isFirst={idx === 0}\n />\n </React.Fragment>\n ))}\n </Box>\n </Box>\n )\n }\n return null\n }\n\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n {/* \u7B2C\u4E00\u884C\uFF1A\u52A8\u753B + \u5F53\u524D\u4EFB\u52A1 + \u7EDF\u8BA1\u4FE1\u606F - \u9694\u79BB\u5728 SpinnerLine \u7EC4\u4EF6\u4E2D */}\n <SpinnerLine\n currentTaskActiveForm={currentTaskActiveForm}\n todos={todos}\n shouldShowTodos={shouldShowTodos}\n fallbackMessage={fallbackMessage.current}\n />\n\n {/* TODO \u5217\u8868 - \u4EC5\u5F53\u53EF\u89C1\u4E14\u6709\u672A\u5B8C\u6210\u4EFB\u52A1\u65F6\u663E\u793A */}\n {shouldShowTodos && <TodoList todos={todos} theme={theme} />}\n </Box>\n )\n})\n\n/**\n * Memoized TODO list to prevent re-rendering when only the spinner updates\n */\ninterface TodoListProps {\n todos: TodoItemType[]\n theme: ReturnType<typeof getTheme>\n}\n\nconst TodoList = memo(function TodoList({ todos, theme }: TodoListProps) {\n return (\n <Box flexDirection=\"column\">\n {todos.map((todo, idx) => (\n <React.Fragment key={`todo-${idx}-${todo.content.slice(0, 20)}`}>\n <TodoItemClaudeStyle todo={todo} theme={theme} isFirst={idx === 0} />\n </React.Fragment>\n ))}\n </Box>\n )\n})\n\n/**\n * Claude Code \u98CE\u683C\u7684 TODO \u9879\n *\n * \u683C\u5F0F\uFF1A\n * ```\n * \u23BF \u2612 \u5DF2\u5B8C\u6210\u4EFB\u52A1 (\u7070\u8272 + \u5220\u9664\u7EBF)\n * \u2610 \u5F85\u5B8C\u6210\u4EFB\u52A1\n * ```\n */\ninterface TodoItemClaudeStyleProps {\n todo: TodoItemType\n theme: ReturnType<typeof getTheme>\n isFirst: boolean\n}\n\n/**\n * Memoized TODO item component with brand-consistent colors\n */\nconst TodoItemClaudeStyle = memo(function TodoItemClaudeStyle({\n todo,\n theme,\n isFirst,\n}: TodoItemClaudeStyleProps) {\n const isCompleted = todo.status === 'completed'\n const isInProgress = todo.status === 'in_progress'\n\n // \u7B26\u53F7\uFF1A\u2610 \u672A\u5B8C\u6210 / \u2612 \u5DF2\u5B8C\u6210\n const symbol = getTodoStatusSymbol(todo.status)\n\n // \u4F7F\u7528\u54C1\u724C\u914D\u8272\uFF1A\n // - \u5DF2\u5B8C\u6210\uFF1Adim \u7070\u8272\n // - \u8FDB\u884C\u4E2D\uFF1A\u54C1\u724C\u4E2D\u95F4\u8272\uFF08\u7C89\u7D2B\uFF09\n // - \u5F85\u5904\u7406\uFF1A\u54C1\u724C\u8D77\u59CB\u8272\uFF08\u7D2B\u84DD\uFF09\n const symbolColor = isCompleted\n ? SEMANTIC_COLORS.dim\n : isInProgress\n ? BRAND_GRADIENT.MIDDLE\n : BRAND_GRADIENT.START\n\n const textColor = isCompleted\n ? SEMANTIC_COLORS.dim\n : SEMANTIC_COLORS.secondary\n\n // \u4F7F\u7528 REPL \u89C4\u8303\u5B9A\u4E49\u7684\u7ED3\u6784\u7B26\u53F7\uFF1A\u7B2C\u4E00\u4E2A\u7528 \" \u23BF \"\uFF0C\u5176\u4ED6\u7528\u7F29\u8FDB\u5BF9\u9F50\n const indent = isFirst\n ? `${getIndent('L1')}${SYMBOLS.CHILD_OUTPUT} `\n : `${getIndent('L1')} `\n\n return (\n <Box flexDirection=\"row\">\n {/* \u7F29\u8FDB - \u4F7F\u7528\u54C1\u724C\u8272 */}\n <Text color={SYMBOL_COLORS.child}>{indent}</Text>\n\n {/* \u72B6\u6001\u7B26\u53F7 - \u4F7F\u7528\u54C1\u724C\u914D\u8272 */}\n <Text color={symbolColor}>{symbol} </Text>\n\n {/* \u4EFB\u52A1\u63CF\u8FF0 */}\n <Text color={textColor} strikethrough={isCompleted}>\n {todo.content}\n </Text>\n </Box>\n )\n})\n"],
5
5
  "mappings": "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",
6
6
  "names": ["SpinnerLine", "t", "TodoPanel", "TodoList", "TodoItemClaudeStyle"]
7
7
  }
@@ -9,6 +9,11 @@ function ToolUseLoader({
9
9
  shouldAnimate
10
10
  }) {
11
11
  const [isVisible, setIsVisible] = React.useState(true);
12
+ React.useEffect(() => {
13
+ if (!shouldAnimate) {
14
+ setIsVisible(true);
15
+ }
16
+ }, [shouldAnimate]);
12
17
  useInterval(() => {
13
18
  if (!shouldAnimate) {
14
19
  return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/ToolUseLoader.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { useInterval } from '@hooks/useInterval'\nimport { BLACK_CIRCLE } from '@constants/figures'\nimport { SYMBOL_COLORS } from '@constants/colors'\n\ntype Props = {\n isError: boolean\n isUnresolved: boolean\n shouldAnimate: boolean\n}\n\nexport function ToolUseLoader({\n isError,\n isUnresolved,\n shouldAnimate,\n}: Props): React.ReactNode {\n const [isVisible, setIsVisible] = React.useState(true)\n\n useInterval(() => {\n if (!shouldAnimate) {\n return\n }\n // To avoid flickering when the tool use confirm is visible, we set the loader to be visible\n // when the tool use confirm is visible.\n setIsVisible(_ => !_)\n }, 600)\n\n // \u4F7F\u7528 SYMBOL_COLORS \u4E0E Logo \u54C1\u724C\u914D\u8272\u4FDD\u6301\u4E00\u81F4\n const color = isUnresolved\n ? SYMBOL_COLORS.running\n : isError\n ? SYMBOL_COLORS.error\n : SYMBOL_COLORS.success\n\n return (\n <Box minWidth={2}>\n <Text color={color}>{isVisible ? BLACK_CIRCLE : ' '}</Text>\n </Box>\n )\n}\n"],
5
- "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAQvB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI;AAErD,cAAY,MAAM;AAChB,QAAI,CAAC,eAAe;AAClB;AAAA,IACF;AAGA,iBAAa,OAAK,CAAC,CAAC;AAAA,EACtB,GAAG,GAAG;AAGN,QAAM,QAAQ,eACV,cAAc,UACd,UACE,cAAc,QACd,cAAc;AAEpB,SACE,oCAAC,OAAI,UAAU,KACb,oCAAC,QAAK,SAAe,YAAY,eAAe,IAAK,CACvD;AAEJ;",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { useInterval } from '@hooks/useInterval'\nimport { BLACK_CIRCLE } from '@constants/figures'\nimport { SYMBOL_COLORS } from '@constants/colors'\n\ntype Props = {\n isError: boolean\n isUnresolved: boolean\n shouldAnimate: boolean\n}\n\nexport function ToolUseLoader({\n isError,\n isUnresolved,\n shouldAnimate,\n}: Props): React.ReactNode {\n const [isVisible, setIsVisible] = React.useState(true)\n\n // Reset visibility when animation stops to prevent the dot from being\n // permanently invisible if it happens to freeze in the \"off\" state\n React.useEffect(() => {\n if (!shouldAnimate) {\n setIsVisible(true)\n }\n }, [shouldAnimate])\n\n useInterval(() => {\n if (!shouldAnimate) {\n return\n }\n // To avoid flickering when the tool use confirm is visible, we set the loader to be visible\n // when the tool use confirm is visible.\n setIsVisible(_ => !_)\n }, 600)\n\n // \u4F7F\u7528 SYMBOL_COLORS \u4E0E Logo \u54C1\u724C\u914D\u8272\u4FDD\u6301\u4E00\u81F4\n const color = isUnresolved\n ? SYMBOL_COLORS.running\n : isError\n ? SYMBOL_COLORS.error\n : SYMBOL_COLORS.success\n\n return (\n <Box minWidth={2}>\n <Text color={color}>{isVisible ? BLACK_CIRCLE : ' '}</Text>\n </Box>\n )\n}\n"],
5
+ "mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAQvB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI;AAIrD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,eAAe;AAClB,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,aAAa,CAAC;AAElB,cAAY,MAAM;AAChB,QAAI,CAAC,eAAe;AAClB;AAAA,IACF;AAGA,iBAAa,OAAK,CAAC,CAAC;AAAA,EACtB,GAAG,GAAG;AAGN,QAAM,QAAQ,eACV,cAAc,UACd,UACE,cAAc,QACd,cAAc;AAEpB,SACE,oCAAC,OAAI,UAAU,KACb,oCAAC,QAAK,SAAe,YAAY,eAAe,IAAK,CACvD;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -32,7 +32,6 @@ function TrustDialog({ onDone }) {
32
32
  }
33
33
  case "no": {
34
34
  process.exit(1);
35
- break;
36
35
  }
37
36
  }
38
37
  }
@@ -40,7 +39,6 @@ function TrustDialog({ onDone }) {
40
39
  useInput((_input, key) => {
41
40
  if (key.escape) {
42
41
  process.exit(0);
43
- return;
44
42
  }
45
43
  });
46
44
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/TrustDialog.tsx"],
4
- "sourcesContent": ["import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { Select } from './CustomSelect/select'\nimport {\n saveCurrentProjectConfig,\n getCurrentProjectConfig,\n} from '@utils/config'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { useExitOnCtrlCD } from '@hooks/useExitOnCtrlCD'\nimport { homedir } from 'os'\nimport { getCwd } from '@utils/state'\nimport Link from './Link'\nimport { SEMANTIC_COLORS } from '@constants/colors'\nimport { t } from '@i18n'\n\ntype Props = {\n onDone(): void\n}\n\nexport function TrustDialog({ onDone }: Props): React.ReactNode {\n const theme = getTheme()\n React.useEffect(() => {}, [])\n\n function onChange(value: 'yes' | 'no') {\n const config = getCurrentProjectConfig()\n switch (value) {\n case 'yes': {\n const isHomeDir = homedir() === getCwd()\n\n if (!isHomeDir) {\n saveCurrentProjectConfig({\n ...config,\n hasTrustDialogAccepted: true,\n })\n }\n onDone()\n break\n }\n case 'no': {\n process.exit(1)\n break\n }\n }\n }\n\n const exitState = useExitOnCtrlCD(() => process.exit(0))\n\n useInput((_input, key) => {\n if (key.escape) {\n process.exit(0)\n return\n }\n })\n\n return (\n <>\n <Box\n flexDirection=\"column\"\n gap={1}\n padding={1}\n borderStyle=\"round\"\n borderColor={theme.warning}\n >\n <Text bold color={theme.warning}>\n {t('dialog.trustTitle')}\n </Text>\n <Text bold>{process.cwd()}</Text>\n\n <Box flexDirection=\"column\" gap={1}>\n <Text>{t('dialog.trustWarning1', { product: PRODUCT_NAME })}</Text>\n <Text>{t('dialog.trustWarning2', { product: PRODUCT_NAME })}</Text>\n </Box>\n\n <Select\n options={[\n { label: t('dialog.yesProceed'), value: 'yes' },\n { label: t('dialog.noExit'), value: 'no' },\n ]}\n onChange={value => onChange(value as 'yes' | 'no')}\n />\n </Box>\n <Box marginLeft={3}>\n <Text color={SEMANTIC_COLORS.dim}>\n {exitState.pending ? (\n <>{t('prompts.pressCtrlDAgain')}</>\n ) : (\n <>{t('dialog.confirmExitPrompt')}</>\n )}\n </Text>\n </Box>\n </>\n )\n}\n"],
5
- "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,KAAK,MAAM,gBAAgB;AACpC,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAChC,SAAS,eAAe;AACxB,SAAS,cAAc;AAEvB,SAAS,uBAAuB;AAChC,SAAS,SAAS;AAMX,SAAS,YAAY,EAAE,OAAO,GAA2B;AAC9D,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,MAAM;AAAA,EAAC,GAAG,CAAC,CAAC;AAE5B,WAAS,SAAS,OAAqB;AACrC,UAAM,SAAS,wBAAwB;AACvC,YAAQ,OAAO;AAAA,MACb,KAAK,OAAO;AACV,cAAM,YAAY,QAAQ,MAAM,OAAO;AAEvC,YAAI,CAAC,WAAW;AACd,mCAAyB;AAAA,YACvB,GAAG;AAAA,YACH,wBAAwB;AAAA,UAC1B,CAAC;AAAA,QACH;AACA,eAAO;AACP;AAAA,MACF;AAAA,MACA,KAAK,MAAM;AACT,gBAAQ,KAAK,CAAC;AACd;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,gBAAgB,MAAM,QAAQ,KAAK,CAAC,CAAC;AAEvD,WAAS,CAAC,QAAQ,QAAQ;AACxB,QAAI,IAAI,QAAQ;AACd,cAAQ,KAAK,CAAC;AACd;AAAA,IACF;AAAA,EACF,CAAC;AAED,SACE,0DACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAc;AAAA,MACd,KAAK;AAAA,MACL,SAAS;AAAA,MACT,aAAY;AAAA,MACZ,aAAa,MAAM;AAAA;AAAA,IAEnB,oCAAC,QAAK,MAAI,MAAC,OAAO,MAAM,WACrB,EAAE,mBAAmB,CACxB;AAAA,IACA,oCAAC,QAAK,MAAI,QAAE,QAAQ,IAAI,CAAE;AAAA,IAE1B,oCAAC,OAAI,eAAc,UAAS,KAAK,KAC/B,oCAAC,YAAM,EAAE,wBAAwB,EAAE,SAAS,aAAa,CAAC,CAAE,GAC5D,oCAAC,YAAM,EAAE,wBAAwB,EAAE,SAAS,aAAa,CAAC,CAAE,CAC9D;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,UACP,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,MAAM;AAAA,UAC9C,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,KAAK;AAAA,QAC3C;AAAA,QACA,UAAU,WAAS,SAAS,KAAqB;AAAA;AAAA,IACnD;AAAA,EACF,GACA,oCAAC,OAAI,YAAY,KACf,oCAAC,QAAK,OAAO,gBAAgB,OAC1B,UAAU,UACT,0DAAG,EAAE,yBAAyB,CAAE,IAEhC,0DAAG,EAAE,0BAA0B,CAAE,CAErC,CACF,CACF;AAEJ;",
4
+ "sourcesContent": ["import React from 'react'\nimport { Box, Text, useInput } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { Select } from './CustomSelect/select'\nimport {\n saveCurrentProjectConfig,\n getCurrentProjectConfig,\n} from '@utils/config'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { useExitOnCtrlCD } from '@hooks/useExitOnCtrlCD'\nimport { homedir } from 'os'\nimport { getCwd } from '@utils/state'\nimport Link from './Link'\nimport { SEMANTIC_COLORS } from '@constants/colors'\nimport { t } from '@i18n'\n\ntype Props = {\n onDone(): void\n}\n\nexport function TrustDialog({ onDone }: Props): React.ReactNode {\n const theme = getTheme()\n React.useEffect(() => {}, [])\n\n function onChange(value: 'yes' | 'no') {\n const config = getCurrentProjectConfig()\n switch (value) {\n case 'yes': {\n const isHomeDir = homedir() === getCwd()\n\n if (!isHomeDir) {\n saveCurrentProjectConfig({\n ...config,\n hasTrustDialogAccepted: true,\n })\n }\n onDone()\n break\n }\n case 'no': {\n process.exit(1)\n }\n }\n }\n\n const exitState = useExitOnCtrlCD(() => process.exit(0))\n\n useInput((_input, key) => {\n if (key.escape) {\n process.exit(0)\n }\n })\n\n return (\n <>\n <Box\n flexDirection=\"column\"\n gap={1}\n padding={1}\n borderStyle=\"round\"\n borderColor={theme.warning}\n >\n <Text bold color={theme.warning}>\n {t('dialog.trustTitle')}\n </Text>\n <Text bold>{process.cwd()}</Text>\n\n <Box flexDirection=\"column\" gap={1}>\n <Text>{t('dialog.trustWarning1', { product: PRODUCT_NAME })}</Text>\n <Text>{t('dialog.trustWarning2', { product: PRODUCT_NAME })}</Text>\n </Box>\n\n <Select\n options={[\n { label: t('dialog.yesProceed'), value: 'yes' },\n { label: t('dialog.noExit'), value: 'no' },\n ]}\n onChange={value => onChange(value as 'yes' | 'no')}\n />\n </Box>\n <Box marginLeft={3}>\n <Text color={SEMANTIC_COLORS.dim}>\n {exitState.pending ? (\n <>{t('prompts.pressCtrlDAgain')}</>\n ) : (\n <>{t('dialog.confirmExitPrompt')}</>\n )}\n </Text>\n </Box>\n </>\n )\n}\n"],
5
+ "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,KAAK,MAAM,gBAAgB;AACpC,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAChC,SAAS,eAAe;AACxB,SAAS,cAAc;AAEvB,SAAS,uBAAuB;AAChC,SAAS,SAAS;AAMX,SAAS,YAAY,EAAE,OAAO,GAA2B;AAC9D,QAAM,QAAQ,SAAS;AACvB,QAAM,UAAU,MAAM;AAAA,EAAC,GAAG,CAAC,CAAC;AAE5B,WAAS,SAAS,OAAqB;AACrC,UAAM,SAAS,wBAAwB;AACvC,YAAQ,OAAO;AAAA,MACb,KAAK,OAAO;AACV,cAAM,YAAY,QAAQ,MAAM,OAAO;AAEvC,YAAI,CAAC,WAAW;AACd,mCAAyB;AAAA,YACvB,GAAG;AAAA,YACH,wBAAwB;AAAA,UAC1B,CAAC;AAAA,QACH;AACA,eAAO;AACP;AAAA,MACF;AAAA,MACA,KAAK,MAAM;AACT,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,gBAAgB,MAAM,QAAQ,KAAK,CAAC,CAAC;AAEvD,WAAS,CAAC,QAAQ,QAAQ;AACxB,QAAI,IAAI,QAAQ;AACd,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAED,SACE,0DACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAc;AAAA,MACd,KAAK;AAAA,MACL,SAAS;AAAA,MACT,aAAY;AAAA,MACZ,aAAa,MAAM;AAAA;AAAA,IAEnB,oCAAC,QAAK,MAAI,MAAC,OAAO,MAAM,WACrB,EAAE,mBAAmB,CACxB;AAAA,IACA,oCAAC,QAAK,MAAI,QAAE,QAAQ,IAAI,CAAE;AAAA,IAE1B,oCAAC,OAAI,eAAc,UAAS,KAAK,KAC/B,oCAAC,YAAM,EAAE,wBAAwB,EAAE,SAAS,aAAa,CAAC,CAAE,GAC5D,oCAAC,YAAM,EAAE,wBAAwB,EAAE,SAAS,aAAa,CAAC,CAAE,CAC9D;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,UACP,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,MAAM;AAAA,UAC9C,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,KAAK;AAAA,QAC3C;AAAA,QACA,UAAU,WAAS,SAAS,KAAqB;AAAA;AAAA,IACnD;AAAA,EACF,GACA,oCAAC,OAAI,YAAY,KACf,oCAAC,QAAK,OAAO,gBAAgB,OAC1B,UAAU,UACT,0DAAG,EAAE,yBAAyB,CAAE,IAEhC,0DAAG,EAAE,0BAA0B,CAAE,CAErC,CACF,CACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -109,7 +109,7 @@ function TaskInModuleView({
109
109
  });
110
110
  useEffect(() => {
111
111
  if (!isRunning || !transcript?.startTime) {
112
- return;
112
+ return void 0;
113
113
  }
114
114
  const interval = setInterval(() => {
115
115
  setElapsedMs(Date.now() - transcript.startTime);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/messages/TaskInModuleView.tsx"],
4
- "sourcesContent": ["/**\n * Task In Module View\n *\n * Renders a single Task within a parallel module or standalone.\n * Supports intermediate state display and final output embedding.\n */\n\nimport React, { useState, useEffect } from 'react'\nimport { Box, Text } from 'ink'\nimport type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport type { AgentTranscript } from '@utils/agentTranscripts'\nimport type { DisplayConfig } from '@minto-types/messageGroup'\nimport { ToolUseLoader } from '@components/ToolUseLoader'\nimport { TreeContinuation } from '@components/TreeConnector'\nimport { TaskToolMessage } from './TaskToolMessage'\nimport {\n getTaskDisplayContent,\n getStatusIcon,\n getStatusColor,\n getToolUseHistoryForDisplay,\n getToolDisplayName,\n truncate,\n formatMetaInfo,\n type MetaInfo,\n} from '@utils/taskDisplayUtils'\nimport { formatDuration } from '@utils/format'\nimport { NestedTasksPreview } from './NestedTasksPreview'\nimport { TaskOutputContent, TaskOutputSummary } from './TaskOutputContent'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { formatStats, formatCount } from '@constants/formatRules'\nimport { SYMBOLS } from '@constants/symbols'\nimport {\n SEMANTIC_COLORS,\n STATUS_COLORS,\n VALUE_TIER_COLORS,\n SYMBOL_COLORS,\n} from '@constants/colors'\n\n/**\n * Format token count with K suffix for readability\n */\nfunction formatTokens(count: number): string {\n if (count >= 1000) {\n return `${(count / 1000).toFixed(1)}k`\n }\n return String(count)\n}\n\n/**\n * Format bidirectional token usage: \u2191input \u2193output\n * Uses compact format without spaces: \u21911.2k \u2193329\n * Distinguishes from global stats which use the spaced format: \u2191 1.2k \u00B7 \u2193 329\n */\nfunction formatBidirectionalTokens(\n inputTokens: number,\n outputTokens: number,\n): string {\n const parts: string[] = []\n if (inputTokens > 0) {\n parts.push(`\u2191${formatTokens(inputTokens)}`)\n }\n if (outputTokens > 0) {\n parts.push(`\u2193${formatTokens(outputTokens)}`)\n }\n return parts.join(' ')\n}\n\n/**\n * Tool Use History Display Component\n *\n * Displays tool use history for completed tasks.\n * - Normal mode: Shows recent N tools with truncated descriptions\n * - Verbose mode: Shows all tools with longer descriptions\n */\ninterface ToolUseHistoryDisplayProps {\n transcript: AgentTranscript\n config: DisplayConfig\n inModule: boolean\n isLastInModule: boolean\n /** When true, forces all tools to show as completed (not executing) */\n isTaskCompleted?: boolean\n}\n\nfunction ToolUseHistoryDisplay({\n transcript,\n config,\n inModule,\n isLastInModule,\n isTaskCompleted = false,\n}: ToolUseHistoryDisplayProps): React.ReactNode {\n const { tools: rawTools, hiddenCount } = getToolUseHistoryForDisplay(\n transcript,\n config,\n )\n\n // When task is completed, force all tools to show as not executing\n // This prevents color flashing due to race conditions in transcript updates\n const tools = isTaskCompleted\n ? rawTools.map(tool => ({ ...tool, isExecuting: false }))\n : rawTools\n\n // Don't render if no tools\n if (tools.length === 0) {\n return null\n }\n\n // Determine max chars for description based on mode\n const maxDescChars = config.showAllChildren\n ? config.maxCharsPerChild // verbose: 200 chars\n : config.maxCharsWithoutChildren // normal: 80 chars\n\n // Helper to render a row with proper tree continuation\n const renderRow = (key: string, content: React.ReactNode) => (\n <Box key={key} flexDirection=\"row\">\n {inModule && <TreeContinuation parentIsLast={isLastInModule} />}\n {!inModule && <Box minWidth={4} />}\n {content}\n </Box>\n )\n\n return (\n <Box flexDirection=\"column\">\n {/* Hidden count indicator */}\n {hiddenCount > 0 &&\n renderRow(\n 'hidden-count',\n <Text color={SEMANTIC_COLORS.dim}>\n ... {hiddenCount} earlier tools hidden\n </Text>,\n )}\n {/* Tool list - each tool gets its own row with tree continuation */}\n {tools.map(tool => {\n const displayName = getToolDisplayName(tool.name)\n const description = truncate(tool.description, maxDescChars)\n return renderRow(\n tool.id,\n <Box flexDirection=\"row\">\n {/* \u4F7F\u7528 SYMBOL_COLORS \u4E0E Logo \u54C1\u724C\u914D\u8272\u4FDD\u6301\u4E00\u81F4 */}\n {tool.isExecuting ? (\n <Text color={SYMBOL_COLORS.running}>{SYMBOLS.TOOL_RUNNING} </Text>\n ) : (\n <Text color={SYMBOL_COLORS.child}>{SYMBOLS.CHILD_OUTPUT} </Text>\n )}\n {/* \u5DE5\u5177\u540D\u4F7F\u7528 secondary \u8272\uFF0C\u53C2\u6570\u4F7F\u7528 dim \u8272 */}\n <Text\n color={\n tool.isExecuting\n ? SYMBOL_COLORS.running\n : SEMANTIC_COLORS.secondary\n }\n >\n {displayName}\n </Text>\n {description && (\n <Text\n color={\n tool.isExecuting ? SYMBOL_COLORS.running : SEMANTIC_COLORS.dim\n }\n >\n ({description})\n </Text>\n )}\n {tool.isExecuting && (\n <Text color={SYMBOL_COLORS.running}>\n {SYMBOLS.SUFFIX_RUNNING}\n </Text>\n )}\n </Box>,\n )\n })}\n </Box>\n )\n}\n\ninterface Props {\n /** Tool use ID of this task */\n toolUseId: string\n /** Task description */\n description: string\n /** Agent type */\n agentType: string\n /** Transcript if available */\n transcript: AgentTranscript | null\n /** Output if completed */\n output: ToolResultBlockParam | null\n /** Display configuration */\n config: DisplayConfig\n /** Whether to animate */\n shouldAnimate: boolean\n /** Whether this is inside a parallel module */\n inModule?: boolean\n /** Whether this is the last item in the module (for tree continuation lines) */\n isLastInModule?: boolean\n}\n\nexport function TaskInModuleView({\n toolUseId,\n description,\n agentType,\n transcript,\n output,\n config,\n shouldAnimate,\n inModule = false,\n isLastInModule = false,\n}: Props): React.ReactNode {\n // Determine completion status: use transcript status if available, or check if output exists\n const transcriptStatus = transcript?.status || 'pending'\n // If output exists but transcript shows pending, the task is actually completed\n const status =\n output && transcriptStatus === 'pending' ? 'completed' : transcriptStatus\n const statusIcon = getStatusIcon(status)\n const statusColor = getStatusColor(status)\n\n // Get display content for intermediate state\n const displayContent = transcript\n ? getTaskDisplayContent(transcript, config)\n : null\n\n const isCompleted = status === 'completed' || !!output\n const isError = status === 'failed'\n const isRunning = status === 'running'\n\n // Real-time elapsed time counter for running tasks\n const [elapsedMs, setElapsedMs] = useState(() => {\n if (transcript?.startTime && isRunning) {\n return Date.now() - transcript.startTime\n }\n return 0\n })\n\n useEffect(() => {\n if (!isRunning || !transcript?.startTime) {\n return\n }\n\n // Update elapsed time every second\n const interval = setInterval(() => {\n setElapsedMs(Date.now() - transcript.startTime)\n }, 1000)\n\n return () => clearInterval(interval)\n }, [isRunning, transcript?.startTime])\n\n // Calculate duration for display\n const getDurationDisplay = (): string | null => {\n if (!transcript) return null\n if (transcript.endTime && transcript.startTime) {\n return formatDuration(transcript.endTime - transcript.startTime)\n }\n if (isRunning && transcript.startTime) {\n // Use real-time elapsed time for running tasks\n return formatDuration(elapsedMs)\n }\n return null\n }\n\n const duration = getDurationDisplay()\n const totalTokens = transcript\n ? transcript.tokenUsage.inputTokens + transcript.tokenUsage.outputTokens\n : 0\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {/* Task header with inline stats for completed tasks */}\n <Box flexDirection=\"row\">\n {!inModule && (\n <ToolUseLoader\n shouldAnimate={shouldAnimate && !isCompleted}\n isUnresolved={!isCompleted}\n isError={isError}\n />\n )}\n {inModule && <Text color={statusColor}>{statusIcon} </Text>}\n <TaskToolMessage\n agentType={agentType}\n bold={true}\n children={description}\n />\n {/* Inline stats for completed tasks in module */}\n {inModule && isCompleted && transcript && (\n <>\n {config.showMetaInfo ? (\n // Verbose mode: \u4F7F\u7528 formatMetaInfo \u663E\u793A \"\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4\"\n <Text color={VALUE_TIER_COLORS.meta}>\n {' '}\n (\n {formatMetaInfo({\n duration: transcript.endTime\n ? transcript.endTime - transcript.startTime\n : undefined,\n model: transcript.model,\n timestamp: transcript.endTime,\n })}\n {transcript.toolUseCount > 0 &&\n ` \u00B7 ${formatCount(transcript.toolUseCount, 'tool')}`}\n )\n </Text>\n ) : (\n // Normal mode: compact stats with bidirectional tokens\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n (\n {formatStats([\n formatCount(transcript.toolUseCount, 'tool'),\n formatBidirectionalTokens(\n transcript.tokenUsage.inputTokens,\n transcript.tokenUsage.outputTokens,\n ),\n duration,\n ])}\n )\n </Text>\n )}\n </>\n )}\n {/* Running indicator with elapsed time, tokens, and model */}\n {inModule && status === 'running' && transcript && (\n <Text color={VALUE_TIER_COLORS.meta}>\n {' '}\n (\n {config.showMetaInfo\n ? formatMetaInfo({\n model: transcript.model,\n timestamp: Date.now(),\n })\n : formatStats([\n transcript.toolUseCount > 0\n ? formatCount(transcript.toolUseCount, 'tool')\n : null,\n duration,\n // Show bidirectional tokens: \u2191input \u2193output\n formatBidirectionalTokens(\n transcript.tokenUsage.inputTokens,\n transcript.tokenUsage.outputTokens,\n ) || null,\n ])}\n )\n </Text>\n )}\n {/* Agent ID for non-module view */}\n {!inModule && transcript && (\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n [{transcript.agentId.slice(0, 8)}]\n </Text>\n )}\n </Box>\n\n {/* Detailed metadata row for non-module view */}\n {!inModule && transcript && (\n <Box marginLeft={4}>\n {config.showMetaInfo ? (\n // Verbose mode: \u4F7F\u7528\u5143\u4FE1\u606F\u683C\u5F0F \"\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4\"\n <Text color={VALUE_TIER_COLORS.meta}>\n {status === 'completed' ? '\u2713' : '\u22EF'}{' '}\n {formatMetaInfo({\n duration: transcript.endTime\n ? transcript.endTime - transcript.startTime\n : undefined,\n model: transcript.model,\n timestamp: transcript.endTime || Date.now(),\n })}\n {' \u00B7 '}Tools: {transcript.toolUseCount} \u00B7 Tokens:{' '}\n {formatTokens(totalTokens)}\n </Text>\n ) : (\n // Normal mode: compact display with bidirectional tokens\n <Text color={SEMANTIC_COLORS.dim}>\n {status === 'completed' ? '\u2713' : '\u22EF'} Tools:{' '}\n {transcript.toolUseCount} |{' '}\n {formatBidirectionalTokens(\n transcript.tokenUsage.inputTokens,\n transcript.tokenUsage.outputTokens,\n )}\n {duration ? ` | ${duration}` : ''}\n </Text>\n )}\n </Box>\n )}\n\n {/* Tool use history - show in BOTH running and completed states for real-time visibility */}\n {transcript && (\n <ToolUseHistoryDisplay\n transcript={transcript}\n config={config}\n inModule={inModule}\n isLastInModule={isLastInModule}\n isTaskCompleted={isCompleted}\n />\n )}\n\n {/* Nested tasks preview (when running and has nested tasks) */}\n {displayContent &&\n status === 'running' &&\n displayContent.type === 'nested' && (\n <Box flexDirection=\"row\">\n {inModule && <TreeContinuation parentIsLast={isLastInModule} />}\n {!inModule && <Box minWidth={4} />}\n <Box>\n <NestedTasksPreview\n tasks={displayContent.children || []}\n hiddenCount={displayContent.hiddenCount || 0}\n />\n </Box>\n </Box>\n )}\n\n {/* Final output (when completed) - respects verbose/normal mode */}\n {output && isCompleted && (\n <Box flexDirection=\"row\">\n {/* Show tree continuation line when in module */}\n {inModule && <TreeContinuation parentIsLast={isLastInModule} />}\n {!inModule && <Box minWidth={4} />}\n <Box flexDirection=\"column\">\n {config.showAllChildren ? (\n // Verbose mode: show full output with header\n <>\n <Text color={SEMANTIC_COLORS.dim}>\n Output \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n </Text>\n <Box marginLeft={2}>\n <TaskOutputContent content={output.content} />\n </Box>\n </>\n ) : (\n // Normal mode: show compact summary with expand hint\n <Box flexDirection=\"row\">\n <TaskOutputSummary\n content={output.content}\n maxChars={config.maxCharsWithoutChildren}\n />\n <CollapsibleHint canExpand={true} />\n </Box>\n )}\n </Box>\n </Box>\n )}\n </Box>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Task In Module View\n *\n * Renders a single Task within a parallel module or standalone.\n * Supports intermediate state display and final output embedding.\n */\n\nimport React, { useState, useEffect } from 'react'\nimport { Box, Text } from 'ink'\nimport type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport type { AgentTranscript } from '@utils/agentTranscripts'\nimport type { DisplayConfig } from '@minto-types/messageGroup'\nimport { ToolUseLoader } from '@components/ToolUseLoader'\nimport { TreeContinuation } from '@components/TreeConnector'\nimport { TaskToolMessage } from './TaskToolMessage'\nimport {\n getTaskDisplayContent,\n getStatusIcon,\n getStatusColor,\n getToolUseHistoryForDisplay,\n getToolDisplayName,\n truncate,\n formatMetaInfo,\n type MetaInfo,\n} from '@utils/taskDisplayUtils'\nimport { formatDuration } from '@utils/format'\nimport { NestedTasksPreview } from './NestedTasksPreview'\nimport { TaskOutputContent, TaskOutputSummary } from './TaskOutputContent'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { formatStats, formatCount } from '@constants/formatRules'\nimport { SYMBOLS } from '@constants/symbols'\nimport {\n SEMANTIC_COLORS,\n STATUS_COLORS,\n VALUE_TIER_COLORS,\n SYMBOL_COLORS,\n} from '@constants/colors'\n\n/**\n * Format token count with K suffix for readability\n */\nfunction formatTokens(count: number): string {\n if (count >= 1000) {\n return `${(count / 1000).toFixed(1)}k`\n }\n return String(count)\n}\n\n/**\n * Format bidirectional token usage: \u2191input \u2193output\n * Uses compact format without spaces: \u21911.2k \u2193329\n * Distinguishes from global stats which use the spaced format: \u2191 1.2k \u00B7 \u2193 329\n */\nfunction formatBidirectionalTokens(\n inputTokens: number,\n outputTokens: number,\n): string {\n const parts: string[] = []\n if (inputTokens > 0) {\n parts.push(`\u2191${formatTokens(inputTokens)}`)\n }\n if (outputTokens > 0) {\n parts.push(`\u2193${formatTokens(outputTokens)}`)\n }\n return parts.join(' ')\n}\n\n/**\n * Tool Use History Display Component\n *\n * Displays tool use history for completed tasks.\n * - Normal mode: Shows recent N tools with truncated descriptions\n * - Verbose mode: Shows all tools with longer descriptions\n */\ninterface ToolUseHistoryDisplayProps {\n transcript: AgentTranscript\n config: DisplayConfig\n inModule: boolean\n isLastInModule: boolean\n /** When true, forces all tools to show as completed (not executing) */\n isTaskCompleted?: boolean\n}\n\nfunction ToolUseHistoryDisplay({\n transcript,\n config,\n inModule,\n isLastInModule,\n isTaskCompleted = false,\n}: ToolUseHistoryDisplayProps): React.ReactNode {\n const { tools: rawTools, hiddenCount } = getToolUseHistoryForDisplay(\n transcript,\n config,\n )\n\n // When task is completed, force all tools to show as not executing\n // This prevents color flashing due to race conditions in transcript updates\n const tools = isTaskCompleted\n ? rawTools.map(tool => ({ ...tool, isExecuting: false }))\n : rawTools\n\n // Don't render if no tools\n if (tools.length === 0) {\n return null\n }\n\n // Determine max chars for description based on mode\n const maxDescChars = config.showAllChildren\n ? config.maxCharsPerChild // verbose: 200 chars\n : config.maxCharsWithoutChildren // normal: 80 chars\n\n // Helper to render a row with proper tree continuation\n const renderRow = (key: string, content: React.ReactNode) => (\n <Box key={key} flexDirection=\"row\">\n {inModule && <TreeContinuation parentIsLast={isLastInModule} />}\n {!inModule && <Box minWidth={4} />}\n {content}\n </Box>\n )\n\n return (\n <Box flexDirection=\"column\">\n {/* Hidden count indicator */}\n {hiddenCount > 0 &&\n renderRow(\n 'hidden-count',\n <Text color={SEMANTIC_COLORS.dim}>\n ... {hiddenCount} earlier tools hidden\n </Text>,\n )}\n {/* Tool list - each tool gets its own row with tree continuation */}\n {tools.map(tool => {\n const displayName = getToolDisplayName(tool.name)\n const description = truncate(tool.description, maxDescChars)\n return renderRow(\n tool.id,\n <Box flexDirection=\"row\">\n {/* \u4F7F\u7528 SYMBOL_COLORS \u4E0E Logo \u54C1\u724C\u914D\u8272\u4FDD\u6301\u4E00\u81F4 */}\n {tool.isExecuting ? (\n <Text color={SYMBOL_COLORS.running}>{SYMBOLS.TOOL_RUNNING} </Text>\n ) : (\n <Text color={SYMBOL_COLORS.child}>{SYMBOLS.CHILD_OUTPUT} </Text>\n )}\n {/* \u5DE5\u5177\u540D\u4F7F\u7528 secondary \u8272\uFF0C\u53C2\u6570\u4F7F\u7528 dim \u8272 */}\n <Text\n color={\n tool.isExecuting\n ? SYMBOL_COLORS.running\n : SEMANTIC_COLORS.secondary\n }\n >\n {displayName}\n </Text>\n {description && (\n <Text\n color={\n tool.isExecuting ? SYMBOL_COLORS.running : SEMANTIC_COLORS.dim\n }\n >\n ({description})\n </Text>\n )}\n {tool.isExecuting && (\n <Text color={SYMBOL_COLORS.running}>\n {SYMBOLS.SUFFIX_RUNNING}\n </Text>\n )}\n </Box>,\n )\n })}\n </Box>\n )\n}\n\ninterface Props {\n /** Tool use ID of this task */\n toolUseId: string\n /** Task description */\n description: string\n /** Agent type */\n agentType: string\n /** Transcript if available */\n transcript: AgentTranscript | null\n /** Output if completed */\n output: ToolResultBlockParam | null\n /** Display configuration */\n config: DisplayConfig\n /** Whether to animate */\n shouldAnimate: boolean\n /** Whether this is inside a parallel module */\n inModule?: boolean\n /** Whether this is the last item in the module (for tree continuation lines) */\n isLastInModule?: boolean\n}\n\nexport function TaskInModuleView({\n toolUseId,\n description,\n agentType,\n transcript,\n output,\n config,\n shouldAnimate,\n inModule = false,\n isLastInModule = false,\n}: Props): React.ReactNode {\n // Determine completion status: use transcript status if available, or check if output exists\n const transcriptStatus = transcript?.status || 'pending'\n // If output exists but transcript shows pending, the task is actually completed\n const status =\n output && transcriptStatus === 'pending' ? 'completed' : transcriptStatus\n const statusIcon = getStatusIcon(status)\n const statusColor = getStatusColor(status)\n\n // Get display content for intermediate state\n const displayContent = transcript\n ? getTaskDisplayContent(transcript, config)\n : null\n\n const isCompleted = status === 'completed' || !!output\n const isError = status === 'failed'\n const isRunning = status === 'running'\n\n // Real-time elapsed time counter for running tasks\n const [elapsedMs, setElapsedMs] = useState(() => {\n if (transcript?.startTime && isRunning) {\n return Date.now() - transcript.startTime\n }\n return 0\n })\n\n useEffect(() => {\n if (!isRunning || !transcript?.startTime) {\n return undefined\n }\n\n // Update elapsed time every second\n const interval = setInterval(() => {\n setElapsedMs(Date.now() - transcript.startTime)\n }, 1000)\n\n return () => clearInterval(interval)\n }, [isRunning, transcript?.startTime])\n\n // Calculate duration for display\n const getDurationDisplay = (): string | null => {\n if (!transcript) return null\n if (transcript.endTime && transcript.startTime) {\n return formatDuration(transcript.endTime - transcript.startTime)\n }\n if (isRunning && transcript.startTime) {\n // Use real-time elapsed time for running tasks\n return formatDuration(elapsedMs)\n }\n return null\n }\n\n const duration = getDurationDisplay()\n const totalTokens = transcript\n ? transcript.tokenUsage.inputTokens + transcript.tokenUsage.outputTokens\n : 0\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n {/* Task header with inline stats for completed tasks */}\n <Box flexDirection=\"row\">\n {!inModule && (\n <ToolUseLoader\n shouldAnimate={shouldAnimate && !isCompleted}\n isUnresolved={!isCompleted}\n isError={isError}\n />\n )}\n {inModule && <Text color={statusColor}>{statusIcon} </Text>}\n <TaskToolMessage\n agentType={agentType}\n bold={true}\n children={description}\n />\n {/* Inline stats for completed tasks in module */}\n {inModule && isCompleted && transcript && (\n <>\n {config.showMetaInfo ? (\n // Verbose mode: \u4F7F\u7528 formatMetaInfo \u663E\u793A \"\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4\"\n <Text color={VALUE_TIER_COLORS.meta}>\n {' '}\n (\n {formatMetaInfo({\n duration: transcript.endTime\n ? transcript.endTime - transcript.startTime\n : undefined,\n model: transcript.model,\n timestamp: transcript.endTime,\n })}\n {transcript.toolUseCount > 0 &&\n ` \u00B7 ${formatCount(transcript.toolUseCount, 'tool')}`}\n )\n </Text>\n ) : (\n // Normal mode: compact stats with bidirectional tokens\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n (\n {formatStats([\n formatCount(transcript.toolUseCount, 'tool'),\n formatBidirectionalTokens(\n transcript.tokenUsage.inputTokens,\n transcript.tokenUsage.outputTokens,\n ),\n duration,\n ])}\n )\n </Text>\n )}\n </>\n )}\n {/* Running indicator with elapsed time, tokens, and model */}\n {inModule && status === 'running' && transcript && (\n <Text color={VALUE_TIER_COLORS.meta}>\n {' '}\n (\n {config.showMetaInfo\n ? formatMetaInfo({\n model: transcript.model,\n timestamp: Date.now(),\n })\n : formatStats([\n transcript.toolUseCount > 0\n ? formatCount(transcript.toolUseCount, 'tool')\n : null,\n duration,\n // Show bidirectional tokens: \u2191input \u2193output\n formatBidirectionalTokens(\n transcript.tokenUsage.inputTokens,\n transcript.tokenUsage.outputTokens,\n ) || null,\n ])}\n )\n </Text>\n )}\n {/* Agent ID for non-module view */}\n {!inModule && transcript && (\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n [{transcript.agentId.slice(0, 8)}]\n </Text>\n )}\n </Box>\n\n {/* Detailed metadata row for non-module view */}\n {!inModule && transcript && (\n <Box marginLeft={4}>\n {config.showMetaInfo ? (\n // Verbose mode: \u4F7F\u7528\u5143\u4FE1\u606F\u683C\u5F0F \"\u8017\u65F6 \u00B7 \u6A21\u578B \u00B7 \u65F6\u95F4\"\n <Text color={VALUE_TIER_COLORS.meta}>\n {status === 'completed' ? '\u2713' : '\u22EF'}{' '}\n {formatMetaInfo({\n duration: transcript.endTime\n ? transcript.endTime - transcript.startTime\n : undefined,\n model: transcript.model,\n timestamp: transcript.endTime || Date.now(),\n })}\n {' \u00B7 '}Tools: {transcript.toolUseCount} \u00B7 Tokens:{' '}\n {formatTokens(totalTokens)}\n </Text>\n ) : (\n // Normal mode: compact display with bidirectional tokens\n <Text color={SEMANTIC_COLORS.dim}>\n {status === 'completed' ? '\u2713' : '\u22EF'} Tools:{' '}\n {transcript.toolUseCount} |{' '}\n {formatBidirectionalTokens(\n transcript.tokenUsage.inputTokens,\n transcript.tokenUsage.outputTokens,\n )}\n {duration ? ` | ${duration}` : ''}\n </Text>\n )}\n </Box>\n )}\n\n {/* Tool use history - show in BOTH running and completed states for real-time visibility */}\n {transcript && (\n <ToolUseHistoryDisplay\n transcript={transcript}\n config={config}\n inModule={inModule}\n isLastInModule={isLastInModule}\n isTaskCompleted={isCompleted}\n />\n )}\n\n {/* Nested tasks preview (when running and has nested tasks) */}\n {displayContent &&\n status === 'running' &&\n displayContent.type === 'nested' && (\n <Box flexDirection=\"row\">\n {inModule && <TreeContinuation parentIsLast={isLastInModule} />}\n {!inModule && <Box minWidth={4} />}\n <Box>\n <NestedTasksPreview\n tasks={displayContent.children || []}\n hiddenCount={displayContent.hiddenCount || 0}\n />\n </Box>\n </Box>\n )}\n\n {/* Final output (when completed) - respects verbose/normal mode */}\n {output && isCompleted && (\n <Box flexDirection=\"row\">\n {/* Show tree continuation line when in module */}\n {inModule && <TreeContinuation parentIsLast={isLastInModule} />}\n {!inModule && <Box minWidth={4} />}\n <Box flexDirection=\"column\">\n {config.showAllChildren ? (\n // Verbose mode: show full output with header\n <>\n <Text color={SEMANTIC_COLORS.dim}>\n Output \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n </Text>\n <Box marginLeft={2}>\n <TaskOutputContent content={output.content} />\n </Box>\n </>\n ) : (\n // Normal mode: show compact summary with expand hint\n <Box flexDirection=\"row\">\n <TaskOutputSummary\n content={output.content}\n maxChars={config.maxCharsWithoutChildren}\n />\n <CollapsibleHint canExpand={true} />\n </Box>\n )}\n </Box>\n </Box>\n )}\n </Box>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -11,7 +11,7 @@ function TaskToolMessage({ agentType, children, bold = true }) {
11
11
  useEffect(() => {
12
12
  if (agentConfigCache.has(agentType)) {
13
13
  setAgentConfig(agentConfigCache.get(agentType));
14
- return;
14
+ return void 0;
15
15
  }
16
16
  let mounted = true;
17
17
  getAgentByType(agentType).then((config) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/messages/TaskToolMessage.tsx"],
4
- "sourcesContent": ["import React, { useEffect, useState, useMemo } from 'react'\nimport { Text } from 'ink'\nimport { getAgentByType } from '@utils/agentLoader'\nimport { getTheme } from '@utils/theme'\n\ninterface Props {\n agentType: string\n children: React.ReactNode\n bold?: boolean\n}\n\n// Simple cache to prevent re-fetching agent configs\nconst agentConfigCache = new Map<string, any>()\n\nexport function TaskToolMessage({ agentType, children, bold = true }: Props) {\n const theme = getTheme()\n const [agentConfig, setAgentConfig] = useState<any>(() => {\n // Return cached config immediately if available\n return agentConfigCache.get(agentType) || null\n })\n\n useEffect(() => {\n // Skip if already cached\n if (agentConfigCache.has(agentType)) {\n setAgentConfig(agentConfigCache.get(agentType))\n return\n }\n\n // Load and cache agent configuration\n let mounted = true\n getAgentByType(agentType)\n .then(config => {\n if (mounted) {\n agentConfigCache.set(agentType, config)\n setAgentConfig(config)\n }\n })\n .catch(() => {\n // Silently handle errors to prevent console noise\n if (mounted) {\n agentConfigCache.set(agentType, null)\n }\n })\n\n return () => {\n mounted = false\n }\n }, [agentType])\n\n // Memoize color calculation to prevent unnecessary re-renders\n const color = useMemo(() => {\n return agentConfig?.color || theme.text\n }, [agentConfig?.color, theme.text])\n\n return (\n <Text color={color} bold={bold}>\n {children}\n </Text>\n )\n}\n"],
5
- "mappings": "AAAA,OAAO,SAAS,WAAW,UAAU,eAAe;AACpD,SAAS,YAAY;AACrB,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB;AASzB,MAAM,mBAAmB,oBAAI,IAAiB;AAEvC,SAAS,gBAAgB,EAAE,WAAW,UAAU,OAAO,KAAK,GAAU;AAC3E,QAAM,QAAQ,SAAS;AACvB,QAAM,CAAC,aAAa,cAAc,IAAI,SAAc,MAAM;AAExD,WAAO,iBAAiB,IAAI,SAAS,KAAK;AAAA,EAC5C,CAAC;AAED,YAAU,MAAM;AAEd,QAAI,iBAAiB,IAAI,SAAS,GAAG;AACnC,qBAAe,iBAAiB,IAAI,SAAS,CAAC;AAC9C;AAAA,IACF;AAGA,QAAI,UAAU;AACd,mBAAe,SAAS,EACrB,KAAK,YAAU;AACd,UAAI,SAAS;AACX,yBAAiB,IAAI,WAAW,MAAM;AACtC,uBAAe,MAAM;AAAA,MACvB;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AAEX,UAAI,SAAS;AACX,yBAAiB,IAAI,WAAW,IAAI;AAAA,MACtC;AAAA,IACF,CAAC;AAEH,WAAO,MAAM;AACX,gBAAU;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAGd,QAAM,QAAQ,QAAQ,MAAM;AAC1B,WAAO,aAAa,SAAS,MAAM;AAAA,EACrC,GAAG,CAAC,aAAa,OAAO,MAAM,IAAI,CAAC;AAEnC,SACE,oCAAC,QAAK,OAAc,QACjB,QACH;AAEJ;",
4
+ "sourcesContent": ["import React, { useEffect, useState, useMemo } from 'react'\nimport { Text } from 'ink'\nimport { getAgentByType } from '@utils/agentLoader'\nimport { getTheme } from '@utils/theme'\n\ninterface Props {\n agentType: string\n children: React.ReactNode\n bold?: boolean\n}\n\n// Simple cache to prevent re-fetching agent configs\nconst agentConfigCache = new Map<string, any>()\n\nexport function TaskToolMessage({ agentType, children, bold = true }: Props) {\n const theme = getTheme()\n const [agentConfig, setAgentConfig] = useState<any>(() => {\n // Return cached config immediately if available\n return agentConfigCache.get(agentType) || null\n })\n\n useEffect(() => {\n // Skip if already cached\n if (agentConfigCache.has(agentType)) {\n setAgentConfig(agentConfigCache.get(agentType))\n return undefined\n }\n\n // Load and cache agent configuration\n let mounted = true\n getAgentByType(agentType)\n .then(config => {\n if (mounted) {\n agentConfigCache.set(agentType, config)\n setAgentConfig(config)\n }\n })\n .catch(() => {\n // Silently handle errors to prevent console noise\n if (mounted) {\n agentConfigCache.set(agentType, null)\n }\n })\n\n return () => {\n mounted = false\n }\n }, [agentType])\n\n // Memoize color calculation to prevent unnecessary re-renders\n const color = useMemo(() => {\n return agentConfig?.color || theme.text\n }, [agentConfig?.color, theme.text])\n\n return (\n <Text color={color} bold={bold}>\n {children}\n </Text>\n )\n}\n"],
5
+ "mappings": "AAAA,OAAO,SAAS,WAAW,UAAU,eAAe;AACpD,SAAS,YAAY;AACrB,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB;AASzB,MAAM,mBAAmB,oBAAI,IAAiB;AAEvC,SAAS,gBAAgB,EAAE,WAAW,UAAU,OAAO,KAAK,GAAU;AAC3E,QAAM,QAAQ,SAAS;AACvB,QAAM,CAAC,aAAa,cAAc,IAAI,SAAc,MAAM;AAExD,WAAO,iBAAiB,IAAI,SAAS,KAAK;AAAA,EAC5C,CAAC;AAED,YAAU,MAAM;AAEd,QAAI,iBAAiB,IAAI,SAAS,GAAG;AACnC,qBAAe,iBAAiB,IAAI,SAAS,CAAC;AAC9C,aAAO;AAAA,IACT;AAGA,QAAI,UAAU;AACd,mBAAe,SAAS,EACrB,KAAK,YAAU;AACd,UAAI,SAAS;AACX,yBAAiB,IAAI,WAAW,MAAM;AACtC,uBAAe,MAAM;AAAA,MACvB;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AAEX,UAAI,SAAS;AACX,yBAAiB,IAAI,WAAW,IAAI;AAAA,MACtC;AAAA,IACF,CAAC;AAEH,WAAO,MAAM;AACX,gBAAU;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAGd,QAAM,QAAQ,QAAQ,MAAM;AAC1B,WAAO,aAAa,SAAS,MAAM;AAAA,EACrC,GAAG,CAAC,aAAa,OAAO,MAAM,IAAI,CAAC;AAEnC,SACE,oCAAC,QAAK,OAAc,QACjB,QACH;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ import { Box, Text } from "ink";
3
3
  import { logError } from "../../utils/log.js";
4
4
  import { useTerminalSize } from "../../hooks/useTerminalSize.js";
5
5
  import { BRAND_GRADIENT, SEMANTIC_COLORS } from "../../constants/colors.js";
6
+ const SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>\s*/g;
6
7
  function UserPromptMessage({
7
8
  addMargin,
8
9
  param: { text }
@@ -12,7 +13,11 @@ function UserPromptMessage({
12
13
  logError("No content found in user prompt message");
13
14
  return null;
14
15
  }
15
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: addMargin ? 1 : 0, width: "100%" }, /* @__PURE__ */ React.createElement(Box, { minWidth: 2, width: 2 }, /* @__PURE__ */ React.createElement(Text, { color: BRAND_GRADIENT.START, bold: true }, "\u276F")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: columns - 4 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.secondary, wrap: "wrap" }, text)));
16
+ const displayText = text.replace(SYSTEM_REMINDER_RE, "").trim();
17
+ if (!displayText) {
18
+ return null;
19
+ }
20
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: addMargin ? 1 : 0, width: "100%" }, /* @__PURE__ */ React.createElement(Box, { minWidth: 2, width: 2 }, /* @__PURE__ */ React.createElement(Text, { color: BRAND_GRADIENT.START, bold: true }, "\u276F")), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: columns - 4 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.secondary, wrap: "wrap" }, displayText)));
16
21
  }
17
22
  export {
18
23
  UserPromptMessage
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/messages/UserPromptMessage.tsx"],
4
- "sourcesContent": ["import React from 'react'\nimport { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { Box, Text } from 'ink'\nimport { logError } from '@utils/log'\nimport { useTerminalSize } from '@hooks/useTerminalSize'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\n\ntype Props = {\n addMargin: boolean\n param: TextBlockParam\n}\n\nexport function UserPromptMessage({\n addMargin,\n param: { text },\n}: Props): React.ReactNode {\n const { columns } = useTerminalSize()\n if (!text) {\n logError('No content found in user prompt message')\n return null\n }\n\n return (\n <Box flexDirection=\"row\" marginTop={addMargin ? 1 : 0} width=\"100%\">\n <Box minWidth={2} width={2}>\n <Text color={BRAND_GRADIENT.START} bold>\n \u276F\n </Text>\n </Box>\n <Box flexDirection=\"column\" width={columns - 4}>\n <Text color={SEMANTIC_COLORS.secondary} wrap=\"wrap\">\n {text}\n </Text>\n </Box>\n </Box>\n )\n}\n"],
5
- "mappings": "AAAA,OAAO,WAAW;AAElB,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAChC,SAAS,gBAAgB,uBAAuB;AAOzC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,OAAO,EAAE,KAAK;AAChB,GAA2B;AACzB,QAAM,EAAE,QAAQ,IAAI,gBAAgB;AACpC,MAAI,CAAC,MAAM;AACT,aAAS,yCAAyC;AAClD,WAAO;AAAA,EACT;AAEA,SACE,oCAAC,OAAI,eAAc,OAAM,WAAW,YAAY,IAAI,GAAG,OAAM,UAC3D,oCAAC,OAAI,UAAU,GAAG,OAAO,KACvB,oCAAC,QAAK,OAAO,eAAe,OAAO,MAAI,QAAC,QAExC,CACF,GACA,oCAAC,OAAI,eAAc,UAAS,OAAO,UAAU,KAC3C,oCAAC,QAAK,OAAO,gBAAgB,WAAW,MAAK,UAC1C,IACH,CACF,CACF;AAEJ;",
4
+ "sourcesContent": ["import React from 'react'\nimport { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { Box, Text } from 'ink'\nimport { logError } from '@utils/log'\nimport { useTerminalSize } from '@hooks/useTerminalSize'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\n\ntype Props = {\n addMargin: boolean\n param: TextBlockParam\n}\n\n// Strip system-reminder tags that should never be visible to the user\nconst SYSTEM_REMINDER_RE = /<system-reminder>[\\s\\S]*?<\\/system-reminder>\\s*/g\n\nexport function UserPromptMessage({\n addMargin,\n param: { text },\n}: Props): React.ReactNode {\n const { columns } = useTerminalSize()\n if (!text) {\n logError('No content found in user prompt message')\n return null\n }\n\n const displayText = text.replace(SYSTEM_REMINDER_RE, '').trim()\n if (!displayText) {\n return null\n }\n\n return (\n <Box flexDirection=\"row\" marginTop={addMargin ? 1 : 0} width=\"100%\">\n <Box minWidth={2} width={2}>\n <Text color={BRAND_GRADIENT.START} bold>\n \u276F\n </Text>\n </Box>\n <Box flexDirection=\"column\" width={columns - 4}>\n <Text color={SEMANTIC_COLORS.secondary} wrap=\"wrap\">\n {displayText}\n </Text>\n </Box>\n </Box>\n )\n}\n"],
5
+ "mappings": "AAAA,OAAO,WAAW;AAElB,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAChC,SAAS,gBAAgB,uBAAuB;AAQhD,MAAM,qBAAqB;AAEpB,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA,OAAO,EAAE,KAAK;AAChB,GAA2B;AACzB,QAAM,EAAE,QAAQ,IAAI,gBAAgB;AACpC,MAAI,CAAC,MAAM;AACT,aAAS,yCAAyC;AAClD,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,QAAQ,oBAAoB,EAAE,EAAE,KAAK;AAC9D,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,SACE,oCAAC,OAAI,eAAc,OAAM,WAAW,YAAY,IAAI,GAAG,OAAM,UAC3D,oCAAC,OAAI,UAAU,GAAG,OAAO,KACvB,oCAAC,QAAK,OAAO,eAAe,OAAO,MAAI,QAAC,QAExC,CACF,GACA,oCAAC,OAAI,eAAc,UAAS,OAAO,UAAU,KAC3C,oCAAC,QAAK,OAAO,gBAAgB,WAAW,MAAK,UAC1C,WACH,CACF,CACF;AAEJ;",
6
6
  "names": []
7
7
  }