@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/tools/FileEditTool/FileEditTool.tsx"],
4
- "sourcesContent": ["import { Hunk } from 'diff'\nimport { existsSync, lstatSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { dirname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool, ValidationResult } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n findSimilarFile,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\nimport { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\nimport { DESCRIPTION } from './prompt'\nimport { applyEdit } from './utils'\nimport {\n hasWritePermission,\n pathInOriginalCwd,\n} from '@utils/permissions/filesystem'\nimport { PROJECT_FILE } from '@constants/product'\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to modify'),\n old_string: z.string().describe('The text to replace'),\n new_string: z\n .string()\n .describe(\n 'The text to replace it with (must be different from old_string)',\n ),\n replace_all: z\n .boolean()\n .optional()\n .default(false)\n .describe('Replace all occurences of old_string (default false)'),\n})\n\nexport type In = typeof inputSchema\n\n// Number of lines of context to include before/after the change in our result message\nconst N_LINES_SNIPPET = 4\n\nexport const FileEditTool = {\n name: 'Edit',\n async description() {\n return 'A tool for editing files'\n },\n async prompt() {\n return DESCRIPTION\n },\n inputSchema,\n userFacingName() {\n return 'Edit'\n },\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // FileEdit modifies files, not safe for concurrent execution\n },\n needsPermissions({ file_path }) {\n return !hasWritePermission(file_path)\n },\n renderToolUseMessage(input, { verbose }) {\n const filePath = verbose\n ? input.file_path\n : relative(getCwd(), input.file_path)\n const replaceAllSuffix = input.replace_all ? ', replace_all: true' : ''\n return `file_path: ${filePath}${replaceAllSuffix}`\n },\n renderToolResultMessage(output, options?: { verbose?: boolean }) {\n const verbose = options?.verbose ?? false\n\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={getTheme().secondaryText}>Edit completed</Text>\n </Box>\n </Box>\n )\n }\n\n const { filePath, structuredPatch } = output\n return (\n <FileEditToolUpdatedMessage\n filePath={filePath}\n structuredPatch={structuredPatch}\n verbose={verbose}\n />\n )\n },\n renderToolUseRejectedMessage(\n { file_path, old_string, new_string, replace_all }: any = {},\n { columns, verbose }: any = {},\n ) {\n try {\n if (!file_path) {\n return <FallbackToolUseRejectedMessage />\n }\n const { patch } = applyEdit(\n file_path,\n old_string,\n new_string,\n replace_all,\n )\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF{' '}\n <Text color={getTheme().error}>\n User rejected {old_string === '' ? 'write' : 'update'} to{' '}\n </Text>\n <Text bold>\n {verbose ? file_path : relative(getCwd(), file_path)}\n </Text>\n </Text>\n {intersperse(\n patch.map(patch => (\n <Box flexDirection=\"column\" paddingLeft={5} key={patch.newStart}>\n <StructuredDiff patch={patch} dim={true} width={columns - 12} />\n </Box>\n )),\n i => (\n <Box paddingLeft={5} key={`ellipsis-${i}`}>\n <Text color={getTheme().secondaryText}>...</Text>\n </Box>\n ),\n )}\n </Box>\n )\n } catch (e) {\n // Handle the case where while we were showing the diff, the user manually made the change.\n // TODO: Find a way to show the diff in this case\n logError(e)\n return (\n <Box flexDirection=\"column\">\n <Text>{' '}\u23BF (No changes)</Text>\n </Box>\n )\n }\n },\n async validateInput(\n { file_path, old_string, new_string, replace_all },\n { readFileTimestamps },\n ) {\n if (old_string === new_string) {\n return {\n result: false,\n message:\n 'No changes to make: old_string and new_string are exactly the same.',\n meta: {\n old_string,\n },\n } as ValidationResult\n }\n\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n\n // Security check: Ensure path is within allowed directory\n if (!pathInOriginalCwd(fullFilePath) && !hasWritePermission(fullFilePath)) {\n return {\n result: false,\n message:\n 'Path traversal detected - file must be within the project directory or an allowed write location.',\n }\n }\n\n // Security check: Check for symlinks that could escape directory boundaries\n if (existsSync(fullFilePath)) {\n try {\n const lstats = lstatSync(fullFilePath)\n if (lstats.isSymbolicLink()) {\n return {\n result: false,\n message:\n 'Cannot edit symbolic links for security reasons. Edit the target file directly.',\n }\n }\n } catch {\n // If we can't stat the file, let other checks handle it\n }\n }\n\n if (existsSync(fullFilePath) && old_string === '') {\n return {\n result: false,\n message: 'Cannot create new file - file already exists.',\n }\n }\n\n if (!existsSync(fullFilePath) && old_string === '') {\n return {\n result: true,\n }\n }\n\n if (!existsSync(fullFilePath)) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n if (fullFilePath.endsWith('.ipynb')) {\n return {\n result: false,\n message: `File is a Jupyter Notebook. Use the ${NotebookEditTool.name} to edit this file.`,\n }\n }\n\n const readTimestamp = readFileTimestamps[fullFilePath]\n if (!readTimestamp) {\n return {\n result: false,\n message:\n 'File has not been read yet. Read it first before writing to it.',\n meta: {\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n // Check if file exists and get its last modified time\n const stats = statSync(fullFilePath)\n const lastWriteTime = stats.mtimeMs\n if (lastWriteTime > readTimestamp) {\n return {\n result: false,\n message:\n 'File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.',\n }\n }\n\n const enc = detectFileEncoding(fullFilePath)\n const file = readFileSync(fullFilePath, enc)\n if (!file.includes(old_string)) {\n return {\n result: false,\n message: `String to replace not found in file.`,\n meta: {\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n const matches = file.split(old_string).length - 1\n if (matches > 1 && !replace_all) {\n return {\n result: false,\n message: `Found ${matches} matches of the string to replace. For safety, this tool only supports replacing exactly one occurrence at a time. Add more lines of context to your edit and try again, or use replace_all to change every instance.`,\n meta: {\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, old_string, new_string, replace_all },\n { readFileTimestamps },\n ) {\n const { patch, updatedFile } = applyEdit(\n file_path,\n old_string,\n new_string,\n replace_all,\n )\n\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const dir = dirname(fullFilePath)\n mkdirSync(dir, { recursive: true })\n const enc = existsSync(fullFilePath)\n ? detectFileEncoding(fullFilePath)\n : 'utf8'\n const endings = existsSync(fullFilePath)\n ? detectLineEndings(fullFilePath)\n : 'LF'\n const originalFile = existsSync(fullFilePath)\n ? readFileSync(fullFilePath, enc)\n : ''\n writeTextContent(fullFilePath, updatedFile, enc, endings)\n\n // Record Agent edit operation for file freshness tracking\n recordFileEdit(fullFilePath, updatedFile)\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n\n // Log when editing CLAUDE.md\n if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`)) {\n }\n\n // Emit file edited event for system reminders\n emitReminderEvent('file:edited', {\n filePath: fullFilePath,\n oldString: old_string,\n newString: new_string,\n timestamp: Date.now(),\n operation:\n old_string === '' ? 'create' : new_string === '' ? 'delete' : 'update',\n })\n\n const data = {\n filePath: file_path,\n oldString: old_string,\n newString: new_string,\n originalFile,\n structuredPatch: patch,\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant({ filePath, originalFile, oldString, newString }) {\n const { snippet, startLine } = getSnippet(\n originalFile || '',\n oldString,\n newString,\n )\n return `The file ${filePath} has been updated. Here's the result of running \\`cat -n\\` on a snippet of the edited file:\n${addLineNumbers({\n content: snippet,\n startLine,\n})}`\n },\n} satisfies Tool<\n typeof inputSchema,\n {\n filePath: string\n oldString: string\n newString: string\n originalFile: string\n structuredPatch: Hunk[]\n }\n>\n\nexport function getSnippet(\n initialText: string,\n oldStr: string,\n newStr: string,\n): { snippet: string; startLine: number } {\n const before = initialText.split(oldStr)[0] ?? ''\n const replacementLine = before.split(/\\r?\\n/).length - 1\n const newFileLines = initialText.replace(oldStr, newStr).split(/\\r?\\n/)\n // Calculate the start and end line numbers for the snippet\n const startLine = Math.max(0, replacementLine - N_LINES_SNIPPET)\n const endLine =\n replacementLine + N_LINES_SNIPPET + newStr.split(/\\r?\\n/).length\n // Get snippet\n const snippetLines = newFileLines.slice(startLine, endLine + 1)\n const snippet = snippetLines.join('\\n')\n return { snippet, startLine: startLine + 1 }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Hunk } from 'diff'\nimport { existsSync, lstatSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { dirname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool, ValidationResult } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n findSimilarFile,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\nimport { triggerBackup } from '@core/backupHook'\nimport { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\nimport { DESCRIPTION } from './prompt'\nimport { applyEdit } from './utils'\nimport {\n hasWritePermission,\n pathInOriginalCwd,\n} from '@utils/permissions/filesystem'\nimport { PROJECT_FILE } from '@constants/product'\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to modify'),\n old_string: z.string().describe('The text to replace'),\n new_string: z\n .string()\n .describe(\n 'The text to replace it with (must be different from old_string)',\n ),\n replace_all: z\n .boolean()\n .optional()\n .default(false)\n .describe('Replace all occurences of old_string (default false)'),\n})\n\nexport type In = typeof inputSchema\n\n// Number of lines of context to include before/after the change in our result message\nconst N_LINES_SNIPPET = 4\n\nexport const FileEditTool = {\n name: 'Edit',\n async description() {\n return 'A tool for editing files'\n },\n async prompt() {\n return DESCRIPTION\n },\n inputSchema,\n userFacingName() {\n return 'Edit'\n },\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // FileEdit modifies files, not safe for concurrent execution\n },\n needsPermissions({ file_path }) {\n return !hasWritePermission(file_path)\n },\n renderToolUseMessage(input, { verbose }) {\n const filePath = verbose\n ? input.file_path\n : relative(getCwd(), input.file_path)\n const replaceAllSuffix = input.replace_all ? ', replace_all: true' : ''\n return `file_path: ${filePath}${replaceAllSuffix}`\n },\n renderToolResultMessage(output, options?: { verbose?: boolean }) {\n const verbose = options?.verbose ?? false\n\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text color={getTheme().secondaryText}>Edit completed</Text>\n </Box>\n </Box>\n )\n }\n\n const { filePath, structuredPatch } = output\n return (\n <FileEditToolUpdatedMessage\n filePath={filePath}\n structuredPatch={structuredPatch}\n verbose={verbose}\n />\n )\n },\n renderToolUseRejectedMessage(\n { file_path, old_string, new_string, replace_all }: any = {},\n { columns, verbose }: any = {},\n ) {\n try {\n if (!file_path) {\n return <FallbackToolUseRejectedMessage />\n }\n const { patch } = applyEdit(\n file_path,\n old_string,\n new_string,\n replace_all,\n )\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF{' '}\n <Text color={getTheme().error}>\n User rejected {old_string === '' ? 'write' : 'update'} to{' '}\n </Text>\n <Text bold>\n {verbose ? file_path : relative(getCwd(), file_path)}\n </Text>\n </Text>\n {intersperse(\n patch.map(patch => (\n <Box flexDirection=\"column\" paddingLeft={5} key={patch.newStart}>\n <StructuredDiff patch={patch} dim={true} width={columns - 12} />\n </Box>\n )),\n i => (\n <Box paddingLeft={5} key={`ellipsis-${i}`}>\n <Text color={getTheme().secondaryText}>...</Text>\n </Box>\n ),\n )}\n </Box>\n )\n } catch (e) {\n // Handle the case where while we were showing the diff, the user manually made the change.\n // TODO: Find a way to show the diff in this case\n logError(e)\n return (\n <Box flexDirection=\"column\">\n <Text>{' '}\u23BF (No changes)</Text>\n </Box>\n )\n }\n },\n async validateInput(\n { file_path, old_string, new_string, replace_all },\n { readFileTimestamps },\n ) {\n if (old_string === new_string) {\n return {\n result: false,\n message:\n 'No changes to make: old_string and new_string are exactly the same.',\n meta: {\n old_string,\n },\n } as ValidationResult\n }\n\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n\n // Security check: Ensure path is within allowed directory\n if (!pathInOriginalCwd(fullFilePath) && !hasWritePermission(fullFilePath)) {\n return {\n result: false,\n message:\n 'Path traversal detected - file must be within the project directory or an allowed write location.',\n }\n }\n\n // Security check: Check for symlinks that could escape directory boundaries\n if (existsSync(fullFilePath)) {\n try {\n const lstats = lstatSync(fullFilePath)\n if (lstats.isSymbolicLink()) {\n return {\n result: false,\n message:\n 'Cannot edit symbolic links for security reasons. Edit the target file directly.',\n }\n }\n } catch {\n // If we can't stat the file, let other checks handle it\n }\n }\n\n if (existsSync(fullFilePath) && old_string === '') {\n return {\n result: false,\n message: 'Cannot create new file - file already exists.',\n }\n }\n\n if (!existsSync(fullFilePath) && old_string === '') {\n return {\n result: true,\n }\n }\n\n if (!existsSync(fullFilePath)) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n if (fullFilePath.endsWith('.ipynb')) {\n return {\n result: false,\n message: `File is a Jupyter Notebook. Use the ${NotebookEditTool.name} to edit this file.`,\n }\n }\n\n const readTimestamp = readFileTimestamps[fullFilePath]\n if (!readTimestamp) {\n return {\n result: false,\n message:\n 'File has not been read yet. Read it first before writing to it.',\n meta: {\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n // Check if file exists and get its last modified time\n const stats = statSync(fullFilePath)\n const lastWriteTime = stats.mtimeMs\n if (lastWriteTime > readTimestamp) {\n return {\n result: false,\n message:\n 'File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.',\n }\n }\n\n const enc = detectFileEncoding(fullFilePath)\n const file = readFileSync(fullFilePath, enc)\n if (!file.includes(old_string)) {\n return {\n result: false,\n message: `String to replace not found in file.`,\n meta: {\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n const matches = file.split(old_string).length - 1\n if (matches > 1 && !replace_all) {\n return {\n result: false,\n message: `Found ${matches} matches of the string to replace. For safety, this tool only supports replacing exactly one occurrence at a time. Add more lines of context to your edit and try again, or use replace_all to change every instance.`,\n meta: {\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, old_string, new_string, replace_all },\n { readFileTimestamps },\n ) {\n const { patch, updatedFile } = applyEdit(\n file_path,\n old_string,\n new_string,\n replace_all,\n )\n\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const dir = dirname(fullFilePath)\n mkdirSync(dir, { recursive: true })\n const enc = existsSync(fullFilePath)\n ? detectFileEncoding(fullFilePath)\n : 'utf8'\n const endings = existsSync(fullFilePath)\n ? detectLineEndings(fullFilePath)\n : 'LF'\n const originalFile = existsSync(fullFilePath)\n ? readFileSync(fullFilePath, enc)\n : ''\n writeTextContent(fullFilePath, updatedFile, enc, endings)\n\n // Record Agent edit operation for file freshness tracking\n recordFileEdit(fullFilePath, updatedFile)\n triggerBackup(\n fullFilePath,\n originalFile || null,\n updatedFile,\n old_string === '' ? 'create' : 'update',\n )\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n\n // Log when editing project doc files\n if (\n fullFilePath.endsWith(`${sep}${PROJECT_FILE}`) ||\n fullFilePath.endsWith(`${sep}AGENTS.md`) ||\n fullFilePath.endsWith(`${sep}CLAUDE.md`)\n ) {\n }\n\n // Emit file edited event for system reminders\n emitReminderEvent('file:edited', {\n filePath: fullFilePath,\n oldString: old_string,\n newString: new_string,\n timestamp: Date.now(),\n operation:\n old_string === '' ? 'create' : new_string === '' ? 'delete' : 'update',\n })\n\n const data = {\n filePath: file_path,\n oldString: old_string,\n newString: new_string,\n originalFile,\n structuredPatch: patch,\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant({ filePath, originalFile, oldString, newString }) {\n const { snippet, startLine } = getSnippet(\n originalFile || '',\n oldString,\n newString,\n )\n return `The file ${filePath} has been updated. Here's the result of running \\`cat -n\\` on a snippet of the edited file:\n${addLineNumbers({\n content: snippet,\n startLine,\n})}`\n },\n} satisfies Tool\n\nexport function getSnippet(\n initialText: string,\n oldStr: string,\n newStr: string,\n): { snippet: string; startLine: number } {\n const before = initialText.split(oldStr)[0] ?? ''\n const replacementLine = before.split(/\\r?\\n/).length - 1\n const newFileLines = initialText.replace(oldStr, newStr).split(/\\r?\\n/)\n // Calculate the start and end line numbers for the snippet\n const startLine = Math.max(0, replacementLine - N_LINES_SNIPPET)\n const endLine =\n replacementLine + N_LINES_SNIPPET + newStr.split(/\\r?\\n/).length\n // Get snippet\n const snippetLines = newFileLines.slice(startLine, endLine + 1)\n const snippet = snippetLines.join('\\n')\n return { snippet, startLine: startLine + 1 }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["patch"]
7
7
  }
