@within-7/minto 0.2.0 → 0.3.3

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 (308) hide show
  1. package/dist/commands/agents/AgentsCommand.js +22 -24
  2. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  3. package/dist/commands/context.js +2 -1
  4. package/dist/commands/context.js.map +2 -2
  5. package/dist/commands/export.js +2 -1
  6. package/dist/commands/export.js.map +2 -2
  7. package/dist/commands/mcp-interactive.js +7 -6
  8. package/dist/commands/mcp-interactive.js.map +2 -2
  9. package/dist/commands/model.js +3 -2
  10. package/dist/commands/model.js.map +2 -2
  11. package/dist/commands/permissions.js +4 -3
  12. package/dist/commands/permissions.js.map +2 -2
  13. package/dist/commands/plugin/AddMarketplaceForm.js +3 -2
  14. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  15. package/dist/commands/plugin/ConfirmDialog.js +2 -1
  16. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  17. package/dist/commands/plugin/ErrorView.js +2 -1
  18. package/dist/commands/plugin/ErrorView.js.map +2 -2
  19. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +5 -4
  20. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  21. package/dist/commands/plugin/InstalledPluginsManager.js +5 -4
  22. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  23. package/dist/commands/plugin/MainMenu.js +2 -1
  24. package/dist/commands/plugin/MainMenu.js.map +2 -2
  25. package/dist/commands/plugin/MarketplaceManager.js +5 -4
  26. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  27. package/dist/commands/plugin/MarketplaceSelector.js +4 -3
  28. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  29. package/dist/commands/plugin/PlaceholderScreen.js +3 -2
  30. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  31. package/dist/commands/plugin/PluginBrowser.js +6 -5
  32. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  33. package/dist/commands/plugin/PluginDetailsInstall.js +5 -4
  34. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  35. package/dist/commands/plugin/PluginDetailsManage.js +4 -3
  36. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  37. package/dist/commands/plugin.js +16 -15
  38. package/dist/commands/plugin.js.map +2 -2
  39. package/dist/commands/sandbox.js +4 -3
  40. package/dist/commands/sandbox.js.map +2 -2
  41. package/dist/commands/setup.js +2 -1
  42. package/dist/commands/setup.js.map +2 -2
  43. package/dist/commands/status.js +2 -1
  44. package/dist/commands/status.js.map +2 -2
  45. package/dist/commands/undo.js +245 -0
  46. package/dist/commands/undo.js.map +7 -0
  47. package/dist/commands.js +2 -0
  48. package/dist/commands.js.map +2 -2
  49. package/dist/components/AgentThinkingBlock.js +1 -1
  50. package/dist/components/AgentThinkingBlock.js.map +2 -2
  51. package/dist/components/AsciiLogo.js +7 -8
  52. package/dist/components/AsciiLogo.js.map +2 -2
  53. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
  54. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  55. package/dist/components/AskUserQuestionDialog/QuestionView.js +2 -1
  56. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  57. package/dist/components/CollapsibleHint.js +2 -1
  58. package/dist/components/CollapsibleHint.js.map +2 -2
  59. package/dist/components/Config.js +3 -2
  60. package/dist/components/Config.js.map +2 -2
  61. package/dist/components/ConsoleOAuthFlow.js +2 -1
  62. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  63. package/dist/components/Cost.js +2 -1
  64. package/dist/components/Cost.js.map +2 -2
  65. package/dist/components/HeaderBar.js +13 -8
  66. package/dist/components/HeaderBar.js.map +2 -2
  67. package/dist/components/HistorySearchOverlay.js +4 -3
  68. package/dist/components/HistorySearchOverlay.js.map +2 -2
  69. package/dist/components/HotkeyHelpPanel.js +8 -11
  70. package/dist/components/HotkeyHelpPanel.js.map +2 -2
  71. package/dist/components/InvalidConfigDialog.js +2 -1
  72. package/dist/components/InvalidConfigDialog.js.map +2 -2
  73. package/dist/components/Logo.js +23 -67
  74. package/dist/components/Logo.js.map +2 -2
  75. package/dist/components/MCPServerApprovalDialog.js +2 -1
  76. package/dist/components/MCPServerApprovalDialog.js.map +2 -2
  77. package/dist/components/MCPServerDialogCopy.js +2 -1
  78. package/dist/components/MCPServerDialogCopy.js.map +2 -2
  79. package/dist/components/MCPServerMultiselectDialog.js +2 -1
  80. package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
  81. package/dist/components/MessageSelector.js +4 -3
  82. package/dist/components/MessageSelector.js.map +2 -2
  83. package/dist/components/ModeIndicator.js +2 -1
  84. package/dist/components/ModeIndicator.js.map +2 -2
  85. package/dist/components/ModelConfig.js +4 -3
  86. package/dist/components/ModelConfig.js.map +2 -2
  87. package/dist/components/ModelListManager.js +4 -3
  88. package/dist/components/ModelListManager.js.map +2 -2
  89. package/dist/components/ModelSelector/ModelSelector.js +26 -13
  90. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  91. package/dist/components/Onboarding.js +3 -2
  92. package/dist/components/Onboarding.js.map +2 -2
  93. package/dist/components/OperationSummary.js +130 -0
  94. package/dist/components/OperationSummary.js.map +7 -0
  95. package/dist/components/PromptInput.js +88 -75
  96. package/dist/components/PromptInput.js.map +2 -2
  97. package/dist/components/SensitiveFileWarning.js +31 -0
  98. package/dist/components/SensitiveFileWarning.js.map +7 -0
  99. package/dist/components/Spinner.js +71 -22
  100. package/dist/components/Spinner.js.map +2 -2
  101. package/dist/components/StructuredDiff.js +6 -8
  102. package/dist/components/StructuredDiff.js.map +2 -2
  103. package/dist/components/SubagentBlock.js +4 -2
  104. package/dist/components/SubagentBlock.js.map +2 -2
  105. package/dist/components/SubagentProgress.js +17 -6
  106. package/dist/components/SubagentProgress.js.map +2 -2
  107. package/dist/components/TaskCard.js +14 -11
  108. package/dist/components/TaskCard.js.map +2 -2
  109. package/dist/components/TextInput.js +9 -1
  110. package/dist/components/TextInput.js.map +2 -2
  111. package/dist/components/TodoPanel.js +44 -26
  112. package/dist/components/TodoPanel.js.map +2 -2
  113. package/dist/components/ToolUseLoader.js +2 -2
  114. package/dist/components/ToolUseLoader.js.map +2 -2
  115. package/dist/components/TreeConnector.js +4 -3
  116. package/dist/components/TreeConnector.js.map +2 -2
  117. package/dist/components/TrustDialog.js +2 -1
  118. package/dist/components/TrustDialog.js.map +2 -2
  119. package/dist/components/binary-feedback/BinaryFeedbackView.js +2 -1
  120. package/dist/components/binary-feedback/BinaryFeedbackView.js.map +2 -2
  121. package/dist/components/messages/AssistantTextMessage.js +17 -9
  122. package/dist/components/messages/AssistantTextMessage.js.map +2 -2
  123. package/dist/components/messages/AssistantToolUseMessage.js +8 -4
  124. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  125. package/dist/components/messages/GroupRenderer.js +2 -1
  126. package/dist/components/messages/GroupRenderer.js.map +2 -2
  127. package/dist/components/messages/NestedTasksPreview.js +13 -1
  128. package/dist/components/messages/NestedTasksPreview.js.map +2 -2
  129. package/dist/components/messages/ParallelTasksGroupView.js +4 -3
  130. package/dist/components/messages/ParallelTasksGroupView.js.map +2 -2
  131. package/dist/components/messages/TaskInModuleView.js +35 -15
  132. package/dist/components/messages/TaskInModuleView.js.map +2 -2
  133. package/dist/components/messages/TaskOutputContent.js +9 -6
  134. package/dist/components/messages/TaskOutputContent.js.map +2 -2
  135. package/dist/components/messages/UserPromptMessage.js +2 -2
  136. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  137. package/dist/constants/colors.js +90 -72
  138. package/dist/constants/colors.js.map +2 -2
  139. package/dist/constants/prompts.js +22 -1
  140. package/dist/constants/prompts.js.map +2 -2
  141. package/dist/constants/toolInputExamples.js +84 -0
  142. package/dist/constants/toolInputExamples.js.map +7 -0
  143. package/dist/core/backupManager.js +321 -0
  144. package/dist/core/backupManager.js.map +7 -0
  145. package/dist/core/costTracker.js +9 -18
  146. package/dist/core/costTracker.js.map +2 -2
  147. package/dist/core/gitAutoCommit.js +287 -0
  148. package/dist/core/gitAutoCommit.js.map +7 -0
  149. package/dist/core/index.js +3 -0
  150. package/dist/core/index.js.map +2 -2
  151. package/dist/core/operationTracker.js +212 -0
  152. package/dist/core/operationTracker.js.map +7 -0
  153. package/dist/core/permissions/rules/allowedToolsRule.js +1 -1
  154. package/dist/core/permissions/rules/allowedToolsRule.js.map +2 -2
  155. package/dist/core/permissions/rules/autoEscalationRule.js +5 -0
  156. package/dist/core/permissions/rules/autoEscalationRule.js.map +2 -2
  157. package/dist/core/permissions/rules/projectBoundaryRule.js +5 -0
  158. package/dist/core/permissions/rules/projectBoundaryRule.js.map +2 -2
  159. package/dist/core/permissions/rules/sensitivePathsRule.js +5 -0
  160. package/dist/core/permissions/rules/sensitivePathsRule.js.map +2 -2
  161. package/dist/core/tokenStats.js +9 -0
  162. package/dist/core/tokenStats.js.map +7 -0
  163. package/dist/core/tokenStatsManager.js +331 -0
  164. package/dist/core/tokenStatsManager.js.map +7 -0
  165. package/dist/entrypoints/cli.js +122 -88
  166. package/dist/entrypoints/cli.js.map +2 -2
  167. package/dist/hooks/useAgentTokenStats.js +72 -0
  168. package/dist/hooks/useAgentTokenStats.js.map +7 -0
  169. package/dist/hooks/useAgentTranscripts.js +30 -6
  170. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  171. package/dist/hooks/useLogMessages.js +12 -1
  172. package/dist/hooks/useLogMessages.js.map +2 -2
  173. package/dist/i18n/locales/en.js +6 -5
  174. package/dist/i18n/locales/en.js.map +2 -2
  175. package/dist/i18n/locales/zh-CN.js +6 -5
  176. package/dist/i18n/locales/zh-CN.js.map +2 -2
  177. package/dist/i18n/types.js.map +1 -1
  178. package/dist/permissions.js +147 -1
  179. package/dist/permissions.js.map +2 -2
  180. package/dist/query.js +78 -4
  181. package/dist/query.js.map +3 -3
  182. package/dist/screens/REPL.js +23 -3
  183. package/dist/screens/REPL.js.map +2 -2
  184. package/dist/screens/ResumeConversation.js +2 -0
  185. package/dist/screens/ResumeConversation.js.map +2 -2
  186. package/dist/services/claude.js +54 -3
  187. package/dist/services/claude.js.map +2 -2
  188. package/dist/services/intelligentCompactor.js +1 -1
  189. package/dist/services/intelligentCompactor.js.map +2 -2
  190. package/dist/services/mcpClient.js +81 -25
  191. package/dist/services/mcpClient.js.map +2 -2
  192. package/dist/services/sandbox/filesystemBoundary.js +58 -17
  193. package/dist/services/sandbox/filesystemBoundary.js.map +2 -2
  194. package/dist/services/taskStore.js +205 -0
  195. package/dist/services/taskStore.js.map +7 -0
  196. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +3 -2
  197. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  198. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +42 -4
  199. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  200. package/dist/tools/BashTool/BashTool.js +43 -7
  201. package/dist/tools/BashTool/BashTool.js.map +2 -2
  202. package/dist/tools/BashTool/prompt.js +184 -34
  203. package/dist/tools/BashTool/prompt.js.map +2 -2
  204. package/dist/tools/FileEditTool/FileEditTool.js +24 -9
  205. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  206. package/dist/tools/FileEditTool/prompt.js +10 -4
  207. package/dist/tools/FileEditTool/prompt.js.map +2 -2
  208. package/dist/tools/FileEditTool/utils.js +10 -4
  209. package/dist/tools/FileEditTool/utils.js.map +2 -2
  210. package/dist/tools/FileReadTool/FileReadTool.js +1 -1
  211. package/dist/tools/FileReadTool/FileReadTool.js.map +1 -1
  212. package/dist/tools/FileReadTool/prompt.js +16 -1
  213. package/dist/tools/FileReadTool/prompt.js.map +2 -2
  214. package/dist/tools/FileWriteTool/FileWriteTool.js +1 -1
  215. package/dist/tools/FileWriteTool/FileWriteTool.js.map +1 -1
  216. package/dist/tools/FileWriteTool/prompt.js +12 -3
  217. package/dist/tools/FileWriteTool/prompt.js.map +2 -2
  218. package/dist/tools/GlobTool/prompt.js +12 -1
  219. package/dist/tools/GlobTool/prompt.js.map +2 -2
  220. package/dist/tools/GrepTool/GrepTool.js +333 -65
  221. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  222. package/dist/tools/GrepTool/prompt.js +15 -8
  223. package/dist/tools/GrepTool/prompt.js.map +2 -2
  224. package/dist/tools/MultiEditTool/prompt.js +5 -3
  225. package/dist/tools/MultiEditTool/prompt.js.map +2 -2
  226. package/dist/tools/NotebookEditTool/NotebookEditTool.js +59 -46
  227. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  228. package/dist/tools/NotebookEditTool/prompt.js +1 -1
  229. package/dist/tools/NotebookEditTool/prompt.js.map +1 -1
  230. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +3 -2
  231. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  232. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +3 -2
  233. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  234. package/dist/tools/PlanModeTool/prompt.js +1 -1
  235. package/dist/tools/PlanModeTool/prompt.js.map +1 -1
  236. package/dist/tools/SkillTool/SkillTool.js +4 -3
  237. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  238. package/dist/tools/SkillTool/prompt.js +1 -1
  239. package/dist/tools/SkillTool/prompt.js.map +1 -1
  240. package/dist/tools/TaskCreateTool/TaskCreateTool.js +102 -0
  241. package/dist/tools/TaskCreateTool/TaskCreateTool.js.map +7 -0
  242. package/dist/tools/TaskCreateTool/prompt.js +47 -0
  243. package/dist/tools/TaskCreateTool/prompt.js.map +7 -0
  244. package/dist/tools/TaskGetTool/TaskGetTool.js +115 -0
  245. package/dist/tools/TaskGetTool/TaskGetTool.js.map +7 -0
  246. package/dist/tools/TaskGetTool/prompt.js +28 -0
  247. package/dist/tools/TaskGetTool/prompt.js.map +7 -0
  248. package/dist/tools/TaskListTool/TaskListTool.js +102 -0
  249. package/dist/tools/TaskListTool/TaskListTool.js.map +7 -0
  250. package/dist/tools/TaskListTool/prompt.js +27 -0
  251. package/dist/tools/TaskListTool/prompt.js.map +7 -0
  252. package/dist/tools/TaskOutputTool/TaskOutputTool.js +3 -2
  253. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +2 -2
  254. package/dist/tools/TaskStopTool/TaskStopTool.js +150 -0
  255. package/dist/tools/TaskStopTool/TaskStopTool.js.map +7 -0
  256. package/dist/tools/TaskStopTool/prompt.js +15 -0
  257. package/dist/tools/TaskStopTool/prompt.js.map +7 -0
  258. package/dist/tools/TaskTool/TaskTool.js +49 -1
  259. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  260. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js +134 -0
  261. package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +7 -0
  262. package/dist/tools/TaskUpdateTool/prompt.js +81 -0
  263. package/dist/tools/TaskUpdateTool/prompt.js.map +7 -0
  264. package/dist/tools/URLFetcherTool/prompt.js +1 -1
  265. package/dist/tools/URLFetcherTool/prompt.js.map +1 -1
  266. package/dist/tools.js +12 -0
  267. package/dist/tools.js.map +2 -2
  268. package/dist/utils/CircuitBreaker.js +242 -0
  269. package/dist/utils/CircuitBreaker.js.map +7 -0
  270. package/dist/utils/ask.js +2 -0
  271. package/dist/utils/ask.js.map +2 -2
  272. package/dist/utils/config.js +47 -5
  273. package/dist/utils/config.js.map +2 -2
  274. package/dist/utils/credentials/CredentialStore.js +1 -0
  275. package/dist/utils/credentials/CredentialStore.js.map +7 -0
  276. package/dist/utils/credentials/EncryptedFileStore.js +157 -0
  277. package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
  278. package/dist/utils/credentials/index.js +37 -0
  279. package/dist/utils/credentials/index.js.map +7 -0
  280. package/dist/utils/credentials/migration.js +82 -0
  281. package/dist/utils/credentials/migration.js.map +7 -0
  282. package/dist/utils/markdown.js +13 -1
  283. package/dist/utils/markdown.js.map +2 -2
  284. package/dist/utils/model.js +15 -2
  285. package/dist/utils/model.js.map +2 -2
  286. package/dist/utils/permissions/filesystem.js +5 -1
  287. package/dist/utils/permissions/filesystem.js.map +2 -2
  288. package/dist/utils/ripgrep.js +53 -1
  289. package/dist/utils/ripgrep.js.map +2 -2
  290. package/dist/utils/safePath.js +132 -0
  291. package/dist/utils/safePath.js.map +7 -0
  292. package/dist/utils/sensitiveFiles.js +125 -0
  293. package/dist/utils/sensitiveFiles.js.map +7 -0
  294. package/dist/utils/taskDisplayUtils.js +9 -9
  295. package/dist/utils/taskDisplayUtils.js.map +2 -2
  296. package/dist/utils/terminal.js +12 -0
  297. package/dist/utils/terminal.js.map +2 -2
  298. package/dist/utils/theme.js +6 -6
  299. package/dist/utils/theme.js.map +1 -1
  300. package/dist/utils/toolRiskClassification.js +207 -0
  301. package/dist/utils/toolRiskClassification.js.map +7 -0
  302. package/dist/utils/tooling/safeRender.js +17 -17
  303. package/dist/utils/tooling/safeRender.js.map +2 -2
  304. package/dist/version.js +2 -2
  305. package/dist/version.js.map +1 -1
  306. package/package.json +22 -28
  307. package/dist/hooks/useCancelRequest.js +0 -31
  308. package/dist/hooks/useCancelRequest.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/FileEditTool/utils.ts"],
