@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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/services/systemReminder.ts"],
4
- "sourcesContent": ["import { getTodos, TodoItem } from '@utils/todoStorage'\n\nexport interface ReminderMessage {\n role: 'system'\n content: string\n isMeta: boolean\n timestamp: number\n type: string\n priority: 'low' | 'medium' | 'high'\n category: 'task' | 'security' | 'performance' | 'general'\n}\n\ninterface ReminderConfig {\n todoEmptyReminder: boolean\n securityReminder: boolean\n performanceReminder: boolean\n maxRemindersPerSession: number\n}\n\ninterface SessionReminderState {\n lastTodoUpdate: number\n lastFileAccess: number\n sessionStartTime: number\n remindersSent: Set<string>\n contextPresent: boolean\n reminderCount: number\n config: ReminderConfig\n}\n\nclass SystemReminderService {\n private sessionState: SessionReminderState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: {\n todoEmptyReminder: true,\n securityReminder: true,\n performanceReminder: true,\n maxRemindersPerSession: 10,\n },\n }\n\n private eventDispatcher = new Map<string, Array<(context: any) => void>>()\n private reminderCache = new Map<string, ReminderMessage>()\n\n constructor() {\n this.setupEventDispatcher()\n }\n\n /**\n * Conditional reminder injection - only when context is present\n * Enhanced with performance optimizations and priority management\n */\n public generateReminders(\n hasContext: boolean = false,\n agentId?: string,\n ): ReminderMessage[] {\n this.sessionState.contextPresent = hasContext\n\n // Only inject when context is present (matching original behavior)\n if (!hasContext) {\n return []\n }\n\n // Check session reminder limit to prevent overload\n if (\n this.sessionState.reminderCount >=\n this.sessionState.config.maxRemindersPerSession\n ) {\n return []\n }\n\n const reminders: ReminderMessage[] = []\n const currentTime = Date.now()\n\n // Use lazy evaluation for performance with agent context\n const reminderGenerators = [\n () => this.dispatchTodoEvent(agentId),\n () => this.dispatchSecurityEvent(),\n () => this.dispatchPerformanceEvent(),\n () => this.getMentionReminders(), // Add mention reminders\n ]\n\n for (const generator of reminderGenerators) {\n if (reminders.length >= 5) break // Slightly increase limit to accommodate mentions\n\n const result = generator()\n if (result) {\n // Handle both single reminders and arrays\n const remindersToAdd = Array.isArray(result) ? result : [result]\n reminders.push(...remindersToAdd)\n this.sessionState.reminderCount += remindersToAdd.length\n }\n }\n\n // Log aggregated metrics instead of individual events for performance\n\n return reminders\n }\n\n private dispatchTodoEvent(agentId?: string): ReminderMessage | null {\n if (!this.sessionState.config.todoEmptyReminder) return null\n\n // Use agent-scoped todo access\n const todos = getTodos(agentId)\n const currentTime = Date.now()\n const agentKey = agentId || 'default'\n\n // Check if this is a fresh session (no todos seen yet)\n if (\n todos.length === 0 &&\n !this.sessionState.remindersSent.has(`todo_empty_${agentKey}`)\n ) {\n this.sessionState.remindersSent.add(`todo_empty_${agentKey}`)\n return this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n 'This is a reminder that your todo list is currently empty. DO NOT mention this to the user explicitly because they are already aware. If you are working on tasks that would benefit from a todo list please use the TodoWrite tool to create one. If not, please feel free to ignore. Again do not mention this message to the user.',\n currentTime,\n )\n }\n\n // Check for todo updates since last seen\n if (todos.length > 0) {\n const reminderKey = `todo_updated_${agentKey}_${todos.length}_${this.getTodoStateHash(todos)}`\n\n // Use cache for performance optimization\n if (this.reminderCache.has(reminderKey)) {\n return this.reminderCache.get(reminderKey)!\n }\n\n if (!this.sessionState.remindersSent.has(reminderKey)) {\n this.sessionState.remindersSent.add(reminderKey)\n // Clear previous todo state reminders for this agent\n this.clearTodoReminders(agentKey)\n\n // Optimize: only include essential todo data\n const todoContent = JSON.stringify(\n todos.map(todo => ({\n content:\n todo.content.length > 100\n ? todo.content.substring(0, 100) + '...'\n : todo.content,\n status: todo.status,\n priority: todo.priority,\n id: todo.id,\n })),\n )\n\n const reminder = this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n `Your todo list has changed. DO NOT mention this explicitly to the user. Here are the latest contents of your todo list:\\n\\n${todoContent}. Continue on with the tasks at hand if applicable.`,\n currentTime,\n )\n\n // Cache the reminder for reuse\n this.reminderCache.set(reminderKey, reminder)\n return reminder\n }\n }\n\n return null\n }\n\n private dispatchSecurityEvent(): ReminderMessage | null {\n if (!this.sessionState.config.securityReminder) return null\n\n const currentTime = Date.now()\n\n // Only inject security reminder once per session when file operations occur\n if (\n this.sessionState.lastFileAccess > 0 &&\n !this.sessionState.remindersSent.has('file_security')\n ) {\n this.sessionState.remindersSent.add('file_security')\n return this.createReminderMessage(\n 'security',\n 'security',\n 'high',\n 'Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.',\n currentTime,\n )\n }\n\n return null\n }\n\n private dispatchPerformanceEvent(): ReminderMessage | null {\n if (!this.sessionState.config.performanceReminder) return null\n\n const currentTime = Date.now()\n const sessionDuration = currentTime - this.sessionState.sessionStartTime\n\n // Remind about performance after long sessions (30 minutes)\n if (\n sessionDuration > 30 * 60 * 1000 &&\n !this.sessionState.remindersSent.has('performance_long_session')\n ) {\n this.sessionState.remindersSent.add('performance_long_session')\n return this.createReminderMessage(\n 'performance',\n 'performance',\n 'low',\n 'Long session detected. Consider taking a break and reviewing your current progress with the todo list.',\n currentTime,\n )\n }\n\n return null\n }\n\n /**\n * Retrieve cached mention reminders\n * Returns recent mentions (within 5 seconds) that haven't expired\n */\n private getMentionReminders(): ReminderMessage[] {\n const currentTime = Date.now()\n const MENTION_FRESHNESS_WINDOW = 5000 // 5 seconds\n const reminders: ReminderMessage[] = []\n const expiredKeys: string[] = []\n\n // Single pass through cache for both collection and cleanup identification\n for (const [key, reminder] of this.reminderCache.entries()) {\n if (this.isMentionReminder(reminder)) {\n const age = currentTime - reminder.timestamp\n if (age <= MENTION_FRESHNESS_WINDOW) {\n reminders.push(reminder)\n } else {\n expiredKeys.push(key)\n }\n }\n }\n\n // Clean up expired mention reminders in separate pass for performance\n expiredKeys.forEach(key => this.reminderCache.delete(key))\n\n return reminders\n }\n\n /**\n * Type guard for mention reminders - centralized type checking\n * Eliminates hardcoded type strings scattered throughout the code\n */\n private isMentionReminder(reminder: ReminderMessage): boolean {\n const mentionTypes = ['agent_mention', 'file_mention', 'ask_model_mention']\n return mentionTypes.includes(reminder.type)\n }\n\n /**\n * Generate reminders for external file changes\n * Called when todo files are modified externally\n */\n public generateFileChangeReminder(context: any): ReminderMessage | null {\n const { agentId, filePath, reminder } = context\n\n if (!reminder) {\n return null\n }\n\n const currentTime = Date.now()\n const reminderKey = `file_changed_${agentId}_${filePath}_${currentTime}`\n\n // Ensure this specific file change reminder is only shown once\n if (this.sessionState.remindersSent.has(reminderKey)) {\n return null\n }\n\n this.sessionState.remindersSent.add(reminderKey)\n\n return this.createReminderMessage(\n 'file_changed',\n 'general',\n 'medium',\n reminder,\n currentTime,\n )\n }\n\n private createReminderMessage(\n type: string,\n category: ReminderMessage['category'],\n priority: ReminderMessage['priority'],\n content: string,\n timestamp: number,\n ): ReminderMessage {\n return {\n role: 'system',\n content: `<system-reminder>\\n${content}\\n</system-reminder>`,\n isMeta: true,\n timestamp,\n type,\n priority,\n category,\n }\n }\n\n private getTodoStateHash(todos: TodoItem[]): string {\n return todos\n .map(t => `${t.id}:${t.status}`)\n .sort()\n .join('|')\n }\n\n private clearTodoReminders(agentId?: string): void {\n const agentKey = agentId || 'default'\n for (const key of this.sessionState.remindersSent) {\n if (key.startsWith(`todo_updated_${agentKey}_`)) {\n this.sessionState.remindersSent.delete(key)\n }\n }\n }\n\n private setupEventDispatcher(): void {\n // Session startup events\n this.addEventListener('session:startup', context => {\n // Reset session state on startup\n this.resetSession()\n\n // Initialize session tracking\n this.sessionState.sessionStartTime = Date.now()\n this.sessionState.contextPresent =\n Object.keys(context.context || {}).length > 0\n })\n\n // Todo change events\n this.addEventListener('todo:changed', context => {\n this.sessionState.lastTodoUpdate = Date.now()\n this.clearTodoReminders(context.agentId)\n })\n\n // Todo file changed externally\n this.addEventListener('todo:file_changed', context => {\n // External file change detected, trigger reminder injection\n const agentId = context.agentId || 'default'\n this.clearTodoReminders(agentId)\n this.sessionState.lastTodoUpdate = Date.now()\n\n // Generate and inject file change reminder immediately\n const reminder = this.generateFileChangeReminder(context)\n if (reminder) {\n // Inject reminder into the latest user message through event system\n this.emitEvent('reminder:inject', {\n reminder: reminder.content,\n agentId,\n type: 'file_changed',\n timestamp: Date.now(),\n })\n }\n })\n\n // File access events\n this.addEventListener('file:read', context => {\n this.sessionState.lastFileAccess = Date.now()\n })\n\n // File edit events for freshness detection\n this.addEventListener('file:edited', context => {\n // File edit handling\n })\n\n // Unified mention event handlers - eliminates code duplication\n this.addEventListener('agent:mentioned', context => {\n this.createMentionReminder({\n type: 'agent_mention',\n key: `agent_mention_${context.agentType}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST use the Task tool with subagent_type=\"${context.agentType}\" to delegate this task to the specified agent. Provide a detailed, self-contained task description that fully captures the user's intent for the ${context.agentType} agent to execute.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('file:mentioned', context => {\n this.createMentionReminder({\n type: 'file_mention',\n key: `file_mention_${context.filePath}_${context.timestamp}`,\n category: 'general',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST read the entire content of the file at path: ${context.filePath} using the Read tool to understand the full context before proceeding with the user's request.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('ask-model:mentioned', context => {\n this.createMentionReminder({\n type: 'ask_model_mention',\n key: `ask_model_mention_${context.modelName}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.modelName}. You MUST use the AskExpertModelTool to consult this specific model for expert opinions and analysis. Provide the user's question or context clearly to get the most relevant response from ${context.modelName}.`,\n timestamp: context.timestamp,\n })\n })\n }\n\n public addEventListener(\n event: string,\n callback: (context: any) => void,\n ): void {\n if (!this.eventDispatcher.has(event)) {\n this.eventDispatcher.set(event, [])\n }\n this.eventDispatcher.get(event)!.push(callback)\n }\n\n public emitEvent(event: string, context: any): void {\n const listeners = this.eventDispatcher.get(event) || []\n listeners.forEach(callback => {\n try {\n callback(context)\n } catch (error) {\n console.error(`Error in event listener for ${event}:`, error)\n }\n })\n }\n\n /**\n * Unified mention reminder creation - eliminates duplicate logic\n * Centralizes reminder creation with consistent deduplication\n */\n private createMentionReminder(params: {\n type: string\n key: string\n category: ReminderMessage['category']\n priority: ReminderMessage['priority']\n content: string\n timestamp: number\n }): void {\n if (!this.sessionState.remindersSent.has(params.key)) {\n this.sessionState.remindersSent.add(params.key)\n\n const reminder = this.createReminderMessage(\n params.type,\n params.category,\n params.priority,\n params.content,\n params.timestamp,\n )\n\n this.reminderCache.set(params.key, reminder)\n }\n }\n\n public resetSession(): void {\n this.sessionState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: { ...this.sessionState.config }, // Preserve config across resets\n }\n this.reminderCache.clear() // Clear cache on session reset\n }\n\n public updateConfig(config: Partial<ReminderConfig>): void {\n this.sessionState.config = { ...this.sessionState.config, ...config }\n }\n\n public getSessionState(): SessionReminderState {\n return { ...this.sessionState }\n }\n}\n\nexport const systemReminderService = new SystemReminderService()\n\nexport const generateSystemReminders = (\n hasContext: boolean = false,\n agentId?: string,\n) => systemReminderService.generateReminders(hasContext, agentId)\n\nexport const generateFileChangeReminder = (context: any) =>\n systemReminderService.generateFileChangeReminder(context)\n\nexport const emitReminderEvent = (event: string, context: any) =>\n systemReminderService.emitEvent(event, context)\n\nexport const resetReminderSession = () => systemReminderService.resetSession()\nexport const getReminderSessionState = () =>\n systemReminderService.getSessionState()\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { getTodos, TodoItem } from '@utils/todoStorage'\n\nexport interface ReminderMessage {\n role: 'system'\n content: string\n isMeta: boolean\n timestamp: number\n type: string\n priority: 'low' | 'medium' | 'high'\n category: 'task' | 'security' | 'performance' | 'general'\n}\n\ninterface ReminderConfig {\n todoEmptyReminder: boolean\n securityReminder: boolean\n performanceReminder: boolean\n maxRemindersPerSession: number\n}\n\ninterface SessionReminderState {\n lastTodoUpdate: number\n lastFileAccess: number\n sessionStartTime: number\n remindersSent: Set<string>\n contextPresent: boolean\n reminderCount: number\n config: ReminderConfig\n}\n\nclass SystemReminderService {\n private sessionState: SessionReminderState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: {\n todoEmptyReminder: true,\n securityReminder: true,\n performanceReminder: true,\n maxRemindersPerSession: 10,\n },\n }\n\n private eventDispatcher = new Map<string, Array<(context: any) => void>>()\n private reminderCache = new Map<string, ReminderMessage>()\n\n constructor() {\n this.setupEventDispatcher()\n }\n\n /**\n * Conditional reminder injection - only when context is present\n * Enhanced with performance optimizations and priority management\n */\n public generateReminders(\n hasContext: boolean = false,\n agentId?: string,\n ): ReminderMessage[] {\n this.sessionState.contextPresent = hasContext\n\n // Only inject when context is present (matching original behavior)\n if (!hasContext) {\n return []\n }\n\n // Check session reminder limit to prevent overload\n if (\n this.sessionState.reminderCount >=\n this.sessionState.config.maxRemindersPerSession\n ) {\n return []\n }\n\n const reminders: ReminderMessage[] = []\n const currentTime = Date.now()\n\n // Use lazy evaluation for performance with agent context\n const reminderGenerators = [\n () => this.dispatchTodoEvent(agentId),\n () => this.dispatchTaskToolsReminder(),\n () => this.dispatchSecurityEvent(),\n () => this.dispatchPerformanceEvent(),\n () => this.getMentionReminders(),\n () => this.getEventReminders(),\n ]\n\n for (const generator of reminderGenerators) {\n if (reminders.length >= 8) break // Accommodate mentions + event reminders\n\n const result = generator()\n if (result) {\n // Handle both single reminders and arrays\n const remindersToAdd = Array.isArray(result) ? result : [result]\n reminders.push(...remindersToAdd)\n this.sessionState.reminderCount += remindersToAdd.length\n }\n }\n\n // Log aggregated metrics instead of individual events for performance\n\n return reminders\n }\n\n private dispatchTodoEvent(agentId?: string): ReminderMessage | null {\n if (!this.sessionState.config.todoEmptyReminder) return null\n\n // Use agent-scoped todo access\n const todos = getTodos(agentId)\n const currentTime = Date.now()\n const agentKey = agentId || 'default'\n\n // Check if this is a fresh session (no todos seen yet)\n if (\n todos.length === 0 &&\n !this.sessionState.remindersSent.has(`todo_empty_${agentKey}`)\n ) {\n this.sessionState.remindersSent.add(`todo_empty_${agentKey}`)\n return this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n 'This is a reminder that your todo list is currently empty. DO NOT mention this to the user explicitly because they are already aware. If you are working on tasks that would benefit from a todo list please use the TodoWrite tool to create one. If not, please feel free to ignore. Again do not mention this message to the user.',\n currentTime,\n )\n }\n\n // Check for todo updates since last seen\n if (todos.length > 0) {\n const reminderKey = `todo_updated_${agentKey}_${todos.length}_${this.getTodoStateHash(todos)}`\n\n // Use cache for performance optimization\n if (this.reminderCache.has(reminderKey)) {\n return this.reminderCache.get(reminderKey)!\n }\n\n if (!this.sessionState.remindersSent.has(reminderKey)) {\n this.sessionState.remindersSent.add(reminderKey)\n // Clear previous todo state reminders for this agent\n this.clearTodoReminders(agentKey)\n\n // Optimize: only include essential todo data\n const todoContent = JSON.stringify(\n todos.map(todo => ({\n content:\n todo.content.length > 100\n ? todo.content.substring(0, 100) + '...'\n : todo.content,\n status: todo.status,\n priority: todo.priority,\n id: todo.id,\n })),\n )\n\n const reminder = this.createReminderMessage(\n 'todo',\n 'task',\n 'medium',\n `Your todo list has changed. DO NOT mention this explicitly to the user. Here are the latest contents of your todo list:\\n\\n${todoContent}. Continue on with the tasks at hand if applicable.`,\n currentTime,\n )\n\n // Cache the reminder for reuse\n this.reminderCache.set(reminderKey, reminder)\n return reminder\n }\n }\n\n return null\n }\n\n private dispatchTaskToolsReminder(): ReminderMessage | null {\n const key = 'task_tools_reminder'\n if (this.sessionState.remindersSent.has(key)) return null\n\n // Only trigger after 5 minutes of session time\n const sessionDuration =\n Date.now() - this.sessionState.sessionStartTime\n if (sessionDuration < 5 * 60 * 1000) return null\n\n // Check if tasks exist via the event cache \u2014 if task:created or task:status\n // events have fired, the user is already using task tools\n const hasTaskActivity = Array.from(\n this.sessionState.remindersSent,\n ).some(k => k.startsWith('task_'))\n if (hasTaskActivity) return null\n\n // Emit the reminder event so the listener caches the message\n this.emitEvent('task:tools_reminder', {})\n return null\n }\n\n private dispatchSecurityEvent(): ReminderMessage | null {\n if (!this.sessionState.config.securityReminder) return null\n\n const currentTime = Date.now()\n\n // Only inject security reminder once per session when file operations occur\n if (\n this.sessionState.lastFileAccess > 0 &&\n !this.sessionState.remindersSent.has('file_security')\n ) {\n this.sessionState.remindersSent.add('file_security')\n return this.createReminderMessage(\n 'security',\n 'security',\n 'high',\n 'Whenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.',\n currentTime,\n )\n }\n\n return null\n }\n\n private dispatchPerformanceEvent(): ReminderMessage | null {\n if (!this.sessionState.config.performanceReminder) return null\n\n const currentTime = Date.now()\n const sessionDuration = currentTime - this.sessionState.sessionStartTime\n\n // Remind about performance after long sessions (30 minutes)\n if (\n sessionDuration > 30 * 60 * 1000 &&\n !this.sessionState.remindersSent.has('performance_long_session')\n ) {\n this.sessionState.remindersSent.add('performance_long_session')\n return this.createReminderMessage(\n 'performance',\n 'performance',\n 'low',\n 'Long session detected. Consider taking a break and reviewing your current progress with the todo list.',\n currentTime,\n )\n }\n\n return null\n }\n\n /**\n * Retrieve cached mention reminders\n * Returns recent mentions (within 5 seconds) that haven't expired\n */\n private getMentionReminders(): ReminderMessage[] {\n const currentTime = Date.now()\n const MENTION_FRESHNESS_WINDOW = 5000 // 5 seconds\n const reminders: ReminderMessage[] = []\n const expiredKeys: string[] = []\n\n // Single pass through cache for both collection and cleanup identification\n for (const [key, reminder] of this.reminderCache.entries()) {\n if (this.isMentionReminder(reminder)) {\n const age = currentTime - reminder.timestamp\n if (age <= MENTION_FRESHNESS_WINDOW) {\n reminders.push(reminder)\n } else {\n expiredKeys.push(key)\n }\n }\n }\n\n // Clean up expired mention reminders in separate pass for performance\n expiredKeys.forEach(key => this.reminderCache.delete(key))\n\n return reminders\n }\n\n /**\n * Type guard for mention reminders - centralized type checking\n * Eliminates hardcoded type strings scattered throughout the code\n */\n private isMentionReminder(reminder: ReminderMessage): boolean {\n const mentionTypes = ['agent_mention', 'file_mention', 'ask_model_mention']\n return mentionTypes.includes(reminder.type)\n }\n\n /**\n * Event-based reminder types for hook results, file status, task status\n */\n private static readonly EVENT_REMINDER_TYPES = new Set([\n // Batch A\n 'hook_additional_context',\n 'hook_blocking_error',\n 'hook_stopped_continuation',\n 'file_empty',\n 'file_truncated',\n 'task_status',\n 'task_tools_reminder',\n // Batch B\n 'output_style_active',\n 'plan_file_reference',\n 'plan_mode_reentry',\n 'plan_exited',\n 'skill_invoked',\n 'mcp_resource_no_content',\n 'compact_file_reference',\n // Batch C\n 'token_usage',\n 'budget_usd',\n 'team_coordination',\n 'team_shutdown',\n 'diagnostics_new',\n 'session_continuation',\n ])\n\n private isEventReminder(reminder: ReminderMessage): boolean {\n return SystemReminderService.EVENT_REMINDER_TYPES.has(reminder.type)\n }\n\n /**\n * Retrieve cached event-based reminders (hook results, file status, task status)\n * Returns recent event reminders within freshness window\n */\n private getEventReminders(): ReminderMessage[] {\n const currentTime = Date.now()\n const EVENT_FRESHNESS_WINDOW = 10000 // 10 seconds\n const reminders: ReminderMessage[] = []\n const expiredKeys: string[] = []\n\n for (const [key, reminder] of this.reminderCache.entries()) {\n if (this.isEventReminder(reminder)) {\n const age = currentTime - reminder.timestamp\n if (age <= EVENT_FRESHNESS_WINDOW) {\n reminders.push(reminder)\n } else {\n expiredKeys.push(key)\n }\n }\n }\n\n expiredKeys.forEach(key => this.reminderCache.delete(key))\n\n return reminders\n }\n\n /**\n * Generate reminders for external file changes\n * Called when todo files are modified externally\n */\n public generateFileChangeReminder(context: any): ReminderMessage | null {\n const { agentId, filePath, reminder } = context\n\n if (!reminder) {\n return null\n }\n\n const currentTime = Date.now()\n const reminderKey = `file_changed_${agentId}_${filePath}_${currentTime}`\n\n // Ensure this specific file change reminder is only shown once\n if (this.sessionState.remindersSent.has(reminderKey)) {\n return null\n }\n\n this.sessionState.remindersSent.add(reminderKey)\n\n return this.createReminderMessage(\n 'file_changed',\n 'general',\n 'medium',\n reminder,\n currentTime,\n )\n }\n\n private createReminderMessage(\n type: string,\n category: ReminderMessage['category'],\n priority: ReminderMessage['priority'],\n content: string,\n timestamp: number,\n ): ReminderMessage {\n return {\n role: 'system',\n content: `<system-reminder>\\n${content}\\n</system-reminder>`,\n isMeta: true,\n timestamp,\n type,\n priority,\n category,\n }\n }\n\n private getTodoStateHash(todos: TodoItem[]): string {\n return todos\n .map(t => `${t.id}:${t.status}`)\n .sort()\n .join('|')\n }\n\n private clearTodoReminders(agentId?: string): void {\n const agentKey = agentId || 'default'\n for (const key of this.sessionState.remindersSent) {\n if (key.startsWith(`todo_updated_${agentKey}_`)) {\n this.sessionState.remindersSent.delete(key)\n }\n }\n }\n\n private setupEventDispatcher(): void {\n // Session startup events\n this.addEventListener('session:startup', context => {\n // Reset session state on startup\n this.resetSession()\n\n // Initialize session tracking\n this.sessionState.sessionStartTime = Date.now()\n this.sessionState.contextPresent =\n Object.keys(context.context || {}).length > 0\n })\n\n // Todo change events\n this.addEventListener('todo:changed', context => {\n this.sessionState.lastTodoUpdate = Date.now()\n this.clearTodoReminders(context.agentId)\n })\n\n // Todo file changed externally\n this.addEventListener('todo:file_changed', context => {\n // External file change detected, cache reminder for next generateReminders() call\n const agentId = context.agentId || 'default'\n this.clearTodoReminders(agentId)\n this.sessionState.lastTodoUpdate = Date.now()\n\n // Generate and cache file change reminder directly\n const reminder = this.generateFileChangeReminder(context)\n if (reminder) {\n const cacheKey = `file_changed_${agentId}_${Date.now()}`\n this.reminderCache.set(cacheKey, reminder)\n }\n })\n\n // File conflict events (external modification between reads)\n this.addEventListener('file:conflict', context => {\n const filePath = context.filePath || context.path || 'unknown'\n const cacheKey = `file_conflict_${filePath}_${Date.now()}`\n\n if (!this.sessionState.remindersSent.has(cacheKey)) {\n this.sessionState.remindersSent.add(cacheKey)\n\n const reminder = this.createReminderMessage(\n 'file_conflict',\n 'general',\n 'high',\n `File ${filePath} was modified externally since last read. Re-read the file before editing to avoid overwriting changes.`,\n Date.now(),\n )\n this.reminderCache.set(cacheKey, reminder)\n }\n })\n\n // File access events\n this.addEventListener('file:read', context => {\n this.sessionState.lastFileAccess = Date.now()\n })\n\n // File edit events for freshness detection\n this.addEventListener('file:edited', context => {\n // File edit handling\n })\n\n // Unified mention event handlers - eliminates code duplication\n this.addEventListener('agent:mentioned', context => {\n this.createMentionReminder({\n type: 'agent_mention',\n key: `agent_mention_${context.agentType}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST use the Task tool with subagent_type=\"${context.agentType}\" to delegate this task to the specified agent. Provide a detailed, self-contained task description that fully captures the user's intent for the ${context.agentType} agent to execute.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('file:mentioned', context => {\n this.createMentionReminder({\n type: 'file_mention',\n key: `file_mention_${context.filePath}_${context.timestamp}`,\n category: 'general',\n priority: 'high',\n content: `The user mentioned @${context.originalMention}. You MUST read the entire content of the file at path: ${context.filePath} using the Read tool to understand the full context before proceeding with the user's request.`,\n timestamp: context.timestamp,\n })\n })\n\n this.addEventListener('ask-model:mentioned', context => {\n this.createMentionReminder({\n type: 'ask_model_mention',\n key: `ask_model_mention_${context.modelName}_${context.timestamp}`,\n category: 'task',\n priority: 'high',\n content: `The user mentioned @${context.modelName}. You MUST use the AskExpertModelTool to consult this specific model for expert opinions and analysis. Provide the user's question or context clearly to get the most relevant response from ${context.modelName}.`,\n timestamp: context.timestamp,\n })\n })\n\n // Hook result events (Batch A system reminders)\n this.addEventListener('hook:additional_context', context => {\n const key = `hook_ctx_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_additional_context',\n 'general',\n 'medium',\n `Additional context from hook \"${context.hookName}\": ${context.content}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('hook:blocking_error', context => {\n const key = `hook_block_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_blocking_error',\n 'general',\n 'high',\n `A hook blocked the action: ${context.reason}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('hook:stopped_continuation', context => {\n const key = `hook_stop_${context.hookName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'hook_stopped_continuation',\n 'general',\n 'high',\n `A hook stopped continuation: ${context.reason}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // File status events\n this.addEventListener('file:empty', context => {\n const key = `file_empty_${context.filePath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'file_empty',\n 'general',\n 'low',\n `File exists but is empty: ${context.filePath}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('file:truncated', context => {\n const key = `file_truncated_${context.filePath}_${context.limit}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'file_truncated',\n 'general',\n 'low',\n `File content was truncated at ${context.limit} lines. Total lines: ${context.totalLines}. Use offset and limit to read more.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Task status events\n this.addEventListener('task:status', context => {\n const key = `task_status_${context.taskId}_${context.status}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'task_status',\n 'task',\n 'medium',\n `Task ${context.taskId} (${context.description || 'unnamed'}): status changed to ${context.status}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('task:tools_reminder', context => {\n const key = 'task_tools_reminder'\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'task_tools_reminder',\n 'task',\n 'low',\n \"The task tools haven't been used recently. If you're working on tasks that would benefit from tracking progress, consider using TaskCreate to add new tasks and TaskUpdate to update task status (set to in_progress when starting, completed when done). Also consider cleaning up the task list if it has become stale. Only use these if relevant to the current work. This is just a gentle reminder - ignore if not applicable. Make sure that you NEVER mention this reminder to the user\",\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Batch B: Output style events\n this.addEventListener('output_style:active', context => {\n const key = `output_style_active_${context.styleName}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'output_style_active',\n 'general',\n 'low',\n `Output style \"${context.styleName}\" is active. Follow its formatting instructions for your responses.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Plan mode events\n this.addEventListener('plan:file_reference', context => {\n const key = `plan_file_ref_${context.planPath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_file_reference',\n 'general',\n 'medium',\n `A plan file exists at: ${context.planPath}. Review it before making implementation decisions.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('plan:mode_reentry', context => {\n const key = `plan_reentry_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_mode_reentry',\n 'general',\n 'medium',\n 'Re-entering plan mode. Review the existing plan and update as needed before implementation.',\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n this.addEventListener('plan:exited', context => {\n const key = `plan_exited_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'plan_exited',\n 'general',\n 'medium',\n 'Exited plan mode. Proceed with implementation following the approved plan.',\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Skill invocation events\n this.addEventListener('skill:invoked', context => {\n const key = `skill_invoked_${context.skillName}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'skill_invoked',\n 'general',\n 'low',\n `Skill \"${context.skillName}\" was invoked. Follow the skill's instructions in the expanded prompt.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // MCP resource events\n this.addEventListener('mcp:resource_no_content', context => {\n const key = `mcp_no_content_${context.resourceUri}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'mcp_resource_no_content',\n 'general',\n 'low',\n `MCP resource \"${context.resourceUri}\" returned no content. It may be unavailable or empty.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Compact file reference events\n this.addEventListener('compact:file_reference', context => {\n const key = `compact_file_ref_${context.filePath}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'compact_file_reference',\n 'general',\n 'low',\n `Note: ${context.filePath} was read before the last conversation was summarized, but the contents are too large to include. Use Read tool if you need to access it.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Batch C: Token usage tracking\n this.addEventListener('token:usage', context => {\n const key = `token_usage_${context.threshold}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'token_usage',\n 'performance',\n 'low',\n `Token usage has reached ${context.percentage}% of context limit (${context.used}/${context.total} tokens).`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Budget tracking\n this.addEventListener('budget:usd', context => {\n const key = `budget_usd_${context.threshold}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'budget_usd',\n 'performance',\n 'medium',\n `USD budget usage: $${context.used.toFixed(2)} of $${context.limit.toFixed(2)} (${context.percentage}%).`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Team coordination\n this.addEventListener('team:coordination', context => {\n const key = `team_coord_${context.teamId}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'team_coordination',\n 'task',\n 'medium',\n `Team coordination: ${context.message}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Team shutdown\n this.addEventListener('team:shutdown', context => {\n const key = `team_shutdown_${context.teamId}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'team_shutdown',\n 'task',\n 'high',\n `Team \"${context.teamId}\" is shutting down. Reason: ${context.reason || 'completed'}`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // New diagnostics\n this.addEventListener('diagnostics:new', context => {\n const key = `diagnostics_${context.source}_${Date.now()}`\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const count = context.count || 1\n const reminder = this.createReminderMessage(\n 'diagnostics_new',\n 'general',\n 'low',\n `${count} new diagnostic${count > 1 ? 's' : ''} detected from ${context.source}. Review before proceeding.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n\n // Session continuation\n this.addEventListener('session:continuation', context => {\n const key = 'session_continuation'\n if (!this.sessionState.remindersSent.has(key)) {\n this.sessionState.remindersSent.add(key)\n const reminder = this.createReminderMessage(\n 'session_continuation',\n 'general',\n 'medium',\n `This session is a continuation of a previous conversation. Key context has been preserved.`,\n Date.now(),\n )\n this.reminderCache.set(key, reminder)\n }\n })\n }\n\n public addEventListener(\n event: string,\n callback: (context: any) => void,\n ): void {\n if (!this.eventDispatcher.has(event)) {\n this.eventDispatcher.set(event, [])\n }\n this.eventDispatcher.get(event)!.push(callback)\n }\n\n public emitEvent(event: string, context: any): void {\n const listeners = this.eventDispatcher.get(event) || []\n listeners.forEach(callback => {\n try {\n callback(context)\n } catch (error) {\n console.error(`Error in event listener for ${event}:`, error)\n }\n })\n }\n\n /**\n * Unified mention reminder creation - eliminates duplicate logic\n * Centralizes reminder creation with consistent deduplication\n */\n private createMentionReminder(params: {\n type: string\n key: string\n category: ReminderMessage['category']\n priority: ReminderMessage['priority']\n content: string\n timestamp: number\n }): void {\n if (!this.sessionState.remindersSent.has(params.key)) {\n this.sessionState.remindersSent.add(params.key)\n\n const reminder = this.createReminderMessage(\n params.type,\n params.category,\n params.priority,\n params.content,\n params.timestamp,\n )\n\n this.reminderCache.set(params.key, reminder)\n }\n }\n\n public resetSession(): void {\n this.sessionState = {\n lastTodoUpdate: 0,\n lastFileAccess: 0,\n sessionStartTime: Date.now(),\n remindersSent: new Set(),\n contextPresent: false,\n reminderCount: 0,\n config: { ...this.sessionState.config }, // Preserve config across resets\n }\n this.reminderCache.clear() // Clear cache on session reset\n }\n\n public updateConfig(config: Partial<ReminderConfig>): void {\n this.sessionState.config = { ...this.sessionState.config, ...config }\n }\n\n public getSessionState(): SessionReminderState {\n return { ...this.sessionState }\n }\n}\n\nexport const systemReminderService = new SystemReminderService()\n\nexport const generateSystemReminders = (\n hasContext: boolean = false,\n agentId?: string,\n) => systemReminderService.generateReminders(hasContext, agentId)\n\nexport const generateFileChangeReminder = (context: any) =>\n systemReminderService.generateFileChangeReminder(context)\n\nexport const emitReminderEvent = (event: string, context: any) =>\n systemReminderService.emitEvent(event, context)\n\nexport const resetReminderSession = () => systemReminderService.resetSession()\nexport const getReminderSessionState = () =>\n systemReminderService.getSessionState()\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import { getSessionState, setSessionState } from "../utils/sessionState.js";
2
2
  import { emitReminderEvent } from "./systemReminder.js";
3
+ import { getHookManager } from "../utils/hookManager.js";
3
4
  const TASK_STORAGE_KEY = "claudeCodeTasks";
4
5
  let taskCache = null;
5
6
  let cacheTimestamp = 0;
@@ -155,6 +156,24 @@ function updateTask(input) {
155
156
  updatedTask,
156
157
  timestamp: now
157
158
  });
159
+ if (input.status && input.status !== existingTask.status) {
160
+ emitReminderEvent("task:status", {
161
+ taskId: updatedTask.id,
162
+ status: updatedTask.status,
163
+ subject: updatedTask.subject
164
+ });
165
+ }
166
+ if (input.status === "completed") {
167
+ const hookMgr = getHookManager();
168
+ if (hookMgr) {
169
+ hookMgr.executeTaskCompleted(
170
+ updatedTask.id,
171
+ updatedTask.subject,
172
+ updatedTask.description
173
+ ).catch(() => {
174
+ });
175
+ }
176
+ }
158
177
  return updatedTask;
159
178
  }
160
179
  function getTaskById(taskId) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/services/taskStore.ts"],
4
- "sourcesContent": ["/**\n * Task Store Service - Claude Code Compatible\n *\n * Provides task management functionality compatible with Claude Code's\n * TaskCreate, TaskUpdate, TaskGet, TaskList tools.\n *\n * Task lifecycle: pending \u2192 in_progress \u2192 completed\n * Tasks can also be deleted (soft delete with 'deleted' status)\n */\n\nimport { getSessionState, setSessionState } from '@utils/sessionState'\nimport { emitReminderEvent } from '@services/systemReminder'\n\n/**\n * Task interface matching Claude Code's task structure\n */\nexport interface Task {\n id: string\n subject: string\n description: string\n status: 'pending' | 'in_progress' | 'completed'\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n blocks?: string[] // Task IDs that this task blocks\n blockedBy?: string[] // Task IDs that block this task\n createdAt: number\n updatedAt: number\n}\n\n/**\n * Task creation input (without auto-generated fields)\n */\nexport interface TaskCreateInput {\n subject: string\n description: string\n activeForm?: string\n metadata?: Record<string, unknown>\n}\n\n/**\n * Task update input (all fields optional except taskId)\n */\nexport interface TaskUpdateInput {\n taskId: string\n status?: 'pending' | 'in_progress' | 'completed' | 'deleted'\n subject?: string\n description?: string\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n addBlocks?: string[]\n addBlockedBy?: string[]\n}\n\nconst TASK_STORAGE_KEY = 'claudeCodeTasks'\n\n// In-memory cache\nlet taskCache: Task[] | null = null\nlet cacheTimestamp = 0\nconst CACHE_TTL = 5000 // 5 seconds\n\n// Auto-incrementing ID counter\nlet nextTaskId = 1\n\nfunction invalidateCache(): void {\n taskCache = null\n cacheTimestamp = 0\n}\n\nfunction getNextTaskId(): string {\n // Find the highest existing ID to avoid conflicts\n const tasks = getAllTasks()\n const existingIds = tasks\n .map(t => parseInt(t.id, 10))\n .filter(id => !isNaN(id))\n\n if (existingIds.length > 0) {\n nextTaskId = Math.max(...existingIds) + 1\n }\n\n return String(nextTaskId++)\n}\n\n/**\n * Generate activeForm from subject if not provided\n * Converts imperative form to present continuous\n */\nfunction generateActiveForm(subject: string): string {\n const trimmed = subject.trim()\n\n const patterns = [\n { regex: /^(Run|run)\\s+(.+)$/i, replacement: 'Running $2' },\n { regex: /^(Build|build)\\s+(.+)$/i, replacement: 'Building $2' },\n { regex: /^(Fix|fix)\\s+(.+)$/i, replacement: 'Fixing $2' },\n { regex: /^(Add|add)\\s+(.+)$/i, replacement: 'Adding $2' },\n { regex: /^(Create|create)\\s+(.+)$/i, replacement: 'Creating $2' },\n { regex: /^(Update|update)\\s+(.+)$/i, replacement: 'Updating $2' },\n { regex: /^(Delete|delete)\\s+(.+)$/i, replacement: 'Deleting $2' },\n { regex: /^(Test|test)\\s+(.+)$/i, replacement: 'Testing $2' },\n { regex: /^(Deploy|deploy)\\s+(.+)$/i, replacement: 'Deploying $2' },\n { regex: /^(Analyze|analyze)\\s+(.+)$/i, replacement: 'Analyzing $2' },\n { regex: /^(Review|review)\\s+(.+)$/i, replacement: 'Reviewing $2' },\n { regex: /^(Write|write)\\s+(.+)$/i, replacement: 'Writing $2' },\n {\n regex: /^(Implement|implement)\\s+(.+)$/i,\n replacement: 'Implementing $2',\n },\n { regex: /^(Refactor|refactor)\\s+(.+)$/i, replacement: 'Refactoring $2' },\n { regex: /^(Debug|debug)\\s+(.+)$/i, replacement: 'Debugging $2' },\n { regex: /^(Configure|configure)\\s+(.+)$/i, replacement: 'Configuring $2' },\n { regex: /^(Install|install)\\s+(.+)$/i, replacement: 'Installing $2' },\n { regex: /^(Remove|remove)\\s+(.+)$/i, replacement: 'Removing $2' },\n { regex: /^(Verify|verify)\\s+(.+)$/i, replacement: 'Verifying $2' },\n { regex: /^(Check|check)\\s+(.+)$/i, replacement: 'Checking $2' },\n ]\n\n for (const { regex, replacement } of patterns) {\n if (regex.test(trimmed)) {\n return trimmed.replace(regex, replacement)\n }\n }\n\n return `Working on: ${trimmed}`\n}\n\n/**\n * Get all tasks from storage\n */\nexport function getAllTasks(): Task[] {\n const now = Date.now()\n\n // Check cache first\n if (taskCache && now - cacheTimestamp < CACHE_TTL) {\n return taskCache\n }\n\n const sessionState = getSessionState() as Record<string, unknown>\n const tasks = (sessionState[TASK_STORAGE_KEY] as Task[]) || []\n\n // Update cache\n taskCache = [...tasks]\n cacheTimestamp = now\n\n return tasks\n}\n\n/**\n * Save tasks to storage\n */\nfunction saveTasks(tasks: Task[]): void {\n setSessionState({\n ...getSessionState(),\n [TASK_STORAGE_KEY]: tasks,\n } as Record<string, unknown>)\n\n invalidateCache()\n}\n\n/**\n * Create a new task\n */\nexport function createTask(input: TaskCreateInput): Task {\n const tasks = getAllTasks()\n const now = Date.now()\n\n const newTask: Task = {\n id: getNextTaskId(),\n subject: input.subject,\n description: input.description,\n status: 'pending',\n activeForm: input.activeForm || generateActiveForm(input.subject),\n metadata: input.metadata,\n createdAt: now,\n updatedAt: now,\n }\n\n const updatedTasks = [...tasks, newTask]\n saveTasks(updatedTasks)\n\n // Emit event for system reminders\n emitReminderEvent('task:created', {\n task: newTask,\n timestamp: now,\n })\n\n return newTask\n}\n\n/**\n * Update an existing task\n */\nexport function updateTask(input: TaskUpdateInput): Task | null {\n const tasks = getAllTasks()\n const taskIndex = tasks.findIndex(t => t.id === input.taskId)\n\n if (taskIndex === -1) {\n return null\n }\n\n const existingTask = tasks[taskIndex]\n const now = Date.now()\n\n // Handle deletion\n if (input.status === 'deleted') {\n const updatedTasks = tasks.filter(t => t.id !== input.taskId)\n saveTasks(updatedTasks)\n\n emitReminderEvent('task:deleted', {\n taskId: input.taskId,\n timestamp: now,\n })\n\n return existingTask\n }\n\n // Build updated task\n const updatedTask: Task = {\n ...existingTask,\n updatedAt: now,\n }\n\n if (input.status !== undefined) {\n updatedTask.status = input.status\n }\n if (input.subject !== undefined) {\n updatedTask.subject = input.subject\n }\n if (input.description !== undefined) {\n updatedTask.description = input.description\n }\n if (input.activeForm !== undefined) {\n updatedTask.activeForm = input.activeForm\n }\n if (input.owner !== undefined) {\n updatedTask.owner = input.owner\n }\n if (input.metadata !== undefined) {\n // Merge metadata, null values delete keys\n const newMetadata = { ...existingTask.metadata }\n for (const [key, value] of Object.entries(input.metadata)) {\n if (value === null) {\n delete newMetadata[key]\n } else {\n newMetadata[key] = value\n }\n }\n updatedTask.metadata =\n Object.keys(newMetadata).length > 0 ? newMetadata : undefined\n }\n\n // Handle dependency updates\n if (input.addBlocks && input.addBlocks.length > 0) {\n const currentBlocks = new Set(existingTask.blocks || [])\n input.addBlocks.forEach(id => currentBlocks.add(id))\n updatedTask.blocks = Array.from(currentBlocks)\n }\n if (input.addBlockedBy && input.addBlockedBy.length > 0) {\n const currentBlockedBy = new Set(existingTask.blockedBy || [])\n input.addBlockedBy.forEach(id => currentBlockedBy.add(id))\n updatedTask.blockedBy = Array.from(currentBlockedBy)\n }\n\n // Update tasks array\n const updatedTasks = [...tasks]\n updatedTasks[taskIndex] = updatedTask\n saveTasks(updatedTasks)\n\n // Emit event\n emitReminderEvent('task:updated', {\n previousTask: existingTask,\n updatedTask,\n timestamp: now,\n })\n\n return updatedTask\n}\n\n/**\n * Get a task by ID\n */\nexport function getTaskById(taskId: string): Task | null {\n const tasks = getAllTasks()\n return tasks.find(t => t.id === taskId) || null\n}\n\n/**\n * Get task list summary for display\n * Returns tasks with summary info suitable for listing\n */\nexport function getTaskList(): Array<{\n id: string\n subject: string\n status: 'pending' | 'in_progress' | 'completed'\n owner?: string\n blockedBy?: string[]\n}> {\n const tasks = getAllTasks()\n\n // Filter out tasks blocked by incomplete tasks\n return tasks.map(task => {\n // Check which blockedBy tasks are still open\n const openBlockers = (task.blockedBy || []).filter(blockerId => {\n const blocker = tasks.find(t => t.id === blockerId)\n return blocker && blocker.status !== 'completed'\n })\n\n return {\n id: task.id,\n subject: task.subject,\n status: task.status,\n owner: task.owner,\n blockedBy: openBlockers.length > 0 ? openBlockers : undefined,\n }\n })\n}\n\n/**\n * Clear all tasks (for testing or reset)\n */\nexport function clearAllTasks(): void {\n saveTasks([])\n nextTaskId = 1\n\n emitReminderEvent('task:cleared', {\n timestamp: Date.now(),\n })\n}\n\n/**\n * Get task statistics\n */\nexport function getTaskStatistics(): {\n total: number\n pending: number\n inProgress: number\n completed: number\n} {\n const tasks = getAllTasks()\n\n return {\n total: tasks.length,\n pending: tasks.filter(t => t.status === 'pending').length,\n inProgress: tasks.filter(t => t.status === 'in_progress').length,\n completed: tasks.filter(t => t.status === 'completed').length,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Task Store Service - Claude Code Compatible\n *\n * Provides task management functionality compatible with Claude Code's\n * TaskCreate, TaskUpdate, TaskGet, TaskList tools.\n *\n * Task lifecycle: pending \u2192 in_progress \u2192 completed\n * Tasks can also be deleted (soft delete with 'deleted' status)\n */\n\nimport { getSessionState, setSessionState } from '@utils/sessionState'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { getHookManager } from '@utils/hookManager'\n\n/**\n * Task interface matching Claude Code's task structure\n */\nexport interface Task {\n id: string\n subject: string\n description: string\n status: 'pending' | 'in_progress' | 'completed'\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n blocks?: string[] // Task IDs that this task blocks\n blockedBy?: string[] // Task IDs that block this task\n createdAt: number\n updatedAt: number\n}\n\n/**\n * Task creation input (without auto-generated fields)\n */\nexport interface TaskCreateInput {\n subject: string\n description: string\n activeForm?: string\n metadata?: Record<string, unknown>\n}\n\n/**\n * Task update input (all fields optional except taskId)\n */\nexport interface TaskUpdateInput {\n taskId: string\n status?: 'pending' | 'in_progress' | 'completed' | 'deleted'\n subject?: string\n description?: string\n activeForm?: string\n owner?: string\n metadata?: Record<string, unknown>\n addBlocks?: string[]\n addBlockedBy?: string[]\n}\n\nconst TASK_STORAGE_KEY = 'claudeCodeTasks'\n\n// In-memory cache\nlet taskCache: Task[] | null = null\nlet cacheTimestamp = 0\nconst CACHE_TTL = 5000 // 5 seconds\n\n// Auto-incrementing ID counter\nlet nextTaskId = 1\n\nfunction invalidateCache(): void {\n taskCache = null\n cacheTimestamp = 0\n}\n\nfunction getNextTaskId(): string {\n // Find the highest existing ID to avoid conflicts\n const tasks = getAllTasks()\n const existingIds = tasks\n .map(t => parseInt(t.id, 10))\n .filter(id => !isNaN(id))\n\n if (existingIds.length > 0) {\n nextTaskId = Math.max(...existingIds) + 1\n }\n\n return String(nextTaskId++)\n}\n\n/**\n * Generate activeForm from subject if not provided\n * Converts imperative form to present continuous\n */\nfunction generateActiveForm(subject: string): string {\n const trimmed = subject.trim()\n\n const patterns = [\n { regex: /^(Run|run)\\s+(.+)$/i, replacement: 'Running $2' },\n { regex: /^(Build|build)\\s+(.+)$/i, replacement: 'Building $2' },\n { regex: /^(Fix|fix)\\s+(.+)$/i, replacement: 'Fixing $2' },\n { regex: /^(Add|add)\\s+(.+)$/i, replacement: 'Adding $2' },\n { regex: /^(Create|create)\\s+(.+)$/i, replacement: 'Creating $2' },\n { regex: /^(Update|update)\\s+(.+)$/i, replacement: 'Updating $2' },\n { regex: /^(Delete|delete)\\s+(.+)$/i, replacement: 'Deleting $2' },\n { regex: /^(Test|test)\\s+(.+)$/i, replacement: 'Testing $2' },\n { regex: /^(Deploy|deploy)\\s+(.+)$/i, replacement: 'Deploying $2' },\n { regex: /^(Analyze|analyze)\\s+(.+)$/i, replacement: 'Analyzing $2' },\n { regex: /^(Review|review)\\s+(.+)$/i, replacement: 'Reviewing $2' },\n { regex: /^(Write|write)\\s+(.+)$/i, replacement: 'Writing $2' },\n {\n regex: /^(Implement|implement)\\s+(.+)$/i,\n replacement: 'Implementing $2',\n },\n { regex: /^(Refactor|refactor)\\s+(.+)$/i, replacement: 'Refactoring $2' },\n { regex: /^(Debug|debug)\\s+(.+)$/i, replacement: 'Debugging $2' },\n { regex: /^(Configure|configure)\\s+(.+)$/i, replacement: 'Configuring $2' },\n { regex: /^(Install|install)\\s+(.+)$/i, replacement: 'Installing $2' },\n { regex: /^(Remove|remove)\\s+(.+)$/i, replacement: 'Removing $2' },\n { regex: /^(Verify|verify)\\s+(.+)$/i, replacement: 'Verifying $2' },\n { regex: /^(Check|check)\\s+(.+)$/i, replacement: 'Checking $2' },\n ]\n\n for (const { regex, replacement } of patterns) {\n if (regex.test(trimmed)) {\n return trimmed.replace(regex, replacement)\n }\n }\n\n return `Working on: ${trimmed}`\n}\n\n/**\n * Get all tasks from storage\n */\nexport function getAllTasks(): Task[] {\n const now = Date.now()\n\n // Check cache first\n if (taskCache && now - cacheTimestamp < CACHE_TTL) {\n return taskCache\n }\n\n const sessionState = getSessionState() as Record<string, unknown>\n const tasks = (sessionState[TASK_STORAGE_KEY] as Task[]) || []\n\n // Update cache\n taskCache = [...tasks]\n cacheTimestamp = now\n\n return tasks\n}\n\n/**\n * Save tasks to storage\n */\nfunction saveTasks(tasks: Task[]): void {\n setSessionState({\n ...getSessionState(),\n [TASK_STORAGE_KEY]: tasks,\n } as Record<string, unknown>)\n\n invalidateCache()\n}\n\n/**\n * Create a new task\n */\nexport function createTask(input: TaskCreateInput): Task {\n const tasks = getAllTasks()\n const now = Date.now()\n\n const newTask: Task = {\n id: getNextTaskId(),\n subject: input.subject,\n description: input.description,\n status: 'pending',\n activeForm: input.activeForm || generateActiveForm(input.subject),\n metadata: input.metadata,\n createdAt: now,\n updatedAt: now,\n }\n\n const updatedTasks = [...tasks, newTask]\n saveTasks(updatedTasks)\n\n // Emit event for system reminders\n emitReminderEvent('task:created', {\n task: newTask,\n timestamp: now,\n })\n\n return newTask\n}\n\n/**\n * Update an existing task\n */\nexport function updateTask(input: TaskUpdateInput): Task | null {\n const tasks = getAllTasks()\n const taskIndex = tasks.findIndex(t => t.id === input.taskId)\n\n if (taskIndex === -1) {\n return null\n }\n\n const existingTask = tasks[taskIndex]\n const now = Date.now()\n\n // Handle deletion\n if (input.status === 'deleted') {\n const updatedTasks = tasks.filter(t => t.id !== input.taskId)\n saveTasks(updatedTasks)\n\n emitReminderEvent('task:deleted', {\n taskId: input.taskId,\n timestamp: now,\n })\n\n return existingTask\n }\n\n // Build updated task\n const updatedTask: Task = {\n ...existingTask,\n updatedAt: now,\n }\n\n if (input.status !== undefined) {\n updatedTask.status = input.status\n }\n if (input.subject !== undefined) {\n updatedTask.subject = input.subject\n }\n if (input.description !== undefined) {\n updatedTask.description = input.description\n }\n if (input.activeForm !== undefined) {\n updatedTask.activeForm = input.activeForm\n }\n if (input.owner !== undefined) {\n updatedTask.owner = input.owner\n }\n if (input.metadata !== undefined) {\n // Merge metadata, null values delete keys\n const newMetadata = { ...existingTask.metadata }\n for (const [key, value] of Object.entries(input.metadata)) {\n if (value === null) {\n delete newMetadata[key]\n } else {\n newMetadata[key] = value\n }\n }\n updatedTask.metadata =\n Object.keys(newMetadata).length > 0 ? newMetadata : undefined\n }\n\n // Handle dependency updates\n if (input.addBlocks && input.addBlocks.length > 0) {\n const currentBlocks = new Set(existingTask.blocks || [])\n input.addBlocks.forEach(id => currentBlocks.add(id))\n updatedTask.blocks = Array.from(currentBlocks)\n }\n if (input.addBlockedBy && input.addBlockedBy.length > 0) {\n const currentBlockedBy = new Set(existingTask.blockedBy || [])\n input.addBlockedBy.forEach(id => currentBlockedBy.add(id))\n updatedTask.blockedBy = Array.from(currentBlockedBy)\n }\n\n // Update tasks array\n const updatedTasks = [...tasks]\n updatedTasks[taskIndex] = updatedTask\n saveTasks(updatedTasks)\n\n // Emit event\n emitReminderEvent('task:updated', {\n previousTask: existingTask,\n updatedTask,\n timestamp: now,\n })\n\n // Emit task:status when status changes\n if (input.status && input.status !== existingTask.status) {\n emitReminderEvent('task:status', {\n taskId: updatedTask.id,\n status: updatedTask.status,\n subject: updatedTask.subject,\n })\n }\n\n // Fire TaskCompleted hook when status changes to 'completed'\n if (input.status === 'completed') {\n const hookMgr = getHookManager()\n if (hookMgr) {\n hookMgr\n .executeTaskCompleted(\n updatedTask.id,\n updatedTask.subject,\n updatedTask.description,\n )\n .catch(() => {\n // Hook errors don't break task completion\n })\n }\n }\n\n return updatedTask\n}\n\n/**\n * Get a task by ID\n */\nexport function getTaskById(taskId: string): Task | null {\n const tasks = getAllTasks()\n return tasks.find(t => t.id === taskId) || null\n}\n\n/**\n * Get task list summary for display\n * Returns tasks with summary info suitable for listing\n */\nexport function getTaskList(): Array<{\n id: string\n subject: string\n status: 'pending' | 'in_progress' | 'completed'\n owner?: string\n blockedBy?: string[]\n}> {\n const tasks = getAllTasks()\n\n // Filter out tasks blocked by incomplete tasks\n return tasks.map(task => {\n // Check which blockedBy tasks are still open\n const openBlockers = (task.blockedBy || []).filter(blockerId => {\n const blocker = tasks.find(t => t.id === blockerId)\n return blocker && blocker.status !== 'completed'\n })\n\n return {\n id: task.id,\n subject: task.subject,\n status: task.status,\n owner: task.owner,\n blockedBy: openBlockers.length > 0 ? openBlockers : undefined,\n }\n })\n}\n\n/**\n * Clear all tasks (for testing or reset)\n */\nexport function clearAllTasks(): void {\n saveTasks([])\n nextTaskId = 1\n\n emitReminderEvent('task:cleared', {\n timestamp: Date.now(),\n })\n}\n\n/**\n * Get task statistics\n */\nexport function getTaskStatistics(): {\n total: number\n pending: number\n inProgress: number\n completed: number\n} {\n const tasks = getAllTasks()\n\n return {\n total: tasks.length,\n pending: tasks.filter(t => t.status === 'pending').length,\n inProgress: tasks.filter(t => t.status === 'in_progress').length,\n completed: tasks.filter(t => t.status === 'completed').length,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["updatedTasks"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/ArchitectTool/ArchitectTool.tsx"],
4
- "sourcesContent": ["import type { TextBlock } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { Box } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport type { Tool } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { getContext } from '@context'\nimport { Message, query } from '@query'\nimport { lastX } from '@utils/generators'\nimport { createUserMessage } from '@utils/messages'\nimport { BashTool } from '@tools/BashTool/BashTool'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { FileWriteTool } from '@tools/FileWriteTool/FileWriteTool'\nimport { GlobTool } from '@tools/GlobTool/GlobTool'\nimport { GrepTool } from '@tools/GrepTool/GrepTool'\nimport { LSTool } from '@tools/lsTool/lsTool'\nimport { ARCHITECT_SYSTEM_PROMPT, DESCRIPTION } from './prompt'\n\nconst FS_EXPLORATION_TOOLS: Tool[] = [\n BashTool,\n LSTool,\n FileReadTool,\n FileWriteTool,\n GlobTool,\n GrepTool,\n]\n\nconst inputSchema = z.strictObject({\n prompt: z\n .string()\n .describe('The technical request or coding task to analyze'),\n context: z\n .string()\n .describe('Optional context from previous conversation or system state')\n .optional(),\n})\n\nexport const ArchitectTool = {\n name: 'Architect',\n async description() {\n return DESCRIPTION\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // ArchitectTool is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Architect'\n },\n async isEnabled() {\n return false\n },\n needsPermissions() {\n return false\n },\n async *call({ prompt, context }, toolUseContext) {\n const content = context\n ? `<context>${context}</context>\\n\\n${prompt}`\n : prompt\n\n const userMessage = createUserMessage(content)\n\n const messages: Message[] = [userMessage]\n\n // We only allow the file exploration tools to be used in the architect tool\n const allowedTools = (toolUseContext.options?.tools ?? []).filter(_ =>\n FS_EXPLORATION_TOOLS.map(_ => _.name).includes(_.name),\n )\n\n // Create a dummy canUseTool function since this tool controls its own tool usage\n const canUseTool = async () => ({ result: true as const })\n\n const lastResponse = await lastX(\n query(\n messages,\n [ARCHITECT_SYSTEM_PROMPT],\n await getContext(),\n canUseTool,\n {\n ...toolUseContext,\n setToolJSX: () => {}, // Dummy function since ArchitectTool doesn't use UI\n options: {\n commands: toolUseContext.options?.commands || [],\n forkNumber: toolUseContext.options?.forkNumber || 0,\n messageLogName: toolUseContext.options?.messageLogName || 'default',\n verbose: toolUseContext.options?.verbose || false,\n safeMode: toolUseContext.options?.safeMode || false,\n maxThinkingTokens: toolUseContext.options?.maxThinkingTokens || 0,\n ...toolUseContext.options,\n tools: allowedTools,\n },\n },\n ),\n )\n\n if (lastResponse.type !== 'assistant') {\n throw new Error(`Invalid response from API`)\n }\n\n const data = lastResponse.message.content.filter(_ => _.type === 'text')\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n async prompt() {\n return DESCRIPTION\n },\n renderResultForAssistant(data: TextBlock[]): string {\n // Guard against undefined or null data\n if (!data || !Array.isArray(data)) {\n return 'Analysis completed'\n }\n return data\n .filter(block => block != null)\n .map(block => block.text || '')\n .join('\\n')\n },\n renderToolUseMessage(input) {\n return Object.entries(input || {})\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(content) {\n // Guard against undefined or null content\n if (!content || !Array.isArray(content)) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <HighlightedCode code=\"Analysis completed\" language=\"markdown\" />\n </Box>\n )\n }\n\n const code = content\n .filter(block => block != null)\n .map(block => block.text || '')\n .join('\\n')\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <HighlightedCode\n code={code || 'Analysis completed'}\n language=\"markdown\"\n />\n </Box>\n )\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n} satisfies Tool<typeof inputSchema, TextBlock[]>\n"],
4
+ "sourcesContent": ["import type { TextBlock } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { Box } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport type { Tool } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { getContext } from '@context'\nimport { Message, query } from '@query'\nimport { lastX } from '@utils/generators'\nimport { createUserMessage } from '@utils/messages'\nimport { BashTool } from '@tools/BashTool/BashTool'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { FileWriteTool } from '@tools/FileWriteTool/FileWriteTool'\nimport { GlobTool } from '@tools/GlobTool/GlobTool'\nimport { GrepTool } from '@tools/GrepTool/GrepTool'\nimport { LSTool } from '@tools/lsTool/lsTool'\nimport { ARCHITECT_SYSTEM_PROMPT, DESCRIPTION } from './prompt'\n\nconst FS_EXPLORATION_TOOLS: Tool[] = [\n BashTool,\n LSTool,\n FileReadTool,\n FileWriteTool,\n GlobTool,\n GrepTool,\n]\n\nconst inputSchema = z.strictObject({\n prompt: z\n .string()\n .describe('The technical request or coding task to analyze'),\n context: z\n .string()\n .describe('Optional context from previous conversation or system state')\n .optional(),\n})\n\nexport const ArchitectTool = {\n name: 'Architect',\n async description() {\n return DESCRIPTION\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // ArchitectTool is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Architect'\n },\n async isEnabled() {\n return false\n },\n needsPermissions() {\n return false\n },\n async *call({ prompt, context }, toolUseContext) {\n const content = context\n ? `<context>${context}</context>\\n\\n${prompt}`\n : prompt\n\n const userMessage = createUserMessage(content)\n\n const messages: Message[] = [userMessage]\n\n // We only allow the file exploration tools to be used in the architect tool\n const allowedTools = (toolUseContext.options?.tools ?? []).filter(_ =>\n FS_EXPLORATION_TOOLS.map(_ => _.name).includes(_.name),\n )\n\n // Create a dummy canUseTool function since this tool controls its own tool usage\n const canUseTool = async () => ({ result: true as const })\n\n const lastResponse = await lastX(\n query(\n messages,\n [ARCHITECT_SYSTEM_PROMPT],\n await getContext(),\n canUseTool,\n {\n ...toolUseContext,\n setToolJSX: () => {}, // Dummy function since ArchitectTool doesn't use UI\n options: {\n commands: toolUseContext.options?.commands || [],\n forkNumber: toolUseContext.options?.forkNumber || 0,\n messageLogName: toolUseContext.options?.messageLogName || 'default',\n verbose: toolUseContext.options?.verbose || false,\n safeMode: toolUseContext.options?.safeMode || false,\n maxThinkingTokens: toolUseContext.options?.maxThinkingTokens || 0,\n ...toolUseContext.options,\n tools: allowedTools,\n },\n },\n ),\n )\n\n if (lastResponse.type !== 'assistant') {\n throw new Error(`Invalid response from API`)\n }\n\n const data = lastResponse.message.content.filter(_ => _.type === 'text')\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n async prompt() {\n return DESCRIPTION\n },\n renderResultForAssistant(data: TextBlock[]): string {\n // Guard against undefined or null data\n if (!data || !Array.isArray(data)) {\n return 'Analysis completed'\n }\n return data\n .filter(block => block != null)\n .map(block => block.text || '')\n .join('\\n')\n },\n renderToolUseMessage(input) {\n return Object.entries(input || {})\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(content) {\n // Guard against undefined or null content\n if (!content || !Array.isArray(content)) {\n return (\n <Box flexDirection=\"column\" gap={1}>\n <HighlightedCode code=\"Analysis completed\" language=\"markdown\" />\n </Box>\n )\n }\n\n const code = content\n .filter(block => block != null)\n .map(block => block.text || '')\n .join('\\n')\n\n return (\n <Box flexDirection=\"column\" gap={1}>\n <HighlightedCode\n code={code || 'Analysis completed'}\n language=\"markdown\"\n />\n </Box>\n )\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n} satisfies Tool\n"],
5
5
  "mappings": "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",
6
6
  "names": ["_"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { Tool, type ExtendedToolUseContext } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport type { UserAnswer } from '@minto-types/askUserQuestion'\nimport { SEMANTIC_COLORS } from '@constants/colors'\n\nconst inputSchema = z.strictObject({\n questions: z\n .array(\n z.object({\n question: z.string().describe('The question to ask the user'),\n header: z\n .string()\n .max(12)\n .optional()\n .describe('Short category label (max 12 chars)'),\n options: z\n .array(\n z.object({\n label: z.string().describe('Option display text'),\n description: z.string().optional().describe('Option explanation'),\n }),\n )\n .min(2)\n .max(4)\n .optional()\n .describe('List of options (2-4 recommended)'),\n multiSelect: z\n .boolean()\n .optional()\n .default(false)\n .describe('Allow multiple selections'),\n }),\n )\n .min(1)\n .max(4)\n .describe('Questions to ask (1-4 questions)'),\n answers: z\n .record(z.string(), z.string())\n .optional()\n .describe('User answers collected by the permission component'),\n metadata: z\n .object({\n source: z\n .string()\n .optional()\n .describe(\n 'Optional identifier for the source of this question (e.g., \"remember\" for /remember command). Used for analytics tracking.',\n ),\n })\n .optional()\n .describe(\n 'Optional metadata for tracking and analytics purposes. Not displayed to user.',\n ),\n})\n\nexport type AskUserQuestionOutput = {\n answers: UserAnswer[]\n timestamp: number\n metadata?: {\n source?: string\n }\n}\n\nexport const AskUserQuestionTool = {\n name: 'AskUserQuestion',\n\n async description() {\n return DESCRIPTION\n },\n\n async prompt() {\n return PROMPT\n },\n\n inputSchema,\n\n userFacingName() {\n return 'Ask User Question'\n },\n\n async isEnabled() {\n return true\n },\n\n isReadOnly() {\n return true // Doesn't modify files\n },\n\n isConcurrencySafe() {\n return false // User interaction is not concurrent-safe\n },\n\n needsPermissions() {\n return false // No special permissions needed\n },\n\n renderResultForAssistant(output: AskUserQuestionOutput) {\n const formattedAnswers = output.answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')\n\n return `User answered:\\n${formattedAnswers}`\n },\n\n renderToolUseMessage(input: z.infer<typeof inputSchema>) {\n const questionCount = input.questions.length\n const questionText =\n questionCount === 1\n ? input.questions[0]!.question\n : `${questionCount} questions`\n return `Asking user: ${questionText}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(output: AskUserQuestionOutput) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>Question completed</Text>\n </Box>\n </Box>\n )\n }\n\n const answers = output.answers || []\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>User provided answers</Text>\n </Box>\n {answers.map((answer, index) => {\n // Guard against null/undefined answers\n if (!answer) return null\n return (\n <Box key={index} flexDirection=\"row\" paddingLeft={4}>\n <Text color=\"#6B7280\">\u2022 </Text>\n {answer.customInput ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: \"{answer.customInput}\"\n </Text>\n ) : answer.selectedOptions ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: Option{' '}\n {answer.selectedOptions.map(i => i + 1).join(', ')}\n </Text>\n ) : (\n <Text color={SEMANTIC_COLORS.dim}>\n Q{(answer.questionIndex ?? index) + 1}: No answer\n </Text>\n )}\n </Box>\n )\n })}\n </Box>\n )\n },\n\n async *call(\n {\n questions,\n answers: providedAnswers,\n metadata,\n }: z.infer<typeof inputSchema>,\n context: ExtendedToolUseContext,\n ) {\n // If answers are already provided (from permission component), use them directly\n if (providedAnswers) {\n // Convert provided answers (Record<string, string>) to UserAnswer[]\n const convertedAnswers: UserAnswer[] = Object.entries(\n providedAnswers,\n ).map(([key, value], index) => ({\n questionIndex: index,\n customInput: value,\n }))\n\n yield {\n type: 'result',\n data: {\n answers: convertedAnswers,\n timestamp: Date.now(),\n ...(metadata && { metadata }),\n },\n resultForAssistant: `User answered:\\n${convertedAnswers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n return\n }\n\n // Check if askUser function is available\n if (!context.askUser) {\n throw new Error(\n 'AskUserQuestion tool requires askUser function in context',\n )\n }\n\n try {\n // Convert parsed questions to UserQuestion type\n const userQuestions = questions.map(q => ({\n question: q.question,\n header: q.header,\n options: q.options?.map(opt => ({\n label: opt.label,\n description: opt.description,\n })),\n multiSelect: q.multiSelect ?? false,\n }))\n\n // Call askUser and wait for user response\n const answers = await context.askUser(userQuestions)\n\n // Return the result\n yield {\n type: 'result',\n data: {\n answers,\n timestamp: Date.now(),\n ...(metadata && { metadata }),\n },\n resultForAssistant: `User answered:\\n${answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n } catch (error) {\n // User cancelled or error occurred\n throw new Error(\n `Failed to get user response: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n },\n} satisfies Tool<typeof inputSchema, AskUserQuestionOutput>\n"],
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { Tool, type ExtendedToolUseContext } from '@tool'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport type { UserAnswer } from '@minto-types/askUserQuestion'\nimport { SEMANTIC_COLORS } from '@constants/colors'\n\nconst inputSchema = z.strictObject({\n questions: z\n .array(\n z.object({\n question: z.string().describe('The question to ask the user'),\n header: z\n .string()\n .max(12)\n .optional()\n .describe('Short category label (max 12 chars)'),\n options: z\n .array(\n z.object({\n label: z.string().describe('Option display text'),\n description: z.string().optional().describe('Option explanation'),\n }),\n )\n .min(2)\n .max(4)\n .optional()\n .describe('List of options (2-4 recommended)'),\n multiSelect: z\n .boolean()\n .optional()\n .default(false)\n .describe('Allow multiple selections'),\n }),\n )\n .min(1)\n .max(4)\n .describe('Questions to ask (1-4 questions)'),\n answers: z\n .record(z.string(), z.string())\n .optional()\n .describe('User answers collected by the permission component'),\n metadata: z\n .object({\n source: z\n .string()\n .optional()\n .describe(\n 'Optional identifier for the source of this question (e.g., \"remember\" for /remember command). Used for analytics tracking.',\n ),\n })\n .optional()\n .describe(\n 'Optional metadata for tracking and analytics purposes. Not displayed to user.',\n ),\n})\n\nexport type AskUserQuestionOutput = {\n answers: UserAnswer[]\n timestamp: number\n metadata?: {\n source?: string\n }\n}\n\nexport const AskUserQuestionTool = {\n name: 'AskUserQuestion',\n\n async description() {\n return DESCRIPTION\n },\n\n async prompt() {\n return PROMPT\n },\n\n inputSchema,\n\n userFacingName() {\n return 'Ask User Question'\n },\n\n async isEnabled() {\n return true\n },\n\n isReadOnly() {\n return true // Doesn't modify files\n },\n\n isConcurrencySafe() {\n return false // User interaction is not concurrent-safe\n },\n\n needsPermissions() {\n return false // No special permissions needed\n },\n\n renderResultForAssistant(output: AskUserQuestionOutput) {\n const formattedAnswers = output.answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')\n\n return `User answered:\\n${formattedAnswers}`\n },\n\n renderToolUseMessage(input: z.infer<typeof inputSchema>) {\n const questionCount = input.questions.length\n const questionText =\n questionCount === 1\n ? input.questions[0]!.question\n : `${questionCount} questions`\n return `Asking user: ${questionText}`\n },\n\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(output: AskUserQuestionOutput) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>Question completed</Text>\n </Box>\n </Box>\n )\n }\n\n const answers = output.answers || []\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n <Box flexDirection=\"row\">\n <Text color=\"#10B981\">{' '}\u23BF </Text>\n <Text>User provided answers</Text>\n </Box>\n {answers.map((answer, index) => {\n // Guard against null/undefined answers\n if (!answer) return null\n return (\n <Box key={index} flexDirection=\"row\" paddingLeft={4}>\n <Text color=\"#6B7280\">\u2022 </Text>\n {answer.customInput ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: \"{answer.customInput}\"\n </Text>\n ) : answer.selectedOptions ? (\n <Text>\n Q{(answer.questionIndex ?? index) + 1}: Option{' '}\n {answer.selectedOptions.map(i => i + 1).join(', ')}\n </Text>\n ) : (\n <Text color={SEMANTIC_COLORS.dim}>\n Q{(answer.questionIndex ?? index) + 1}: No answer\n </Text>\n )}\n </Box>\n )\n })}\n </Box>\n )\n },\n\n async *call(\n {\n questions,\n answers: providedAnswers,\n metadata,\n }: z.infer<typeof inputSchema>,\n context: ExtendedToolUseContext,\n ) {\n // If answers are already provided (from permission component), use them directly\n if (providedAnswers) {\n // Convert provided answers (Record<string, string>) to UserAnswer[]\n const convertedAnswers: UserAnswer[] = Object.entries(\n providedAnswers,\n ).map(([key, value], index) => ({\n questionIndex: index,\n customInput: value,\n }))\n\n yield {\n type: 'result',\n data: {\n answers: convertedAnswers,\n timestamp: Date.now(),\n ...(metadata && { metadata }),\n },\n resultForAssistant: `User answered:\\n${convertedAnswers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n return\n }\n\n // Check if askUser function is available\n if (!context.askUser) {\n throw new Error(\n 'AskUserQuestion tool requires askUser function in context',\n )\n }\n\n try {\n // Convert parsed questions to UserQuestion type\n const userQuestions = questions.map(q => ({\n question: q.question,\n header: q.header,\n options: q.options?.map(opt => ({\n label: opt.label,\n description: opt.description,\n })),\n multiSelect: q.multiSelect ?? false,\n }))\n\n // Call askUser and wait for user response\n const answers = await context.askUser(userQuestions)\n\n // Return the result\n yield {\n type: 'result',\n data: {\n answers,\n timestamp: Date.now(),\n ...(metadata && { metadata }),\n },\n resultForAssistant: `User answered:\\n${answers\n .map(answer => {\n if (answer.customInput) {\n return `Q${answer.questionIndex + 1}: \"${answer.customInput}\"`\n }\n if (answer.selectedOptions) {\n return `Q${answer.questionIndex + 1}: Option ${answer.selectedOptions.map(i => i + 1).join(', ')}`\n }\n return `Q${answer.questionIndex + 1}: No answer`\n })\n .join('\\n')}`,\n }\n } catch (error) {\n // User cancelled or error occurred\n throw new Error(\n `Failed to get user response: ${error instanceof Error ? error.message : 'Unknown error'}`,\n )\n }\n },\n} satisfies Tool\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/BashOutputTool/BashOutputTool.tsx"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport { BashOutputToolResultMessage } from './BashOutputToolResultMessage'\nimport { PROMPT } from './prompt'\n\nexport const inputSchema = z.strictObject({\n bash_id: z\n .string()\n .describe('The ID of the background shell to retrieve output from'),\n filter: z\n .string()\n .optional()\n .describe(\n 'Optional regular expression to filter the output lines. Only lines matching this regex will be included in the result.',\n ),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stderr: string\n status: string\n hasMore: boolean\n}\n\nexport const BashOutputTool = {\n name: 'BashOutput',\n async description() {\n return 'Retrieves output from a running or completed background bash shell'\n },\n async prompt() {\n return PROMPT\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true\n },\n inputSchema,\n userFacingName() {\n return 'BashOutput'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n return false // Reading output doesn't require permissions\n },\n renderToolUseMessage({ bash_id, filter }) {\n return filter ? `${bash_id} (filter: ${filter})` : bash_id\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(content) {\n return <BashOutputToolResultMessage content={content} />\n },\n renderResultForAssistant({ stdout, stderr, status, hasMore }) {\n const parts: string[] = []\n\n if (status) {\n parts.push(`Status: ${status}`)\n }\n\n if (stdout) {\n parts.push(`Stdout:\\n${stdout}`)\n }\n\n if (stderr) {\n parts.push(`Stderr:\\n${stderr}`)\n }\n\n if (hasMore) {\n parts.push('(More output may be available - output is truncated)')\n }\n\n if (parts.length === 0) {\n return 'No new output available'\n }\n\n return parts.join('\\n\\n')\n },\n async *call({ bash_id, filter }, { abortController }) {\n const manager = BackgroundShellManager.getInstance()\n const shell = manager.get(bash_id)\n\n if (!shell) {\n const data: Out = {\n stdout: '',\n stderr: `Error: Background shell '${bash_id}' not found`,\n status: 'not_found',\n hasMore: false,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n // Get new output since last read\n const output = manager.getNewOutput(bash_id, filter)\n\n if (!output) {\n const data: Out = {\n stdout: '',\n stderr: `Error: Could not retrieve output for shell '${bash_id}'`,\n status: shell.status,\n hasMore: false,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n const MAX_LINES = 100\n const stdout = output.stdout.slice(0, MAX_LINES).join('\\n')\n const stderr = output.stderr.slice(0, MAX_LINES).join('\\n')\n const hasMore =\n output.stdout.length > MAX_LINES || output.stderr.length > MAX_LINES\n\n const data: Out = {\n stdout,\n stderr,\n status: shell.status,\n hasMore,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n },\n} satisfies Tool<In, Out>\n"],
4
+ "sourcesContent": ["import * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport { BashOutputToolResultMessage } from './BashOutputToolResultMessage'\nimport { PROMPT } from './prompt'\n\nexport const inputSchema = z.strictObject({\n bash_id: z\n .string()\n .describe('The ID of the background shell to retrieve output from'),\n filter: z\n .string()\n .optional()\n .describe(\n 'Optional regular expression to filter the output lines. Only lines matching this regex will be included in the result.',\n ),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stderr: string\n status: string\n hasMore: boolean\n}\n\nexport const BashOutputTool = {\n name: 'BashOutput',\n async description() {\n return 'Retrieves output from a running or completed background bash shell'\n },\n async prompt() {\n return PROMPT\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true\n },\n inputSchema,\n userFacingName() {\n return 'BashOutput'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n return false // Reading output doesn't require permissions\n },\n renderToolUseMessage({ bash_id, filter }) {\n return filter ? `${bash_id} (filter: ${filter})` : bash_id\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(content) {\n return <BashOutputToolResultMessage content={content} />\n },\n renderResultForAssistant({ stdout, stderr, status, hasMore }) {\n const parts: string[] = []\n\n if (status) {\n parts.push(`Status: ${status}`)\n }\n\n if (stdout) {\n parts.push(`Stdout:\\n${stdout}`)\n }\n\n if (stderr) {\n parts.push(`Stderr:\\n${stderr}`)\n }\n\n if (hasMore) {\n parts.push('(More output may be available - output is truncated)')\n }\n\n if (parts.length === 0) {\n return 'No new output available'\n }\n\n return parts.join('\\n\\n')\n },\n async *call({ bash_id, filter }, { abortController }) {\n const manager = BackgroundShellManager.getInstance()\n const shell = manager.get(bash_id)\n\n if (!shell) {\n const data: Out = {\n stdout: '',\n stderr: `Error: Background shell '${bash_id}' not found`,\n status: 'not_found',\n hasMore: false,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n // Get new output since last read\n const output = manager.getNewOutput(bash_id, filter)\n\n if (!output) {\n const data: Out = {\n stdout: '',\n stderr: `Error: Could not retrieve output for shell '${bash_id}'`,\n status: shell.status,\n hasMore: false,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n const MAX_LINES = 100\n const stdout = output.stdout.slice(0, MAX_LINES).join('\\n')\n const stderr = output.stderr.slice(0, MAX_LINES).join('\\n')\n const hasMore =\n output.stdout.length > MAX_LINES || output.stderr.length > MAX_LINES\n\n const data: Out = {\n stdout,\n stderr,\n status: shell.status,\n hasMore,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n },\n} satisfies Tool\n"],
5
5
  "mappings": "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",
6
6
  "names": ["data"]
7
7
  }
@@ -158,6 +158,34 @@ const BashTool = {
158
158
  return `${stdout.trim()}${hasBoth ? "\n" : ""}${errorMessage.trim()}`;
159
159
  },
160
160
  async *call({ command, timeout = 12e4, run_in_background = false }, { abortController, readFileTimestamps }) {
161
+ try {
162
+ const { getCurrentProjectConfig } = await import("../../utils/config.js");
163
+ const projectConfig = getCurrentProjectConfig();
164
+ if (projectConfig.sandbox?.enabled) {
165
+ const { getSandboxController } = await import("../../services/sandbox/index.js");
166
+ const sandbox = getSandboxController(getCwd());
167
+ if (await sandbox.isAvailable()) {
168
+ const validation = await sandbox.validateCommand(command);
169
+ if (!validation.valid) {
170
+ const reason = validation.violations.map((v) => v.details || v.type).join("; ");
171
+ const data = {
172
+ stdout: "",
173
+ stdoutLines: 0,
174
+ stderr: `Sandbox violation: ${reason || "Command blocked by sandbox policy"}`,
175
+ stderrLines: 1,
176
+ interrupted: false
177
+ };
178
+ yield {
179
+ type: "result",
180
+ resultForAssistant: `Sandbox blocked: ${reason}`,
181
+ data
182
+ };
183
+ return;
184
+ }
185
+ }
186
+ }
187
+ } catch {
188
+ }
161
189
  if (run_in_background) {
162
190
  const shellId = BackgroundShellManager.getInstance().create(
163
191
  command,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/BashTool/BashTool.tsx"],
4
- "sourcesContent": ["import { statSync } from 'fs'\nimport { EOL } from 'os'\nimport { isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { queryQuick } from '@services/claude'\nimport { Tool, ValidationResult } from '@tool'\nimport { splitCommand } from '@utils/commands'\nimport { isInDirectory } from '@utils/file'\nimport { logError } from '@utils/log'\nimport {\n PersistentShell,\n type StreamingYield,\n type StreamingResult,\n} from '@utils/PersistentShell'\nimport { getCwd, getOriginalCwd } from '@utils/state'\nimport { getGlobalConfig } from '@utils/config'\nimport { getModelManager } from '@utils/model'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport BashToolResultMessage from './BashToolResultMessage'\nimport {\n BANNED_COMMANDS,\n PROMPT,\n matchesDangerousPattern,\n detectDangerousPatterns,\n} from './prompt'\nimport { formatOutput, getCommandFilePaths } from './utils'\n\nexport const inputSchema = z.strictObject({\n command: z.string().describe('The command to execute'),\n timeout: z\n .number()\n .optional()\n .describe('Optional timeout in milliseconds (max 600000)'),\n run_in_background: z\n .boolean()\n .optional()\n .describe('Set to true to run this command in the background'),\n description: z\n .string()\n .optional()\n .describe(\n `Clear, concise description of what this command does in active voice. Never use words like \"complex\" or \"risk\" in the description - just describe what it does.\n\nFor simple commands (git, npm, standard CLI tools), keep it brief (5-10 words):\n- ls \u2192 \"List files in current directory\"\n- git status \u2192 \"Show working tree status\"\n- npm install \u2192 \"Install package dependencies\"\n\nFor commands that are harder to parse at a glance (piped commands, obscure flags, etc.), add enough context to clarify what it does:\n- find . -name \"*.tmp\" -exec rm {} \\\\; \u2192 \"Find and delete all .tmp files recursively\"\n- git reset --hard origin/main \u2192 \"Discard all local changes and match remote main\"\n- curl -s url | jq '.data[]' \u2192 \"Fetch JSON from URL and extract data array elements\"`,\n ),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stdoutLines: number // Total number of lines in original stdout, even if `stdout` is now truncated\n stderr: string\n stderrLines: number // Total number of lines in original stderr, even if `stderr` is now truncated\n interrupted: boolean\n shellId?: string // Present if run_in_background is true\n}\n\nexport const BashTool = {\n name: 'Bash',\n async description() {\n return 'Executes shell commands on your computer'\n },\n async prompt() {\n const config = getGlobalConfig()\n // \uD83D\uDD27 Fix: Use ModelManager to get actual current model\n const modelManager = getModelManager()\n const modelName =\n modelManager.getModelName('main') || '<No Model Configured>'\n // Substitute the placeholder in the static PROMPT string\n return PROMPT.replace(/{MODEL_NAME}/g, modelName)\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // BashTool modifies state/files, not safe for concurrent execution\n },\n inputSchema,\n userFacingName() {\n return 'Bash'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n // Always check per-project permissions for BashTool\n return true\n },\n async validateInput({ command }): Promise<ValidationResult> {\n // Detect dangerous patterns (critical only block, others warn)\n const dangerousPatterns = detectDangerousPatterns(command)\n const criticalPatterns = dangerousPatterns.filter(\n p => p.severity === 'critical',\n )\n\n if (criticalPatterns.length > 0) {\n const patternNames = criticalPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY: Critical dangerous patterns detected in command: ${patternNames}`,\n )\n return {\n result: false,\n message: `Command contains critical security risks: ${patternNames}. This command has been blocked for security reasons.`,\n }\n }\n\n // Log warnings for high-severity patterns (non-blocking)\n const highPatterns = dangerousPatterns.filter(p => p.severity === 'high')\n if (highPatterns.length > 0) {\n const patternNames = highPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY WARNING: High-severity patterns detected in command: ${patternNames}. User confirmation required.`,\n )\n }\n\n const commands = splitCommand(command)\n for (const cmd of commands) {\n const parts = cmd.split(' ')\n const baseCmd = parts[0]\n\n // Check if command is banned\n if (baseCmd && BANNED_COMMANDS.includes(baseCmd.toLowerCase())) {\n return {\n result: false,\n message: `Command '${baseCmd}' is not allowed for security reasons`,\n }\n }\n\n // Also check for commands that might be invoked with full path\n // e.g., /usr/bin/curl, /bin/rm\n if (baseCmd && (baseCmd.startsWith('/') || baseCmd.startsWith('./'))) {\n const cmdName = baseCmd.split('/').pop()?.toLowerCase()\n if (cmdName && BANNED_COMMANDS.includes(cmdName)) {\n return {\n result: false,\n message: `Command '${cmdName}' is not allowed for security reasons (full path: ${baseCmd})`,\n }\n }\n }\n\n // Special handling for cd command\n if (baseCmd === 'cd' && parts[1]) {\n const targetDir = parts[1]!.replace(/^['\"]|['\"]$/g, '') // Remove quotes if present\n const fullTargetDir = isAbsolute(targetDir)\n ? targetDir\n : resolve(getCwd(), targetDir)\n if (\n !isInDirectory(\n relative(getOriginalCwd(), fullTargetDir),\n relative(getCwd(), getOriginalCwd()),\n )\n ) {\n return {\n result: false,\n message: `ERROR: cd to '${fullTargetDir}' was blocked. For security, ${PRODUCT_NAME} may only change directories to child directories of the original working directory (${getOriginalCwd()}) for this session.`,\n }\n }\n }\n }\n\n return { result: true }\n },\n renderToolUseMessage({ command, description }) {\n // Clean up any command that uses the quoted HEREDOC pattern\n let displayCommand = command\n if (command.includes(\"\\\"$(cat <<'EOF'\")) {\n const match = command.match(\n /^(.*?)\"?\\$\\(cat <<'EOF'\\n([\\s\\S]*?)\\n\\s*EOF\\n\\s*\\)\"(.*)$/,\n )\n if (match && match[1] && match[2]) {\n const prefix = match[1]\n const content = match[2]\n const suffix = match[3] || ''\n displayCommand = `${prefix.trim()} \"${content.trim()}\"${suffix.trim()}`\n }\n }\n\n // If description is provided, show it instead of (or alongside) the command\n if (description) {\n return description\n }\n\n return displayCommand\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(content) {\n return <BashToolResultMessage content={content} verbose={false} />\n },\n renderResultForAssistant({ interrupted, stdout, stderr }) {\n let errorMessage = stderr.trim()\n if (interrupted) {\n if (stderr) errorMessage += EOL\n errorMessage += '<error>Command was aborted before completion</error>'\n }\n const hasBoth = stdout.trim() && errorMessage\n return `${stdout.trim()}${hasBoth ? '\\n' : ''}${errorMessage.trim()}`\n },\n async *call(\n { command, timeout = 120000, run_in_background = false },\n { abortController, readFileTimestamps },\n ) {\n // Handle background execution\n if (run_in_background) {\n const shellId = BackgroundShellManager.getInstance().create(\n command,\n getCwd(),\n )\n\n const data: Out = {\n stdout: `Background shell started with ID: ${shellId}`,\n stdoutLines: 1,\n stderr: '',\n stderrLines: 0,\n interrupted: false,\n shellId,\n }\n\n yield {\n type: 'result',\n resultForAssistant: `Started background shell: ${shellId}\\nCommand: ${command}`,\n data,\n }\n return\n }\n\n let stdout = ''\n let stderr = ''\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether this tool invocation has completed\n // to prevent async callbacks from accessing stale context\n let isCompleted = false\n\n // \uD83D\uDD27 Check if already cancelled before starting execution\n if (abortController.signal.aborted) {\n const data: Out = {\n stdout: '',\n stdoutLines: 0,\n stderr: 'Command cancelled before execution',\n stderrLines: 1,\n interrupted: true,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n try {\n // Execute commands with streaming output\n let result: StreamingResult | null = null\n let streamedStdout = ''\n let streamedStderr = ''\n\n // Use streaming execution for real-time output\n const streamingGenerator = PersistentShell.getInstance().execStreaming(\n command,\n abortController.signal,\n timeout,\n )\n\n for await (const chunk of streamingGenerator) {\n if (chunk.type === 'chunk') {\n // Accumulate streamed output\n if (chunk.stdout) {\n streamedStdout += chunk.stdout\n }\n if (chunk.stderr) {\n streamedStderr += chunk.stderr\n }\n\n // Yield progress update for real-time UI feedback\n // Use StreamingProgressContent format for proper rendering\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Bash',\n stdout: streamedStdout,\n stderr: streamedStderr,\n isStreaming: true,\n },\n }\n } else if (chunk.type === 'result') {\n // Final result\n result = chunk\n }\n }\n\n // Use the final result (or construct from streamed data)\n if (!result) {\n result = {\n type: 'result',\n stdout: streamedStdout,\n stderr: streamedStderr,\n code: 0,\n interrupted: false,\n }\n }\n\n stdout += (result.stdout || '').trim() + EOL\n stderr += (result.stderr || '').trim() + EOL\n if (result.code !== 0) {\n stderr += `Exit code ${result.code}`\n }\n\n if (!isInDirectory(getCwd(), getOriginalCwd())) {\n // Shell directory is outside original working directory, reset it\n await PersistentShell.getInstance().setCwd(getOriginalCwd())\n stderr = `${stderr.trim()}${EOL}Shell cwd was reset to ${getOriginalCwd()}`\n }\n\n // Update read timestamps for any files referenced by the command\n // Don't block the main thread!\n // Skip this in tests because it makes fixtures non-deterministic (they might not always get written),\n // so will be missing in CI.\n if (process.env.NODE_ENV !== 'test') {\n getCommandFilePaths(command, stdout).then(filePaths => {\n // \uD83D\uDD27 CRITICAL FIX: Check if tool invocation has already completed\n // to prevent accessing potentially stale context\n if (isCompleted) {\n return // Tool has finished, don't access readFileTimestamps\n }\n\n for (const filePath of filePaths) {\n const fullFilePath = isAbsolute(filePath)\n ? filePath\n : resolve(getCwd(), filePath)\n\n // Try/catch in case the file doesn't exist (because Haiku didn't properly extract it)\n try {\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n } catch (e) {\n logError(e)\n }\n }\n })\n }\n\n const { totalLines: stdoutLines, truncatedContent: stdoutContent } =\n formatOutput(stdout.trim())\n const { totalLines: stderrLines, truncatedContent: stderrContent } =\n formatOutput(stderr.trim())\n\n const data: Out = {\n stdout: stdoutContent,\n stdoutLines,\n stderr: stderrContent,\n stderrLines,\n interrupted: result.interrupted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } catch (error) {\n // \uD83D\uDD27 Handle cancellation or other errors properly\n const isAborted = abortController.signal.aborted\n const errorMessage = isAborted\n ? 'Command was cancelled by user'\n : `Command failed: ${error instanceof Error ? error.message : String(error)}`\n\n const data: Out = {\n stdout: stdout.trim(),\n stdoutLines: stdout.split('\\n').length,\n stderr: errorMessage,\n stderrLines: 1,\n interrupted: isAborted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } finally {\n // \uD83D\uDD27 CRITICAL FIX: Mark tool invocation as completed\n // This prevents async callbacks (like getCommandFilePaths) from\n // accessing potentially stale context after this tool returns\n isCompleted = true\n }\n },\n} satisfies Tool<In, Out>\n"],
5
- "mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,WAAW;AACpB,SAAS,YAAY,UAAU,eAAe;AAC9C,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAC/C,SAAS,oBAAoB;AAG7B,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,OAGK;AACP,SAAS,QAAQ,sBAAsB;AACvC,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAChC,SAAS,8BAA8B;AACvC,OAAO,2BAA2B;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AACP,SAAS,cAAc,2BAA2B;AAE3C,MAAM,cAAc,EAAE,aAAa;AAAA,EACxC,SAAS,EAAE,OAAO,EAAE,SAAS,wBAAwB;AAAA,EACrD,SAAS,EACN,OAAO,EACP,SAAS,EACT,SAAS,+CAA+C;AAAA,EAC3D,mBAAmB,EAChB,QAAQ,EACR,SAAS,EACT,SAAS,mDAAmD;AAAA,EAC/D,aAAa,EACV,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWF;AACJ,CAAC;AAYM,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA,MAAM,SAAS;AACb,UAAM,SAAS,gBAAgB;AAE/B,UAAM,eAAe,gBAAgB;AACrC,UAAM,YACJ,aAAa,aAAa,MAAM,KAAK;AAEvC,WAAO,OAAO,QAAQ,iBAAiB,SAAS;AAAA,EAClD;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EACA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,mBAA4B;AAE1B,WAAO;AAAA,EACT;AAAA,EACA,MAAM,cAAc,EAAE,QAAQ,GAA8B;AAE1D,UAAM,oBAAoB,wBAAwB,OAAO;AACzD,UAAM,mBAAmB,kBAAkB;AAAA,MACzC,OAAK,EAAE,aAAa;AAAA,IACtB;AAEA,QAAI,iBAAiB,SAAS,GAAG;AAC/B,YAAM,eAAe,iBAAiB,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI;AAChE;AAAA,QACE,8DAA8D,YAAY;AAAA,MAC5E;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,6CAA6C,YAAY;AAAA,MACpE;AAAA,IACF;AAGA,UAAM,eAAe,kBAAkB,OAAO,OAAK,EAAE,aAAa,MAAM;AACxE,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,eAAe,aAAa,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI;AAC5D;AAAA,QACE,iEAAiE,YAAY;AAAA,MAC/E;AAAA,IACF;AAEA,UAAM,WAAW,aAAa,OAAO;AACrC,eAAW,OAAO,UAAU;AAC1B,YAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,YAAM,UAAU,MAAM,CAAC;AAGvB,UAAI,WAAW,gBAAgB,SAAS,QAAQ,YAAY,CAAC,GAAG;AAC9D,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,SAAS,YAAY,OAAO;AAAA,QAC9B;AAAA,MACF;AAIA,UAAI,YAAY,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,IAAI,IAAI;AACpE,cAAM,UAAU,QAAQ,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AACtD,YAAI,WAAW,gBAAgB,SAAS,OAAO,GAAG;AAChD,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,SAAS,YAAY,OAAO,qDAAqD,OAAO;AAAA,UAC1F;AAAA,QACF;AAAA,MACF;AAGA,UAAI,YAAY,QAAQ,MAAM,CAAC,GAAG;AAChC,cAAM,YAAY,MAAM,CAAC,EAAG,QAAQ,gBAAgB,EAAE;AACtD,cAAM,gBAAgB,WAAW,SAAS,IACtC,YACA,QAAQ,OAAO,GAAG,SAAS;AAC/B,YACE,CAAC;AAAA,UACC,SAAS,eAAe,GAAG,aAAa;AAAA,UACxC,SAAS,OAAO,GAAG,eAAe,CAAC;AAAA,QACrC,GACA;AACA,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,SAAS,iBAAiB,aAAa,gCAAgC,YAAY,wFAAwF,eAAe,CAAC;AAAA,UAC7L;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,KAAK;AAAA,EACxB;AAAA,EACA,qBAAqB,EAAE,SAAS,YAAY,GAAG;AAE7C,QAAI,iBAAiB;AACrB,QAAI,QAAQ,SAAS,gBAAiB,GAAG;AACvC,YAAM,QAAQ,QAAQ;AAAA,QACpB;AAAA,MACF;AACA,UAAI,SAAS,MAAM,CAAC,KAAK,MAAM,CAAC,GAAG;AACjC,cAAM,SAAS,MAAM,CAAC;AACtB,cAAM,UAAU,MAAM,CAAC;AACvB,cAAM,SAAS,MAAM,CAAC,KAAK;AAC3B,yBAAiB,GAAG,OAAO,KAAK,CAAC,KAAK,QAAQ,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC;AAAA,MACvE;AAAA,IACF;AAGA,QAAI,aAAa;AACf,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EACA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EAEA,wBAAwB,SAAS;AAC/B,WAAO,oCAAC,yBAAsB,SAAkB,SAAS,OAAO;AAAA,EAClE;AAAA,EACA,yBAAyB,EAAE,aAAa,QAAQ,OAAO,GAAG;AACxD,QAAI,eAAe,OAAO,KAAK;AAC/B,QAAI,aAAa;AACf,UAAI,OAAQ,iBAAgB;AAC5B,sBAAgB;AAAA,IAClB;AACA,UAAM,UAAU,OAAO,KAAK,KAAK;AACjC,WAAO,GAAG,OAAO,KAAK,CAAC,GAAG,UAAU,OAAO,EAAE,GAAG,aAAa,KAAK,CAAC;AAAA,EACrE;AAAA,EACA,OAAO,KACL,EAAE,SAAS,UAAU,MAAQ,oBAAoB,MAAM,GACvD,EAAE,iBAAiB,mBAAmB,GACtC;AAEA,QAAI,mBAAmB;AACrB,YAAM,UAAU,uBAAuB,YAAY,EAAE;AAAA,QACnD;AAAA,QACA,OAAO;AAAA,MACT;AAEA,YAAM,OAAY;AAAA,QAChB,QAAQ,qCAAqC,OAAO;AAAA,QACpD,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,aAAa;AAAA,QACb;AAAA,MACF;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,6BAA6B,OAAO;AAAA,WAAc,OAAO;AAAA,QAC7E;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,SAAS;AACb,QAAI,SAAS;AAIb,QAAI,cAAc;AAGlB,QAAI,gBAAgB,OAAO,SAAS;AAClC,YAAM,OAAY;AAAA,QAChB,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyB,IAAI;AAAA,QACtD;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI;AAEF,UAAI,SAAiC;AACrC,UAAI,iBAAiB;AACrB,UAAI,iBAAiB;AAGrB,YAAM,qBAAqB,gBAAgB,YAAY,EAAE;AAAA,QACvD;AAAA,QACA,gBAAgB;AAAA,QAChB;AAAA,MACF;AAEA,uBAAiB,SAAS,oBAAoB;AAC5C,YAAI,MAAM,SAAS,SAAS;AAE1B,cAAI,MAAM,QAAQ;AAChB,8BAAkB,MAAM;AAAA,UAC1B;AACA,cAAI,MAAM,QAAQ;AAChB,8BAAkB,MAAM;AAAA,UAC1B;AAIA,gBAAM;AAAA,YACJ,MAAM;AAAA,YACN,SAAS;AAAA,cACP,MAAM;AAAA,cACN,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,QAAQ;AAAA,cACR,aAAa;AAAA,YACf;AAAA,UACF;AAAA,QACF,WAAW,MAAM,SAAS,UAAU;AAElC,mBAAS;AAAA,QACX;AAAA,MACF;AAGA,UAAI,CAAC,QAAQ;AACX,iBAAS;AAAA,UACP,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,MACF;AAEA,iBAAW,OAAO,UAAU,IAAI,KAAK,IAAI;AACzC,iBAAW,OAAO,UAAU,IAAI,KAAK,IAAI;AACzC,UAAI,OAAO,SAAS,GAAG;AACrB,kBAAU,aAAa,OAAO,IAAI;AAAA,MACpC;AAEA,UAAI,CAAC,cAAc,OAAO,GAAG,eAAe,CAAC,GAAG;AAE9C,cAAM,gBAAgB,YAAY,EAAE,OAAO,eAAe,CAAC;AAC3D,iBAAS,GAAG,OAAO,KAAK,CAAC,GAAG,GAAG,0BAA0B,eAAe,CAAC;AAAA,MAC3E;AAMA,UAAI,QAAQ,IAAI,aAAa,QAAQ;AACnC,4BAAoB,SAAS,MAAM,EAAE,KAAK,eAAa;AAGrD,cAAI,aAAa;AACf;AAAA,UACF;AAEA,qBAAW,YAAY,WAAW;AAChC,kBAAM,eAAe,WAAW,QAAQ,IACpC,WACA,QAAQ,OAAO,GAAG,QAAQ;AAG9B,gBAAI;AACF,iCAAmB,YAAY,IAAI,SAAS,YAAY,EAAE;AAAA,YAC5D,SAAS,GAAG;AACV,uBAAS,CAAC;AAAA,YACZ;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,EAAE,YAAY,aAAa,kBAAkB,cAAc,IAC/D,aAAa,OAAO,KAAK,CAAC;AAC5B,YAAM,EAAE,YAAY,aAAa,kBAAkB,cAAc,IAC/D,aAAa,OAAO,KAAK,CAAC;AAE5B,YAAM,OAAY;AAAA,QAChB,QAAQ;AAAA,QACR;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,QACA,aAAa,OAAO;AAAA,MACtB;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyB,IAAI;AAAA,QACtD;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAEd,YAAM,YAAY,gBAAgB,OAAO;AACzC,YAAM,eAAe,YACjB,kCACA,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAE7E,YAAM,OAAY;AAAA,QAChB,QAAQ,OAAO,KAAK;AAAA,QACpB,aAAa,OAAO,MAAM,IAAI,EAAE;AAAA,QAChC,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyB,IAAI;AAAA,QACtD;AAAA,MACF;AAAA,IACF,UAAE;AAIA,oBAAc;AAAA,IAChB;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { statSync } from 'fs'\nimport { EOL } from 'os'\nimport { isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { queryQuick } from '@services/claude'\nimport { Tool, ValidationResult } from '@tool'\nimport { splitCommand } from '@utils/commands'\nimport { isInDirectory } from '@utils/file'\nimport { logError } from '@utils/log'\nimport {\n PersistentShell,\n type StreamingYield,\n type StreamingResult,\n} from '@utils/PersistentShell'\nimport { getCwd, getOriginalCwd } from '@utils/state'\nimport { getGlobalConfig } from '@utils/config'\nimport { getModelManager } from '@utils/model'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport BashToolResultMessage from './BashToolResultMessage'\nimport {\n BANNED_COMMANDS,\n PROMPT,\n matchesDangerousPattern,\n detectDangerousPatterns,\n} from './prompt'\nimport { formatOutput, getCommandFilePaths } from './utils'\n\nexport const inputSchema = z.strictObject({\n command: z.string().describe('The command to execute'),\n timeout: z\n .number()\n .optional()\n .describe('Optional timeout in milliseconds (max 600000)'),\n run_in_background: z\n .boolean()\n .optional()\n .describe('Set to true to run this command in the background'),\n description: z\n .string()\n .optional()\n .describe(\n `Clear, concise description of what this command does in active voice. Never use words like \"complex\" or \"risk\" in the description - just describe what it does.\n\nFor simple commands (git, npm, standard CLI tools), keep it brief (5-10 words):\n- ls \u2192 \"List files in current directory\"\n- git status \u2192 \"Show working tree status\"\n- npm install \u2192 \"Install package dependencies\"\n\nFor commands that are harder to parse at a glance (piped commands, obscure flags, etc.), add enough context to clarify what it does:\n- find . -name \"*.tmp\" -exec rm {} \\\\; \u2192 \"Find and delete all .tmp files recursively\"\n- git reset --hard origin/main \u2192 \"Discard all local changes and match remote main\"\n- curl -s url | jq '.data[]' \u2192 \"Fetch JSON from URL and extract data array elements\"`,\n ),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stdoutLines: number // Total number of lines in original stdout, even if `stdout` is now truncated\n stderr: string\n stderrLines: number // Total number of lines in original stderr, even if `stderr` is now truncated\n interrupted: boolean\n shellId?: string // Present if run_in_background is true\n}\n\nexport const BashTool = {\n name: 'Bash',\n async description() {\n return 'Executes shell commands on your computer'\n },\n async prompt() {\n const config = getGlobalConfig()\n // \uD83D\uDD27 Fix: Use ModelManager to get actual current model\n const modelManager = getModelManager()\n const modelName =\n modelManager.getModelName('main') || '<No Model Configured>'\n // Substitute the placeholder in the static PROMPT string\n return PROMPT.replace(/{MODEL_NAME}/g, modelName)\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // BashTool modifies state/files, not safe for concurrent execution\n },\n inputSchema,\n userFacingName() {\n return 'Bash'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n // Always check per-project permissions for BashTool\n return true\n },\n async validateInput({ command }): Promise<ValidationResult> {\n // Detect dangerous patterns (critical only block, others warn)\n const dangerousPatterns = detectDangerousPatterns(command)\n const criticalPatterns = dangerousPatterns.filter(\n p => p.severity === 'critical',\n )\n\n if (criticalPatterns.length > 0) {\n const patternNames = criticalPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY: Critical dangerous patterns detected in command: ${patternNames}`,\n )\n return {\n result: false,\n message: `Command contains critical security risks: ${patternNames}. This command has been blocked for security reasons.`,\n }\n }\n\n // Log warnings for high-severity patterns (non-blocking)\n const highPatterns = dangerousPatterns.filter(p => p.severity === 'high')\n if (highPatterns.length > 0) {\n const patternNames = highPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY WARNING: High-severity patterns detected in command: ${patternNames}. User confirmation required.`,\n )\n }\n\n const commands = splitCommand(command)\n for (const cmd of commands) {\n const parts = cmd.split(' ')\n const baseCmd = parts[0]\n\n // Check if command is banned\n if (baseCmd && BANNED_COMMANDS.includes(baseCmd.toLowerCase())) {\n return {\n result: false,\n message: `Command '${baseCmd}' is not allowed for security reasons`,\n }\n }\n\n // Also check for commands that might be invoked with full path\n // e.g., /usr/bin/curl, /bin/rm\n if (baseCmd && (baseCmd.startsWith('/') || baseCmd.startsWith('./'))) {\n const cmdName = baseCmd.split('/').pop()?.toLowerCase()\n if (cmdName && BANNED_COMMANDS.includes(cmdName)) {\n return {\n result: false,\n message: `Command '${cmdName}' is not allowed for security reasons (full path: ${baseCmd})`,\n }\n }\n }\n\n // Special handling for cd command\n if (baseCmd === 'cd' && parts[1]) {\n const targetDir = parts[1]!.replace(/^['\"]|['\"]$/g, '') // Remove quotes if present\n const fullTargetDir = isAbsolute(targetDir)\n ? targetDir\n : resolve(getCwd(), targetDir)\n if (\n !isInDirectory(\n relative(getOriginalCwd(), fullTargetDir),\n relative(getCwd(), getOriginalCwd()),\n )\n ) {\n return {\n result: false,\n message: `ERROR: cd to '${fullTargetDir}' was blocked. For security, ${PRODUCT_NAME} may only change directories to child directories of the original working directory (${getOriginalCwd()}) for this session.`,\n }\n }\n }\n }\n\n return { result: true }\n },\n renderToolUseMessage({ command, description }) {\n // Clean up any command that uses the quoted HEREDOC pattern\n let displayCommand = command\n if (command.includes(\"\\\"$(cat <<'EOF'\")) {\n const match = command.match(\n /^(.*?)\"?\\$\\(cat <<'EOF'\\n([\\s\\S]*?)\\n\\s*EOF\\n\\s*\\)\"(.*)$/,\n )\n if (match && match[1] && match[2]) {\n const prefix = match[1]\n const content = match[2]\n const suffix = match[3] || ''\n displayCommand = `${prefix.trim()} \"${content.trim()}\"${suffix.trim()}`\n }\n }\n\n // If description is provided, show it instead of (or alongside) the command\n if (description) {\n return description\n }\n\n return displayCommand\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(content) {\n return <BashToolResultMessage content={content} verbose={false} />\n },\n renderResultForAssistant({ interrupted, stdout, stderr }) {\n let errorMessage = stderr.trim()\n if (interrupted) {\n if (stderr) errorMessage += EOL\n errorMessage += '<error>Command was aborted before completion</error>'\n }\n const hasBoth = stdout.trim() && errorMessage\n return `${stdout.trim()}${hasBoth ? '\\n' : ''}${errorMessage.trim()}`\n },\n async *call(\n { command, timeout = 120000, run_in_background = false },\n { abortController, readFileTimestamps },\n ) {\n // Sandbox pre-validation (if enabled)\n try {\n const { getCurrentProjectConfig } = await import('@utils/config')\n const projectConfig = getCurrentProjectConfig()\n if (projectConfig.sandbox?.enabled) {\n const { getSandboxController } = await import('@services/sandbox')\n const sandbox = getSandboxController(getCwd())\n if (await sandbox.isAvailable()) {\n const validation = await sandbox.validateCommand(command)\n if (!validation.valid) {\n const reason = validation.violations\n .map(v => v.details || v.type)\n .join('; ')\n const data: Out = {\n stdout: '',\n stdoutLines: 0,\n stderr: `Sandbox violation: ${reason || 'Command blocked by sandbox policy'}`,\n stderrLines: 1,\n interrupted: false,\n }\n yield {\n type: 'result',\n resultForAssistant: `Sandbox blocked: ${reason}`,\n data,\n }\n return\n }\n }\n }\n } catch {\n // Sandbox not available or error - continue without it\n }\n\n // Handle background execution\n if (run_in_background) {\n const shellId = BackgroundShellManager.getInstance().create(\n command,\n getCwd(),\n )\n\n const data: Out = {\n stdout: `Background shell started with ID: ${shellId}`,\n stdoutLines: 1,\n stderr: '',\n stderrLines: 0,\n interrupted: false,\n shellId,\n }\n\n yield {\n type: 'result',\n resultForAssistant: `Started background shell: ${shellId}\\nCommand: ${command}`,\n data,\n }\n return\n }\n\n let stdout = ''\n let stderr = ''\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether this tool invocation has completed\n // to prevent async callbacks from accessing stale context\n let isCompleted = false\n\n // \uD83D\uDD27 Check if already cancelled before starting execution\n if (abortController.signal.aborted) {\n const data: Out = {\n stdout: '',\n stdoutLines: 0,\n stderr: 'Command cancelled before execution',\n stderrLines: 1,\n interrupted: true,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n try {\n // Execute commands with streaming output\n let result: StreamingResult | null = null\n let streamedStdout = ''\n let streamedStderr = ''\n\n // Use streaming execution for real-time output\n const streamingGenerator = PersistentShell.getInstance().execStreaming(\n command,\n abortController.signal,\n timeout,\n )\n\n for await (const chunk of streamingGenerator) {\n if (chunk.type === 'chunk') {\n // Accumulate streamed output\n if (chunk.stdout) {\n streamedStdout += chunk.stdout\n }\n if (chunk.stderr) {\n streamedStderr += chunk.stderr\n }\n\n // Yield progress update for real-time UI feedback\n // Use StreamingProgressContent format for proper rendering\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Bash',\n stdout: streamedStdout,\n stderr: streamedStderr,\n isStreaming: true,\n },\n }\n } else if (chunk.type === 'result') {\n // Final result\n result = chunk\n }\n }\n\n // Use the final result (or construct from streamed data)\n if (!result) {\n result = {\n type: 'result',\n stdout: streamedStdout,\n stderr: streamedStderr,\n code: 0,\n interrupted: false,\n }\n }\n\n stdout += (result.stdout || '').trim() + EOL\n stderr += (result.stderr || '').trim() + EOL\n if (result.code !== 0) {\n stderr += `Exit code ${result.code}`\n }\n\n if (!isInDirectory(getCwd(), getOriginalCwd())) {\n // Shell directory is outside original working directory, reset it\n await PersistentShell.getInstance().setCwd(getOriginalCwd())\n stderr = `${stderr.trim()}${EOL}Shell cwd was reset to ${getOriginalCwd()}`\n }\n\n // Update read timestamps for any files referenced by the command\n // Don't block the main thread!\n // Skip this in tests because it makes fixtures non-deterministic (they might not always get written),\n // so will be missing in CI.\n if (process.env.NODE_ENV !== 'test') {\n getCommandFilePaths(command, stdout).then(filePaths => {\n // \uD83D\uDD27 CRITICAL FIX: Check if tool invocation has already completed\n // to prevent accessing potentially stale context\n if (isCompleted) {\n return // Tool has finished, don't access readFileTimestamps\n }\n\n for (const filePath of filePaths) {\n const fullFilePath = isAbsolute(filePath)\n ? filePath\n : resolve(getCwd(), filePath)\n\n // Try/catch in case the file doesn't exist (because Haiku didn't properly extract it)\n try {\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n } catch (e) {\n logError(e)\n }\n }\n })\n }\n\n const { totalLines: stdoutLines, truncatedContent: stdoutContent } =\n formatOutput(stdout.trim())\n const { totalLines: stderrLines, truncatedContent: stderrContent } =\n formatOutput(stderr.trim())\n\n const data: Out = {\n stdout: stdoutContent,\n stdoutLines,\n stderr: stderrContent,\n stderrLines,\n interrupted: result.interrupted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } catch (error) {\n // \uD83D\uDD27 Handle cancellation or other errors properly\n const isAborted = abortController.signal.aborted\n const errorMessage = isAborted\n ? 'Command was cancelled by user'\n : `Command failed: ${error instanceof Error ? error.message : String(error)}`\n\n const data: Out = {\n stdout: stdout.trim(),\n stdoutLines: stdout.split('\\n').length,\n stderr: errorMessage,\n stderrLines: 1,\n interrupted: isAborted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } finally {\n // \uD83D\uDD27 CRITICAL FIX: Mark tool invocation as completed\n // This prevents async callbacks (like getCommandFilePaths) from\n // accessing potentially stale context after this tool returns\n isCompleted = true\n }\n },\n} satisfies Tool\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -19,6 +19,7 @@ import { getCwd } from "../../utils/state.js";
19
19
  import { getTheme } from "../../utils/theme.js";
20
20
  import { emitReminderEvent } from "../../services/systemReminder.js";
21
21
  import { recordFileEdit } from "../../services/fileFreshness.js";
22
+ import { triggerBackup } from "../../core/backupHook.js";
22
23
  import { NotebookEditTool } from "../NotebookEditTool/NotebookEditTool.js";
23
24
  import { DESCRIPTION } from "./prompt.js";
24
25
  import { applyEdit } from "./utils.js";
@@ -213,8 +214,14 @@ const FileEditTool = {
213
214
  const originalFile = existsSync(fullFilePath) ? readFileSync(fullFilePath, enc) : "";
214
215
  writeTextContent(fullFilePath, updatedFile, enc, endings);
215
216
  recordFileEdit(fullFilePath, updatedFile);
217
+ triggerBackup(
218
+ fullFilePath,
219
+ originalFile || null,
220
+ updatedFile,
221
+ old_string === "" ? "create" : "update"
222
+ );
216
223
  readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs;
217
- if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`)) {
224
+ if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`) || fullFilePath.endsWith(`${sep}AGENTS.md`) || fullFilePath.endsWith(`${sep}CLAUDE.md`)) {
218
225
  }
219
226
  emitReminderEvent("file:edited", {
220
227
  filePath: fullFilePath,