@@ -167,6 +167,20 @@ const FileReadTool = {
167
167
  lineOffset,
168
168
  limit
169
169
  );
170
+ if (content.length === 0 && totalLines === 0) {
171
+ emitReminderEvent("file:empty", {
172
+ filePath: fullFilePath,
173
+ timestamp: Date.now()
174
+ });
175
+ }
176
+ if (limit && totalLines > lineCount) {
177
+ emitReminderEvent("file:truncated", {
178
+ filePath: fullFilePath,
179
+ limit: lineCount,
180
+ totalLines,
181
+ timestamp: Date.now()
182
+ });
183
+ }
170
184
  if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {
171
185
  throw new Error(formatFileSizeError(content.length));
172
186
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/FileReadTool/FileReadTool.tsx"],
4
- "sourcesContent": ["import { ImageBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { statSync } from 'node:fs'\nimport { Box, Text } from 'ink'\nimport * as path from 'node:path'\nimport { extname, relative } from 'node:path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport type { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport {\n addLineNumbers,\n findSimilarFile,\n normalizeFilePath,\n readTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport {\n recordFileRead,\n generateFileModificationReminder,\n} from '@services/fileFreshness'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\nimport { secureFileService } from '@utils/secureFile'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_OUTPUT_SIZE = 0.25 * 1024 * 1024 // 0.25MB in bytes\n\n// Common image extensions\nconst IMAGE_EXTENSIONS = new Set([\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.webp',\n])\n\n// Maximum dimensions for images\nconst MAX_WIDTH = 2000\nconst MAX_HEIGHT = 2000\nconst MAX_IMAGE_SIZE = 3.75 * 1024 * 1024 // 5MB in bytes, with base64 encoding\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once',\n ),\n limit: z\n .number()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once.',\n ),\n})\n\nexport const FileReadTool = {\n name: 'Read',\n async description() {\n return DESCRIPTION\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // FileRead is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Read'\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ file_path }) {\n return !hasReadPermission(file_path || getCwd())\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, ...rest } = input\n const entries = [\n ['file_path', verbose ? file_path : relative(getCwd(), file_path)],\n ...Object.entries(rest),\n ]\n return entries\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output || !output.type) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n\n const verbose = false // Set default value for verbose\n // TODO: Render recursively\n switch (output.type) {\n case 'image':\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>Read image</Text>\n </Box>\n </Box>\n )\n case 'text': {\n // Guard against missing file property\n if (!output.file) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n const { filePath, content, numLines } = output.file\n const contentWithFallback = content || '(No content)'\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Box flexDirection=\"column\">\n <HighlightedCode\n code={\n verbose\n ? contentWithFallback\n : contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')\n }\n language={extname(filePath || '').slice(1)}\n />\n {!verbose && (numLines || 0) > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{(numLines || 0) - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n )\n }\n default:\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n async validateInput({ file_path, offset, limit }) {\n const fullFilePath = normalizeFilePath(file_path)\n\n // Use secure file service to check if file exists and get file info\n const fileCheck = secureFileService.safeGetFileInfo(fullFilePath)\n if (!fileCheck.success) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n const stats = fileCheck.stats!\n const fileSize = stats.size\n const ext = path.extname(fullFilePath).toLowerCase()\n\n // Skip size check for image files - they have their own size limits\n if (!IMAGE_EXTENSIONS.has(ext)) {\n // If file is too large and no offset/limit provided\n if (fileSize > MAX_OUTPUT_SIZE && !offset && !limit) {\n return {\n result: false,\n message: formatFileSizeError(fileSize),\n meta: { fileSize },\n }\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, offset = 1, limit = undefined },\n { readFileTimestamps },\n ) {\n const ext = path.extname(file_path).toLowerCase()\n const fullFilePath = normalizeFilePath(file_path)\n\n // Record file read for freshness tracking\n recordFileRead(fullFilePath)\n\n // Emit file read event for system reminders\n emitReminderEvent('file:read', {\n filePath: fullFilePath,\n extension: ext,\n timestamp: Date.now(),\n })\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = Date.now()\n\n // Check for file modifications and generate reminder if needed\n const modificationReminder = generateFileModificationReminder(fullFilePath)\n if (modificationReminder) {\n emitReminderEvent('file:modified', {\n filePath: fullFilePath,\n reminder: modificationReminder,\n timestamp: Date.now(),\n })\n }\n\n // If it's an image file, process and return base64 encoded contents\n if (IMAGE_EXTENSIONS.has(ext)) {\n const data = await readImage(fullFilePath, ext)\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n // Handle offset properly - if offset is 0, don't subtract 1\n const lineOffset = offset === 0 ? 0 : offset - 1\n const { content, lineCount, totalLines } = readTextContent(\n fullFilePath,\n lineOffset,\n limit,\n )\n\n // Add size validation after reading for non-image files\n if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {\n throw new Error(formatFileSizeError(content.length))\n }\n\n const data = {\n type: 'text' as const,\n file: {\n filePath: file_path,\n content: content,\n numLines: lineCount,\n startLine: offset,\n totalLines,\n },\n }\n\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant(data) {\n // Guard against undefined or null data\n if (!data || !data.type) {\n return 'File read completed'\n }\n\n switch (data.type) {\n case 'image':\n // Guard against missing file property\n if (!data.file) {\n return 'Image read completed'\n }\n return [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: data.file.base64 || '',\n media_type: data.file.type || 'image/png',\n },\n },\n ]\n case 'text':\n // Guard against missing file property\n if (!data.file) {\n return 'File read completed'\n }\n return addLineNumbers(data.file)\n default:\n return 'File read completed'\n }\n },\n} satisfies Tool<\n typeof inputSchema,\n | {\n type: 'text'\n file: {\n filePath: string\n content: string\n numLines: number\n startLine: number\n totalLines: number\n }\n }\n | {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n }\n>\n\nconst formatFileSizeError = (sizeInBytes: number) =>\n `File content (${Math.round(sizeInBytes / 1024)}KB) exceeds maximum allowed size (${Math.round(MAX_OUTPUT_SIZE / 1024)}KB). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.`\n\nfunction createImageResponse(\n buffer: Buffer,\n ext: string,\n): {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n} {\n return {\n type: 'image',\n file: {\n base64: buffer.toString('base64'),\n type: `image/${ext.slice(1)}` as ImageBlockParam.Source['media_type'],\n },\n }\n}\n\nasync function readImage(\n filePath: string,\n ext: string,\n): Promise<{\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n}> {\n try {\n const stats = statSync(filePath)\n const sharp = (\n (await import('sharp')) as unknown as { default: typeof import('sharp') }\n ).default\n\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n const image = sharp(fileReadResult.content as Buffer)\n const metadata = await image.metadata()\n\n if (!metadata.width || !metadata.height) {\n if (stats.size > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n }\n\n // Calculate dimensions while maintaining aspect ratio\n let width = metadata.width || 0\n let height = metadata.height || 0\n\n // Check if the original file just works\n if (\n stats.size <= MAX_IMAGE_SIZE &&\n width <= MAX_WIDTH &&\n height <= MAX_HEIGHT\n ) {\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n\n if (width > MAX_WIDTH) {\n height = Math.round((height * MAX_WIDTH) / width)\n width = MAX_WIDTH\n }\n\n if (height > MAX_HEIGHT) {\n width = Math.round((width * MAX_HEIGHT) / height)\n height = MAX_HEIGHT\n }\n\n // Resize image and convert to buffer\n const resizedImageBuffer = await image\n .resize(width, height, {\n fit: 'inside',\n withoutEnlargement: true,\n })\n .toBuffer()\n\n // If still too large after resize, compress quality\n if (resizedImageBuffer.length > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n\n return createImageResponse(resizedImageBuffer, ext)\n } catch (e) {\n logError(e)\n // If any error occurs during processing, return original image\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { ImageBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { statSync } from 'node:fs'\nimport { Box, Text } from 'ink'\nimport * as path from 'node:path'\nimport { extname, relative } from 'node:path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport type { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport {\n addLineNumbers,\n findSimilarFile,\n normalizeFilePath,\n readTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport {\n recordFileRead,\n generateFileModificationReminder,\n} from '@services/fileFreshness'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\nimport { secureFileService } from '@utils/secureFile'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_OUTPUT_SIZE = 0.25 * 1024 * 1024 // 0.25MB in bytes\n\n// Common image extensions\nconst IMAGE_EXTENSIONS = new Set([\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.webp',\n])\n\n// Maximum dimensions for images\nconst MAX_WIDTH = 2000\nconst MAX_HEIGHT = 2000\nconst MAX_IMAGE_SIZE = 3.75 * 1024 * 1024 // 5MB in bytes, with base64 encoding\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once',\n ),\n limit: z\n .number()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once.',\n ),\n})\n\nexport const FileReadTool = {\n name: 'Read',\n async description() {\n return DESCRIPTION\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // FileRead is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Read'\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ file_path }) {\n return !hasReadPermission(file_path || getCwd())\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, ...rest } = input\n const entries = [\n ['file_path', verbose ? file_path : relative(getCwd(), file_path)],\n ...Object.entries(rest),\n ]\n return entries\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output || !output.type) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n\n const verbose = false // Set default value for verbose\n // TODO: Render recursively\n switch (output.type) {\n case 'image':\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>Read image</Text>\n </Box>\n </Box>\n )\n case 'text': {\n // Guard against missing file property\n if (!output.file) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n const { filePath, content, numLines } = output.file\n const contentWithFallback = content || '(No content)'\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Box flexDirection=\"column\">\n <HighlightedCode\n code={\n verbose\n ? contentWithFallback\n : contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')\n }\n language={extname(filePath || '').slice(1)}\n />\n {!verbose && (numLines || 0) > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{(numLines || 0) - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n )\n }\n default:\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;</Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n async validateInput({ file_path, offset, limit }) {\n const fullFilePath = normalizeFilePath(file_path)\n\n // Use secure file service to check if file exists and get file info\n const fileCheck = secureFileService.safeGetFileInfo(fullFilePath)\n if (!fileCheck.success) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n const stats = fileCheck.stats!\n const fileSize = stats.size\n const ext = path.extname(fullFilePath).toLowerCase()\n\n // Skip size check for image files - they have their own size limits\n if (!IMAGE_EXTENSIONS.has(ext)) {\n // If file is too large and no offset/limit provided\n if (fileSize > MAX_OUTPUT_SIZE && !offset && !limit) {\n return {\n result: false,\n message: formatFileSizeError(fileSize),\n meta: { fileSize },\n }\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, offset = 1, limit = undefined },\n { readFileTimestamps },\n ) {\n const ext = path.extname(file_path).toLowerCase()\n const fullFilePath = normalizeFilePath(file_path)\n\n // Record file read for freshness tracking\n recordFileRead(fullFilePath)\n\n // Emit file read event for system reminders\n emitReminderEvent('file:read', {\n filePath: fullFilePath,\n extension: ext,\n timestamp: Date.now(),\n })\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = Date.now()\n\n // Check for file modifications and generate reminder if needed\n const modificationReminder = generateFileModificationReminder(fullFilePath)\n if (modificationReminder) {\n emitReminderEvent('file:modified', {\n filePath: fullFilePath,\n reminder: modificationReminder,\n timestamp: Date.now(),\n })\n }\n\n // If it's an image file, process and return base64 encoded contents\n if (IMAGE_EXTENSIONS.has(ext)) {\n const data = await readImage(fullFilePath, ext)\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n // Handle offset properly - if offset is 0, don't subtract 1\n const lineOffset = offset === 0 ? 0 : offset - 1\n const { content, lineCount, totalLines } = readTextContent(\n fullFilePath,\n lineOffset,\n limit,\n )\n\n // Emit file:empty reminder if file exists but has no content\n if (content.length === 0 && totalLines === 0) {\n emitReminderEvent('file:empty', {\n filePath: fullFilePath,\n timestamp: Date.now(),\n })\n }\n\n // Emit file:truncated reminder if content was limited\n if (limit && totalLines > lineCount) {\n emitReminderEvent('file:truncated', {\n filePath: fullFilePath,\n limit: lineCount,\n totalLines,\n timestamp: Date.now(),\n })\n }\n\n // Add size validation after reading for non-image files\n if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {\n throw new Error(formatFileSizeError(content.length))\n }\n\n const data = {\n type: 'text' as const,\n file: {\n filePath: file_path,\n content: content,\n numLines: lineCount,\n startLine: offset,\n totalLines,\n },\n }\n\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant(data) {\n // Guard against undefined or null data\n if (!data || !data.type) {\n return 'File read completed'\n }\n\n switch (data.type) {\n case 'image':\n // Guard against missing file property\n if (!data.file) {\n return 'Image read completed'\n }\n return [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: data.file.base64 || '',\n media_type: data.file.type || 'image/png',\n },\n },\n ]\n case 'text':\n // Guard against missing file property\n if (!data.file) {\n return 'File read completed'\n }\n return addLineNumbers(data.file)\n default:\n return 'File read completed'\n }\n },\n} satisfies Tool\n\nconst formatFileSizeError = (sizeInBytes: number) =>\n `File content (${Math.round(sizeInBytes / 1024)}KB) exceeds maximum allowed size (${Math.round(MAX_OUTPUT_SIZE / 1024)}KB). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.`\n\nfunction createImageResponse(\n buffer: Buffer,\n ext: string,\n): {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n} {\n return {\n type: 'image',\n file: {\n base64: buffer.toString('base64'),\n type: `image/${ext.slice(1)}` as ImageBlockParam.Source['media_type'],\n },\n }\n}\n\nasync function readImage(\n filePath: string,\n ext: string,\n): Promise<{\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n}> {\n try {\n const stats = statSync(filePath)\n const sharp = (\n (await import('sharp')) as unknown as { default: typeof import('sharp') }\n ).default\n\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n const image = sharp(fileReadResult.content as Buffer)\n const metadata = await image.metadata()\n\n if (!metadata.width || !metadata.height) {\n if (stats.size > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n }\n\n // Calculate dimensions while maintaining aspect ratio\n let width = metadata.width || 0\n let height = metadata.height || 0\n\n // Check if the original file just works\n if (\n stats.size <= MAX_IMAGE_SIZE &&\n width <= MAX_WIDTH &&\n height <= MAX_HEIGHT\n ) {\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n\n if (width > MAX_WIDTH) {\n height = Math.round((height * MAX_WIDTH) / width)\n width = MAX_WIDTH\n }\n\n if (height > MAX_HEIGHT) {\n width = Math.round((width * MAX_HEIGHT) / height)\n height = MAX_HEIGHT\n }\n\n // Resize image and convert to buffer\n const resizedImageBuffer = await image\n .resize(width, height, {\n fit: 'inside',\n withoutEnlargement: true,\n })\n .toBuffer()\n\n // If still too large after resize, compress quality\n if (resizedImageBuffer.length > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n\n return createImageResponse(resizedImageBuffer, ext)\n } catch (e) {\n logError(e)\n // If any error occurs during processing, return original image\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["data", "fileReadResult"]
7
7
  }
@@ -25,6 +25,7 @@ import { getPatch } from "../../utils/diff.js";
25
25
  import { PROJECT_FILE } from "../../constants/product.js";
26
26
  import { emitReminderEvent } from "../../services/systemReminder.js";
27
27
  import { recordFileEdit } from "../../services/fileFreshness.js";
28
+ import { triggerBackup } from "../../core/backupHook.js";
28
29
  const MAX_LINES_TO_RENDER = 5;
29
30
  const MAX_LINES_TO_RENDER_FOR_ASSISTANT = 16e3;
30
31
  const TRUNCATED_MESSAGE = "<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with Grep in order to find the line numbers of what you are looking for.</NOTE>";
@@ -141,8 +142,14 @@ const FileWriteTool = {
141
142
  mkdirSync(dir, { recursive: true });
142
143
  writeTextContent(fullFilePath, content, enc, endings);
143
144
  recordFileEdit(fullFilePath, content);
145
+ triggerBackup(
146
+ fullFilePath,
147
+ oldContent,
148
+ content,
149
+ oldContent ? "update" : "create"
150
+ );
144
151
  readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs;
145
- if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`)) {
152
+ if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`) || fullFilePath.endsWith(`${sep}AGENTS.md`) || fullFilePath.endsWith(`${sep}CLAUDE.md`)) {
146
153
  }
147
154
  emitReminderEvent("file:edited", {
148
155
  filePath: fullFilePath,
@@ -193,6 +200,8 @@ ${addLineNumbers({
193
200
  content: content.split(/\r?\n/).length > MAX_LINES_TO_RENDER_FOR_ASSISTANT ? content.split(/\r?\n/).slice(0, MAX_LINES_TO_RENDER_FOR_ASSISTANT).join("\n") + TRUNCATED_MESSAGE : content,
194
201
  startLine: 1
195
202
  })}`;
203
+ default:
204
+ return `File operation completed at: ${filePath}`;
196
205
  }
197
206
  }
198
207
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/FileWriteTool/FileWriteTool.tsx"],
4
- "sourcesContent": ["import { Hunk } from 'diff'\nimport { existsSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { EOL } from 'os'\nimport { dirname, extname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport type { Tool } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n detectRepoLineEndings,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { PROMPT } from './prompt'\nimport { hasWritePermission } from '@utils/permissions/filesystem'\nimport { getPatch } from '@utils/diff'\nimport { PROJECT_FILE } from '@constants/product'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_LINES_TO_RENDER_FOR_ASSISTANT = 16000\nconst TRUNCATED_MESSAGE =\n '<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with Grep in order to find the line numbers of what you are looking for.</NOTE>'\n\nconst inputSchema = z.strictObject({\n file_path: z\n .string()\n .describe(\n 'The absolute path to the file to write (must be absolute, not relative)',\n ),\n content: z.string().describe('The content to write to the file'),\n})\n\nexport const FileWriteTool = {\n name: 'Write',\n async description() {\n return 'Write a file to the local filesystem.'\n },\n userFacingName: () => 'Write',\n async prompt() {\n return PROMPT\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // FileWriteTool modifies state/files, not safe for concurrent execution\n },\n needsPermissions({ file_path }) {\n return !hasWritePermission(file_path)\n },\n renderToolUseMessage(input, { verbose }) {\n return `file_path: ${verbose ? input.file_path : relative(getCwd(), input.file_path)}`\n },\n renderToolUseRejectedMessage(\n { file_path, content }: any = {},\n { columns, verbose }: any = {},\n ) {\n try {\n if (!file_path) {\n return <FallbackToolUseRejectedMessage />\n }\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const oldFileExists = existsSync(fullFilePath)\n const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'\n const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null\n const type = oldContent ? 'update' : 'create'\n const patch = getPatch({\n filePath: file_path,\n fileContents: oldContent ?? '',\n oldStr: oldContent ?? '',\n newStr: content,\n })\n\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF{' '}\n <Text color={getTheme().error}>\n User rejected {type === 'update' ? 'update' : 'write'} to{' '}\n </Text>\n <Text bold>\n {verbose ? file_path : relative(getCwd(), file_path)}\n </Text>\n </Text>\n {intersperse(\n patch.map(_ => (\n <Box flexDirection=\"column\" paddingLeft={5} key={_.newStart}>\n <StructuredDiff patch={_} dim={true} width={columns - 12} />\n </Box>\n )),\n i => (\n <Box paddingLeft={5} key={`ellipsis-${i}`}>\n <Text color={getTheme().secondaryText}>...</Text>\n </Box>\n ),\n )}\n </Box>\n )\n } catch (e) {\n // Handle the case where while we were showing the diff, the user manually made the change.\n // TODO: Find a way to show the diff in this case\n logError(e)\n return (\n <Box flexDirection=\"column\">\n <Text>{' '}\u23BF (No changes)</Text>\n </Box>\n )\n }\n },\n renderToolResultMessage(\n { filePath, content, structuredPatch, type },\n options?: { verbose?: boolean },\n ) {\n const verbose = options?.verbose ?? false\n switch (type) {\n case 'create': {\n const contentWithFallback = content || '(No content)'\n const numLines = content.split(EOL).length\n\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF Wrote {numLines} lines to{' '}\n <Text bold>\n {verbose ? filePath : relative(getCwd(), filePath)}\n </Text>\n </Text>\n {/* Only show code preview in verbose mode */}\n {verbose && (\n <Box flexDirection=\"column\" paddingLeft={5}>\n <HighlightedCode\n code={contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')}\n language={extname(filePath).slice(1)}\n />\n {numLines > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{numLines - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n )}\n </Box>\n )\n }\n case 'update':\n return (\n <FileEditToolUpdatedMessage\n filePath={filePath}\n structuredPatch={structuredPatch}\n verbose={verbose}\n />\n )\n }\n },\n async validateInput({ file_path }, { readFileTimestamps }) {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n if (!existsSync(fullFilePath)) {\n return { result: true }\n }\n\n const readTimestamp = readFileTimestamps[fullFilePath]\n if (!readTimestamp) {\n return {\n result: false,\n message:\n 'File has not been read yet. Read it first before writing to it.',\n }\n }\n\n // Check if file exists and get its last modified time\n const stats = statSync(fullFilePath)\n const lastWriteTime = stats.mtimeMs\n if (lastWriteTime > readTimestamp) {\n return {\n result: false,\n message:\n 'File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.',\n }\n }\n\n return { result: true }\n },\n async *call({ file_path, content }, { readFileTimestamps }) {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const dir = dirname(fullFilePath)\n const oldFileExists = existsSync(fullFilePath)\n const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'\n const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null\n\n const endings = oldFileExists\n ? detectLineEndings(fullFilePath)\n : await detectRepoLineEndings(getCwd())\n\n mkdirSync(dir, { recursive: true })\n writeTextContent(fullFilePath, content, enc, endings!)\n\n // Record Agent edit operation for file freshness tracking\n recordFileEdit(fullFilePath, content)\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n\n // Log when writing to CLAUDE.md\n if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`)) {\n }\n\n // Emit file edited event for system reminders\n emitReminderEvent('file:edited', {\n filePath: fullFilePath,\n content,\n oldContent: oldContent || '',\n timestamp: Date.now(),\n operation: oldFileExists ? 'update' : 'create',\n })\n\n if (oldContent) {\n const patch = getPatch({\n filePath: file_path,\n fileContents: oldContent,\n oldStr: oldContent,\n newStr: content,\n })\n\n const data = {\n type: 'update' as const,\n filePath: file_path,\n content,\n structuredPatch: patch,\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n const data = {\n type: 'create' as const,\n filePath: file_path,\n content,\n structuredPatch: [],\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant({ filePath, content, type }) {\n switch (type) {\n case 'create':\n return `File created successfully at: ${filePath}`\n case 'update':\n return `The file ${filePath} has been updated. Here's the result of running \\`cat -n\\` on a snippet of the edited file:\n${addLineNumbers({\n content:\n content.split(/\\r?\\n/).length > MAX_LINES_TO_RENDER_FOR_ASSISTANT\n ? content\n .split(/\\r?\\n/)\n .slice(0, MAX_LINES_TO_RENDER_FOR_ASSISTANT)\n .join('\\n') + TRUNCATED_MESSAGE\n : content,\n startLine: 1,\n})}`\n }\n },\n} satisfies Tool<\n typeof inputSchema,\n {\n type: 'create' | 'update'\n filePath: string\n content: string\n structuredPatch: Hunk[]\n }\n>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Hunk } from 'diff'\nimport { existsSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { EOL } from 'os'\nimport { dirname, extname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport type { Tool } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n detectRepoLineEndings,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { PROMPT } from './prompt'\nimport { hasWritePermission } from '@utils/permissions/filesystem'\nimport { getPatch } from '@utils/diff'\nimport { PROJECT_FILE } from '@constants/product'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\nimport { triggerBackup } from '@core/backupHook'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_LINES_TO_RENDER_FOR_ASSISTANT = 16000\nconst TRUNCATED_MESSAGE =\n '<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with Grep in order to find the line numbers of what you are looking for.</NOTE>'\n\nconst inputSchema = z.strictObject({\n file_path: z\n .string()\n .describe(\n 'The absolute path to the file to write (must be absolute, not relative)',\n ),\n content: z.string().describe('The content to write to the file'),\n})\n\nexport const FileWriteTool = {\n name: 'Write',\n async description() {\n return 'Write a file to the local filesystem.'\n },\n userFacingName: () => 'Write',\n async prompt() {\n return PROMPT\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // FileWriteTool modifies state/files, not safe for concurrent execution\n },\n needsPermissions({ file_path }) {\n return !hasWritePermission(file_path)\n },\n renderToolUseMessage(input, { verbose }) {\n return `file_path: ${verbose ? input.file_path : relative(getCwd(), input.file_path)}`\n },\n renderToolUseRejectedMessage(\n { file_path, content }: any = {},\n { columns, verbose }: any = {},\n ) {\n try {\n if (!file_path) {\n return <FallbackToolUseRejectedMessage />\n }\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const oldFileExists = existsSync(fullFilePath)\n const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'\n const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null\n const type = oldContent ? 'update' : 'create'\n const patch = getPatch({\n filePath: file_path,\n fileContents: oldContent ?? '',\n oldStr: oldContent ?? '',\n newStr: content,\n })\n\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF{' '}\n <Text color={getTheme().error}>\n User rejected {type === 'update' ? 'update' : 'write'} to{' '}\n </Text>\n <Text bold>\n {verbose ? file_path : relative(getCwd(), file_path)}\n </Text>\n </Text>\n {intersperse(\n patch.map(_ => (\n <Box flexDirection=\"column\" paddingLeft={5} key={_.newStart}>\n <StructuredDiff patch={_} dim={true} width={columns - 12} />\n </Box>\n )),\n i => (\n <Box paddingLeft={5} key={`ellipsis-${i}`}>\n <Text color={getTheme().secondaryText}>...</Text>\n </Box>\n ),\n )}\n </Box>\n )\n } catch (e) {\n // Handle the case where while we were showing the diff, the user manually made the change.\n // TODO: Find a way to show the diff in this case\n logError(e)\n return (\n <Box flexDirection=\"column\">\n <Text>{' '}\u23BF (No changes)</Text>\n </Box>\n )\n }\n },\n renderToolResultMessage(\n { filePath, content, structuredPatch, type },\n options?: { verbose?: boolean },\n ) {\n const verbose = options?.verbose ?? false\n switch (type) {\n case 'create': {\n const contentWithFallback = content || '(No content)'\n const numLines = content.split(EOL).length\n\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF Wrote {numLines} lines to{' '}\n <Text bold>\n {verbose ? filePath : relative(getCwd(), filePath)}\n </Text>\n </Text>\n {/* Only show code preview in verbose mode */}\n {verbose && (\n <Box flexDirection=\"column\" paddingLeft={5}>\n <HighlightedCode\n code={contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')}\n language={extname(filePath).slice(1)}\n />\n {numLines > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{numLines - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n )}\n </Box>\n )\n }\n case 'update':\n return (\n <FileEditToolUpdatedMessage\n filePath={filePath}\n structuredPatch={structuredPatch}\n verbose={verbose}\n />\n )\n }\n },\n async validateInput({ file_path }, { readFileTimestamps }) {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n if (!existsSync(fullFilePath)) {\n return { result: true }\n }\n\n const readTimestamp = readFileTimestamps[fullFilePath]\n if (!readTimestamp) {\n return {\n result: false,\n message:\n 'File has not been read yet. Read it first before writing to it.',\n }\n }\n\n // Check if file exists and get its last modified time\n const stats = statSync(fullFilePath)\n const lastWriteTime = stats.mtimeMs\n if (lastWriteTime > readTimestamp) {\n return {\n result: false,\n message:\n 'File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.',\n }\n }\n\n return { result: true }\n },\n async *call({ file_path, content }, { readFileTimestamps }) {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const dir = dirname(fullFilePath)\n const oldFileExists = existsSync(fullFilePath)\n const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'\n const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null\n\n const endings = oldFileExists\n ? detectLineEndings(fullFilePath)\n : await detectRepoLineEndings(getCwd())\n\n mkdirSync(dir, { recursive: true })\n writeTextContent(fullFilePath, content, enc, endings!)\n\n // Record Agent edit operation for file freshness tracking\n recordFileEdit(fullFilePath, content)\n triggerBackup(\n fullFilePath,\n oldContent,\n content,\n oldContent ? 'update' : 'create',\n )\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n\n // Log when writing to project doc files\n if (\n fullFilePath.endsWith(`${sep}${PROJECT_FILE}`) ||\n fullFilePath.endsWith(`${sep}AGENTS.md`) ||\n fullFilePath.endsWith(`${sep}CLAUDE.md`)\n ) {\n }\n\n // Emit file edited event for system reminders\n emitReminderEvent('file:edited', {\n filePath: fullFilePath,\n content,\n oldContent: oldContent || '',\n timestamp: Date.now(),\n operation: oldFileExists ? 'update' : 'create',\n })\n\n if (oldContent) {\n const patch = getPatch({\n filePath: file_path,\n fileContents: oldContent,\n oldStr: oldContent,\n newStr: content,\n })\n\n const data = {\n type: 'update' as const,\n filePath: file_path,\n content,\n structuredPatch: patch,\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n const data = {\n type: 'create' as const,\n filePath: file_path,\n content,\n structuredPatch: [],\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant({ filePath, content, type }) {\n switch (type) {\n case 'create':\n return `File created successfully at: ${filePath}`\n case 'update':\n return `The file ${filePath} has been updated. Here's the result of running \\`cat -n\\` on a snippet of the edited file:\n${addLineNumbers({\n content:\n content.split(/\\r?\\n/).length > MAX_LINES_TO_RENDER_FOR_ASSISTANT\n ? content\n .split(/\\r?\\n/)\n .slice(0, MAX_LINES_TO_RENDER_FOR_ASSISTANT)\n .join('\\n') + TRUNCATED_MESSAGE\n : content,\n startLine: 1,\n})}`\n default:\n return `File operation completed at: ${filePath}`\n }\n },\n} satisfies Tool\n"],
5
+ "mappings": "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",
6
6
  "names": ["data"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/GlobTool/GlobTool.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numFiles = output?.numFiles ?? 0\n const durationMs = output?.durationMs ?? 0\n // Show expand hint when there are many files and not in verbose mode\n const showExpandHint = !verbose && numFiles > 5\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ pattern, path }, { abortController }) {\n const start = Date.now()\n const { files, truncated } = await glob(\n pattern,\n path ?? getCwd(),\n { limit: 100, offset: 0 },\n abortController.signal,\n )\n const output: Output = {\n filenames: files,\n durationMs: Date.now() - start,\n numFiles: files.length,\n truncated,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n let result = output.filenames.join('\\n')\n if (output.filenames.length === 0) {\n result = 'No files found'\n }\n // Only add truncation message if results were actually truncated\n else if (output.truncated) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n} satisfies Tool<typeof inputSchema, Output>\n"],
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numFiles = output?.numFiles ?? 0\n const durationMs = output?.durationMs ?? 0\n // Show expand hint when there are many files and not in verbose mode\n const showExpandHint = !verbose && numFiles > 5\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ pattern, path }, { abortController }) {\n const start = Date.now()\n const { files, truncated } = await glob(\n pattern,\n path ?? getCwd(),\n { limit: 100, offset: 0 },\n abortController.signal,\n )\n const output: Output = {\n filenames: files,\n durationMs: Date.now() - start,\n numFiles: files.length,\n truncated,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n let result = output.filenames.join('\\n')\n if (output.filenames.length === 0) {\n result = 'No files found'\n }\n // Only add truncation message if results were actually truncated\n else if (output.truncated) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\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/GrepTool/GrepTool.tsx"],
4
- "sourcesContent": ["import { stat } from 'fs/promises'\nimport { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { getAbsolutePath, getAbsoluteAndRelativePaths } from '@utils/file'\nimport { ripGrepStreaming, ripGrepStreamingWithContent } from '@utils/ripgrep'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe(\n 'File or directory to search in (rg PATH). Defaults to current working directory.',\n ),\n // Backward compatible: 'include' is mapped to 'glob'\n include: z\n .string()\n .optional()\n .describe(\n 'File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\") - DEPRECATED: use glob instead',\n ),\n glob: z\n .string()\n .optional()\n .describe(\n 'Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\") - maps to rg --glob',\n ),\n type: z\n .string()\n .optional()\n .describe(\n 'File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types.',\n ),\n output_mode: z\n .enum(['content', 'files_with_matches', 'count'])\n .optional()\n .describe(\n 'Output mode: \"content\" shows matching lines, \"files_with_matches\" shows file paths (default), \"count\" shows match counts',\n ),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.',\n ),\n context: z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before and after each match (rg -C). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-A': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show after each match (rg -A). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-B': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before each match (rg -B). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-C': z.number().optional().describe('Alias for context.'),\n '-n': z\n .boolean()\n .optional()\n .describe(\n 'Show line numbers in output (rg -n). Requires output_mode: \"content\", ignored otherwise. Defaults to true.',\n ),\n '-i': z.boolean().optional().describe('Case insensitive search (rg -i)'),\n head_limit: z\n .number()\n .optional()\n .describe(\n 'Limit output to first N lines/entries, equivalent to \"| head -N\". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 0 (unlimited).',\n ),\n offset: z\n .number()\n .optional()\n .describe(\n 'Skip first N lines/entries before applying head_limit, equivalent to \"| tail -n +N | head -N\". Works across all output modes. Defaults to 0.',\n ),\n})\n\nconst MAX_RESULTS = 100\n\ntype Input = typeof inputSchema\ntype OutputMode = 'content' | 'files_with_matches' | 'count'\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n // For content mode\n content?: string\n numMatches?: number\n // For count mode\n counts?: Array<{ file: string; count: number }>\n // Output mode used\n outputMode: OutputMode\n}\n\nexport const GrepTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Grep is read-only, safe for concurrent execution\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage(input, { verbose }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode,\n multiline,\n context,\n '-i': caseInsensitive,\n } = input\n const { absolutePath, relativePath } = getAbsoluteAndRelativePaths(path)\n const effectiveGlob = glob || include\n\n const parts: string[] = [`pattern: \"${pattern}\"`]\n if (relativePath || verbose) {\n parts.push(`path: \"${verbose ? absolutePath : relativePath}\"`)\n }\n if (effectiveGlob) {\n parts.push(`glob: \"${effectiveGlob}\"`)\n }\n if (type) {\n parts.push(`type: \"${type}\"`)\n }\n if (output_mode && output_mode !== 'files_with_matches') {\n parts.push(`mode: \"${output_mode}\"`)\n }\n if (multiline) {\n parts.push('multiline')\n }\n if (context) {\n parts.push(`context: ${context}`)\n }\n if (caseInsensitive === false) {\n parts.push('case-sensitive')\n }\n\n return parts.join(', ')\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n // Convert string to Output type using tmpDeserializeOldLogResult if needed\n output = output as unknown as Output\n }\n\n const numFiles = output?.numFiles ?? 0\n const numMatches = output?.numMatches ?? 0\n const durationMs = output?.durationMs ?? 0\n const outputMode = output?.outputMode ?? 'files_with_matches'\n\n // Different display based on output mode\n if (outputMode === 'content') {\n const showExpandHint = !verbose && numMatches > 10\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else if (outputMode === 'count') {\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Counted </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else {\n // files_with_matches (default)\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n }\n },\n renderResultForAssistant(output: Output) {\n const { numFiles, filenames, content, counts, numMatches, outputMode } =\n output\n\n if (outputMode === 'content') {\n if (!content || numMatches === 0) {\n return 'No matches found'\n }\n let result = content\n if (numMatches && numMatches > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else if (outputMode === 'count') {\n if (!counts || counts.length === 0) {\n return 'No matches found'\n }\n const totalMatches = counts.reduce((sum, c) => sum + c.count, 0)\n let result = `Found ${totalMatches} match${totalMatches === 1 ? '' : 'es'} in ${numFiles} file${numFiles === 1 ? '' : 's'}\\n`\n result += counts\n .slice(0, MAX_RESULTS)\n .map(c => `${c.file}:${c.count}`)\n .join('\\n')\n if (counts.length > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else {\n // files_with_matches (default)\n if (numFiles === 0) {\n return 'No files found'\n }\n let result = `Found ${numFiles} file${numFiles === 1 ? '' : 's'}\\n${filenames.slice(0, MAX_RESULTS).join('\\n')}`\n if (numFiles > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n }\n },\n async *call(input, { abortController }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode = 'files_with_matches',\n multiline,\n context,\n '-A': afterContext,\n '-B': beforeContext,\n '-C': contextAlias,\n '-n': lineNumbers = true,\n '-i': caseInsensitive = true,\n head_limit = 0,\n offset = 0,\n } = input\n\n const start = Date.now()\n const absolutePath = getAbsolutePath(path) || getCwd()\n\n // Use glob if provided, fall back to include for backward compatibility\n const effectiveGlob = glob || include\n\n // Determine effective context value (context takes precedence over -C alias)\n const effectiveContext = context ?? contextAlias\n\n // Build ripgrep arguments based on output mode\n const args: string[] = []\n\n // Case sensitivity\n if (caseInsensitive) {\n args.push('-i')\n }\n\n // Multiline mode\n if (multiline) {\n args.push('-U', '--multiline-dotall')\n }\n\n // File type filter\n if (type) {\n args.push('--type', type)\n }\n\n // Glob filter\n if (effectiveGlob) {\n args.push('--glob', effectiveGlob)\n }\n\n // Output mode specific flags\n if (output_mode === 'files_with_matches') {\n args.push('-l')\n } else if (output_mode === 'count') {\n args.push('-c')\n } else if (output_mode === 'content') {\n // For content mode, add context and line number flags\n if (lineNumbers) {\n args.push('-n')\n }\n if (effectiveContext !== undefined && effectiveContext > 0) {\n args.push('-C', String(effectiveContext))\n } else {\n if (beforeContext !== undefined && beforeContext > 0) {\n args.push('-B', String(beforeContext))\n }\n if (afterContext !== undefined && afterContext > 0) {\n args.push('-A', String(afterContext))\n }\n }\n }\n\n // Add the pattern\n args.push(pattern)\n\n // Different processing based on output mode\n if (output_mode === 'content') {\n // Content mode: collect full output with context\n const contentLines: string[] = []\n const filesWithMatches = new Set<string>()\n let matchCount = 0\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n contentLines.push(chunk.line)\n // Track files (lines starting with filename:linenum:)\n const fileMatch = chunk.line.match(/^([^:]+):(\\d+):/)\n if (fileMatch) {\n filesWithMatches.add(fileMatch[1])\n matchCount++\n }\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchCount} match${matchCount === 1 ? '' : 'es'} in ${filesWithMatches.size} file${filesWithMatches.size === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Apply offset and head_limit\n let resultLines = contentLines\n if (offset > 0) {\n resultLines = resultLines.slice(offset)\n }\n if (head_limit > 0) {\n resultLines = resultLines.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: Array.from(filesWithMatches),\n content: resultLines.join('\\n'),\n numMatches: matchCount,\n numFiles: filesWithMatches.size,\n durationMs: Date.now() - start,\n outputMode: 'content',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else if (output_mode === 'count') {\n // Count mode: collect file:count pairs\n const counts: Array<{ file: string; count: number }> = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n // Format: filename:count\n const match = chunk.line.match(/^(.+):(\\d+)$/)\n if (match) {\n counts.push({ file: match[1], count: parseInt(match[2], 10) })\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found matches in ${counts.length} file${counts.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n }\n\n // Sort by count descending, then by filename\n counts.sort((a, b) => {\n const countDiff = b.count - a.count\n if (countDiff !== 0) return countDiff\n return a.file.localeCompare(b.file)\n })\n\n // Apply offset and head_limit\n let resultCounts = counts\n if (offset > 0) {\n resultCounts = resultCounts.slice(offset)\n }\n if (head_limit > 0) {\n resultCounts = resultCounts.slice(0, head_limit)\n }\n\n const totalMatches = resultCounts.reduce((sum, c) => sum + c.count, 0)\n\n const output: Output = {\n filenames: resultCounts.map(c => c.file),\n counts: resultCounts,\n numMatches: totalMatches,\n numFiles: resultCounts.length,\n durationMs: Date.now() - start,\n outputMode: 'count',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else {\n // files_with_matches mode (default): list matching files\n const matchedFiles: string[] = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreaming(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'match') {\n matchedFiles.push(chunk.file)\n\n // Yield progress update periodically for real-time feedback\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchedFiles.length} file${matchedFiles.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Sort by modification time\n const stats = await Promise.all(\n matchedFiles.map(_ => stat(_).catch(() => null)),\n )\n let matches = matchedFiles\n .map((file, i) => [file, stats[i]] as const)\n .filter(([, s]) => s !== null)\n .sort((a, b) => {\n if (process.env.NODE_ENV === 'test') {\n // In tests, we always want to sort by filename, so that results are deterministic\n return a[0].localeCompare(b[0])\n }\n const timeComparison = (b[1]?.mtimeMs ?? 0) - (a[1]?.mtimeMs ?? 0)\n if (timeComparison === 0) {\n // Sort by filename as a tiebreaker\n return a[0].localeCompare(b[0])\n }\n return timeComparison\n })\n .map(_ => _[0])\n\n // Apply offset and head_limit\n if (offset > 0) {\n matches = matches.slice(offset)\n }\n if (head_limit > 0) {\n matches = matches.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: matches,\n durationMs: Date.now() - start,\n numFiles: matches.length,\n outputMode: 'files_with_matches',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n }\n },\n} satisfies Tool<Input, Output>\n"],
4
+ "sourcesContent": ["import { stat } from 'fs/promises'\nimport { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { getAbsolutePath, getAbsoluteAndRelativePaths } from '@utils/file'\nimport { ripGrepStreaming, ripGrepStreamingWithContent } from '@utils/ripgrep'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe(\n 'File or directory to search in (rg PATH). Defaults to current working directory.',\n ),\n // Backward compatible: 'include' is mapped to 'glob'\n include: z\n .string()\n .optional()\n .describe(\n 'File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\") - DEPRECATED: use glob instead',\n ),\n glob: z\n .string()\n .optional()\n .describe(\n 'Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\") - maps to rg --glob',\n ),\n type: z\n .string()\n .optional()\n .describe(\n 'File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types.',\n ),\n output_mode: z\n .enum(['content', 'files_with_matches', 'count'])\n .optional()\n .describe(\n 'Output mode: \"content\" shows matching lines, \"files_with_matches\" shows file paths (default), \"count\" shows match counts',\n ),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.',\n ),\n context: z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before and after each match (rg -C). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-A': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show after each match (rg -A). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-B': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before each match (rg -B). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-C': z.number().optional().describe('Alias for context.'),\n '-n': z\n .boolean()\n .optional()\n .describe(\n 'Show line numbers in output (rg -n). Requires output_mode: \"content\", ignored otherwise. Defaults to true.',\n ),\n '-i': z.boolean().optional().describe('Case insensitive search (rg -i)'),\n head_limit: z\n .number()\n .optional()\n .describe(\n 'Limit output to first N lines/entries, equivalent to \"| head -N\". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 0 (unlimited).',\n ),\n offset: z\n .number()\n .optional()\n .describe(\n 'Skip first N lines/entries before applying head_limit, equivalent to \"| tail -n +N | head -N\". Works across all output modes. Defaults to 0.',\n ),\n})\n\nconst MAX_RESULTS = 100\n\ntype Input = typeof inputSchema\ntype OutputMode = 'content' | 'files_with_matches' | 'count'\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n // For content mode\n content?: string\n numMatches?: number\n // For count mode\n counts?: Array<{ file: string; count: number }>\n // Output mode used\n outputMode: OutputMode\n}\n\nexport const GrepTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Grep is read-only, safe for concurrent execution\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage(input, { verbose }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode,\n multiline,\n context,\n '-i': caseInsensitive,\n } = input\n const { absolutePath, relativePath } = getAbsoluteAndRelativePaths(path)\n const effectiveGlob = glob || include\n\n const parts: string[] = [`pattern: \"${pattern}\"`]\n if (relativePath || verbose) {\n parts.push(`path: \"${verbose ? absolutePath : relativePath}\"`)\n }\n if (effectiveGlob) {\n parts.push(`glob: \"${effectiveGlob}\"`)\n }\n if (type) {\n parts.push(`type: \"${type}\"`)\n }\n if (output_mode && output_mode !== 'files_with_matches') {\n parts.push(`mode: \"${output_mode}\"`)\n }\n if (multiline) {\n parts.push('multiline')\n }\n if (context) {\n parts.push(`context: ${context}`)\n }\n if (caseInsensitive === false) {\n parts.push('case-sensitive')\n }\n\n return parts.join(', ')\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n // Convert string to Output type using tmpDeserializeOldLogResult if needed\n output = output as unknown as Output\n }\n\n const numFiles = output?.numFiles ?? 0\n const numMatches = output?.numMatches ?? 0\n const durationMs = output?.durationMs ?? 0\n const outputMode = output?.outputMode ?? 'files_with_matches'\n\n // Different display based on output mode\n if (outputMode === 'content') {\n const showExpandHint = !verbose && numMatches > 10\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else if (outputMode === 'count') {\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Counted </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else {\n // files_with_matches (default)\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n }\n },\n renderResultForAssistant(output: Output) {\n const { numFiles, filenames, content, counts, numMatches, outputMode } =\n output\n\n if (outputMode === 'content') {\n if (!content || numMatches === 0) {\n return 'No matches found'\n }\n let result = content\n if (numMatches && numMatches > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else if (outputMode === 'count') {\n if (!counts || counts.length === 0) {\n return 'No matches found'\n }\n const totalMatches = counts.reduce((sum, c) => sum + c.count, 0)\n let result = `Found ${totalMatches} match${totalMatches === 1 ? '' : 'es'} in ${numFiles} file${numFiles === 1 ? '' : 's'}\\n`\n result += counts\n .slice(0, MAX_RESULTS)\n .map(c => `${c.file}:${c.count}`)\n .join('\\n')\n if (counts.length > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else {\n // files_with_matches (default)\n if (numFiles === 0) {\n return 'No files found'\n }\n let result = `Found ${numFiles} file${numFiles === 1 ? '' : 's'}\\n${filenames.slice(0, MAX_RESULTS).join('\\n')}`\n if (numFiles > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n }\n },\n async *call(input, { abortController }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode = 'files_with_matches',\n multiline,\n context,\n '-A': afterContext,\n '-B': beforeContext,\n '-C': contextAlias,\n '-n': lineNumbers = true,\n '-i': caseInsensitive = true,\n head_limit = 0,\n offset = 0,\n } = input\n\n const start = Date.now()\n const absolutePath = getAbsolutePath(path) || getCwd()\n\n // Use glob if provided, fall back to include for backward compatibility\n const effectiveGlob = glob || include\n\n // Determine effective context value (context takes precedence over -C alias)\n const effectiveContext = context ?? contextAlias\n\n // Build ripgrep arguments based on output mode\n const args: string[] = []\n\n // Case sensitivity\n if (caseInsensitive) {\n args.push('-i')\n }\n\n // Multiline mode\n if (multiline) {\n args.push('-U', '--multiline-dotall')\n }\n\n // File type filter\n if (type) {\n args.push('--type', type)\n }\n\n // Glob filter\n if (effectiveGlob) {\n args.push('--glob', effectiveGlob)\n }\n\n // Output mode specific flags\n if (output_mode === 'files_with_matches') {\n args.push('-l')\n } else if (output_mode === 'count') {\n args.push('-c')\n } else if (output_mode === 'content') {\n // For content mode, add context and line number flags\n if (lineNumbers) {\n args.push('-n')\n }\n if (effectiveContext !== undefined && effectiveContext > 0) {\n args.push('-C', String(effectiveContext))\n } else {\n if (beforeContext !== undefined && beforeContext > 0) {\n args.push('-B', String(beforeContext))\n }\n if (afterContext !== undefined && afterContext > 0) {\n args.push('-A', String(afterContext))\n }\n }\n }\n\n // Add the pattern\n args.push(pattern)\n\n // Different processing based on output mode\n if (output_mode === 'content') {\n // Content mode: collect full output with context\n const contentLines: string[] = []\n const filesWithMatches = new Set<string>()\n let matchCount = 0\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n contentLines.push(chunk.line)\n // Track files (lines starting with filename:linenum:)\n const fileMatch = chunk.line.match(/^([^:]+):(\\d+):/)\n if (fileMatch) {\n filesWithMatches.add(fileMatch[1])\n matchCount++\n }\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchCount} match${matchCount === 1 ? '' : 'es'} in ${filesWithMatches.size} file${filesWithMatches.size === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Apply offset and head_limit\n let resultLines = contentLines\n if (offset > 0) {\n resultLines = resultLines.slice(offset)\n }\n if (head_limit > 0) {\n resultLines = resultLines.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: Array.from(filesWithMatches),\n content: resultLines.join('\\n'),\n numMatches: matchCount,\n numFiles: filesWithMatches.size,\n durationMs: Date.now() - start,\n outputMode: 'content',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else if (output_mode === 'count') {\n // Count mode: collect file:count pairs\n const counts: Array<{ file: string; count: number }> = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n // Format: filename:count\n const match = chunk.line.match(/^(.+):(\\d+)$/)\n if (match) {\n counts.push({ file: match[1], count: parseInt(match[2], 10) })\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found matches in ${counts.length} file${counts.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n }\n\n // Sort by count descending, then by filename\n counts.sort((a, b) => {\n const countDiff = b.count - a.count\n if (countDiff !== 0) return countDiff\n return a.file.localeCompare(b.file)\n })\n\n // Apply offset and head_limit\n let resultCounts = counts\n if (offset > 0) {\n resultCounts = resultCounts.slice(offset)\n }\n if (head_limit > 0) {\n resultCounts = resultCounts.slice(0, head_limit)\n }\n\n const totalMatches = resultCounts.reduce((sum, c) => sum + c.count, 0)\n\n const output: Output = {\n filenames: resultCounts.map(c => c.file),\n counts: resultCounts,\n numMatches: totalMatches,\n numFiles: resultCounts.length,\n durationMs: Date.now() - start,\n outputMode: 'count',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else {\n // files_with_matches mode (default): list matching files\n const matchedFiles: string[] = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreaming(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'match') {\n matchedFiles.push(chunk.file)\n\n // Yield progress update periodically for real-time feedback\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchedFiles.length} file${matchedFiles.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Sort by modification time\n const stats = await Promise.all(\n matchedFiles.map(_ => stat(_).catch(() => null)),\n )\n let matches = matchedFiles\n .map((file, i) => [file, stats[i]] as const)\n .filter(([, s]) => s !== null)\n .sort((a, b) => {\n if (process.env.NODE_ENV === 'test') {\n // In tests, we always want to sort by filename, so that results are deterministic\n return a[0].localeCompare(b[0])\n }\n const timeComparison = (b[1]?.mtimeMs ?? 0) - (a[1]?.mtimeMs ?? 0)\n if (timeComparison === 0) {\n // Sort by filename as a tiebreaker\n return a[0].localeCompare(b[0])\n }\n return timeComparison\n })\n .map(_ => _[0])\n\n // Apply offset and head_limit\n if (offset > 0) {\n matches = matches.slice(offset)\n }\n if (head_limit > 0) {\n matches = matches.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: matches,\n durationMs: Date.now() - start,\n numFiles: matches.length,\n outputMode: 'files_with_matches',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\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/KillShellTool/KillShellTool.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 { KillShellToolResultMessage } from './KillShellToolResultMessage'\nimport { PROMPT } from './prompt'\n\nexport const inputSchema = z.strictObject({\n shell_id: z.string().describe('The ID of the background shell to kill'),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n success: boolean\n message: string\n shellId: string\n}\n\nexport const KillShellTool = {\n name: 'KillShell',\n async description() {\n return 'Kills a running background bash shell by its ID'\n },\n async prompt() {\n return PROMPT\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return true\n },\n inputSchema,\n userFacingName() {\n return 'KillShell'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n return false // Killing background shells doesn't require user permission\n },\n renderToolUseMessage({ shell_id }) {\n return shell_id\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(content) {\n return <KillShellToolResultMessage content={content} />\n },\n renderResultForAssistant({ success, message }) {\n return message\n },\n async *call({ shell_id }, { abortController }) {\n const manager = BackgroundShellManager.getInstance()\n const shell = manager.get(shell_id)\n\n if (!shell) {\n const data: Out = {\n success: false,\n message: `Error: Background shell '${shell_id}' not found`,\n shellId: shell_id,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n if (shell.status !== 'running') {\n const data: Out = {\n success: false,\n message: `Error: Shell '${shell_id}' is already ${shell.status}`,\n shellId: shell_id,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n const killed = manager.kill(shell_id)\n\n const data: Out = {\n success: killed,\n message: killed\n ? `Successfully killed background shell '${shell_id}'`\n : `Failed to kill background shell '${shell_id}'`,\n shellId: shell_id,\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 { KillShellToolResultMessage } from './KillShellToolResultMessage'\nimport { PROMPT } from './prompt'\n\nexport const inputSchema = z.strictObject({\n shell_id: z.string().describe('The ID of the background shell to kill'),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n success: boolean\n message: string\n shellId: string\n}\n\nexport const KillShellTool = {\n name: 'KillShell',\n async description() {\n return 'Kills a running background bash shell by its ID'\n },\n async prompt() {\n return PROMPT\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return true\n },\n inputSchema,\n userFacingName() {\n return 'KillShell'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n return false // Killing background shells doesn't require user permission\n },\n renderToolUseMessage({ shell_id }) {\n return shell_id\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(content) {\n return <KillShellToolResultMessage content={content} />\n },\n renderResultForAssistant({ success, message }) {\n return message\n },\n async *call({ shell_id }, { abortController }) {\n const manager = BackgroundShellManager.getInstance()\n const shell = manager.get(shell_id)\n\n if (!shell) {\n const data: Out = {\n success: false,\n message: `Error: Background shell '${shell_id}' not found`,\n shellId: shell_id,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n if (shell.status !== 'running') {\n const data: Out = {\n success: false,\n message: `Error: Shell '${shell_id}' is already ${shell.status}`,\n shellId: shell_id,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n const killed = manager.kill(shell_id)\n\n const data: Out = {\n success: killed,\n message: killed\n ? `Successfully killed background shell '${shell_id}'`\n : `Failed to kill background shell '${shell_id}'`,\n shellId: shell_id,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n },\n} satisfies Tool\n"],
5
5
  "mappings": "AAAA,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAE/C,SAAS,8BAA8B;AACvC,SAAS,kCAAkC;AAC3C,SAAS,cAAc;AAEhB,MAAM,cAAc,EAAE,aAAa;AAAA,EACxC,UAAU,EAAE,OAAO,EAAE,SAAS,wCAAwC;AACxE,CAAC;AASM,MAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;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;AAC1B,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,EAAE,SAAS,GAAG;AACjC,WAAO;AAAA,EACT;AAAA,EACA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,SAAS;AAC/B,WAAO,oCAAC,8BAA2B,SAAkB;AAAA,EACvD;AAAA,EACA,yBAAyB,EAAE,SAAS,QAAQ,GAAG;AAC7C,WAAO;AAAA,EACT;AAAA,EACA,OAAO,KAAK,EAAE,SAAS,GAAG,EAAE,gBAAgB,GAAG;AAC7C,UAAM,UAAU,uBAAuB,YAAY;AACnD,UAAM,QAAQ,QAAQ,IAAI,QAAQ;AAElC,QAAI,CAAC,OAAO;AACV,YAAMA,QAAY;AAAA,QAChB,SAAS;AAAA,QACT,SAAS,4BAA4B,QAAQ;AAAA,QAC7C,SAAS;AAAA,MACX;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyBA,KAAI;AAAA,QACtD,MAAAA;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,MAAM,WAAW,WAAW;AAC9B,YAAMA,QAAY;AAAA,QAChB,SAAS;AAAA,QACT,SAAS,iBAAiB,QAAQ,gBAAgB,MAAM,MAAM;AAAA,QAC9D,SAAS;AAAA,MACX;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyBA,KAAI;AAAA,QACtD,MAAAA;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAS,QAAQ,KAAK,QAAQ;AAEpC,UAAM,OAAY;AAAA,MAChB,SAAS;AAAA,MACT,SAAS,SACL,yCAAyC,QAAQ,MACjD,oCAAoC,QAAQ;AAAA,MAChD,SAAS;AAAA,IACX;AAEA,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,oBAAoB,KAAK,yBAAyB,IAAI;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": ["data"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/ListMcpResourcesTool/ListMcpResourcesTool.tsx"],
4
- "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { listMCPResources, McpResource } from '@services/mcpClient'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { getTheme } from '@utils/theme'\n\nconst inputSchema = z.strictObject({\n server: z\n .string()\n .optional()\n .describe(\n 'Optional server name to filter resources from a specific MCP server',\n ),\n})\n\ntype Output = {\n durationMs: number\n resources: McpResource[]\n}\n\nexport const ListMcpResourcesTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'List MCP Resources'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Read-only operation, safe for concurrent execution\n },\n needsPermissions() {\n return false // No file system access needed\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ server }, { verbose }) {\n if (server) {\n return `server: \"${server}\"`\n }\n return verbose ? 'Listing all MCP resources' : ''\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;No resources available</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Listed MCP resources</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numResources = output?.resources?.length ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numResources} </Text>\n <Text>\n {numResources === 0 || numResources > 1 ? 'resources' : 'resource'}\n </Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ server }) {\n const start = Date.now()\n const resources = await listMCPResources(server)\n const output: Output = {\n resources,\n durationMs: Date.now() - start,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n if (output.resources.length === 0) {\n return 'No MCP resources available'\n }\n\n const lines: string[] = []\n lines.push(`Found ${output.resources.length} MCP resource(s):`)\n lines.push('')\n\n // Group by server\n const byServer = output.resources.reduce(\n (acc, r) => {\n if (!acc[r.serverName]) acc[r.serverName] = []\n acc[r.serverName].push(r)\n return acc\n },\n {} as Record<string, McpResource[]>,\n )\n\n for (const [serverName, resources] of Object.entries(byServer)) {\n lines.push(`Server: ${serverName}`)\n for (const r of resources) {\n lines.push(` - ${r.name}`)\n lines.push(` URI: ${r.uri}`)\n if (r.description) {\n lines.push(` Description: ${r.description}`)\n }\n if (r.mimeType) {\n lines.push(` MIME Type: ${r.mimeType}`)\n }\n }\n lines.push('')\n }\n\n return lines.join('\\n')\n },\n} satisfies Tool<typeof inputSchema, Output>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { listMCPResources, McpResource } from '@services/mcpClient'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { getTheme } from '@utils/theme'\n\nconst inputSchema = z.strictObject({\n server: z\n .string()\n .optional()\n .describe(\n 'Optional server name to filter resources from a specific MCP server',\n ),\n})\n\ntype Output = {\n durationMs: number\n resources: McpResource[]\n}\n\nexport const ListMcpResourcesTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'List MCP Resources'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Read-only operation, safe for concurrent execution\n },\n needsPermissions() {\n return false // No file system access needed\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ server }, { verbose }) {\n if (server) {\n return `server: \"${server}\"`\n }\n return verbose ? 'Listing all MCP resources' : ''\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;No resources available</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Listed MCP resources</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numResources = output?.resources?.length ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text>&nbsp;&nbsp;\u23BF &nbsp;Found </Text>\n <Text bold>{numResources} </Text>\n <Text>\n {numResources === 0 || numResources > 1 ? 'resources' : 'resource'}\n </Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ server }) {\n const start = Date.now()\n const resources = await listMCPResources(server)\n const output: Output = {\n resources,\n durationMs: Date.now() - start,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n if (output.resources.length === 0) {\n return 'No MCP resources available'\n }\n\n const lines: string[] = []\n lines.push(`Found ${output.resources.length} MCP resource(s):`)\n lines.push('')\n\n // Group by server\n const byServer = output.resources.reduce(\n (acc, r) => {\n if (!acc[r.serverName]) acc[r.serverName] = []\n acc[r.serverName].push(r)\n return acc\n },\n {} as Record<string, McpResource[]>,\n )\n\n for (const [serverName, resources] of Object.entries(byServer) as [string, McpResource[]][]) {\n lines.push(`Server: ${serverName}`)\n for (const r of resources) {\n lines.push(` - ${r.name}`)\n lines.push(` URI: ${r.uri}`)\n if (r.description) {\n lines.push(` Description: ${r.description}`)\n }\n if (r.mimeType) {\n lines.push(` MIME Type: ${r.mimeType}`)\n }\n }\n lines.push('')\n }\n\n return lines.join('\\n')\n },\n} satisfies Tool\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }