@within-7/minto 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/dist/Tool.js.map +2 -2
  2. package/dist/commands/agents/AgentsCommand.js +461 -657
  3. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  4. package/dist/commands/agents/types.js +1 -0
  5. package/dist/commands/agents/types.js.map +2 -2
  6. package/dist/commands/agents/utils/fileOperations.js +96 -36
  7. package/dist/commands/agents/utils/fileOperations.js.map +3 -3
  8. package/dist/commands/agents/utils/index.js +3 -1
  9. package/dist/commands/agents/utils/index.js.map +2 -2
  10. package/dist/commands/context.js +54 -23
  11. package/dist/commands/context.js.map +2 -2
  12. package/dist/commands/ctx_viz.js +1 -1
  13. package/dist/commands/effort.js +87 -0
  14. package/dist/commands/effort.js.map +7 -0
  15. package/dist/commands/export.js +684 -94
  16. package/dist/commands/export.js.map +2 -2
  17. package/dist/commands/ide.js +18 -0
  18. package/dist/commands/ide.js.map +7 -0
  19. package/dist/commands/language.js +19 -46
  20. package/dist/commands/language.js.map +2 -2
  21. package/dist/commands/mcp-interactive.js +425 -217
  22. package/dist/commands/mcp-interactive.js.map +2 -2
  23. package/dist/commands/memory.js +168 -0
  24. package/dist/commands/memory.js.map +7 -0
  25. package/dist/commands/model.js +457 -65
  26. package/dist/commands/model.js.map +2 -2
  27. package/dist/commands/outputStyle.js +64 -0
  28. package/dist/commands/outputStyle.js.map +7 -0
  29. package/dist/commands/permissions.js +75 -49
  30. package/dist/commands/permissions.js.map +2 -2
  31. package/dist/commands/plugin/utils.js +33 -1
  32. package/dist/commands/plugin/utils.js.map +2 -2
  33. package/dist/commands/plugin.js +891 -185
  34. package/dist/commands/plugin.js.map +3 -3
  35. package/dist/commands/refreshCommands.js +2 -0
  36. package/dist/commands/refreshCommands.js.map +2 -2
  37. package/dist/commands/resume.js +1 -1
  38. package/dist/commands/resume.js.map +1 -1
  39. package/dist/commands/review.js +51 -0
  40. package/dist/commands/review.js.map +7 -0
  41. package/dist/commands/sandbox.js +168 -70
  42. package/dist/commands/sandbox.js.map +2 -2
  43. package/dist/commands/setup.js +593 -107
  44. package/dist/commands/setup.js.map +2 -2
  45. package/dist/commands/stats.js +188 -131
  46. package/dist/commands/stats.js.map +2 -2
  47. package/dist/commands/status.js +75 -13
  48. package/dist/commands/status.js.map +2 -2
  49. package/dist/commands/terminalSetup.js +6 -0
  50. package/dist/commands/terminalSetup.js.map +2 -2
  51. package/dist/commands/undo.js +146 -174
  52. package/dist/commands/undo.js.map +2 -2
  53. package/dist/commands/vim.js +22 -0
  54. package/dist/commands/vim.js.map +7 -0
  55. package/dist/commands.js +12 -0
  56. package/dist/commands.js.map +2 -2
  57. package/dist/components/Help.js +165 -32
  58. package/dist/components/Help.js.map +2 -2
  59. package/dist/components/HighlightedCode.js +1 -0
  60. package/dist/components/HighlightedCode.js.map +2 -2
  61. package/dist/components/InfoPanel/InfoPanel.js +123 -0
  62. package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
  63. package/dist/components/InfoPanel/index.js +5 -0
  64. package/dist/components/InfoPanel/index.js.map +7 -0
  65. package/dist/components/InfoPanel/types.js +1 -0
  66. package/dist/components/InfoPanel/types.js.map +7 -0
  67. package/dist/components/ModelSelector/BrandTextInput.js +43 -0
  68. package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
  69. package/dist/components/ModelSelector/ModelSelector.js +590 -565
  70. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  71. package/dist/components/ModelSelector/WizardContainer.js +45 -0
  72. package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
  73. package/dist/components/ModelSelector/index.js +1 -3
  74. package/dist/components/ModelSelector/index.js.map +2 -2
  75. package/dist/components/PromptInput.js +26 -11
  76. package/dist/components/PromptInput.js.map +2 -2
  77. package/dist/components/PulseLabel.js +44 -0
  78. package/dist/components/PulseLabel.js.map +7 -0
  79. package/dist/components/RequestStatusIndicator.js +1 -1
  80. package/dist/components/RequestStatusIndicator.js.map +1 -1
  81. package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
  82. package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
  83. package/dist/components/SimpleSelector/index.js +5 -0
  84. package/dist/components/SimpleSelector/index.js.map +7 -0
  85. package/dist/components/SimpleSelector/types.js +1 -0
  86. package/dist/components/SimpleSelector/types.js.map +7 -0
  87. package/dist/components/Spinner.js +12 -42
  88. package/dist/components/Spinner.js.map +3 -3
  89. package/dist/components/StartupStatus.js +57 -0
  90. package/dist/components/StartupStatus.js.map +7 -0
  91. package/dist/components/StatusOverlayContent.js +21 -0
  92. package/dist/components/StatusOverlayContent.js.map +7 -0
  93. package/dist/components/SubagentBlock.js +43 -6
  94. package/dist/components/SubagentBlock.js.map +2 -2
  95. package/dist/components/TabbedListView/ScrollableList.js +31 -5
  96. package/dist/components/TabbedListView/ScrollableList.js.map +2 -2
  97. package/dist/components/TabbedListView/TabBar.js +13 -8
  98. package/dist/components/TabbedListView/TabBar.js.map +2 -2
  99. package/dist/components/TabbedListView/TabbedListView.js +123 -48
  100. package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
  101. package/dist/components/TodoPanel.js +1 -1
  102. package/dist/components/TodoPanel.js.map +1 -1
  103. package/dist/components/ToolUseLoader.js +5 -0
  104. package/dist/components/ToolUseLoader.js.map +2 -2
  105. package/dist/components/TrustDialog.js +0 -2
  106. package/dist/components/TrustDialog.js.map +2 -2
  107. package/dist/components/messages/TaskInModuleView.js +1 -1
  108. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  109. package/dist/components/messages/TaskToolMessage.js +1 -1
  110. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  111. package/dist/components/messages/UserPromptMessage.js +6 -1
  112. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  113. package/dist/constants/modelCapabilities.js +103 -18
  114. package/dist/constants/modelCapabilities.js.map +2 -2
  115. package/dist/constants/product.js +2 -0
  116. package/dist/constants/product.js.map +2 -2
  117. package/dist/constants/prompts/agentPrompt.js +30 -0
  118. package/dist/constants/prompts/agentPrompt.js.map +7 -0
  119. package/dist/constants/prompts/codeConventions.js +27 -0
  120. package/dist/constants/prompts/codeConventions.js.map +7 -0
  121. package/dist/constants/prompts/doingTasks.js +15 -0
  122. package/dist/constants/prompts/doingTasks.js.map +7 -0
  123. package/dist/constants/prompts/envInfo.js +17 -0
  124. package/dist/constants/prompts/envInfo.js.map +7 -0
  125. package/dist/constants/prompts/executingWithCare.js +17 -0
  126. package/dist/constants/prompts/executingWithCare.js.map +7 -0
  127. package/dist/constants/prompts/identity.js +10 -0
  128. package/dist/constants/prompts/identity.js.map +7 -0
  129. package/dist/constants/prompts/index.js +78 -0
  130. package/dist/constants/prompts/index.js.map +7 -0
  131. package/dist/constants/prompts/taskManagement.js +60 -0
  132. package/dist/constants/prompts/taskManagement.js.map +7 -0
  133. package/dist/constants/prompts/toneAndStyle.js +62 -0
  134. package/dist/constants/prompts/toneAndStyle.js.map +7 -0
  135. package/dist/constants/prompts/toolUsagePolicy.js +38 -0
  136. package/dist/constants/prompts/toolUsagePolicy.js.map +7 -0
  137. package/dist/constants/prompts.js +5 -176
  138. package/dist/constants/prompts.js.map +2 -2
  139. package/dist/constants/providerRegistry.js +235 -0
  140. package/dist/constants/providerRegistry.js.map +7 -0
  141. package/dist/constants/providers.js +35 -0
  142. package/dist/constants/providers.js.map +7 -0
  143. package/dist/context/PermissionContext.js +0 -1
  144. package/dist/context/PermissionContext.js.map +2 -2
  145. package/dist/context.js +87 -31
  146. package/dist/context.js.map +2 -2
  147. package/dist/core/backupHook.js +29 -0
  148. package/dist/core/backupHook.js.map +7 -0
  149. package/dist/core/config/defaults.js +11 -2
  150. package/dist/core/config/defaults.js.map +2 -2
  151. package/dist/core/config/schema.js +21 -3
  152. package/dist/core/config/schema.js.map +2 -2
  153. package/dist/core/costTracker.js +18 -16
  154. package/dist/core/costTracker.js.map +2 -2
  155. package/dist/core/index.js +0 -1
  156. package/dist/core/index.js.map +2 -2
  157. package/dist/core/tokenStatsManager.js +22 -4
  158. package/dist/core/tokenStatsManager.js.map +2 -2
  159. package/dist/cost-tracker.js +0 -16
  160. package/dist/cost-tracker.js.map +2 -2
  161. package/dist/entrypoints/bootstrap.js +3 -1
  162. package/dist/entrypoints/bootstrap.js.map +2 -2
  163. package/dist/entrypoints/cli.js +81 -68
  164. package/dist/entrypoints/cli.js.map +2 -2
  165. package/dist/hooks/useAgentTokenStats.js +1 -1
  166. package/dist/hooks/useAgentTokenStats.js.map +2 -2
  167. package/dist/hooks/useAgentTranscripts.js +2 -1
  168. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  169. package/dist/hooks/useBackgroundShells.js +29 -0
  170. package/dist/hooks/useBackgroundShells.js.map +7 -0
  171. package/dist/hooks/useCanUseTool.js +1 -1
  172. package/dist/hooks/useCanUseTool.js.map +2 -2
  173. package/dist/hooks/useDeferredLoading.js +64 -0
  174. package/dist/hooks/useDeferredLoading.js.map +7 -0
  175. package/dist/hooks/useHookStatus.js +1 -1
  176. package/dist/hooks/useHookStatus.js.map +2 -2
  177. package/dist/hooks/useSessionTracking.js +55 -0
  178. package/dist/hooks/useSessionTracking.js.map +7 -0
  179. package/dist/hooks/useTerminalSize.js +21 -0
  180. package/dist/hooks/useTerminalSize.js.map +2 -2
  181. package/dist/hooks/useTextInput.js +1 -0
  182. package/dist/hooks/useTextInput.js.map +2 -2
  183. package/dist/hooks/useUnifiedCompletion.js +3 -2
  184. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  185. package/dist/i18n/locales/en.js +299 -1
  186. package/dist/i18n/locales/en.js.map +2 -2
  187. package/dist/i18n/locales/zh-CN.js +300 -2
  188. package/dist/i18n/locales/zh-CN.js.map +2 -2
  189. package/dist/i18n/types.js.map +1 -1
  190. package/dist/messages.js +41 -17
  191. package/dist/messages.js.map +2 -2
  192. package/dist/permissions.js +94 -1
  193. package/dist/permissions.js.map +2 -2
  194. package/dist/query.js +27 -19
  195. package/dist/query.js.map +2 -2
  196. package/dist/screens/REPL.js +83 -74
  197. package/dist/screens/REPL.js.map +2 -2
  198. package/dist/services/adapters/responsesAPI.js +6 -0
  199. package/dist/services/adapters/responsesAPI.js.map +2 -2
  200. package/dist/services/agentTeams/index.js +35 -0
  201. package/dist/services/agentTeams/index.js.map +7 -0
  202. package/dist/services/agentTeams/mailbox.js +114 -0
  203. package/dist/services/agentTeams/mailbox.js.map +7 -0
  204. package/dist/services/agentTeams/teamManager.js +149 -0
  205. package/dist/services/agentTeams/teamManager.js.map +7 -0
  206. package/dist/services/agentTeams/teamTaskStore.js +114 -0
  207. package/dist/services/agentTeams/teamTaskStore.js.map +7 -0
  208. package/dist/services/agentTeams/teammateSpawner.js +80 -0
  209. package/dist/services/agentTeams/teammateSpawner.js.map +7 -0
  210. package/dist/services/checkpointManager.js +16 -3
  211. package/dist/services/checkpointManager.js.map +2 -2
  212. package/dist/services/claude.js +19 -1728
  213. package/dist/services/claude.js.map +3 -3
  214. package/dist/services/customCommands.js +30 -8
  215. package/dist/services/customCommands.js.map +2 -2
  216. package/dist/services/gpt5ConnectionTest.js +4 -2
  217. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  218. package/dist/services/hookExecutor.js +411 -127
  219. package/dist/services/hookExecutor.js.map +2 -2
  220. package/dist/services/llm/anthropicProvider.js +807 -0
  221. package/dist/services/llm/anthropicProvider.js.map +7 -0
  222. package/dist/services/llm/dispatch.js +218 -0
  223. package/dist/services/llm/dispatch.js.map +7 -0
  224. package/dist/services/llm/index.js +44 -0
  225. package/dist/services/llm/index.js.map +7 -0
  226. package/dist/services/llm/mintoContext.js +69 -0
  227. package/dist/services/llm/mintoContext.js.map +7 -0
  228. package/dist/services/llm/openaiProvider.js +622 -0
  229. package/dist/services/llm/openaiProvider.js.map +7 -0
  230. package/dist/services/llm/types.js +157 -0
  231. package/dist/services/llm/types.js.map +7 -0
  232. package/dist/services/mcpClient.js +183 -33
  233. package/dist/services/mcpClient.js.map +2 -2
  234. package/dist/services/notifier.js +14 -0
  235. package/dist/services/notifier.js.map +2 -2
  236. package/dist/services/oauth.js +4 -2
  237. package/dist/services/oauth.js.map +2 -2
  238. package/dist/services/openai.js +66 -56
  239. package/dist/services/openai.js.map +3 -3
  240. package/dist/services/outputStyles.js +102 -21
  241. package/dist/services/outputStyles.js.map +2 -2
  242. package/dist/services/plugins/lspServers.js +1 -1
  243. package/dist/services/plugins/lspServers.js.map +2 -2
  244. package/dist/services/plugins/pluginRuntime.js +2 -1
  245. package/dist/services/plugins/pluginRuntime.js.map +2 -2
  246. package/dist/services/plugins/pluginValidation.js +10 -3
  247. package/dist/services/plugins/pluginValidation.js.map +2 -2
  248. package/dist/services/plugins/skillMarketplace.js +20 -9
  249. package/dist/services/plugins/skillMarketplace.js.map +2 -2
  250. package/dist/services/sentry.js +1 -1
  251. package/dist/services/sentry.js.map +2 -2
  252. package/dist/services/sessionMemory.js +16 -3
  253. package/dist/services/sessionMemory.js.map +2 -2
  254. package/dist/services/systemReminder.js +367 -9
  255. package/dist/services/systemReminder.js.map +2 -2
  256. package/dist/services/taskStore.js +19 -0
  257. package/dist/services/taskStore.js.map +2 -2
  258. package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
  259. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +1 -1
  260. package/dist/tools/BashOutputTool/BashOutputTool.js.map +1 -1
  261. package/dist/tools/BashTool/BashTool.js +28 -0
  262. package/dist/tools/BashTool/BashTool.js.map +2 -2
  263. package/dist/tools/FileEditTool/FileEditTool.js +8 -1
  264. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  265. package/dist/tools/FileReadTool/FileReadTool.js +14 -0
  266. package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
  267. package/dist/tools/FileWriteTool/FileWriteTool.js +10 -1
  268. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  269. package/dist/tools/GlobTool/GlobTool.js.map +1 -1
  270. package/dist/tools/GrepTool/GrepTool.js.map +1 -1
  271. package/dist/tools/KillShellTool/KillShellTool.js.map +1 -1
  272. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
  273. package/dist/tools/LspTool/LspTool.js +11 -2
  274. package/dist/tools/LspTool/LspTool.js.map +2 -2
  275. package/dist/tools/MCPTool/MCPTool.js.map +1 -1
  276. package/dist/tools/MemoryReadTool/MemoryReadTool.js +2 -1
  277. package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
  278. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +2 -1
  279. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
  280. package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
  281. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  282. package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
  283. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  284. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +1 -1
  285. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +8 -2
  286. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  287. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +2 -0
  288. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  289. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +1 -1
  290. package/dist/tools/SlashCommandTool/SlashCommandTool.js +174 -18
  291. package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +3 -3
  292. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +1 -1
  293. package/dist/tools/TaskGetTool/TaskGetTool.js.map +1 -1
  294. package/dist/tools/TaskListTool/TaskListTool.js.map +1 -1
  295. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +1 -1
  296. package/dist/tools/TaskStopTool/TaskStopTool.js.map +1 -1
  297. package/dist/tools/TaskTool/TaskTool.js +84 -11
  298. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  299. package/dist/tools/TaskTool/prompt.js +12 -6
  300. package/dist/tools/TaskTool/prompt.js.map +2 -2
  301. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +1 -1
  302. package/dist/tools/ThinkTool/ThinkTool.js.map +1 -1
  303. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +1 -1
  304. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +1 -1
  305. package/dist/tools/WebSearchTool/WebSearchTool.js.map +1 -1
  306. package/dist/tools/WebSearchTool/searchProviders.js +2 -1
  307. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  308. package/dist/tools/lsTool/lsTool.js.map +2 -2
  309. package/dist/tools/lsTool/prompt.js.map +1 -1
  310. package/dist/tools.js +14 -3
  311. package/dist/tools.js.map +2 -2
  312. package/dist/types/PermissionMode.js +21 -1
  313. package/dist/types/PermissionMode.js.map +2 -2
  314. package/dist/types/agentTeams.js +1 -0
  315. package/dist/types/agentTeams.js.map +7 -0
  316. package/dist/types/hooks.js +8 -2
  317. package/dist/types/hooks.js.map +2 -2
  318. package/dist/types/plugin.js +3 -5
  319. package/dist/types/plugin.js.map +2 -2
  320. package/dist/utils/agentHookExecutor.js +1 -4
  321. package/dist/utils/agentHookExecutor.js.map +2 -2
  322. package/dist/utils/agentLoader.js +91 -15
  323. package/dist/utils/agentLoader.js.map +2 -2
  324. package/dist/utils/agentMemory.js.map +2 -2
  325. package/dist/utils/animationManager.js +1 -1
  326. package/dist/utils/animationManager.js.map +2 -2
  327. package/dist/utils/ask.js +1 -1
  328. package/dist/utils/async.js +5 -1
  329. package/dist/utils/async.js.map +2 -2
  330. package/dist/utils/autoCompactCore.js +60 -0
  331. package/dist/utils/autoCompactCore.js.map +2 -2
  332. package/dist/utils/claudeCodeSync.js +439 -0
  333. package/dist/utils/claudeCodeSync.js.map +7 -0
  334. package/dist/utils/config.js +27 -151
  335. package/dist/utils/config.js.map +2 -2
  336. package/dist/utils/configSchema.js +227 -0
  337. package/dist/utils/configSchema.js.map +7 -0
  338. package/dist/utils/debugLogger.js.map +2 -2
  339. package/dist/utils/env.js +4 -3
  340. package/dist/utils/env.js.map +2 -2
  341. package/dist/utils/envConfig.js +34 -0
  342. package/dist/utils/envConfig.js.map +3 -3
  343. package/dist/utils/execFileNoThrow.js +2 -1
  344. package/dist/utils/execFileNoThrow.js.map +2 -2
  345. package/dist/utils/gpt5.js +146 -0
  346. package/dist/utils/gpt5.js.map +7 -0
  347. package/dist/utils/hookManager.js +374 -140
  348. package/dist/utils/hookManager.js.map +2 -2
  349. package/dist/utils/markdown.js +47 -0
  350. package/dist/utils/markdown.js.map +2 -2
  351. package/dist/utils/marketplaceManager.js +80 -43
  352. package/dist/utils/marketplaceManager.js.map +2 -2
  353. package/dist/utils/memoizeWithTTL.js +25 -0
  354. package/dist/utils/memoizeWithTTL.js.map +7 -0
  355. package/dist/utils/messages.js +2 -2
  356. package/dist/utils/messages.js.map +2 -2
  357. package/dist/utils/model.js +34 -9
  358. package/dist/utils/model.js.map +2 -2
  359. package/dist/utils/pluginInstaller.js +68 -29
  360. package/dist/utils/pluginInstaller.js.map +2 -2
  361. package/dist/utils/pluginLoader.js +249 -57
  362. package/dist/utils/pluginLoader.js.map +2 -2
  363. package/dist/utils/repoFetcher.js +110 -0
  364. package/dist/utils/repoFetcher.js.map +7 -0
  365. package/dist/utils/safeFetch.js +45 -0
  366. package/dist/utils/safeFetch.js.map +7 -0
  367. package/dist/utils/skillLoader.js +77 -12
  368. package/dist/utils/skillLoader.js.map +2 -2
  369. package/dist/utils/streamingState.js +52 -0
  370. package/dist/utils/streamingState.js.map +7 -0
  371. package/dist/utils/stringSubstitution.js +4 -5
  372. package/dist/utils/stringSubstitution.js.map +2 -2
  373. package/dist/utils/style.js +6 -3
  374. package/dist/utils/style.js.map +2 -2
  375. package/dist/utils/teamConfig.js +162 -16
  376. package/dist/utils/teamConfig.js.map +2 -2
  377. package/dist/utils/terminal.js +1 -1
  378. package/dist/utils/terminal.js.map +2 -2
  379. package/dist/utils/toolRiskClassification.js +0 -6
  380. package/dist/utils/toolRiskClassification.js.map +2 -2
  381. package/dist/version.js +2 -2
  382. package/dist/version.js.map +1 -1
  383. package/package.json +7 -6
@@ -56,7 +56,25 @@ function SubagentTodosSection({
56
56
  ))));
57
57
  }
58
58
  function extractSubagentTodos(subagent) {
59
- return null;
59
+ let latestTodos = null;
60
+ for (const msg of subagent.messages) {
61
+ if (msg.type === "assistant" && "message" in msg) {
62
+ const todoBlocks = msg.message.content.filter(
63
+ (block) => block.type === "tool_use" && block.name === "TodoWrite"
64
+ );
65
+ for (const block of todoBlocks) {
66
+ if ("input" in block && block.input?.todos) {
67
+ latestTodos = block.input.todos.map((t, i) => ({
68
+ id: t.id ?? String(i),
69
+ content: t.content ?? "",
70
+ status: t.status ?? "pending",
71
+ priority: t.priority ?? "medium"
72
+ }));
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return latestTodos;
60
78
  }
61
79
  function extractThinkingMessage(subagent) {
62
80
  for (const msg of subagent.messages) {
@@ -76,6 +94,20 @@ function extractThinkingMessage(subagent) {
76
94
  }
77
95
  function extractToolExecutions(subagent) {
78
96
  const tools = [];
97
+ const toolResultTimes = /* @__PURE__ */ new Map();
98
+ let msgIndex = 0;
99
+ for (const msg of subagent.messages) {
100
+ if (msg.type === "user" && "message" in msg) {
101
+ const resultBlocks = (Array.isArray(msg.message.content) ? msg.message.content : []).filter((block) => block.type === "tool_result");
102
+ for (const rb of resultBlocks) {
103
+ if ("tool_use_id" in rb) {
104
+ toolResultTimes.set(rb.tool_use_id, msgIndex);
105
+ }
106
+ }
107
+ }
108
+ msgIndex++;
109
+ }
110
+ let toolIndex = 0;
79
111
  for (const msg of subagent.messages) {
80
112
  if (msg.type === "assistant" && "message" in msg) {
81
113
  const toolBlocks = msg.message.content.filter(
@@ -83,16 +115,21 @@ function extractToolExecutions(subagent) {
83
115
  );
84
116
  for (const toolBlock of toolBlocks) {
85
117
  if ("name" in toolBlock && "id" in toolBlock) {
118
+ const totalDuration = (subagent.metrics.endTime || Date.now()) - subagent.metrics.startTime;
119
+ const toolCount = subagent.metrics.toolUseCount || tools.length + 1;
120
+ const estimatedDuration = Math.max(
121
+ 1,
122
+ Math.round(totalDuration / toolCount)
123
+ );
86
124
  tools.push({
87
125
  id: toolBlock.id,
88
126
  name: toolBlock.name,
89
127
  input: toolBlock.input,
90
- status: "completed",
91
- // Simplified for now
92
- startTime: subagent.metrics.startTime,
93
- durationMs: 100
94
- // Placeholder
128
+ status: toolResultTimes.has(toolBlock.id) ? "completed" : "running",
129
+ startTime: subagent.metrics.startTime + toolIndex * estimatedDuration,
130
+ durationMs: estimatedDuration
95
131
  });
132
+ toolIndex++;
96
133
  }
97
134
  }
98
135
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/SubagentBlock.tsx"],
4
- "sourcesContent": ["/**\n * SubagentBlock - Embedded subagent execution display\n *\n * Renders a complete subagent execution hierarchically with:\n * - Header (task name, agent type, status)\n * - Optional: Subtasks (if subagent has todos)\n * - Optional: Thinking phase\n * - Optional: Tool executions\n * - Optional: Response\n * - Footer (completion status)\n *\n * Supports collapsible internal content\n */\n\nimport React, { useState } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { SYMBOLS, getTodoStatusSymbol } from '@constants/symbols'\nimport { AgentThinkingBlock } from './AgentThinkingBlock'\nimport { ToolExecutionBlock } from './ToolExecutionBlock'\nimport { AgentResponseBlock } from './AgentResponseBlock'\nimport { useAgentTokenStats, formatTokenCount } from '@hooks/useAgentTokenStats'\nimport type { SubagentState } from '@minto-types/subagent'\nimport type { TodoItem } from '@utils/todoStorage'\nimport type { AssistantMessage } from '@minto-types/conversation'\nimport type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\n\ninterface Props {\n subagent: SubagentState\n indent?: number\n isExpanded?: boolean\n}\n\n/**\n * SubagentBlock - Hierarchical embedded subagent display\n *\n * Visual format (indent=1):\n * ```\n * \uD83E\uDD16 Launching subagent: backend-architect\n * \u251C\u2500 Task: Design authentication system\n * \u2502\n * \u2502 \uD83E\uDD14 Analyzing requirements... (300ms)\n * \u2502\n * \u2502 \uD83D\uDD27 Tool: Read package.json\n * \u2502 \u2514\u2500 \u2713 Read 50 lines (80ms)\n * \u2502\n * \u2502 \uD83D\uDCAC I recommend using JWT...\n * \u2502\n * \u2514\u2500 \u2713 Completed (2.5s, 1200 tokens)\n * ```\n */\nexport function SubagentBlock({\n subagent,\n indent = 0,\n isExpanded = false,\n}: Props): React.ReactNode {\n const theme = getTheme()\n const [expanded, setExpanded] = useState(isExpanded)\n const marginLeft = indent * 2\n\n // Get real-time token stats from unified TokenStatsManager\n const tokenStats = useAgentTokenStats(subagent.id)\n\n // Extract todos if present (from subagent execution)\n const todos = extractSubagentTodos(subagent)\n const thinking = extractThinkingMessage(subagent)\n const toolExecutions = extractToolExecutions(subagent)\n const response = extractResponse(subagent)\n\n // Calculate metrics\n const duration = subagent.metrics.endTime\n ? ((subagent.metrics.endTime - subagent.metrics.startTime) / 1000).toFixed(\n 1,\n )\n : '...'\n // Use token stats from TokenStatsManager (single source of truth)\n const tokenCount = tokenStats?.grandTotalTokens ?? 0\n\n // Status indicator\n const statusIndicator = getStatusIndicator(subagent.status)\n const statusColor = getStatusColor(subagent.status, theme)\n\n return (\n <Box flexDirection=\"column\" marginLeft={marginLeft} marginTop={1}>\n {/* Subagent Header */}\n <Box flexDirection=\"row\">\n <Text color={theme.brand}>\uD83E\uDD16 </Text>\n <Text>\n Launching subagent: <Text bold>{subagent.agentType}</Text>\n </Text>\n </Box>\n\n {/* Task description */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u251C\u2500 </Text>\n <Text>Task: {subagent.taskName}</Text>\n </Box>\n\n {/* Collapsible internal content */}\n {expanded && (\n <Box flexDirection=\"column\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2502</Text>\n\n {/* Subagent subtasks (if present) */}\n {todos && todos.length > 0 && (\n <SubagentTodosSection todos={todos} theme={theme} />\n )}\n\n {/* Subagent thinking */}\n {thinking && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentThinkingBlock\n message={thinking.message}\n startTime={thinking.startTime}\n showSpinner={subagent.status === 'running'}\n showElapsedTime={false}\n />\n </Box>\n )}\n\n {/* Subagent tool executions */}\n {toolExecutions.map((tool, idx) => (\n <Box key={idx} flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <ToolExecutionBlock tool={tool} indent={0} />\n </Box>\n ))}\n\n {/* Subagent response */}\n {response && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentResponseBlock\n message={response}\n indent={0}\n isSubagent={true}\n />\n </Box>\n )}\n\n <Text color={theme.dimmedText}>\u2502</Text>\n </Box>\n )}\n\n {/* Subagent Footer (completion status) */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2514\u2500 </Text>\n <Text color={statusColor}>\n {statusIndicator} {getStatusLabel(subagent.status)}\n </Text>\n {subagent.status === 'completed' && (\n <Text color={theme.mutedText}>\n {' '}\n ({duration}s, {formatTokenCount(tokenCount)} tokens)\n </Text>\n )}\n </Box>\n\n {/* Expand/collapse hint (only when collapsed) */}\n {!expanded && (\n <Box flexDirection=\"row\" marginLeft={4}>\n <Text color={theme.mutedText}>\n Press <Text bold>Ctrl+O</Text> to expand details\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * SubagentTodosSection - Renders subagent's subtasks\n */\nfunction SubagentTodosSection({\n todos,\n theme,\n}: {\n todos: TodoItem[]\n theme: ReturnType<typeof getTheme>\n}) {\n return (\n <Box flexDirection=\"column\" marginLeft={1} marginY={1}>\n <Box flexDirection=\"row\">\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text color={theme.dimmedText}>Subtasks:</Text>\n </Box>\n {todos.map((todo, idx) => (\n <Box key={idx} flexDirection=\"row\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text>{getTodoStatusSymbol(todo.status)} </Text>\n <Text\n color={todo.status === 'completed' ? theme.dim : undefined}\n strikethrough={todo.status === 'completed'}\n >\n {todo.content}\n </Text>\n </Box>\n ))}\n </Box>\n )\n}\n\n/**\n * Helper: Extract todos from subagent messages\n * (This would be populated from TodoWriteTool calls within the subagent)\n */\nfunction extractSubagentTodos(subagent: SubagentState): TodoItem[] | null {\n // TODO: Implement extraction of todos from subagent messages\n // For now, return null (will be implemented in Phase 4)\n return null\n}\n\n/**\n * Helper: Extract thinking message from subagent\n */\nfunction extractThinkingMessage(\n subagent: SubagentState,\n): { message: string; startTime: number } | null {\n // Find thinking blocks in messages\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const thinkingBlock = msg.message.content.find(\n (block: any) => block.type === 'thinking',\n )\n if (thinkingBlock && 'thinking' in thinkingBlock) {\n return {\n message: thinkingBlock.thinking || 'Thinking...',\n startTime: subagent.metrics.startTime,\n }\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Extract tool executions from subagent\n */\nfunction extractToolExecutions(subagent: SubagentState): Array<{\n id: string\n name: string\n input: any\n status: 'pending' | 'running' | 'completed' | 'error'\n startTime: number\n durationMs?: number\n}> {\n const tools: Array<any> = []\n\n // Extract tool_use blocks from messages\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const toolBlocks = msg.message.content.filter(\n (block: any) => block.type === 'tool_use',\n )\n for (const toolBlock of toolBlocks) {\n if ('name' in toolBlock && 'id' in toolBlock) {\n tools.push({\n id: toolBlock.id,\n name: toolBlock.name,\n input: toolBlock.input,\n status: 'completed', // Simplified for now\n startTime: subagent.metrics.startTime,\n durationMs: 100, // Placeholder\n })\n }\n }\n }\n }\n\n return tools\n}\n\n/**\n * Helper: Extract response message from subagent\n */\nfunction extractResponse(subagent: SubagentState): AssistantMessage | null {\n // Find last assistant message with text content\n for (let i = subagent.messages.length - 1; i >= 0; i--) {\n const msg = subagent.messages[i]\n if (msg.type === 'assistant' && 'message' in msg) {\n const hasText = msg.message.content.some(\n (block: any) => block.type === 'text' && block.text.trim(),\n )\n if (hasText) {\n return msg as AssistantMessage\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Get status indicator symbol\n */\nfunction getStatusIndicator(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return '\u25CB'\n case 'queued':\n return '\u2139'\n case 'running':\n return '\u22EF'\n case 'completed':\n return '\u2713'\n case 'error':\n return '\u2716'\n }\n}\n\n/**\n * Helper: Get status color\n */\nfunction getStatusColor(\n status: SubagentState['status'],\n theme: ReturnType<typeof getTheme>,\n): string {\n switch (status) {\n case 'completed':\n return theme.success\n case 'error':\n return theme.error\n case 'running':\n return theme.brand\n default:\n return theme.dimmedText\n }\n}\n\n/**\n * Helper: Get status label\n */\nfunction getStatusLabel(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return 'Initializing...'\n case 'queued':\n return 'Queued'\n case 'running':\n return 'Running...'\n case 'completed':\n return 'Done'\n case 'error':\n return 'Error'\n }\n}\n"],
5
- "mappings": "AAcA,OAAO,SAAS,gBAAgB;AAChC,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB;AACzB,SAAkB,2BAA2B;AAC7C,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB,wBAAwB;AA8B9C,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,SAAS;AAAA,EACT,aAAa;AACf,GAA2B;AACzB,QAAM,QAAQ,SAAS;AACvB,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,UAAU;AACnD,QAAM,aAAa,SAAS;AAG5B,QAAM,aAAa,mBAAmB,SAAS,EAAE;AAGjD,QAAM,QAAQ,qBAAqB,QAAQ;AAC3C,QAAM,WAAW,uBAAuB,QAAQ;AAChD,QAAM,iBAAiB,sBAAsB,QAAQ;AACrD,QAAM,WAAW,gBAAgB,QAAQ;AAGzC,QAAM,WAAW,SAAS,QAAQ,YAC5B,SAAS,QAAQ,UAAU,SAAS,QAAQ,aAAa,KAAM;AAAA,IAC/D;AAAA,EACF,IACA;AAEJ,QAAM,aAAa,YAAY,oBAAoB;AAGnD,QAAM,kBAAkB,mBAAmB,SAAS,MAAM;AAC1D,QAAM,cAAc,eAAe,SAAS,QAAQ,KAAK;AAEzD,SACE,oCAAC,OAAI,eAAc,UAAS,YAAwB,WAAW,KAE7D,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAO,MAAM,SAAO,YAAG,GAC7B,oCAAC,YAAK,wBACgB,oCAAC,QAAK,MAAI,QAAE,SAAS,SAAU,CACrD,CACF,GAGA,oCAAC,OAAI,eAAc,OAAM,YAAY,KACnC,oCAAC,QAAK,OAAO,MAAM,cAAY,eAAG,GAClC,oCAAC,YAAK,UAAO,SAAS,QAAS,CACjC,GAGC,YACC,oCAAC,OAAI,eAAc,UAAS,YAAY,KACtC,oCAAC,QAAK,OAAO,MAAM,cAAY,QAAC,GAG/B,SAAS,MAAM,SAAS,KACvB,oCAAC,wBAAqB,OAAc,OAAc,GAInD,YACC,oCAAC,OAAI,eAAc,UAAS,YAAY,KACtC,oCAAC,QAAK,OAAO,MAAM,cAAY,SAAE,GACjC;AAAA,IAAC;AAAA;AAAA,MACC,SAAS,SAAS;AAAA,MAClB,WAAW,SAAS;AAAA,MACpB,aAAa,SAAS,WAAW;AAAA,MACjC,iBAAiB;AAAA;AAAA,EACnB,CACF,GAID,eAAe,IAAI,CAAC,MAAM,QACzB,oCAAC,OAAI,KAAK,KAAK,eAAc,UAAS,YAAY,KAChD,oCAAC,QAAK,OAAO,MAAM,cAAY,SAAE,GACjC,oCAAC,sBAAmB,MAAY,QAAQ,GAAG,CAC7C,CACD,GAGA,YACC,oCAAC,OAAI,eAAc,UAAS,YAAY,KACtC,oCAAC,QAAK,OAAO,MAAM,cAAY,SAAE,GACjC;AAAA,IAAC;AAAA;AAAA,MACC,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,YAAY;AAAA;AAAA,EACd,CACF,GAGF,oCAAC,QAAK,OAAO,MAAM,cAAY,QAAC,CAClC,GAIF,oCAAC,OAAI,eAAc,OAAM,YAAY,KACnC,oCAAC,QAAK,OAAO,MAAM,cAAY,eAAG,GAClC,oCAAC,QAAK,OAAO,eACV,iBAAgB,KAAE,eAAe,SAAS,MAAM,CACnD,GACC,SAAS,WAAW,eACnB,oCAAC,QAAK,OAAO,MAAM,aAChB,KAAI,KACH,UAAS,OAAI,iBAAiB,UAAU,GAAE,UAC9C,CAEJ,GAGC,CAAC,YACA,oCAAC,OAAI,eAAc,OAAM,YAAY,KACnC,oCAAC,QAAK,OAAO,MAAM,aAAW,UACtB,oCAAC,QAAK,MAAI,QAAC,QAAM,GAAO,oBAChC,CACF,CAEJ;AAEJ;AAKA,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA;AACF,GAGG;AACD,SACE,oCAAC,OAAI,eAAc,UAAS,YAAY,GAAG,SAAS,KAClD,oCAAC,OAAI,eAAc,SACjB,oCAAC,QAAK,OAAO,MAAM,cAAY,SAAE,GACjC,oCAAC,QAAK,OAAO,MAAM,cAAY,WAAS,CAC1C,GACC,MAAM,IAAI,CAAC,MAAM,QAChB,oCAAC,OAAI,KAAK,KAAK,eAAc,OAAM,YAAY,KAC7C,oCAAC,QAAK,OAAO,MAAM,cAAY,SAAE,GACjC,oCAAC,YAAM,oBAAoB,KAAK,MAAM,GAAE,GAAC,GACzC;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,KAAK,WAAW,cAAc,MAAM,MAAM;AAAA,MACjD,eAAe,KAAK,WAAW;AAAA;AAAA,IAE9B,KAAK;AAAA,EACR,CACF,CACD,CACH;AAEJ;AAMA,SAAS,qBAAqB,UAA4C;AAGxE,SAAO;AACT;AAKA,SAAS,uBACP,UAC+C;AAE/C,aAAW,OAAO,SAAS,UAAU;AACnC,QAAI,IAAI,SAAS,eAAe,aAAa,KAAK;AAChD,YAAM,gBAAgB,IAAI,QAAQ,QAAQ;AAAA,QACxC,CAAC,UAAe,MAAM,SAAS;AAAA,MACjC;AACA,UAAI,iBAAiB,cAAc,eAAe;AAChD,eAAO;AAAA,UACL,SAAS,cAAc,YAAY;AAAA,UACnC,WAAW,SAAS,QAAQ;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,sBAAsB,UAO5B;AACD,QAAM,QAAoB,CAAC;AAG3B,aAAW,OAAO,SAAS,UAAU;AACnC,QAAI,IAAI,SAAS,eAAe,aAAa,KAAK;AAChD,YAAM,aAAa,IAAI,QAAQ,QAAQ;AAAA,QACrC,CAAC,UAAe,MAAM,SAAS;AAAA,MACjC;AACA,iBAAW,aAAa,YAAY;AAClC,YAAI,UAAU,aAAa,QAAQ,WAAW;AAC5C,gBAAM,KAAK;AAAA,YACT,IAAI,UAAU;AAAA,YACd,MAAM,UAAU;AAAA,YAChB,OAAO,UAAU;AAAA,YACjB,QAAQ;AAAA;AAAA,YACR,WAAW,SAAS,QAAQ;AAAA,YAC5B,YAAY;AAAA;AAAA,UACd,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,gBAAgB,UAAkD;AAEzE,WAAS,IAAI,SAAS,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AACtD,UAAM,MAAM,SAAS,SAAS,CAAC;AAC/B,QAAI,IAAI,SAAS,eAAe,aAAa,KAAK;AAChD,YAAM,UAAU,IAAI,QAAQ,QAAQ;AAAA,QAClC,CAAC,UAAe,MAAM,SAAS,UAAU,MAAM,KAAK,KAAK;AAAA,MAC3D;AACA,UAAI,SAAS;AACX,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,mBAAmB,QAAyC;AACnE,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAKA,SAAS,eACP,QACA,OACQ;AACR,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf;AACE,aAAO,MAAM;AAAA,EACjB;AACF;AAKA,SAAS,eAAe,QAAyC;AAC/D,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;",
4
+ "sourcesContent": ["/**\n * SubagentBlock - Embedded subagent execution display\n *\n * Renders a complete subagent execution hierarchically with:\n * - Header (task name, agent type, status)\n * - Optional: Subtasks (if subagent has todos)\n * - Optional: Thinking phase\n * - Optional: Tool executions\n * - Optional: Response\n * - Footer (completion status)\n *\n * Supports collapsible internal content\n */\n\nimport React, { useState } from 'react'\nimport { Box, Text } from 'ink'\nimport { getTheme } from '@utils/theme'\nimport { SYMBOLS, getTodoStatusSymbol } from '@constants/symbols'\nimport { AgentThinkingBlock } from './AgentThinkingBlock'\nimport { ToolExecutionBlock } from './ToolExecutionBlock'\nimport { AgentResponseBlock } from './AgentResponseBlock'\nimport { useAgentTokenStats, formatTokenCount } from '@hooks/useAgentTokenStats'\nimport type { SubagentState } from '@minto-types/subagent'\nimport type { TodoItem } from '@utils/todoStorage'\nimport type { AssistantMessage } from '@minto-types/conversation'\nimport type { TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\n\ninterface Props {\n subagent: SubagentState\n indent?: number\n isExpanded?: boolean\n}\n\n/**\n * SubagentBlock - Hierarchical embedded subagent display\n *\n * Visual format (indent=1):\n * ```\n * \uD83E\uDD16 Launching subagent: backend-architect\n * \u251C\u2500 Task: Design authentication system\n * \u2502\n * \u2502 \uD83E\uDD14 Analyzing requirements... (300ms)\n * \u2502\n * \u2502 \uD83D\uDD27 Tool: Read package.json\n * \u2502 \u2514\u2500 \u2713 Read 50 lines (80ms)\n * \u2502\n * \u2502 \uD83D\uDCAC I recommend using JWT...\n * \u2502\n * \u2514\u2500 \u2713 Completed (2.5s, 1200 tokens)\n * ```\n */\nexport function SubagentBlock({\n subagent,\n indent = 0,\n isExpanded = false,\n}: Props): React.ReactNode {\n const theme = getTheme()\n const [expanded, setExpanded] = useState(isExpanded)\n const marginLeft = indent * 2\n\n // Get real-time token stats from unified TokenStatsManager\n const tokenStats = useAgentTokenStats(subagent.id)\n\n // Extract todos if present (from subagent execution)\n const todos = extractSubagentTodos(subagent)\n const thinking = extractThinkingMessage(subagent)\n const toolExecutions = extractToolExecutions(subagent)\n const response = extractResponse(subagent)\n\n // Calculate metrics\n const duration = subagent.metrics.endTime\n ? ((subagent.metrics.endTime - subagent.metrics.startTime) / 1000).toFixed(\n 1,\n )\n : '...'\n // Use token stats from TokenStatsManager (single source of truth)\n const tokenCount = tokenStats?.grandTotalTokens ?? 0\n\n // Status indicator\n const statusIndicator = getStatusIndicator(subagent.status)\n const statusColor = getStatusColor(subagent.status, theme)\n\n return (\n <Box flexDirection=\"column\" marginLeft={marginLeft} marginTop={1}>\n {/* Subagent Header */}\n <Box flexDirection=\"row\">\n <Text color={theme.brand}>\uD83E\uDD16 </Text>\n <Text>\n Launching subagent: <Text bold>{subagent.agentType}</Text>\n </Text>\n </Box>\n\n {/* Task description */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u251C\u2500 </Text>\n <Text>Task: {subagent.taskName}</Text>\n </Box>\n\n {/* Collapsible internal content */}\n {expanded && (\n <Box flexDirection=\"column\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2502</Text>\n\n {/* Subagent subtasks (if present) */}\n {todos && todos.length > 0 && (\n <SubagentTodosSection todos={todos} theme={theme} />\n )}\n\n {/* Subagent thinking */}\n {thinking && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentThinkingBlock\n message={thinking.message}\n startTime={thinking.startTime}\n showSpinner={subagent.status === 'running'}\n showElapsedTime={false}\n />\n </Box>\n )}\n\n {/* Subagent tool executions */}\n {toolExecutions.map((tool, idx) => (\n <Box key={idx} flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <ToolExecutionBlock tool={tool} indent={0} />\n </Box>\n ))}\n\n {/* Subagent response */}\n {response && (\n <Box flexDirection=\"column\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <AgentResponseBlock\n message={response}\n indent={0}\n isSubagent={true}\n />\n </Box>\n )}\n\n <Text color={theme.dimmedText}>\u2502</Text>\n </Box>\n )}\n\n {/* Subagent Footer (completion status) */}\n <Box flexDirection=\"row\" marginLeft={2}>\n <Text color={theme.dimmedText}>\u2514\u2500 </Text>\n <Text color={statusColor}>\n {statusIndicator} {getStatusLabel(subagent.status)}\n </Text>\n {subagent.status === 'completed' && (\n <Text color={theme.mutedText}>\n {' '}\n ({duration}s, {formatTokenCount(tokenCount)} tokens)\n </Text>\n )}\n </Box>\n\n {/* Expand/collapse hint (only when collapsed) */}\n {!expanded && (\n <Box flexDirection=\"row\" marginLeft={4}>\n <Text color={theme.mutedText}>\n Press <Text bold>Ctrl+O</Text> to expand details\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n\n/**\n * SubagentTodosSection - Renders subagent's subtasks\n */\nfunction SubagentTodosSection({\n todos,\n theme,\n}: {\n todos: TodoItem[]\n theme: ReturnType<typeof getTheme>\n}) {\n return (\n <Box flexDirection=\"column\" marginLeft={1} marginY={1}>\n <Box flexDirection=\"row\">\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text color={theme.dimmedText}>Subtasks:</Text>\n </Box>\n {todos.map((todo, idx) => (\n <Box key={idx} flexDirection=\"row\" marginLeft={1}>\n <Text color={theme.dimmedText}>\u2502 </Text>\n <Text>{getTodoStatusSymbol(todo.status)} </Text>\n <Text\n color={todo.status === 'completed' ? theme.dim : undefined}\n strikethrough={todo.status === 'completed'}\n >\n {todo.content}\n </Text>\n </Box>\n ))}\n </Box>\n )\n}\n\n/**\n * Helper: Extract todos from subagent messages\n * (This would be populated from TodoWriteTool calls within the subagent)\n */\nfunction extractSubagentTodos(subagent: SubagentState): TodoItem[] | null {\n // Extract todos from TodoWrite tool_use blocks in subagent messages\n let latestTodos: TodoItem[] | null = null\n\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const todoBlocks = msg.message.content.filter(\n (block: any) => block.type === 'tool_use' && block.name === 'TodoWrite',\n )\n for (const block of todoBlocks) {\n if ('input' in block && block.input?.todos) {\n // Each TodoWrite call replaces the full list\n latestTodos = (block.input.todos as any[]).map((t, i) => ({\n id: t.id ?? String(i),\n content: t.content ?? '',\n status: t.status ?? 'pending',\n priority: t.priority ?? 'medium',\n }))\n }\n }\n }\n }\n\n return latestTodos\n}\n\n/**\n * Helper: Extract thinking message from subagent\n */\nfunction extractThinkingMessage(\n subagent: SubagentState,\n): { message: string; startTime: number } | null {\n // Find thinking blocks in messages\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const thinkingBlock = msg.message.content.find(\n (block: any) => block.type === 'thinking',\n )\n if (thinkingBlock && 'thinking' in thinkingBlock) {\n return {\n message: thinkingBlock.thinking || 'Thinking...',\n startTime: subagent.metrics.startTime,\n }\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Extract tool executions from subagent\n */\nfunction extractToolExecutions(subagent: SubagentState): Array<{\n id: string\n name: string\n input: any\n status: 'pending' | 'running' | 'completed' | 'error'\n startTime: number\n durationMs?: number\n}> {\n const tools: Array<any> = []\n\n // Extract tool_use blocks from messages, pair with tool_result for duration\n const toolResultTimes = new Map<string, number>()\n let msgIndex = 0\n\n for (const msg of subagent.messages) {\n if (msg.type === 'user' && 'message' in msg) {\n const resultBlocks = (\n Array.isArray(msg.message.content) ? msg.message.content : []\n ).filter((block: any) => block.type === 'tool_result')\n for (const rb of resultBlocks) {\n if ('tool_use_id' in rb) {\n toolResultTimes.set(rb.tool_use_id as string, msgIndex)\n }\n }\n }\n msgIndex++\n }\n\n let toolIndex = 0\n for (const msg of subagent.messages) {\n if (msg.type === 'assistant' && 'message' in msg) {\n const toolBlocks = msg.message.content.filter(\n (block: any) => block.type === 'tool_use',\n )\n for (const toolBlock of toolBlocks) {\n if ('name' in toolBlock && 'id' in toolBlock) {\n // Estimate duration: spread evenly across total subagent time\n const totalDuration =\n (subagent.metrics.endTime || Date.now()) -\n subagent.metrics.startTime\n const toolCount = subagent.metrics.toolUseCount || tools.length + 1\n const estimatedDuration = Math.max(\n 1,\n Math.round(totalDuration / toolCount),\n )\n\n tools.push({\n id: toolBlock.id,\n name: toolBlock.name,\n input: toolBlock.input,\n status: toolResultTimes.has(toolBlock.id) ? 'completed' : 'running',\n startTime:\n subagent.metrics.startTime + toolIndex * estimatedDuration,\n durationMs: estimatedDuration,\n })\n toolIndex++\n }\n }\n }\n }\n\n return tools\n}\n\n/**\n * Helper: Extract response message from subagent\n */\nfunction extractResponse(subagent: SubagentState): AssistantMessage | null {\n // Find last assistant message with text content\n for (let i = subagent.messages.length - 1; i >= 0; i--) {\n const msg = subagent.messages[i]\n if (msg.type === 'assistant' && 'message' in msg) {\n const hasText = msg.message.content.some(\n (block: any) => block.type === 'text' && block.text.trim(),\n )\n if (hasText) {\n return msg as AssistantMessage\n }\n }\n }\n return null\n}\n\n/**\n * Helper: Get status indicator symbol\n */\nfunction getStatusIndicator(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return '\u25CB'\n case 'queued':\n return '\u2139'\n case 'running':\n return '\u22EF'\n case 'completed':\n return '\u2713'\n case 'error':\n return '\u2716'\n }\n}\n\n/**\n * Helper: Get status color\n */\nfunction getStatusColor(\n status: SubagentState['status'],\n theme: ReturnType<typeof getTheme>,\n): string {\n switch (status) {\n case 'completed':\n return theme.success\n case 'error':\n return theme.error\n case 'running':\n return theme.brand\n default:\n return theme.dimmedText\n }\n}\n\n/**\n * Helper: Get status label\n */\nfunction getStatusLabel(status: SubagentState['status']): string {\n switch (status) {\n case 'initializing':\n return 'Initializing...'\n case 'queued':\n return 'Queued'\n case 'running':\n return 'Running...'\n case 'completed':\n return 'Done'\n case 'error':\n return 'Error'\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -17,11 +17,26 @@ const getStatusIcon = (status) => {
17
17
  };
18
18
  function DefaultItemRenderer({
19
19
  item,
20
- isFocused
20
+ isFocused,
21
+ multiSelect,
22
+ isSelected
21
23
  }) {
22
24
  const statusInfo = getStatusIcon(item.status);
23
- const textColor = isFocused ? BRAND_GRADIENT.START : SEMANTIC_COLORS.dim;
24
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: isFocused ? BRAND_GRADIENT.START : void 0 }, isFocused ? "\u25C6 " : " "), statusInfo && /* @__PURE__ */ React.createElement(Text, { color: statusInfo.color }, statusInfo.icon, " "), /* @__PURE__ */ React.createElement(Text, { color: textColor }, item.label, item.description && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, " ", item.description), item.metadata && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, " \xB7 ", item.metadata)));
25
+ const checkboxWidth = multiSelect ? 2 : 0;
26
+ const focusWidth = 2;
27
+ const statusWidth = statusInfo ? 2 : 0;
28
+ const prefixPad = checkboxWidth + focusWidth + statusWidth;
29
+ const labelColor = isFocused ? BRAND_GRADIENT.START : SEMANTIC_COLORS.secondary;
30
+ const descColor = SEMANTIC_COLORS.dim;
31
+ const metaColor = SEMANTIC_COLORS.muted;
32
+ const hasDescription = !!item.description;
33
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, multiSelect && /* @__PURE__ */ React.createElement(
34
+ Text,
35
+ {
36
+ color: isSelected ? SEMANTIC_COLORS.success : SEMANTIC_COLORS.dim
37
+ },
38
+ isSelected ? "\u2611 " : "\u2610 "
39
+ ), /* @__PURE__ */ React.createElement(Text, { color: isFocused ? BRAND_GRADIENT.START : void 0 }, isFocused ? "\u25C6 " : " "), statusInfo && /* @__PURE__ */ React.createElement(Text, { color: statusInfo.color }, statusInfo.icon, " "), /* @__PURE__ */ React.createElement(Text, { bold: isFocused, color: labelColor }, item.label), item.metadata && /* @__PURE__ */ React.createElement(Text, { color: metaColor }, " \xB7 ", item.metadata)), hasDescription && /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: descColor }, " ".repeat(prefixPad), item.description)));
25
40
  }
26
41
  function ScrollableList({
27
42
  items,
@@ -30,7 +45,9 @@ function ScrollableList({
30
45
  onSelect,
31
46
  visibleCount,
32
47
  groupByCategory = false,
33
- renderItem
48
+ renderItem,
49
+ multiSelect,
50
+ selectedIds
34
51
  }) {
35
52
  const { visibleItems, startIndex, endIndex } = useMemo(() => {
36
53
  if (items.length === 0) {
@@ -80,7 +97,16 @@ function ScrollableList({
80
97
  );
81
98
  }
82
99
  const isFocused = globalIndex === focusedIndex;
83
- const itemElement = renderItem ? renderItem(item, isFocused) : /* @__PURE__ */ React.createElement(DefaultItemRenderer, { item, isFocused });
100
+ const isSelected = selectedIds?.has(item.id) ?? false;
101
+ const itemElement = renderItem ? renderItem(item, isFocused, isSelected) : /* @__PURE__ */ React.createElement(
102
+ DefaultItemRenderer,
103
+ {
104
+ item,
105
+ isFocused,
106
+ multiSelect,
107
+ isSelected
108
+ }
109
+ );
84
110
  displayElements.push(/* @__PURE__ */ React.createElement(Box, { key: item.id }, itemElement));
85
111
  });
86
112
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, hasMoreAbove && /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " \u2191 ", countAbove, " more above...")), displayElements, hasMoreBelow && /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " \u2193 ", countBelow, " more below...")));
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/TabbedListView/ScrollableList.tsx"],
4
- "sourcesContent": ["/**\n * ScrollableList Component\n *\n * A scrollable list matching the /command suggestion UI pattern.\n * Uses \u25C6 indicator for selection, brand colors, and scroll indicators.\n */\n\nimport React, { useMemo } from 'react'\nimport { Box, Text } from 'ink'\nimport { SEMANTIC_COLORS, BRAND_GRADIENT } from '@constants/colors'\nimport { t } from '@i18n'\nimport type { ListItem, ScrollableListProps, CategoryGroup } from './types'\n\n/** Status icon mapping */\nconst getStatusIcon = (status?: ListItem['status']) => {\n switch (status) {\n case 'enabled':\n return { icon: '\u2714', color: SEMANTIC_COLORS.success }\n case 'disabled':\n return { icon: '\u25CB', color: SEMANTIC_COLORS.dim }\n case 'running':\n return { icon: '\u25D0', color: SEMANTIC_COLORS.running }\n case 'error':\n return { icon: '\u2717', color: SEMANTIC_COLORS.error }\n default:\n return null\n }\n}\n\n/** Default item renderer - matches command suggestion style */\nfunction DefaultItemRenderer({\n item,\n isFocused,\n}: {\n item: ListItem\n isFocused: boolean\n}) {\n const statusInfo = getStatusIcon(item.status)\n\n // Use brand gradient color for selection, dim for others\n const textColor = isFocused ? BRAND_GRADIENT.START : SEMANTIC_COLORS.dim\n\n return (\n <Box flexDirection=\"row\">\n {/* Selection indicator: \u25C6 for selected, 2 spaces for unselected */}\n <Text color={isFocused ? BRAND_GRADIENT.START : undefined}>\n {isFocused ? '\u25C6 ' : ' '}\n </Text>\n\n {/* Status icon if present */}\n {statusInfo && <Text color={statusInfo.color}>{statusInfo.icon} </Text>}\n\n {/* Main content */}\n <Text color={textColor}>\n {item.label}\n {item.description && (\n <Text color={SEMANTIC_COLORS.muted}> {item.description}</Text>\n )}\n {item.metadata && (\n <Text color={SEMANTIC_COLORS.muted}> \u00B7 {item.metadata}</Text>\n )}\n </Text>\n </Box>\n )\n}\n\nexport function ScrollableList({\n items,\n focusedIndex,\n onFocusChange,\n onSelect,\n visibleCount,\n groupByCategory = false,\n renderItem,\n}: ScrollableListProps) {\n // Calculate scroll window to keep selected item visible (same logic as command suggestions)\n const { visibleItems, startIndex, endIndex } = useMemo(() => {\n if (items.length === 0) {\n return { visibleItems: [], startIndex: 0, endIndex: 0 }\n }\n\n // If all items fit in window, show them all\n if (items.length <= visibleCount) {\n return {\n visibleItems: items.map((item, i) => ({ item, globalIndex: i })),\n startIndex: 0,\n endIndex: items.length,\n }\n }\n\n // Calculate scroll window to keep focused item visible\n const halfWindow = Math.floor(visibleCount / 2)\n let start = Math.max(0, focusedIndex - halfWindow)\n let end = start + visibleCount\n\n // Adjust if we're near the end\n if (end > items.length) {\n end = items.length\n start = Math.max(0, end - visibleCount)\n }\n\n return {\n visibleItems: items.slice(start, end).map((item, i) => ({\n item,\n globalIndex: start + i,\n })),\n startIndex: start,\n endIndex: end,\n }\n }, [items, focusedIndex, visibleCount])\n\n // Scroll indicators\n const hasMoreAbove = startIndex > 0\n const hasMoreBelow = endIndex < items.length\n const countAbove = startIndex\n const countBelow = items.length - endIndex\n\n // Build display with optional category headers\n const displayElements: React.ReactNode[] = []\n let currentCategory = ''\n\n visibleItems.forEach(({ item, globalIndex }, visibleIndex) => {\n // Add category header if category changed and grouping is enabled\n if (groupByCategory && item.category !== currentCategory) {\n currentCategory = item.category || 'Other'\n displayElements.push(\n <Box\n key={`category-${currentCategory}-${globalIndex}`}\n marginTop={visibleIndex > 0 ? 1 : 0}\n >\n <Text bold color={SEMANTIC_COLORS.secondary}>\n {currentCategory}\n </Text>\n </Box>,\n )\n }\n\n const isFocused = globalIndex === focusedIndex\n const itemElement = renderItem ? (\n renderItem(item, isFocused)\n ) : (\n <DefaultItemRenderer item={item} isFocused={isFocused} />\n )\n\n displayElements.push(<Box key={item.id}>{itemElement}</Box>)\n })\n\n return (\n <Box flexDirection=\"column\">\n {/* Scroll up indicator */}\n {hasMoreAbove && (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}> \u2191 {countAbove} more above...</Text>\n </Box>\n )}\n\n {/* List items */}\n {displayElements}\n\n {/* Scroll down indicator */}\n {hasMoreBelow && (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}> \u2193 {countBelow} more below...</Text>\n </Box>\n )}\n </Box>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * ScrollableList Component\n *\n * A scrollable list matching the /command suggestion UI pattern.\n * Uses \u25C6 indicator for selection, brand colors, and scroll indicators.\n */\n\nimport React, { useMemo } from 'react'\nimport { Box, Text } from 'ink'\nimport { SEMANTIC_COLORS, BRAND_GRADIENT } from '@constants/colors'\nimport { t } from '@i18n'\nimport type { ListItem, ScrollableListProps, CategoryGroup } from './types'\n\n/** Status icon mapping */\nconst getStatusIcon = (status?: ListItem['status']) => {\n switch (status) {\n case 'enabled':\n return { icon: '\u2714', color: SEMANTIC_COLORS.success }\n case 'disabled':\n return { icon: '\u25CB', color: SEMANTIC_COLORS.dim }\n case 'running':\n return { icon: '\u25D0', color: SEMANTIC_COLORS.running }\n case 'error':\n return { icon: '\u2717', color: SEMANTIC_COLORS.error }\n default:\n return null\n }\n}\n\n/** Default item renderer - two-line layout with clear visual hierarchy */\nfunction DefaultItemRenderer({\n item,\n isFocused,\n multiSelect,\n isSelected,\n}: {\n item: ListItem\n isFocused: boolean\n multiSelect?: boolean\n isSelected?: boolean\n}) {\n const statusInfo = getStatusIcon(item.status)\n\n // Prefix width: checkbox \"\u2611 \" (2) + \"\u25C6 \" (2) + optional status icon \"\u2714 \" (2)\n const checkboxWidth = multiSelect ? 2 : 0\n const focusWidth = 2\n const statusWidth = statusInfo ? 2 : 0\n const prefixPad = checkboxWidth + focusWidth + statusWidth\n\n // Colors: focused items use brand gradient, unfocused use secondary/dim\n const labelColor = isFocused\n ? BRAND_GRADIENT.START\n : SEMANTIC_COLORS.secondary\n const descColor = SEMANTIC_COLORS.dim\n const metaColor = SEMANTIC_COLORS.muted\n\n const hasDescription = !!item.description\n\n return (\n <Box flexDirection=\"column\">\n {/* Line 1: [checkbox] + indicator + status + label + metadata */}\n <Box flexDirection=\"row\">\n {multiSelect && (\n <Text\n color={isSelected ? SEMANTIC_COLORS.success : SEMANTIC_COLORS.dim}\n >\n {isSelected ? '\u2611 ' : '\u2610 '}\n </Text>\n )}\n <Text color={isFocused ? BRAND_GRADIENT.START : undefined}>\n {isFocused ? '\u25C6 ' : ' '}\n </Text>\n {statusInfo && <Text color={statusInfo.color}>{statusInfo.icon} </Text>}\n <Text bold={isFocused} color={labelColor}>\n {item.label}\n </Text>\n {item.metadata && <Text color={metaColor}> \u00B7 {item.metadata}</Text>}\n </Box>\n\n {/* Line 2: description (indented to align under label) */}\n {hasDescription && (\n <Box>\n <Text color={descColor}>\n {' '.repeat(prefixPad)}\n {item.description}\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n\nexport function ScrollableList({\n items,\n focusedIndex,\n onFocusChange,\n onSelect,\n visibleCount,\n groupByCategory = false,\n renderItem,\n multiSelect,\n selectedIds,\n}: ScrollableListProps) {\n // Calculate scroll window to keep selected item visible (same logic as command suggestions)\n const { visibleItems, startIndex, endIndex } = useMemo(() => {\n if (items.length === 0) {\n return { visibleItems: [], startIndex: 0, endIndex: 0 }\n }\n\n // If all items fit in window, show them all\n if (items.length <= visibleCount) {\n return {\n visibleItems: items.map((item, i) => ({ item, globalIndex: i })),\n startIndex: 0,\n endIndex: items.length,\n }\n }\n\n // Calculate scroll window to keep focused item visible\n const halfWindow = Math.floor(visibleCount / 2)\n let start = Math.max(0, focusedIndex - halfWindow)\n let end = start + visibleCount\n\n // Adjust if we're near the end\n if (end > items.length) {\n end = items.length\n start = Math.max(0, end - visibleCount)\n }\n\n return {\n visibleItems: items.slice(start, end).map((item, i) => ({\n item,\n globalIndex: start + i,\n })),\n startIndex: start,\n endIndex: end,\n }\n }, [items, focusedIndex, visibleCount])\n\n // Scroll indicators\n const hasMoreAbove = startIndex > 0\n const hasMoreBelow = endIndex < items.length\n const countAbove = startIndex\n const countBelow = items.length - endIndex\n\n // Build display with optional category headers\n const displayElements: React.ReactNode[] = []\n let currentCategory = ''\n\n visibleItems.forEach(({ item, globalIndex }, visibleIndex) => {\n // Add category header if category changed and grouping is enabled\n if (groupByCategory && item.category !== currentCategory) {\n currentCategory = item.category || 'Other'\n displayElements.push(\n <Box\n key={`category-${currentCategory}-${globalIndex}`}\n marginTop={visibleIndex > 0 ? 1 : 0}\n >\n <Text bold color={SEMANTIC_COLORS.secondary}>\n {currentCategory}\n </Text>\n </Box>,\n )\n }\n\n const isFocused = globalIndex === focusedIndex\n const isSelected = selectedIds?.has(item.id) ?? false\n const itemElement = renderItem ? (\n renderItem(item, isFocused, isSelected)\n ) : (\n <DefaultItemRenderer\n item={item}\n isFocused={isFocused}\n multiSelect={multiSelect}\n isSelected={isSelected}\n />\n )\n\n displayElements.push(<Box key={item.id}>{itemElement}</Box>)\n })\n\n return (\n <Box flexDirection=\"column\">\n {/* Scroll up indicator */}\n {hasMoreAbove && (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}> \u2191 {countAbove} more above...</Text>\n </Box>\n )}\n\n {/* List items */}\n {displayElements}\n\n {/* Scroll down indicator */}\n {hasMoreBelow && (\n <Box>\n <Text color={SEMANTIC_COLORS.dim}> \u2193 {countBelow} more below...</Text>\n </Box>\n )}\n </Box>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,16 +2,21 @@ import React from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import { BRAND_GRADIENT, SEMANTIC_COLORS } from "../../constants/colors.js";
4
4
  function TabBar({ tabs, activeTab, onTabChange }) {
5
- return /* @__PURE__ */ React.createElement(Box, null, tabs.map((tab, index) => {
5
+ return /* @__PURE__ */ React.createElement(Box, { flexShrink: 0 }, tabs.map((tab, index) => {
6
6
  const isActive = tab.id === activeTab;
7
7
  const isLast = index === tabs.length - 1;
8
- return /* @__PURE__ */ React.createElement(Box, { key: tab.id }, isActive ? (
9
- // Active tab: background highlight with brand color
10
- /* @__PURE__ */ React.createElement(Text, { backgroundColor: BRAND_GRADIENT.START, color: "black", bold: true }, " ", tab.label, tab.badge !== void 0 && tab.badge > 0 ? ` (${tab.badge})` : "", " ")
11
- ) : (
12
- // Inactive tab: dim text
13
- /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", tab.label, tab.badge !== void 0 && tab.badge > 0 && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, " (", tab.badge, ")"), " ")
14
- ), !isLast && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, "\u2502"));
8
+ const badgeSuffix = tab.badge !== void 0 && tab.badge > 0 ? ` (${tab.badge})` : "";
9
+ const label = ` ${tab.label}${badgeSuffix} `;
10
+ return /* @__PURE__ */ React.createElement(React.Fragment, { key: tab.id }, isActive ? /* @__PURE__ */ React.createElement(
11
+ Text,
12
+ {
13
+ wrap: "truncate",
14
+ backgroundColor: BRAND_GRADIENT.START,
15
+ color: "black",
16
+ bold: true
17
+ },
18
+ label
19
+ ) : /* @__PURE__ */ React.createElement(Text, { wrap: "truncate", color: SEMANTIC_COLORS.dim }, label), !isLast && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, "\u2502"));
15
20
  }), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, " Tab/\u2190\u2192"));
16
21
  }
17
22
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/TabbedListView/TabBar.tsx"],
4
- "sourcesContent": ["/**\n * TabBar Component\n *\n * Displays tab navigation matching the brand style.\n * Uses background color for active tab selection.\n */\n\nimport React from 'react'\nimport { Box, Text } from 'ink'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport type { TabBarProps } from './types'\n\nexport function TabBar({ tabs, activeTab, onTabChange }: TabBarProps) {\n return (\n <Box>\n {tabs.map((tab, index) => {\n const isActive = tab.id === activeTab\n const isLast = index === tabs.length - 1\n\n return (\n <Box key={tab.id}>\n {isActive ? (\n // Active tab: background highlight with brand color\n <Text backgroundColor={BRAND_GRADIENT.START} color=\"black\" bold>\n {' '}\n {tab.label}\n {tab.badge !== undefined && tab.badge > 0\n ? ` (${tab.badge})`\n : ''}{' '}\n </Text>\n ) : (\n // Inactive tab: dim text\n <Text color={SEMANTIC_COLORS.dim}>\n {' '}\n {tab.label}\n {tab.badge !== undefined && tab.badge > 0 && (\n <Text color={SEMANTIC_COLORS.muted}> ({tab.badge})</Text>\n )}{' '}\n </Text>\n )}\n {!isLast && <Text color={SEMANTIC_COLORS.muted}>\u2502</Text>}\n </Box>\n )\n })}\n <Text color={SEMANTIC_COLORS.muted}> Tab/\u2190\u2192</Text>\n </Box>\n )\n}\n"],
5
- "mappings": "AAOA,OAAO,WAAW;AAClB,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB,uBAAuB;AAGzC,SAAS,OAAO,EAAE,MAAM,WAAW,YAAY,GAAgB;AACpE,SACE,oCAAC,WACE,KAAK,IAAI,CAAC,KAAK,UAAU;AACxB,UAAM,WAAW,IAAI,OAAO;AAC5B,UAAM,SAAS,UAAU,KAAK,SAAS;AAEvC,WACE,oCAAC,OAAI,KAAK,IAAI,MACX;AAAA;AAAA,MAEC,oCAAC,QAAK,iBAAiB,eAAe,OAAO,OAAM,SAAQ,MAAI,QAC5D,KACA,IAAI,OACJ,IAAI,UAAU,UAAa,IAAI,QAAQ,IACpC,KAAK,IAAI,KAAK,MACd,IAAI,GACV;AAAA;AAAA;AAAA,MAGA,oCAAC,QAAK,OAAO,gBAAgB,OAC1B,KACA,IAAI,OACJ,IAAI,UAAU,UAAa,IAAI,QAAQ,KACtC,oCAAC,QAAK,OAAO,gBAAgB,SAAO,MAAG,IAAI,OAAM,GAAC,GACjD,GACL;AAAA,OAED,CAAC,UAAU,oCAAC,QAAK,OAAO,gBAAgB,SAAO,QAAC,CACnD;AAAA,EAEJ,CAAC,GACD,oCAAC,QAAK,OAAO,gBAAgB,SAAO,mBAAO,CAC7C;AAEJ;",
4
+ "sourcesContent": ["/**\n * TabBar Component\n *\n * Displays tab navigation matching the brand style.\n * Uses background color for active tab selection.\n *\n * Layout notes:\n * - Each tab Box uses flexShrink={0} to prevent Yoga from compressing tabs\n * - Text uses wrap=\"truncate\" to prevent line wrapping in tight layouts\n * - Both active and inactive tabs use identical string content (no nested <Text>)\n * to ensure consistent width measurement by Ink's Yoga engine\n */\n\nimport React from 'react'\nimport { Box, Text } from 'ink'\nimport { BRAND_GRADIENT, SEMANTIC_COLORS } from '@constants/colors'\nimport type { TabBarProps } from './types'\n\nexport function TabBar({ tabs, activeTab, onTabChange }: TabBarProps) {\n return (\n <Box flexShrink={0}>\n {tabs.map((tab, index) => {\n const isActive = tab.id === activeTab\n const isLast = index === tabs.length - 1\n\n // Build label string identically for active/inactive to ensure\n // consistent width measurement by Yoga\n const badgeSuffix =\n tab.badge !== undefined && tab.badge > 0 ? ` (${tab.badge})` : ''\n const label = ` ${tab.label}${badgeSuffix} `\n\n return (\n <React.Fragment key={tab.id}>\n {isActive ? (\n <Text\n wrap=\"truncate\"\n backgroundColor={BRAND_GRADIENT.START}\n color=\"black\"\n bold\n >\n {label}\n </Text>\n ) : (\n <Text wrap=\"truncate\" color={SEMANTIC_COLORS.dim}>\n {label}\n </Text>\n )}\n {!isLast && <Text color={SEMANTIC_COLORS.muted}>\u2502</Text>}\n </React.Fragment>\n )\n })}\n <Text color={SEMANTIC_COLORS.muted}> Tab/\u2190\u2192</Text>\n </Box>\n )\n}\n"],
5
+ "mappings": "AAaA,OAAO,WAAW;AAClB,SAAS,KAAK,YAAY;AAC1B,SAAS,gBAAgB,uBAAuB;AAGzC,SAAS,OAAO,EAAE,MAAM,WAAW,YAAY,GAAgB;AACpE,SACE,oCAAC,OAAI,YAAY,KACd,KAAK,IAAI,CAAC,KAAK,UAAU;AACxB,UAAM,WAAW,IAAI,OAAO;AAC5B,UAAM,SAAS,UAAU,KAAK,SAAS;AAIvC,UAAM,cACJ,IAAI,UAAU,UAAa,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,MAAM;AACjE,UAAM,QAAQ,IAAI,IAAI,KAAK,GAAG,WAAW;AAEzC,WACE,oCAAC,MAAM,UAAN,EAAe,KAAK,IAAI,MACtB,WACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,iBAAiB,eAAe;AAAA,QAChC,OAAM;AAAA,QACN,MAAI;AAAA;AAAA,MAEH;AAAA,IACH,IAEA,oCAAC,QAAK,MAAK,YAAW,OAAO,gBAAgB,OAC1C,KACH,GAED,CAAC,UAAU,oCAAC,QAAK,OAAO,gBAAgB,SAAO,QAAC,CACnD;AAAA,EAEJ,CAAC,GACD,oCAAC,QAAK,OAAO,gBAAgB,SAAO,mBAAO,CAC7C;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -5,7 +5,10 @@ import { TabBar } from "./TabBar.js";
5
5
  import { SearchInput } from "./SearchInput.js";
6
6
  import { ScrollableList } from "./ScrollableList.js";
7
7
  import { SimpleSpinner } from "../Spinner.js";
8
- const MAX_VISIBLE_ITEMS = 8;
8
+ import { StatusOverlayContent } from "../StatusOverlayContent.js";
9
+ import { useTerminalSize } from "../../hooks/useTerminalSize.js";
10
+ const PROMPT_HEIGHT = 5;
11
+ const CHROME_HEIGHT = 6;
9
12
  function TabbedListView({
10
13
  title,
11
14
  tabs,
@@ -16,15 +19,31 @@ function TabbedListView({
16
19
  searchPlaceholder = "Search...",
17
20
  searchQuery = "",
18
21
  onSearchChange,
22
+ onSearchSubmit,
19
23
  onSelect,
20
24
  onClose,
25
+ onBack,
21
26
  footerHint,
22
27
  isLoading = false,
23
28
  loadingText = "Loading...",
24
29
  emptyText = "No items found",
25
30
  groupByCategory = false,
26
- renderItem
31
+ renderItem,
32
+ statusOverlay,
33
+ isActive = true,
34
+ showItemCount = true,
35
+ statusDismissHint,
36
+ multiSelect,
37
+ selectedIds,
38
+ onSelectionChange,
39
+ onMultiSelect,
40
+ multiSelectActionLabel
27
41
  }) {
42
+ const { rows } = useTerminalSize();
43
+ const visibleCount = Math.min(
44
+ 8,
45
+ Math.max(3, rows - PROMPT_HEIGHT - CHROME_HEIGHT)
46
+ );
28
47
  const [focusArea, setFocusArea] = useState(
29
48
  searchEnabled ? "search" : "list"
30
49
  );
@@ -60,57 +79,105 @@ function TabbedListView({
60
79
  },
61
80
  [onSelect]
62
81
  );
63
- useInput((input, key) => {
64
- if (key.escape) {
65
- onClose();
66
- return;
67
- }
68
- if (key.tab) {
69
- cycleTab(key.shift ? "left" : "right");
70
- return;
71
- }
72
- if (focusArea === "tabs" || focusArea === "search") {
73
- if (key.leftArrow) {
74
- cycleTab("left");
82
+ useInput(
83
+ (input, key) => {
84
+ if (statusOverlay && statusOverlay.type !== "loading") {
85
+ if (key.escape || key.return) {
86
+ onClose();
87
+ }
75
88
  return;
76
89
  }
77
- if (key.rightArrow) {
78
- cycleTab("right");
90
+ if (statusOverlay) return;
91
+ if (key.escape) {
92
+ if (onBack) {
93
+ onBack();
94
+ } else {
95
+ onClose();
96
+ }
79
97
  return;
80
98
  }
81
- }
82
- if (key.downArrow) {
83
- if (focusArea === "search") {
84
- setFocusArea("list");
99
+ if (key.tab) {
100
+ cycleTab(key.shift ? "left" : "right");
85
101
  return;
86
102
  }
87
- if (focusArea === "list" && filteredItems.length > 0) {
88
- setFocusedIndex(
89
- (prev) => prev < filteredItems.length - 1 ? prev + 1 : prev
90
- );
103
+ if (focusArea === "tabs" || focusArea === "search") {
104
+ if (key.leftArrow) {
105
+ cycleTab("left");
106
+ return;
107
+ }
108
+ if (key.rightArrow) {
109
+ cycleTab("right");
110
+ return;
111
+ }
112
+ }
113
+ if (key.downArrow) {
114
+ if (focusArea === "search") {
115
+ setFocusArea("list");
116
+ return;
117
+ }
118
+ if (focusArea === "list" && filteredItems.length > 0) {
119
+ setFocusedIndex(
120
+ (prev) => prev < filteredItems.length - 1 ? prev + 1 : prev
121
+ );
122
+ return;
123
+ }
124
+ }
125
+ if (key.upArrow) {
126
+ if (focusArea === "list") {
127
+ if (focusedIndex > 0) {
128
+ setFocusedIndex((prev) => prev - 1);
129
+ } else if (searchEnabled) {
130
+ setFocusArea("search");
131
+ }
132
+ return;
133
+ }
134
+ }
135
+ if (key.return && focusArea === "search" && onSearchSubmit) {
136
+ onSearchSubmit(searchQuery);
91
137
  return;
92
138
  }
93
- }
94
- if (key.upArrow) {
95
- if (focusArea === "list") {
96
- if (focusedIndex > 0) {
97
- setFocusedIndex((prev) => prev - 1);
98
- } else if (searchEnabled) {
99
- setFocusArea("search");
139
+ if (input === " " && multiSelect && focusArea === "list" && filteredItems.length > 0) {
140
+ const item = filteredItems[focusedIndex];
141
+ if (item && onSelectionChange) {
142
+ const isCurrentlySelected = selectedIds?.has(item.id) ?? false;
143
+ onSelectionChange(item.id, !isCurrentlySelected);
100
144
  }
101
145
  return;
102
146
  }
103
- }
104
- if (key.return && focusArea === "list" && filteredItems.length > 0) {
105
- handleSelect(filteredItems[focusedIndex]);
106
- return;
107
- }
108
- if (input === "/" && searchEnabled && focusArea !== "search") {
109
- setFocusArea("search");
110
- return;
111
- }
112
- });
147
+ if (input === "a" && multiSelect && focusArea === "list" && filteredItems.length > 0) {
148
+ if (onSelectionChange) {
149
+ const allSelected = filteredItems.every(
150
+ (item) => selectedIds?.has(item.id)
151
+ );
152
+ for (const item of filteredItems) {
153
+ onSelectionChange(item.id, !allSelected);
154
+ }
155
+ }
156
+ return;
157
+ }
158
+ if (key.return && focusArea === "list" && filteredItems.length > 0) {
159
+ if (multiSelect && selectedIds && selectedIds.size > 0 && onMultiSelect) {
160
+ const selectedItems = filteredItems.filter(
161
+ (item) => selectedIds.has(item.id)
162
+ );
163
+ if (selectedItems.length > 0) {
164
+ onMultiSelect(selectedItems);
165
+ return;
166
+ }
167
+ }
168
+ handleSelect(filteredItems[focusedIndex]);
169
+ return;
170
+ }
171
+ if (input === "/" && searchEnabled && focusArea !== "search") {
172
+ setFocusArea("search");
173
+ return;
174
+ }
175
+ },
176
+ { isActive }
177
+ );
113
178
  const defaultHint = "\u2191\u2193 Navigate \xB7 Enter Select \xB7 Tab/\u2190\u2192 Switch \xB7 Esc Close";
179
+ const multiSelectHint = "\u2191\u2193 Navigate \xB7 Space Select \xB7 a All \xB7 Enter Action \xB7 Tab Switch \xB7 Esc Close";
180
+ const displayHint = statusOverlay ? statusOverlay.type === "loading" ? "" : statusDismissHint || "Esc Close" : multiSelect ? selectedIds && selectedIds.size > 0 && multiSelectActionLabel ? `${multiSelectActionLabel} \xB7 ${footerHint || multiSelectHint}` : footerHint || multiSelectHint : footerHint || defaultHint;
114
181
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React.createElement(
115
182
  Box,
116
183
  {
@@ -124,8 +191,8 @@ function TabbedListView({
124
191
  paddingX: 1,
125
192
  flexDirection: "column"
126
193
  },
127
- /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: BRAND_GRADIENT.START }, "\u25C6"), /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.secondary }, " ", title), filteredItems.length > 0 && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " (", filteredItems.length, ")")),
128
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(TabBar, { tabs, activeTab, onTabChange }))
194
+ /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: BRAND_GRADIENT.START }, "\u25C6"), /* @__PURE__ */ React.createElement(Text, { bold: true, color: SEMANTIC_COLORS.secondary }, " ", title), showItemCount && !statusOverlay && filteredItems.length > 0 && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " (", filteredItems.length, ")")),
195
+ /* @__PURE__ */ React.createElement(Box, { marginTop: 1, flexShrink: 0 }, /* @__PURE__ */ React.createElement(TabBar, { tabs, activeTab, onTabChange }))
129
196
  ), /* @__PURE__ */ React.createElement(
130
197
  Box,
131
198
  {
@@ -140,7 +207,7 @@ function TabbedListView({
140
207
  paddingX: 1,
141
208
  paddingY: 1
142
209
  },
143
- searchEnabled && /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(
210
+ searchEnabled && !statusOverlay && /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(
144
211
  SearchInput,
145
212
  {
146
213
  value: searchQuery,
@@ -150,19 +217,27 @@ function TabbedListView({
150
217
  isActive: focusArea === "search"
151
218
  }
152
219
  )),
153
- /* @__PURE__ */ React.createElement(Box, { key: `content-${activeTab}`, flexDirection: "column" }, isLoading ? /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(SimpleSpinner, null), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", loadingText)) : filteredItems.length === 0 ? /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, emptyText)) : /* @__PURE__ */ React.createElement(
220
+ /* @__PURE__ */ React.createElement(Box, { key: `content-${activeTab}`, flexDirection: "column" }, statusOverlay ? /* @__PURE__ */ React.createElement(
221
+ StatusOverlayContent,
222
+ {
223
+ type: statusOverlay.type,
224
+ message: statusOverlay.message
225
+ }
226
+ ) : isLoading ? /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(SimpleSpinner, null), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", loadingText)) : filteredItems.length === 0 ? /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, emptyText)) : /* @__PURE__ */ React.createElement(
154
227
  ScrollableList,
155
228
  {
156
229
  items: filteredItems,
157
230
  focusedIndex,
158
231
  onFocusChange: setFocusedIndex,
159
232
  onSelect: handleSelect,
160
- visibleCount: MAX_VISIBLE_ITEMS,
233
+ visibleCount,
161
234
  groupByCategory,
162
- renderItem
235
+ renderItem,
236
+ multiSelect,
237
+ selectedIds
163
238
  }
164
239
  )),
165
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, footerHint || defaultHint))
240
+ displayHint && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.muted }, displayHint))
166
241
  ));
167
242
  }
168
243
  export {