4
- "sourcesContent": ["import { isAbsolute, resolve } from 'path'\nimport { getCwd } from '@utils/state'\nimport { readFileSync } from 'fs'\nimport { detectFileEncoding } from '@utils/file'\nimport { type Hunk } from 'diff'\nimport { getPatch } from '@utils/diff'\n\n/**\n * Applies an edit to a file and returns the patch and updated file.\n * Does not write the file to disk.\n */\nexport function applyEdit(\n file_path: string,\n old_string: string,\n new_string: string,\n): { patch: Hunk[]; updatedFile: string } {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n\n let originalFile\n let updatedFile\n if (old_string === '') {\n // Create new file\n originalFile = ''\n updatedFile = new_string\n } else {\n // Edit existing file\n const enc = detectFileEncoding(fullFilePath)\n originalFile = readFileSync(fullFilePath, enc)\n if (new_string === '') {\n if (\n !old_string.endsWith('\\n') &&\n originalFile.includes(old_string + '\\n')\n ) {\n updatedFile = originalFile.replace(old_string + '\\n', () => new_string)\n } else {\n updatedFile = originalFile.replace(old_string, () => new_string)\n }\n } else {\n updatedFile = originalFile.replace(old_string, () => new_string)\n }\n if (updatedFile === originalFile) {\n throw new Error(\n 'Original and edited file match exactly. Failed to apply edit.',\n )\n }\n }\n\n const patch = getPatch({\n filePath: file_path,\n fileContents: originalFile,\n oldStr: originalFile,\n newStr: updatedFile,\n })\n\n return { patch, updatedFile }\n}\n"],
5
- "mappings": "AAAA,SAAS,YAAY,eAAe;AACpC,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AAEnC,SAAS,gBAAgB;AAMlB,SAAS,UACd,WACA,YACA,YACwC;AACxC,QAAM,eAAe,WAAW,SAAS,IACrC,YACA,QAAQ,OAAO,GAAG,SAAS;AAE/B,MAAI;AACJ,MAAI;AACJ,MAAI,eAAe,IAAI;AAErB,mBAAe;AACf,kBAAc;AAAA,EAChB,OAAO;AAEL,UAAM,MAAM,mBAAmB,YAAY;AAC3C,mBAAe,aAAa,cAAc,GAAG;AAC7C,QAAI,eAAe,IAAI;AACrB,UACE,CAAC,WAAW,SAAS,IAAI,KACzB,aAAa,SAAS,aAAa,IAAI,GACvC;AACA,sBAAc,aAAa,QAAQ,aAAa,MAAM,MAAM,UAAU;AAAA,MACxE,OAAO;AACL,sBAAc,aAAa,QAAQ,YAAY,MAAM,UAAU;AAAA,MACjE;AAAA,IACF,OAAO;AACL,oBAAc,aAAa,QAAQ,YAAY,MAAM,UAAU;AAAA,IACjE;AACA,QAAI,gBAAgB,cAAc;AAChC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS;AAAA,IACrB,UAAU;AAAA,IACV,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AAED,SAAO,EAAE,OAAO,YAAY;AAC9B;",
4
+ "sourcesContent": ["import { isAbsolute, resolve } from 'path'\nimport { getCwd } from '@utils/state'\nimport { readFileSync } from 'fs'\nimport { detectFileEncoding } from '@utils/file'\nimport { type Hunk } from 'diff'\nimport { getPatch } from '@utils/diff'\n\n/**\n * Applies an edit to a file and returns the patch and updated file.\n * Does not write the file to disk.\n * @param replace_all - If true, replaces all occurrences of old_string. Defaults to false (replace first occurrence only).\n */\nexport function applyEdit(\n file_path: string,\n old_string: string,\n new_string: string,\n replace_all: boolean = false,\n): { patch: Hunk[]; updatedFile: string } {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n\n // Helper function to perform replacement based on replace_all flag\n const replaceString = (\n source: string,\n oldStr: string,\n newStr: string,\n ): string => {\n if (replace_all) {\n return source.replaceAll(oldStr, newStr)\n }\n return source.replace(oldStr, () => newStr)\n }\n\n let originalFile\n let updatedFile\n if (old_string === '') {\n // Create new file\n originalFile = ''\n updatedFile = new_string\n } else {\n // Edit existing file\n const enc = detectFileEncoding(fullFilePath)\n originalFile = readFileSync(fullFilePath, enc)\n if (new_string === '') {\n if (\n !old_string.endsWith('\\n') &&\n originalFile.includes(old_string + '\\n')\n ) {\n updatedFile = replaceString(originalFile, old_string + '\\n', new_string)\n } else {\n updatedFile = replaceString(originalFile, old_string, new_string)\n }\n } else {\n updatedFile = replaceString(originalFile, old_string, new_string)\n }\n if (updatedFile === originalFile) {\n throw new Error(\n 'Original and edited file match exactly. Failed to apply edit.',\n )\n }\n }\n\n const patch = getPatch({\n filePath: file_path,\n fileContents: originalFile,\n oldStr: originalFile,\n newStr: updatedFile,\n })\n\n return { patch, updatedFile }\n}\n"],
5
+ "mappings": "AAAA,SAAS,YAAY,eAAe;AACpC,SAAS,cAAc;AACvB,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AAEnC,SAAS,gBAAgB;AAOlB,SAAS,UACd,WACA,YACA,YACA,cAAuB,OACiB;AACxC,QAAM,eAAe,WAAW,SAAS,IACrC,YACA,QAAQ,OAAO,GAAG,SAAS;AAG/B,QAAM,gBAAgB,CACpB,QACA,QACA,WACW;AACX,QAAI,aAAa;AACf,aAAO,OAAO,WAAW,QAAQ,MAAM;AAAA,IACzC;AACA,WAAO,OAAO,QAAQ,QAAQ,MAAM,MAAM;AAAA,EAC5C;AAEA,MAAI;AACJ,MAAI;AACJ,MAAI,eAAe,IAAI;AAErB,mBAAe;AACf,kBAAc;AAAA,EAChB,OAAO;AAEL,UAAM,MAAM,mBAAmB,YAAY;AAC3C,mBAAe,aAAa,cAAc,GAAG;AAC7C,QAAI,eAAe,IAAI;AACrB,UACE,CAAC,WAAW,SAAS,IAAI,KACzB,aAAa,SAAS,aAAa,IAAI,GACvC;AACA,sBAAc,cAAc,cAAc,aAAa,MAAM,UAAU;AAAA,MACzE,OAAO;AACL,sBAAc,cAAc,cAAc,YAAY,UAAU;AAAA,MAClE;AAAA,IACF,OAAO;AACL,oBAAc,cAAc,cAAc,YAAY,UAAU;AAAA,IAClE;AACA,QAAI,gBAAgB,cAAc;AAChC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS;AAAA,IACrB,UAAU;AAAA,IACV,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AAED,SAAO,EAAE,OAAO,YAAY;AAC9B;",
6
6
  "names": []
7
7
  }
@@ -46,7 +46,7 @@ const inputSchema = z.strictObject({
46
46
  )
47
47
  });
48
48
  const FileReadTool = {
49
- name: "View",
49
+ name: "Read",
50
50
  async description() {
51
51
  return DESCRIPTION;
52
52
  },
@@ -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: 'View',\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"],
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
5
  "mappings": "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",
6
6
  "names": ["data", "fileReadResult"]
7
7
  }
@@ -2,7 +2,22 @@ import { NotebookReadTool } from "../NotebookReadTool/NotebookReadTool.js";
2
2
  const MAX_LINES_TO_READ = 2e3;
3
3
  const MAX_LINE_LENGTH = 2e3;
4
4
  const DESCRIPTION = "Read a file from the local filesystem.";
5
- const PROMPT = `Reads a file from the local filesystem. The file_path parameter must be an absolute path, not a relative path. By default, it reads up to ${MAX_LINES_TO_READ} lines starting from the beginning of the file. You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters. Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated. For image files, the tool will display the image for you. For Jupyter notebooks (.ipynb files), use the ${NotebookReadTool.name} instead.`;
5
+ const PROMPT = `Reads a file from the local filesystem. You can access any file directly by using this tool.
6
+ Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
7
+
8
+ Usage:
9
+ - The file_path parameter must be an absolute path, not a relative path
10
+ - By default, it reads up to ${MAX_LINES_TO_READ} lines starting from the beginning of the file
11
+ - You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
12
+ - Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated
13
+ - Results are returned using cat -n format, with line numbers starting at 1
14
+ - This tool allows reading images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as the LLM is multimodal.
15
+ - This tool can read PDF files (.pdf). PDFs are processed page by page, extracting both text and visual content for analysis.
16
+ - For Jupyter notebooks (.ipynb files), use the ${NotebookReadTool.name} instead, which returns all cells with their outputs, combining code, text, and visualizations.
17
+ - This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.
18
+ - You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.
19
+ - You will regularly be asked to read screenshots. If the user provides a path to a screenshot, ALWAYS use this tool to view the file at the path. This tool will work with all temporary file paths.
20
+ - If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`;
6
21
  export {
7
22
  DESCRIPTION,
8
23
  PROMPT
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/FileReadTool/prompt.ts"],
4
- "sourcesContent": ["import { NotebookReadTool } from '@tools/NotebookReadTool/NotebookReadTool'\n\nconst MAX_LINES_TO_READ = 2000\nconst MAX_LINE_LENGTH = 2000\n\nexport const DESCRIPTION = 'Read a file from the local filesystem.'\nexport const PROMPT = `Reads a file from the local filesystem. The file_path parameter must be an absolute path, not a relative path. By default, it reads up to ${MAX_LINES_TO_READ} lines starting from the beginning of the file. You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters. Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated. For image files, the tool will display the image for you. For Jupyter notebooks (.ipynb files), use the ${NotebookReadTool.name} instead.`\n"],
5
- "mappings": "AAAA,SAAS,wBAAwB;AAEjC,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;AAEjB,MAAM,cAAc;AACpB,MAAM,SAAS,6IAA6I,iBAAiB,8OAA8O,eAAe,0IAA0I,iBAAiB,IAAI;",
4
+ "sourcesContent": ["import { NotebookReadTool } from '@tools/NotebookReadTool/NotebookReadTool'\n\nconst MAX_LINES_TO_READ = 2000\nconst MAX_LINE_LENGTH = 2000\n\nexport const DESCRIPTION = 'Read a file from the local filesystem.'\nexport const PROMPT = `Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to ${MAX_LINES_TO_READ} lines starting from the beginning of the file\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool allows reading images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as the LLM is multimodal.\n- This tool can read PDF files (.pdf). PDFs are processed page by page, extracting both text and visual content for analysis.\n- For Jupyter notebooks (.ipynb files), use the ${NotebookReadTool.name} instead, which returns all cells with their outputs, combining code, text, and visualizations.\n- This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.\n- You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.\n- You will regularly be asked to read screenshots. If the user provides a path to a screenshot, ALWAYS use this tool to view the file at the path. This tool will work with all temporary file paths.\n- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB;AAEjC,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;AAEjB,MAAM,cAAc;AACpB,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,+BAKS,iBAAiB;AAAA;AAAA,0BAEtB,eAAe;AAAA;AAAA;AAAA;AAAA,kDAIS,iBAAiB,IAAI;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -35,7 +35,7 @@ const inputSchema = z.strictObject({
35
35
  content: z.string().describe("The content to write to the file")
36
36
  });
37
37
  const FileWriteTool = {
38
- name: "Replace",
38
+ name: "Write",
39
39
  async description() {
40
40
  return "Write a file to the local filesystem.";
41
41
  },
@@ -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: 'Replace',\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"],
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
5
  "mappings": "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",
6
6
  "names": ["data"]
7
7
  }
@@ -1,11 +1,20 @@
1
- const PROMPT = `Write a file to the local filesystem. Overwrites the existing file if there is one.
1
+ import { FileReadTool } from "../FileReadTool/FileReadTool.js";
2
+ import { LSTool } from "../lsTool/lsTool.js";
3
+ const PROMPT = `Writes a file to the local filesystem.
4
+
5
+ Usage:
6
+ - This tool will overwrite the existing file if there is one at the provided path.
7
+ - If this is an existing file, you MUST use the ${FileReadTool.name} tool first to read the file's contents. This tool will fail if you did not read the file first.
8
+ - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
9
+ - NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
10
+ - Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
2
11
 
3
12
  Before using this tool:
4
13
 
5
- 1. Use the ReadFile tool to understand the file's contents and context
14
+ 1. Use the ${FileReadTool.name} tool to understand the file's contents and context
6
15
 
7
16
  2. Directory Verification (only applicable when creating new files):
8
- - Use the LS tool to verify the parent directory exists and is the correct location`;
17
+ - Use the ${LSTool.name} tool to verify the parent directory exists and is the correct location`;
9
18
  const DESCRIPTION = "Write a file to the local filesystem.";
10
19
  export {
11
20
  DESCRIPTION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/FileWriteTool/prompt.ts"],
4
- "sourcesContent": ["export const PROMPT = `Write a file to the local filesystem. Overwrites the existing file if there is one.\n\nBefore using this tool:\n\n1. Use the ReadFile tool to understand the file's contents and context\n\n2. Directory Verification (only applicable when creating new files):\n - Use the LS tool to verify the parent directory exists and is the correct location`\n\nexport const DESCRIPTION = 'Write a file to the local filesystem.'\n"],
5
- "mappings": "AAAO,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASf,MAAM,cAAc;",
4
+ "sourcesContent": ["import { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { LSTool } from '@tools/lsTool/lsTool'\n\nexport const PROMPT = `Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the ${FileReadTool.name} tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\n\nBefore using this tool:\n\n1. Use the ${FileReadTool.name} tool to understand the file's contents and context\n\n2. Directory Verification (only applicable when creating new files):\n - Use the ${LSTool.name} tool to verify the parent directory exists and is the correct location`\n\nexport const DESCRIPTION = 'Write a file to the local filesystem.'\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AAEhB,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA,kDAI4B,aAAa,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAOtD,aAAa,IAAI;AAAA;AAAA;AAAA,eAGf,OAAO,IAAI;AAEnB,MAAM,cAAc;",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,20 @@
1
- const TOOL_NAME_FOR_PROMPT = "GlobTool";
1
+ const TOOL_NAME_FOR_PROMPT = "Glob";
2
2
  const DESCRIPTION = `- Fast file pattern matching tool that works with any codebase size
3
3
  - Supports glob patterns like "**/*.js" or "src/**/*.ts"
4
4
  - Returns matching file paths sorted by modification time
5
5
  - Use this tool when you need to find files by name patterns
6
6
  - When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead
7
+ - You can call multiple tools in a single response. It is always better to speculatively perform multiple searches in parallel if they are potentially useful.
8
+
9
+ Common glob pattern examples:
10
+ - "**/*.ts" - All TypeScript files in any directory
11
+ - "**/*.{ts,tsx}" - All TypeScript and TSX files
12
+ - "src/**/*.ts" - TypeScript files only in src directory
13
+ - "**/test/**/*.ts" - Test files in any test directory
14
+ - "**/__tests__/*.test.ts" - Test files following Jest convention
15
+ - "*.json" - JSON files in root directory only
16
+ - "**/*.config.{js,ts}" - Config files with .config suffix
17
+ - "**/!(node_modules)/**/*.ts" - TypeScript files excluding node_modules
7
18
  `;
8
19
  export {
9
20
  DESCRIPTION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/GlobTool/prompt.ts"],
4
- "sourcesContent": ["export const TOOL_NAME_FOR_PROMPT = 'GlobTool'\n\nexport const DESCRIPTION = `- Fast file pattern matching tool that works with any codebase size\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns\n- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead\n`\n"],
5
- "mappings": "AAAO,MAAM,uBAAuB;AAE7B,MAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["export const TOOL_NAME_FOR_PROMPT = 'Glob'\n\nexport const DESCRIPTION = `- Fast file pattern matching tool that works with any codebase size\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns\n- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead\n- You can call multiple tools in a single response. It is always better to speculatively perform multiple searches in parallel if they are potentially useful.\n\nCommon glob pattern examples:\n- \"**/*.ts\" - All TypeScript files in any directory\n- \"**/*.{ts,tsx}\" - All TypeScript and TSX files\n- \"src/**/*.ts\" - TypeScript files only in src directory\n- \"**/test/**/*.ts\" - Test files in any test directory\n- \"**/__tests__/*.test.ts\" - Test files following Jest convention\n- \"*.json\" - JSON files in root directory only\n- \"**/*.config.{js,ts}\" - Config files with .config suffix\n- \"**/!(node_modules)/**/*.ts\" - TypeScript files excluding node_modules\n`\n"],
5
+ "mappings": "AAAO,MAAM,uBAAuB;AAE7B,MAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }