@within-7/minto 0.2.0 → 0.3.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 (254) 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 +7 -4
  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/toolInputExamples.js +84 -0
  140. package/dist/constants/toolInputExamples.js.map +7 -0
  141. package/dist/core/backupManager.js +321 -0
  142. package/dist/core/backupManager.js.map +7 -0
  143. package/dist/core/costTracker.js +9 -18
  144. package/dist/core/costTracker.js.map +2 -2
  145. package/dist/core/gitAutoCommit.js +287 -0
  146. package/dist/core/gitAutoCommit.js.map +7 -0
  147. package/dist/core/index.js +3 -0
  148. package/dist/core/index.js.map +2 -2
  149. package/dist/core/operationTracker.js +212 -0
  150. package/dist/core/operationTracker.js.map +7 -0
  151. package/dist/core/permissions/rules/allowedToolsRule.js +1 -1
  152. package/dist/core/permissions/rules/allowedToolsRule.js.map +2 -2
  153. package/dist/core/permissions/rules/autoEscalationRule.js +5 -0
  154. package/dist/core/permissions/rules/autoEscalationRule.js.map +2 -2
  155. package/dist/core/permissions/rules/projectBoundaryRule.js +5 -0
  156. package/dist/core/permissions/rules/projectBoundaryRule.js.map +2 -2
  157. package/dist/core/permissions/rules/sensitivePathsRule.js +5 -0
  158. package/dist/core/permissions/rules/sensitivePathsRule.js.map +2 -2
  159. package/dist/core/tokenStats.js +9 -0
  160. package/dist/core/tokenStats.js.map +7 -0
  161. package/dist/core/tokenStatsManager.js +331 -0
  162. package/dist/core/tokenStatsManager.js.map +7 -0
  163. package/dist/entrypoints/cli.js +115 -87
  164. package/dist/entrypoints/cli.js.map +2 -2
  165. package/dist/hooks/useAgentTokenStats.js +72 -0
  166. package/dist/hooks/useAgentTokenStats.js.map +7 -0
  167. package/dist/hooks/useAgentTranscripts.js +30 -6
  168. package/dist/hooks/useAgentTranscripts.js.map +2 -2
  169. package/dist/hooks/useLogMessages.js +12 -1
  170. package/dist/hooks/useLogMessages.js.map +2 -2
  171. package/dist/i18n/locales/en.js +6 -5
  172. package/dist/i18n/locales/en.js.map +2 -2
  173. package/dist/i18n/locales/zh-CN.js +6 -5
  174. package/dist/i18n/locales/zh-CN.js.map +2 -2
  175. package/dist/i18n/types.js.map +1 -1
  176. package/dist/permissions.js +28 -1
  177. package/dist/permissions.js.map +2 -2
  178. package/dist/query.js +78 -4
  179. package/dist/query.js.map +3 -3
  180. package/dist/screens/REPL.js +23 -3
  181. package/dist/screens/REPL.js.map +2 -2
  182. package/dist/services/claude.js +54 -3
  183. package/dist/services/claude.js.map +2 -2
  184. package/dist/services/intelligentCompactor.js +1 -1
  185. package/dist/services/intelligentCompactor.js.map +2 -2
  186. package/dist/services/mcpClient.js +81 -25
  187. package/dist/services/mcpClient.js.map +2 -2
  188. package/dist/services/sandbox/filesystemBoundary.js +58 -17
  189. package/dist/services/sandbox/filesystemBoundary.js.map +2 -2
  190. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +3 -2
  191. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  192. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +2 -1
  193. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  194. package/dist/tools/BashTool/BashTool.js +22 -3
  195. package/dist/tools/BashTool/BashTool.js.map +2 -2
  196. package/dist/tools/BashTool/prompt.js +178 -34
  197. package/dist/tools/BashTool/prompt.js.map +2 -2
  198. package/dist/tools/FileEditTool/prompt.js +6 -3
  199. package/dist/tools/FileEditTool/prompt.js.map +2 -2
  200. package/dist/tools/FileWriteTool/prompt.js +4 -2
  201. package/dist/tools/FileWriteTool/prompt.js.map +2 -2
  202. package/dist/tools/MultiEditTool/prompt.js +5 -3
  203. package/dist/tools/MultiEditTool/prompt.js.map +2 -2
  204. package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -1
  205. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  206. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +3 -2
  207. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
  208. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +3 -2
  209. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
  210. package/dist/tools/PlanModeTool/prompt.js +1 -1
  211. package/dist/tools/PlanModeTool/prompt.js.map +1 -1
  212. package/dist/tools/SkillTool/SkillTool.js +4 -3
  213. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  214. package/dist/tools/SkillTool/prompt.js +1 -1
  215. package/dist/tools/SkillTool/prompt.js.map +1 -1
  216. package/dist/tools/TaskOutputTool/TaskOutputTool.js +3 -2
  217. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +2 -2
  218. package/dist/tools/TaskTool/TaskTool.js +8 -0
  219. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  220. package/dist/utils/CircuitBreaker.js +242 -0
  221. package/dist/utils/CircuitBreaker.js.map +7 -0
  222. package/dist/utils/ask.js +2 -0
  223. package/dist/utils/ask.js.map +2 -2
  224. package/dist/utils/config.js +47 -5
  225. package/dist/utils/config.js.map +2 -2
  226. package/dist/utils/credentials/CredentialStore.js +1 -0
  227. package/dist/utils/credentials/CredentialStore.js.map +7 -0
  228. package/dist/utils/credentials/EncryptedFileStore.js +157 -0
  229. package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
  230. package/dist/utils/credentials/index.js +37 -0
  231. package/dist/utils/credentials/index.js.map +7 -0
  232. package/dist/utils/credentials/migration.js +82 -0
  233. package/dist/utils/credentials/migration.js.map +7 -0
  234. package/dist/utils/markdown.js +13 -1
  235. package/dist/utils/markdown.js.map +2 -2
  236. package/dist/utils/permissions/filesystem.js +5 -1
  237. package/dist/utils/permissions/filesystem.js.map +2 -2
  238. package/dist/utils/safePath.js +132 -0
  239. package/dist/utils/safePath.js.map +7 -0
  240. package/dist/utils/sensitiveFiles.js +125 -0
  241. package/dist/utils/sensitiveFiles.js.map +7 -0
  242. package/dist/utils/taskDisplayUtils.js +9 -9
  243. package/dist/utils/taskDisplayUtils.js.map +2 -2
  244. package/dist/utils/theme.js +6 -6
  245. package/dist/utils/theme.js.map +1 -1
  246. package/dist/utils/toolRiskClassification.js +207 -0
  247. package/dist/utils/toolRiskClassification.js.map +7 -0
  248. package/dist/utils/tooling/safeRender.js +5 -4
  249. package/dist/utils/tooling/safeRender.js.map +2 -2
  250. package/dist/version.js +2 -2
  251. package/dist/version.js.map +1 -1
  252. package/package.json +9 -7
  253. package/dist/hooks/useCancelRequest.js +0 -31
  254. package/dist/hooks/useCancelRequest.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/BashTool/BashTool.tsx"],
4
- "sourcesContent": ["import { statSync } from 'fs'\nimport { EOL } from 'os'\nimport { isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { queryQuick } from '@services/claude'\nimport { Tool, ValidationResult } from '@tool'\nimport { splitCommand } from '@utils/commands'\nimport { isInDirectory } from '@utils/file'\nimport { logError } from '@utils/log'\nimport {\n PersistentShell,\n type StreamingYield,\n type StreamingResult,\n} from '@utils/PersistentShell'\nimport { getCwd, getOriginalCwd } from '@utils/state'\nimport { getGlobalConfig } from '@utils/config'\nimport { getModelManager } from '@utils/model'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport BashToolResultMessage from './BashToolResultMessage'\nimport { BANNED_COMMANDS, PROMPT, matchesDangerousPattern } from './prompt'\nimport { formatOutput, getCommandFilePaths } from './utils'\n\nexport const inputSchema = z.strictObject({\n command: z.string().describe('The command to execute'),\n timeout: z\n .number()\n .optional()\n .describe('Optional timeout in milliseconds (max 600000)'),\n run_in_background: z\n .boolean()\n .optional()\n .describe('Set to true to run this command in the background'),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stdoutLines: number // Total number of lines in original stdout, even if `stdout` is now truncated\n stderr: string\n stderrLines: number // Total number of lines in original stderr, even if `stderr` is now truncated\n interrupted: boolean\n shellId?: string // Present if run_in_background is true\n}\n\nexport const BashTool = {\n name: 'Bash',\n async description() {\n return 'Executes shell commands on your computer'\n },\n async prompt() {\n const config = getGlobalConfig()\n // \uD83D\uDD27 Fix: Use ModelManager to get actual current model\n const modelManager = getModelManager()\n const modelName =\n modelManager.getModelName('main') || '<No Model Configured>'\n // Substitute the placeholder in the static PROMPT string\n return PROMPT.replace(/{MODEL_NAME}/g, modelName)\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // BashTool modifies state/files, not safe for concurrent execution\n },\n inputSchema,\n userFacingName() {\n return 'Bash'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n // Always check per-project permissions for BashTool\n return true\n },\n async validateInput({ command }): Promise<ValidationResult> {\n // Check for dangerous patterns in the entire command first\n if (matchesDangerousPattern(command)) {\n return {\n result: false,\n message: `Command contains dangerous patterns and has been blocked for security reasons`,\n }\n }\n\n const commands = splitCommand(command)\n for (const cmd of commands) {\n const parts = cmd.split(' ')\n const baseCmd = parts[0]\n\n // Check if command is banned\n if (baseCmd && BANNED_COMMANDS.includes(baseCmd.toLowerCase())) {\n return {\n result: false,\n message: `Command '${baseCmd}' is not allowed for security reasons`,\n }\n }\n\n // Also check for commands that might be invoked with full path\n // e.g., /usr/bin/curl, /bin/rm\n if (baseCmd && (baseCmd.startsWith('/') || baseCmd.startsWith('./'))) {\n const cmdName = baseCmd.split('/').pop()?.toLowerCase()\n if (cmdName && BANNED_COMMANDS.includes(cmdName)) {\n return {\n result: false,\n message: `Command '${cmdName}' is not allowed for security reasons (full path: ${baseCmd})`,\n }\n }\n }\n\n // Special handling for cd command\n if (baseCmd === 'cd' && parts[1]) {\n const targetDir = parts[1]!.replace(/^['\"]|['\"]$/g, '') // Remove quotes if present\n const fullTargetDir = isAbsolute(targetDir)\n ? targetDir\n : resolve(getCwd(), targetDir)\n if (\n !isInDirectory(\n relative(getOriginalCwd(), fullTargetDir),\n relative(getCwd(), getOriginalCwd()),\n )\n ) {\n return {\n result: false,\n message: `ERROR: cd to '${fullTargetDir}' was blocked. For security, ${PRODUCT_NAME} may only change directories to child directories of the original working directory (${getOriginalCwd()}) for this session.`,\n }\n }\n }\n }\n\n return { result: true }\n },\n renderToolUseMessage({ command }) {\n // Clean up any command that uses the quoted HEREDOC pattern\n if (command.includes(\"\\\"$(cat <<'EOF'\")) {\n const match = command.match(\n /^(.*?)\"?\\$\\(cat <<'EOF'\\n([\\s\\S]*?)\\n\\s*EOF\\n\\s*\\)\"(.*)$/,\n )\n if (match && match[1] && match[2]) {\n const prefix = match[1]\n const content = match[2]\n const suffix = match[3] || ''\n return `${prefix.trim()} \"${content.trim()}\"${suffix.trim()}`\n }\n }\n return command\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(content) {\n return <BashToolResultMessage content={content} verbose={false} />\n },\n renderResultForAssistant({ interrupted, stdout, stderr }) {\n let errorMessage = stderr.trim()\n if (interrupted) {\n if (stderr) errorMessage += EOL\n errorMessage += '<error>Command was aborted before completion</error>'\n }\n const hasBoth = stdout.trim() && errorMessage\n return `${stdout.trim()}${hasBoth ? '\\n' : ''}${errorMessage.trim()}`\n },\n async *call(\n { command, timeout = 120000, run_in_background = false },\n { abortController, readFileTimestamps },\n ) {\n // Handle background execution\n if (run_in_background) {\n const shellId = BackgroundShellManager.getInstance().create(\n command,\n getCwd(),\n )\n\n const data: Out = {\n stdout: `Background shell started with ID: ${shellId}`,\n stdoutLines: 1,\n stderr: '',\n stderrLines: 0,\n interrupted: false,\n shellId,\n }\n\n yield {\n type: 'result',\n resultForAssistant: `Started background shell: ${shellId}\\nCommand: ${command}`,\n data,\n }\n return\n }\n\n let stdout = ''\n let stderr = ''\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether this tool invocation has completed\n // to prevent async callbacks from accessing stale context\n let isCompleted = false\n\n // \uD83D\uDD27 Check if already cancelled before starting execution\n if (abortController.signal.aborted) {\n const data: Out = {\n stdout: '',\n stdoutLines: 0,\n stderr: 'Command cancelled before execution',\n stderrLines: 1,\n interrupted: true,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n try {\n // Execute commands with streaming output\n let result: StreamingResult | null = null\n let streamedStdout = ''\n let streamedStderr = ''\n\n // Use streaming execution for real-time output\n const streamingGenerator = PersistentShell.getInstance().execStreaming(\n command,\n abortController.signal,\n timeout,\n )\n\n for await (const chunk of streamingGenerator) {\n if (chunk.type === 'chunk') {\n // Accumulate streamed output\n if (chunk.stdout) {\n streamedStdout += chunk.stdout\n }\n if (chunk.stderr) {\n streamedStderr += chunk.stderr\n }\n\n // Yield progress update for real-time UI feedback\n // Use StreamingProgressContent format for proper rendering\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Bash',\n stdout: streamedStdout,\n stderr: streamedStderr,\n isStreaming: true,\n },\n }\n } else if (chunk.type === 'result') {\n // Final result\n result = chunk\n }\n }\n\n // Use the final result (or construct from streamed data)\n if (!result) {\n result = {\n type: 'result',\n stdout: streamedStdout,\n stderr: streamedStderr,\n code: 0,\n interrupted: false,\n }\n }\n\n stdout += (result.stdout || '').trim() + EOL\n stderr += (result.stderr || '').trim() + EOL\n if (result.code !== 0) {\n stderr += `Exit code ${result.code}`\n }\n\n if (!isInDirectory(getCwd(), getOriginalCwd())) {\n // Shell directory is outside original working directory, reset it\n await PersistentShell.getInstance().setCwd(getOriginalCwd())\n stderr = `${stderr.trim()}${EOL}Shell cwd was reset to ${getOriginalCwd()}`\n }\n\n // Update read timestamps for any files referenced by the command\n // Don't block the main thread!\n // Skip this in tests because it makes fixtures non-deterministic (they might not always get written),\n // so will be missing in CI.\n if (process.env.NODE_ENV !== 'test') {\n getCommandFilePaths(command, stdout).then(filePaths => {\n // \uD83D\uDD27 CRITICAL FIX: Check if tool invocation has already completed\n // to prevent accessing potentially stale context\n if (isCompleted) {\n return // Tool has finished, don't access readFileTimestamps\n }\n\n for (const filePath of filePaths) {\n const fullFilePath = isAbsolute(filePath)\n ? filePath\n : resolve(getCwd(), filePath)\n\n // Try/catch in case the file doesn't exist (because Haiku didn't properly extract it)\n try {\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n } catch (e) {\n logError(e)\n }\n }\n })\n }\n\n const { totalLines: stdoutLines, truncatedContent: stdoutContent } =\n formatOutput(stdout.trim())\n const { totalLines: stderrLines, truncatedContent: stderrContent } =\n formatOutput(stderr.trim())\n\n const data: Out = {\n stdout: stdoutContent,\n stdoutLines,\n stderr: stderrContent,\n stderrLines,\n interrupted: result.interrupted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } catch (error) {\n // \uD83D\uDD27 Handle cancellation or other errors properly\n const isAborted = abortController.signal.aborted\n const errorMessage = isAborted\n ? 'Command was cancelled by user'\n : `Command failed: ${error instanceof Error ? error.message : String(error)}`\n\n const data: Out = {\n stdout: stdout.trim(),\n stdoutLines: stdout.split('\\n').length,\n stderr: errorMessage,\n stderrLines: 1,\n interrupted: isAborted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } finally {\n // \uD83D\uDD27 CRITICAL FIX: Mark tool invocation as completed\n // This prevents async callbacks (like getCommandFilePaths) from\n // accessing potentially stale context after this tool returns\n isCompleted = true\n }\n },\n} satisfies Tool<In, Out>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { statSync } from 'fs'\nimport { EOL } from 'os'\nimport { isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { PRODUCT_NAME } from '@constants/product'\nimport { queryQuick } from '@services/claude'\nimport { Tool, ValidationResult } from '@tool'\nimport { splitCommand } from '@utils/commands'\nimport { isInDirectory } from '@utils/file'\nimport { logError } from '@utils/log'\nimport {\n PersistentShell,\n type StreamingYield,\n type StreamingResult,\n} from '@utils/PersistentShell'\nimport { getCwd, getOriginalCwd } from '@utils/state'\nimport { getGlobalConfig } from '@utils/config'\nimport { getModelManager } from '@utils/model'\nimport { BackgroundShellManager } from '@utils/BackgroundShellManager'\nimport BashToolResultMessage from './BashToolResultMessage'\nimport {\n BANNED_COMMANDS,\n PROMPT,\n matchesDangerousPattern,\n detectDangerousPatterns,\n} from './prompt'\nimport { formatOutput, getCommandFilePaths } from './utils'\n\nexport const inputSchema = z.strictObject({\n command: z.string().describe('The command to execute'),\n timeout: z\n .number()\n .optional()\n .describe('Optional timeout in milliseconds (max 600000)'),\n run_in_background: z\n .boolean()\n .optional()\n .describe('Set to true to run this command in the background'),\n})\n\ntype In = typeof inputSchema\nexport type Out = {\n stdout: string\n stdoutLines: number // Total number of lines in original stdout, even if `stdout` is now truncated\n stderr: string\n stderrLines: number // Total number of lines in original stderr, even if `stderr` is now truncated\n interrupted: boolean\n shellId?: string // Present if run_in_background is true\n}\n\nexport const BashTool = {\n name: 'Bash',\n async description() {\n return 'Executes shell commands on your computer'\n },\n async prompt() {\n const config = getGlobalConfig()\n // \uD83D\uDD27 Fix: Use ModelManager to get actual current model\n const modelManager = getModelManager()\n const modelName =\n modelManager.getModelName('main') || '<No Model Configured>'\n // Substitute the placeholder in the static PROMPT string\n return PROMPT.replace(/{MODEL_NAME}/g, modelName)\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // BashTool modifies state/files, not safe for concurrent execution\n },\n inputSchema,\n userFacingName() {\n return 'Bash'\n },\n async isEnabled() {\n return true\n },\n needsPermissions(): boolean {\n // Always check per-project permissions for BashTool\n return true\n },\n async validateInput({ command }): Promise<ValidationResult> {\n // Detect dangerous patterns (critical only block, others warn)\n const dangerousPatterns = detectDangerousPatterns(command)\n const criticalPatterns = dangerousPatterns.filter(\n p => p.severity === 'critical',\n )\n\n if (criticalPatterns.length > 0) {\n const patternNames = criticalPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY: Critical dangerous patterns detected in command: ${patternNames}`,\n )\n return {\n result: false,\n message: `Command contains critical security risks: ${patternNames}. This command has been blocked for security reasons.`,\n }\n }\n\n // Log warnings for high-severity patterns (non-blocking)\n const highPatterns = dangerousPatterns.filter(p => p.severity === 'high')\n if (highPatterns.length > 0) {\n const patternNames = highPatterns.map(p => p.name).join(', ')\n logError(\n `SECURITY WARNING: High-severity patterns detected in command: ${patternNames}. User confirmation required.`,\n )\n }\n\n const commands = splitCommand(command)\n for (const cmd of commands) {\n const parts = cmd.split(' ')\n const baseCmd = parts[0]\n\n // Check if command is banned\n if (baseCmd && BANNED_COMMANDS.includes(baseCmd.toLowerCase())) {\n return {\n result: false,\n message: `Command '${baseCmd}' is not allowed for security reasons`,\n }\n }\n\n // Also check for commands that might be invoked with full path\n // e.g., /usr/bin/curl, /bin/rm\n if (baseCmd && (baseCmd.startsWith('/') || baseCmd.startsWith('./'))) {\n const cmdName = baseCmd.split('/').pop()?.toLowerCase()\n if (cmdName && BANNED_COMMANDS.includes(cmdName)) {\n return {\n result: false,\n message: `Command '${cmdName}' is not allowed for security reasons (full path: ${baseCmd})`,\n }\n }\n }\n\n // Special handling for cd command\n if (baseCmd === 'cd' && parts[1]) {\n const targetDir = parts[1]!.replace(/^['\"]|['\"]$/g, '') // Remove quotes if present\n const fullTargetDir = isAbsolute(targetDir)\n ? targetDir\n : resolve(getCwd(), targetDir)\n if (\n !isInDirectory(\n relative(getOriginalCwd(), fullTargetDir),\n relative(getCwd(), getOriginalCwd()),\n )\n ) {\n return {\n result: false,\n message: `ERROR: cd to '${fullTargetDir}' was blocked. For security, ${PRODUCT_NAME} may only change directories to child directories of the original working directory (${getOriginalCwd()}) for this session.`,\n }\n }\n }\n }\n\n return { result: true }\n },\n renderToolUseMessage({ command }) {\n // Clean up any command that uses the quoted HEREDOC pattern\n if (command.includes(\"\\\"$(cat <<'EOF'\")) {\n const match = command.match(\n /^(.*?)\"?\\$\\(cat <<'EOF'\\n([\\s\\S]*?)\\n\\s*EOF\\n\\s*\\)\"(.*)$/,\n )\n if (match && match[1] && match[2]) {\n const prefix = match[1]\n const content = match[2]\n const suffix = match[3] || ''\n return `${prefix.trim()} \"${content.trim()}\"${suffix.trim()}`\n }\n }\n return command\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(content) {\n return <BashToolResultMessage content={content} verbose={false} />\n },\n renderResultForAssistant({ interrupted, stdout, stderr }) {\n let errorMessage = stderr.trim()\n if (interrupted) {\n if (stderr) errorMessage += EOL\n errorMessage += '<error>Command was aborted before completion</error>'\n }\n const hasBoth = stdout.trim() && errorMessage\n return `${stdout.trim()}${hasBoth ? '\\n' : ''}${errorMessage.trim()}`\n },\n async *call(\n { command, timeout = 120000, run_in_background = false },\n { abortController, readFileTimestamps },\n ) {\n // Handle background execution\n if (run_in_background) {\n const shellId = BackgroundShellManager.getInstance().create(\n command,\n getCwd(),\n )\n\n const data: Out = {\n stdout: `Background shell started with ID: ${shellId}`,\n stdoutLines: 1,\n stderr: '',\n stderrLines: 0,\n interrupted: false,\n shellId,\n }\n\n yield {\n type: 'result',\n resultForAssistant: `Started background shell: ${shellId}\\nCommand: ${command}`,\n data,\n }\n return\n }\n\n let stdout = ''\n let stderr = ''\n\n // \uD83D\uDD27 CRITICAL FIX: Track whether this tool invocation has completed\n // to prevent async callbacks from accessing stale context\n let isCompleted = false\n\n // \uD83D\uDD27 Check if already cancelled before starting execution\n if (abortController.signal.aborted) {\n const data: Out = {\n stdout: '',\n stdoutLines: 0,\n stderr: 'Command cancelled before execution',\n stderrLines: 1,\n interrupted: true,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n return\n }\n\n try {\n // Execute commands with streaming output\n let result: StreamingResult | null = null\n let streamedStdout = ''\n let streamedStderr = ''\n\n // Use streaming execution for real-time output\n const streamingGenerator = PersistentShell.getInstance().execStreaming(\n command,\n abortController.signal,\n timeout,\n )\n\n for await (const chunk of streamingGenerator) {\n if (chunk.type === 'chunk') {\n // Accumulate streamed output\n if (chunk.stdout) {\n streamedStdout += chunk.stdout\n }\n if (chunk.stderr) {\n streamedStderr += chunk.stderr\n }\n\n // Yield progress update for real-time UI feedback\n // Use StreamingProgressContent format for proper rendering\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Bash',\n stdout: streamedStdout,\n stderr: streamedStderr,\n isStreaming: true,\n },\n }\n } else if (chunk.type === 'result') {\n // Final result\n result = chunk\n }\n }\n\n // Use the final result (or construct from streamed data)\n if (!result) {\n result = {\n type: 'result',\n stdout: streamedStdout,\n stderr: streamedStderr,\n code: 0,\n interrupted: false,\n }\n }\n\n stdout += (result.stdout || '').trim() + EOL\n stderr += (result.stderr || '').trim() + EOL\n if (result.code !== 0) {\n stderr += `Exit code ${result.code}`\n }\n\n if (!isInDirectory(getCwd(), getOriginalCwd())) {\n // Shell directory is outside original working directory, reset it\n await PersistentShell.getInstance().setCwd(getOriginalCwd())\n stderr = `${stderr.trim()}${EOL}Shell cwd was reset to ${getOriginalCwd()}`\n }\n\n // Update read timestamps for any files referenced by the command\n // Don't block the main thread!\n // Skip this in tests because it makes fixtures non-deterministic (they might not always get written),\n // so will be missing in CI.\n if (process.env.NODE_ENV !== 'test') {\n getCommandFilePaths(command, stdout).then(filePaths => {\n // \uD83D\uDD27 CRITICAL FIX: Check if tool invocation has already completed\n // to prevent accessing potentially stale context\n if (isCompleted) {\n return // Tool has finished, don't access readFileTimestamps\n }\n\n for (const filePath of filePaths) {\n const fullFilePath = isAbsolute(filePath)\n ? filePath\n : resolve(getCwd(), filePath)\n\n // Try/catch in case the file doesn't exist (because Haiku didn't properly extract it)\n try {\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n } catch (e) {\n logError(e)\n }\n }\n })\n }\n\n const { totalLines: stdoutLines, truncatedContent: stdoutContent } =\n formatOutput(stdout.trim())\n const { totalLines: stderrLines, truncatedContent: stderrContent } =\n formatOutput(stderr.trim())\n\n const data: Out = {\n stdout: stdoutContent,\n stdoutLines,\n stderr: stderrContent,\n stderrLines,\n interrupted: result.interrupted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } catch (error) {\n // \uD83D\uDD27 Handle cancellation or other errors properly\n const isAborted = abortController.signal.aborted\n const errorMessage = isAborted\n ? 'Command was cancelled by user'\n : `Command failed: ${error instanceof Error ? error.message : String(error)}`\n\n const data: Out = {\n stdout: stdout.trim(),\n stdoutLines: stdout.split('\\n').length,\n stderr: errorMessage,\n stderrLines: 1,\n interrupted: isAborted,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(data),\n data,\n }\n } finally {\n // \uD83D\uDD27 CRITICAL FIX: Mark tool invocation as completed\n // This prevents async callbacks (like getCommandFilePaths) from\n // accessing potentially stale context after this tool returns\n isCompleted = true\n }\n },\n} satisfies Tool<In, Out>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -176,41 +176,185 @@ const BANNED_COMMANDS = [
176
176
  "ssh-agent",
177
177
  "ssh-add"
178
178
  ];
179
- const DANGEROUS_PATTERNS = [
180
- // Fork bomb patterns
181
- /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;?\s*:/,
182
- /\.\s*\(\)\s*\{\s*\.\s*\|\s*\.\s*&\s*\}\s*;?\s*\./,
183
- // Shell expansion attacks
184
- /\$\(\s*<\s*\/dev\/tcp/i,
185
- /\/dev\/tcp\//i,
186
- /\/dev\/udp\//i,
187
- // Reverse shell patterns
188
- /mkfifo.*nc/i,
189
- /bash\s+-i\s+>&\s*\/dev\/tcp/i,
190
- // Base64 decode and execute
191
- /base64\s+(-d|--decode).*\|\s*(bash|sh|eval)/i,
192
- // Hex decode and execute
193
- /xxd\s+-r.*\|\s*(bash|sh|eval)/i,
194
- // Download and execute
195
- /curl.*\|\s*(bash|sh)/i,
196
- /wget.*\|\s*(bash|sh)/i,
197
- // Overwrite system files
198
- />\s*\/etc\/(passwd|shadow|sudoers)/i,
199
- />\s*\/boot\//i,
200
- />\s*\/dev\/sd[a-z]/i,
201
- />\s*\/dev\/nvme/i,
202
- // History manipulation
203
- /HISTFILE=/i,
204
- /unset\s+HISTFILE/i,
205
- /history\s+-c/i,
206
- // Environment variable injection
207
- /LD_PRELOAD=/i,
208
- /LD_LIBRARY_PATH=/i,
209
- /DYLD_INSERT_LIBRARIES=/i
210
- // macOS equivalent
179
+ const DANGEROUS_PATTERN_METADATA = [
180
+ // CRITICAL: Command Injection & Execution Bypass
181
+ {
182
+ pattern: /\$\([^)]+\)/,
183
+ name: "command substitution $(...)",
184
+ severity: "critical"
185
+ },
186
+ {
187
+ pattern: /`[^`]+`/,
188
+ name: "backtick command substitution",
189
+ severity: "critical"
190
+ },
191
+ {
192
+ pattern: /;\s*(rm|chmod|chown|dd|mkfs)\b/i,
193
+ name: "chained destructive command",
194
+ severity: "critical"
195
+ },
196
+ {
197
+ pattern: /&&\s*(rm|chmod|chown|dd|mkfs)\b/i,
198
+ name: "conditional destructive command",
199
+ severity: "critical"
200
+ },
201
+ {
202
+ pattern: /\|\s*bash\b/i,
203
+ name: "pipe to bash",
204
+ severity: "critical"
205
+ },
206
+ {
207
+ pattern: /\|\s*sh\b/i,
208
+ name: "pipe to sh",
209
+ severity: "critical"
210
+ },
211
+ {
212
+ pattern: /curl.*\|\s*(bash|sh)\b/i,
213
+ name: "curl pipe to shell",
214
+ severity: "critical"
215
+ },
216
+ {
217
+ pattern: /wget.*\|\s*(bash|sh)\b/i,
218
+ name: "wget pipe to shell",
219
+ severity: "critical"
220
+ },
221
+ {
222
+ pattern: /eval\s+['"$]/,
223
+ name: "eval with dynamic input",
224
+ severity: "critical"
225
+ },
226
+ // CRITICAL: Destructive Operations
227
+ {
228
+ pattern: /\brm\s+-rf\s+\/(?!\S)/,
229
+ name: "rm -rf /",
230
+ severity: "critical"
231
+ },
232
+ {
233
+ pattern: />\s*\/dev\/sd[a-z]/i,
234
+ name: "write to block device",
235
+ severity: "critical"
236
+ },
237
+ {
238
+ pattern: />\s*\/dev\/nvme/i,
239
+ name: "write to NVMe device",
240
+ severity: "critical"
241
+ },
242
+ {
243
+ pattern: />\s*\/etc\/(passwd|shadow|sudoers)/i,
244
+ name: "overwrite critical system files",
245
+ severity: "critical"
246
+ },
247
+ {
248
+ pattern: />\s*\/boot\//i,
249
+ name: "overwrite boot files",
250
+ severity: "critical"
251
+ },
252
+ // CRITICAL: Fork Bomb & DoS
253
+ {
254
+ pattern: /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;?\s*:/,
255
+ name: "fork bomb pattern ()",
256
+ severity: "critical"
257
+ },
258
+ {
259
+ pattern: /\.\s*\(\)\s*\{\s*\.\s*\|\s*\.\s*&\s*\}\s*;?\s*\./,
260
+ name: "fork bomb pattern (.)",
261
+ severity: "critical"
262
+ },
263
+ // HIGH: Reverse Shell & Data Exfiltration
264
+ {
265
+ pattern: /\$\(\s*<\s*\/dev\/tcp/i,
266
+ name: "TCP socket connection",
267
+ severity: "high"
268
+ },
269
+ {
270
+ pattern: /\/dev\/tcp\//i,
271
+ name: "direct TCP device access",
272
+ severity: "high"
273
+ },
274
+ {
275
+ pattern: /\/dev\/udp\//i,
276
+ name: "direct UDP device access",
277
+ severity: "high"
278
+ },
279
+ {
280
+ pattern: /mkfifo.*nc/i,
281
+ name: "named pipe with netcat",
282
+ severity: "high"
283
+ },
284
+ {
285
+ pattern: /bash\s+-i\s+>&\s*\/dev\/tcp/i,
286
+ name: "interactive bash reverse shell",
287
+ severity: "high"
288
+ },
289
+ // HIGH: Data Encoding & Obfuscation
290
+ {
291
+ pattern: /base64\s+(-d|--decode).*\|\s*(bash|sh|eval)/i,
292
+ name: "base64 decode and execute",
293
+ severity: "high"
294
+ },
295
+ {
296
+ pattern: /xxd\s+-r.*\|\s*(bash|sh|eval)/i,
297
+ name: "hex decode and execute",
298
+ severity: "high"
299
+ },
300
+ // HIGH: System & Security Manipulation
301
+ {
302
+ pattern: /HISTFILE=/i,
303
+ name: "history file manipulation",
304
+ severity: "high"
305
+ },
306
+ {
307
+ pattern: /unset\s+HISTFILE/i,
308
+ name: "disable command history",
309
+ severity: "high"
310
+ },
311
+ {
312
+ pattern: /history\s+-c/i,
313
+ name: "clear command history",
314
+ severity: "high"
315
+ },
316
+ {
317
+ pattern: /LD_PRELOAD=/i,
318
+ name: "library preload injection",
319
+ severity: "high"
320
+ },
321
+ {
322
+ pattern: /LD_LIBRARY_PATH=/i,
323
+ name: "library path injection",
324
+ severity: "high"
325
+ },
326
+ {
327
+ pattern: /DYLD_INSERT_LIBRARIES=/i,
328
+ name: "macOS library injection",
329
+ severity: "high"
330
+ },
331
+ // MEDIUM: Suspicious Command Patterns
332
+ {
333
+ pattern: /\$\{[^}]*\}/,
334
+ name: "variable expansion",
335
+ severity: "medium"
336
+ }
211
337
  ];
338
+ function detectDangerousPatterns(command) {
339
+ const matches = [];
340
+ for (const { pattern, name, severity } of DANGEROUS_PATTERN_METADATA) {
341
+ const match = command.match(pattern);
342
+ if (match) {
343
+ matches.push({
344
+ pattern: pattern.toString(),
345
+ name,
346
+ match: match[0],
347
+ severity
348
+ });
349
+ }
350
+ }
351
+ return matches;
352
+ }
212
353
  function matchesDangerousPattern(command) {
213
- return DANGEROUS_PATTERNS.some((pattern) => pattern.test(command));
354
+ const criticalMatches = detectDangerousPatterns(command).filter(
355
+ (m) => m.severity === "critical"
356
+ );
357
+ return criticalMatches.length > 0;
214
358
  }
215
359
  const PROMPT = `Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
216
360
 
@@ -360,10 +504,10 @@ Important:
360
504
  - Never update git config`;
361
505
  export {
362
506
  BANNED_COMMANDS,
363
- DANGEROUS_PATTERNS,
364
507
  MAX_OUTPUT_LENGTH,
365
508
  MAX_RENDERED_LINES,
366
509
  PROMPT,
510
+ detectDangerousPatterns,
367
511
  matchesDangerousPattern
368
512
  };
369
513
  //# sourceMappingURL=prompt.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/BashTool/prompt.ts"],
4
- "sourcesContent": ["import { PRODUCT_NAME, PRODUCT_URL } from '@constants/product'\nimport { TOOL_NAME as TASK_TOOL_NAME } from '@tools/TaskTool/constants'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { TOOL_NAME_FOR_PROMPT as GLOB_TOOL_NAME } from '@tools/GlobTool/prompt'\nimport { TOOL_NAME_FOR_PROMPT as GREP_TOOL_NAME } from '@tools/GrepTool/prompt'\nimport { LSTool } from '@tools/lsTool/lsTool'\n\nexport const MAX_OUTPUT_LENGTH = 30000\nexport const MAX_RENDERED_LINES = 5\n\n/**\n * Banned commands for security\n *\n * Categories:\n * - Network tools: Could be used for data exfiltration or network attacks\n * - System modification: Could damage the system or escalate privileges\n * - Shell execution: Could bypass command restrictions\n * - Browsers: Could open external content\n * - Dangerous file operations: Could cause data loss\n */\nexport const BANNED_COMMANDS = [\n // Network tools - data exfiltration risk\n 'curl',\n 'curlie',\n 'wget',\n 'axel',\n 'aria2c',\n 'nc',\n 'netcat',\n 'ncat',\n 'telnet',\n 'lynx',\n 'w3m',\n 'links',\n 'httpie',\n 'xh',\n 'http-prompt',\n 'ftp',\n 'sftp',\n 'scp',\n 'rsync',\n 'socat',\n 'nmap',\n 'tcpdump',\n 'wireshark',\n 'tshark',\n\n // Browsers - could open external content\n 'chrome',\n 'chromium',\n 'firefox',\n 'safari',\n 'opera',\n 'brave',\n 'open', // macOS open command could launch apps/URLs\n\n // Privilege escalation - dangerous system access\n 'sudo',\n 'su',\n 'doas',\n 'pkexec',\n 'gksudo',\n 'kdesudo',\n\n // Shell execution - could bypass restrictions\n 'eval',\n 'source',\n 'exec',\n 'bash',\n 'sh',\n 'zsh',\n 'fish',\n 'csh',\n 'tcsh',\n 'ksh',\n 'dash',\n 'ash',\n 'xargs', // Can execute arbitrary commands\n 'parallel', // GNU parallel can execute commands\n 'xonsh',\n 'powershell',\n 'pwsh',\n 'cmd',\n\n // Dangerous file operations - data loss risk\n 'rm',\n 'rmdir',\n 'shred',\n 'srm',\n 'wipe',\n 'dd', // Can overwrite disks\n 'mkfs', // Can format disks\n 'fdisk',\n 'parted',\n 'wipefs',\n\n // System modification - could damage system\n 'chmod',\n 'chown',\n 'chgrp',\n 'chattr',\n 'setfacl',\n 'shutdown',\n 'reboot',\n 'poweroff',\n 'halt',\n 'init',\n 'systemctl',\n 'service',\n 'launchctl', // macOS service control\n\n // Process control - could affect system stability\n 'kill',\n 'killall',\n 'pkill',\n\n // Aliases and functions - could hide malicious commands\n 'alias',\n 'unalias',\n 'function',\n\n // Cron/scheduling - persistent access\n 'crontab',\n 'at',\n 'atq',\n 'atrm',\n\n // User management - privilege escalation\n 'useradd',\n 'userdel',\n 'usermod',\n 'groupadd',\n 'groupdel',\n 'groupmod',\n 'passwd',\n 'chpasswd',\n\n // Package managers - could install malware\n 'apt',\n 'apt-get',\n 'aptitude',\n 'dpkg',\n 'yum',\n 'dnf',\n 'rpm',\n 'pacman',\n 'brew',\n 'port', // MacPorts\n 'pip',\n 'pip3',\n 'npm',\n 'yarn',\n 'pnpm',\n 'gem',\n 'cargo',\n 'go', // go install could install packages\n\n // Compilers/interpreters with execution capability\n 'python',\n 'python3',\n 'python2',\n 'ruby',\n 'perl',\n 'php',\n 'node',\n 'deno',\n 'bun',\n 'lua',\n 'awk',\n 'gawk',\n 'mawk',\n 'nawk',\n 'sed', // Can execute commands with e flag\n\n // Container/VM - could escape sandbox\n 'docker',\n 'podman',\n 'kubectl',\n 'vagrant',\n 'virsh',\n 'qemu',\n 'virtualbox',\n 'vboxmanage',\n\n // System info that could aid attacks\n 'env', // Exposes environment variables including secrets\n\n // Encryption/keys - could exfiltrate secrets\n 'gpg',\n 'openssl',\n 'ssh-keygen',\n 'ssh-agent',\n 'ssh-add',\n]\n\n/**\n * Patterns that indicate dangerous command usage\n * These are checked in addition to banned commands\n */\nexport const DANGEROUS_PATTERNS = [\n // Fork bomb patterns\n /:\\(\\)\\s*\\{\\s*:\\s*\\|\\s*:\\s*&\\s*\\}\\s*;?\\s*:/,\n /\\.\\s*\\(\\)\\s*\\{\\s*\\.\\s*\\|\\s*\\.\\s*&\\s*\\}\\s*;?\\s*\\./,\n\n // Shell expansion attacks\n /\\$\\(\\s*<\\s*\\/dev\\/tcp/i,\n /\\/dev\\/tcp\\//i,\n /\\/dev\\/udp\\//i,\n\n // Reverse shell patterns\n /mkfifo.*nc/i,\n /bash\\s+-i\\s+>&\\s*\\/dev\\/tcp/i,\n\n // Base64 decode and execute\n /base64\\s+(-d|--decode).*\\|\\s*(bash|sh|eval)/i,\n\n // Hex decode and execute\n /xxd\\s+-r.*\\|\\s*(bash|sh|eval)/i,\n\n // Download and execute\n /curl.*\\|\\s*(bash|sh)/i,\n /wget.*\\|\\s*(bash|sh)/i,\n\n // Overwrite system files\n />\\s*\\/etc\\/(passwd|shadow|sudoers)/i,\n />\\s*\\/boot\\//i,\n />\\s*\\/dev\\/sd[a-z]/i,\n />\\s*\\/dev\\/nvme/i,\n\n // History manipulation\n /HISTFILE=/i,\n /unset\\s+HISTFILE/i,\n /history\\s+-c/i,\n\n // Environment variable injection\n /LD_PRELOAD=/i,\n /LD_LIBRARY_PATH=/i,\n /DYLD_INSERT_LIBRARIES=/i, // macOS equivalent\n]\n\n/**\n * Check if a command matches any dangerous pattern\n */\nexport function matchesDangerousPattern(command: string): boolean {\n return DANGEROUS_PATTERNS.some(pattern => pattern.test(command))\n}\n\nexport const PROMPT = `Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.\n\nBefore executing the command, please follow these steps:\n\n1. Directory Verification:\n - If the command will create new directories or files, first use the LS tool to verify the parent directory exists and is the correct location\n - For example, before running \"mkdir foo/bar\", first use LS to check that \"foo\" exists and is the intended parent directory\n\n2. Security Check:\n - For security and to limit the threat of a prompt injection attack, some commands are limited or banned. If you use a disallowed command, you will receive an error message explaining the restriction. Explain the error to the User.\n - Verify that the command is not one of the banned commands: ${BANNED_COMMANDS.join(', ')}.\n\n3. Command Execution:\n - After ensuring proper quoting, execute the command.\n - Capture the output of the command.\n\n4. Output Processing:\n - If the output exceeds ${MAX_OUTPUT_LENGTH} characters, output will be truncated before being returned to you.\n - Prepare the output for display to the user.\n\n5. Return Result:\n - Provide the processed output of the command.\n - If any errors occurred during execution, include those in the output.\n\nUsage notes:\n - The command argument is required.\n - You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 30 minutes.\n - You can set run_in_background to true to run the command in the background. This is useful for long-running processes like dev servers, build watches, or monitoring tasks. When running in background, you will receive a shell_id that can be used with BashOutputTool and KillShellTool to monitor and manage the task.\n - VERY IMPORTANT: You MUST avoid using search commands like \\`find\\` and \\`grep\\`. Instead use ${GREP_TOOL_NAME}, ${GLOB_TOOL_NAME}, or ${TASK_TOOL_NAME} to search. You MUST avoid read tools like \\`cat\\`, \\`head\\`, \\`tail\\`, and \\`ls\\`, and use ${FileReadTool.name} and ${LSTool.name} to read files.\n - When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings).\n - IMPORTANT: All commands share the same shell session. Shell state (environment variables, virtual environments, current directory, etc.) persist between commands. For example, if you set an environment variable as part of a command, the environment variable will persist for subsequent commands.\n - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \\`cd\\`. You may use \\`cd\\` if the User explicitly requests it.\n <good-example>\n pytest /foo/bar/tests\n </good-example>\n <bad-example>\n cd /foo/bar && pytest tests\n </bad-example>\n\n# Committing changes with git\n\nWhen the user asks you to create a new git commit, follow these steps carefully:\n\n1. Start with a single message that contains exactly three tool_use blocks that do the following (it is VERY IMPORTANT that you send these tool_use blocks in a single message, otherwise it will feel slow to the user!):\n - Run a git status command to see all untracked files.\n - Run a git diff command to see both staged and unstaged changes that will be committed.\n - Run a git log command to see recent commit messages, so that you can follow this repository's commit message style.\n\n2. Use the git context at the start of this conversation to determine which files are relevant to your commit. Add relevant untracked files to the staging area. Do not commit files that were already modified at the start of this conversation, if they are not relevant to your commit.\n\n3. Analyze all staged changes (both previously staged and newly added) and draft a commit message. Wrap your analysis process in <commit_analysis> tags:\n\n<commit_analysis>\n- List the files that have been changed or added\n- Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.)\n- Brainstorm the purpose or motivation behind these changes\n- Do not use tools to explore code, beyond what is available in the git context\n- Assess the impact of these changes on the overall project\n- Check for any sensitive information that shouldn't be committed\n- Draft a concise (1-2 sentences) commit message that focuses on the \"why\" rather than the \"what\"\n- Ensure your language is clear, concise, and to the point\n- Ensure the message accurately reflects the changes and their purpose (i.e. \"add\" means a wholly new feature, \"update\" means an enhancement to an existing feature, \"fix\" means a bug fix, etc.)\n- Ensure the message is not generic (avoid words like \"Update\" or \"Fix\" without context)\n- Review the draft message to ensure it accurately reflects the changes and their purpose\n</commit_analysis>\n\n4. Create the commit with a message ending with:\n\uD83E\uDD16 Generated with ${PRODUCT_NAME} & {MODEL_NAME}\nCo-Authored-By: ${PRODUCT_NAME} <noreply@${PRODUCT_NAME}.com>\n\n- In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:\n<example>\ngit commit -m \"$(cat <<'EOF'\n Commit message here.\n\n \uD83E\uDD16 Generated with ${PRODUCT_NAME} & {MODEL_NAME}\n Co-Authored-By: ${PRODUCT_NAME} <noreply@${PRODUCT_NAME}.com>\n EOF\n )\"\n</example>\n\n5. If the commit fails due to pre-commit hook changes, retry the commit ONCE to include these automated changes. If it fails again, it usually means a pre-commit hook is preventing the commit. If the commit succeeds but you notice that files were modified by the pre-commit hook, you MUST amend your commit to include them.\n\n6. Finally, run git status to make sure the commit succeeded.\n\nImportant notes:\n- When possible, combine the \"git add\" and \"git commit\" commands into a single \"git commit -am\" command, to speed things up\n- However, be careful not to stage files (e.g. with \\`git add .\\`) for commits that aren't part of the change, they may have untracked files they want to keep around, but not commit.\n- NEVER update the git config\n- DO NOT push to the remote repository\n- IMPORTANT: Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported.\n- If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit\n- Ensure your commit message is meaningful and concise. It should explain the purpose of the changes, not just describe them.\n- Return an empty response - the user will see the git output directly\n\n# Creating pull requests\nUse the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed.\n\nIMPORTANT: When the user asks you to create a pull request, follow these steps carefully:\n\n1. Understand the current state of the branch. Remember to send a single message that contains multiple tool_use blocks (it is VERY IMPORTANT that you do this in a single message, otherwise it will feel slow to the user!):\n - Run a git status command to see all untracked files.\n - Run a git diff command to see both staged and unstaged changes that will be committed.\n - Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote\n - Run a git log command and \\`git diff main...HEAD\\` to understand the full commit history for the current branch (from the time it diverged from the \\`main\\` branch.)\n\n2. Create new branch if needed\n\n3. Commit changes if needed\n\n4. Push to remote with -u flag if needed\n\n5. Analyze all changes that will be included in the pull request, making sure to look at all relevant commits (not just the latest commit, but all commits that will be included in the pull request!), and draft a pull request summary. Wrap your analysis process in <pr_analysis> tags:\n\n<pr_analysis>\n- List the commits since diverging from the main branch\n- Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.)\n- Brainstorm the purpose or motivation behind these changes\n- Assess the impact of these changes on the overall project\n- Do not use tools to explore code, beyond what is available in the git context\n- Check for any sensitive information that shouldn't be committed\n- Draft a concise (1-2 bullet points) pull request summary that focuses on the \"why\" rather than the \"what\"\n- Ensure the summary accurately reflects all changes since diverging from the main branch\n- Ensure your language is clear, concise, and to the point\n- Ensure the summary accurately reflects the changes and their purpose (ie. \"add\" means a wholly new feature, \"update\" means an enhancement to an existing feature, \"fix\" means a bug fix, etc.)\n- Ensure the summary is not generic (avoid words like \"Update\" or \"Fix\" without context)\n- Review the draft summary to ensure it accurately reflects the changes and their purpose\n</pr_analysis>\n\n6. Create PR using gh pr create with the format below. Use a HEREDOC to pass the body to ensure correct formatting.\n<example>\ngh pr create --title \"the pr title\" --body \"$(cat <<'EOF'\n## Summary\n<1-3 bullet points>\n\n## Test plan\n[Checklist of TODOs for testing the pull request...]\n\n\uD83E\uDD16 Generated with [${PRODUCT_NAME}](${PRODUCT_URL}) & {MODEL_NAME}\nEOF\n)\"\n</example>\n\nImportant:\n- Return an empty response - the user will see the gh output directly\n- Never update git config`\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { PRODUCT_NAME, PRODUCT_URL } from '@constants/product'\nimport { TOOL_NAME as TASK_TOOL_NAME } from '@tools/TaskTool/constants'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { TOOL_NAME_FOR_PROMPT as GLOB_TOOL_NAME } from '@tools/GlobTool/prompt'\nimport { TOOL_NAME_FOR_PROMPT as GREP_TOOL_NAME } from '@tools/GrepTool/prompt'\nimport { LSTool } from '@tools/lsTool/lsTool'\n\nexport const MAX_OUTPUT_LENGTH = 30000\nexport const MAX_RENDERED_LINES = 5\n\n/**\n * Banned commands for security\n *\n * Categories:\n * - Network tools: Could be used for data exfiltration or network attacks\n * - System modification: Could damage the system or escalate privileges\n * - Shell execution: Could bypass command restrictions\n * - Browsers: Could open external content\n * - Dangerous file operations: Could cause data loss\n */\nexport const BANNED_COMMANDS = [\n // Network tools - data exfiltration risk\n 'curl',\n 'curlie',\n 'wget',\n 'axel',\n 'aria2c',\n 'nc',\n 'netcat',\n 'ncat',\n 'telnet',\n 'lynx',\n 'w3m',\n 'links',\n 'httpie',\n 'xh',\n 'http-prompt',\n 'ftp',\n 'sftp',\n 'scp',\n 'rsync',\n 'socat',\n 'nmap',\n 'tcpdump',\n 'wireshark',\n 'tshark',\n\n // Browsers - could open external content\n 'chrome',\n 'chromium',\n 'firefox',\n 'safari',\n 'opera',\n 'brave',\n 'open', // macOS open command could launch apps/URLs\n\n // Privilege escalation - dangerous system access\n 'sudo',\n 'su',\n 'doas',\n 'pkexec',\n 'gksudo',\n 'kdesudo',\n\n // Shell execution - could bypass restrictions\n 'eval',\n 'source',\n 'exec',\n 'bash',\n 'sh',\n 'zsh',\n 'fish',\n 'csh',\n 'tcsh',\n 'ksh',\n 'dash',\n 'ash',\n 'xargs', // Can execute arbitrary commands\n 'parallel', // GNU parallel can execute commands\n 'xonsh',\n 'powershell',\n 'pwsh',\n 'cmd',\n\n // Dangerous file operations - data loss risk\n 'rm',\n 'rmdir',\n 'shred',\n 'srm',\n 'wipe',\n 'dd', // Can overwrite disks\n 'mkfs', // Can format disks\n 'fdisk',\n 'parted',\n 'wipefs',\n\n // System modification - could damage system\n 'chmod',\n 'chown',\n 'chgrp',\n 'chattr',\n 'setfacl',\n 'shutdown',\n 'reboot',\n 'poweroff',\n 'halt',\n 'init',\n 'systemctl',\n 'service',\n 'launchctl', // macOS service control\n\n // Process control - could affect system stability\n 'kill',\n 'killall',\n 'pkill',\n\n // Aliases and functions - could hide malicious commands\n 'alias',\n 'unalias',\n 'function',\n\n // Cron/scheduling - persistent access\n 'crontab',\n 'at',\n 'atq',\n 'atrm',\n\n // User management - privilege escalation\n 'useradd',\n 'userdel',\n 'usermod',\n 'groupadd',\n 'groupdel',\n 'groupmod',\n 'passwd',\n 'chpasswd',\n\n // Package managers - could install malware\n 'apt',\n 'apt-get',\n 'aptitude',\n 'dpkg',\n 'yum',\n 'dnf',\n 'rpm',\n 'pacman',\n 'brew',\n 'port', // MacPorts\n 'pip',\n 'pip3',\n 'npm',\n 'yarn',\n 'pnpm',\n 'gem',\n 'cargo',\n 'go', // go install could install packages\n\n // Compilers/interpreters with execution capability\n 'python',\n 'python3',\n 'python2',\n 'ruby',\n 'perl',\n 'php',\n 'node',\n 'deno',\n 'bun',\n 'lua',\n 'awk',\n 'gawk',\n 'mawk',\n 'nawk',\n 'sed', // Can execute commands with e flag\n\n // Container/VM - could escape sandbox\n 'docker',\n 'podman',\n 'kubectl',\n 'vagrant',\n 'virsh',\n 'qemu',\n 'virtualbox',\n 'vboxmanage',\n\n // System info that could aid attacks\n 'env', // Exposes environment variables including secrets\n\n // Encryption/keys - could exfiltrate secrets\n 'gpg',\n 'openssl',\n 'ssh-keygen',\n 'ssh-agent',\n 'ssh-add',\n]\n\n/**\n * Detailed dangerous pattern match information\n */\nexport interface DangerousPatternMatch {\n pattern: string\n name: string\n match: string\n severity: 'critical' | 'high' | 'medium'\n}\n\n/**\n * Mapping of dangerous patterns with metadata\n */\nconst DANGEROUS_PATTERN_METADATA: Array<{\n pattern: RegExp\n name: string\n severity: 'critical' | 'high' | 'medium'\n}> = [\n // CRITICAL: Command Injection & Execution Bypass\n {\n pattern: /\\$\\([^)]+\\)/,\n name: 'command substitution $(...)',\n severity: 'critical',\n },\n {\n pattern: /`[^`]+`/,\n name: 'backtick command substitution',\n severity: 'critical',\n },\n {\n pattern: /;\\s*(rm|chmod|chown|dd|mkfs)\\b/i,\n name: 'chained destructive command',\n severity: 'critical',\n },\n {\n pattern: /&&\\s*(rm|chmod|chown|dd|mkfs)\\b/i,\n name: 'conditional destructive command',\n severity: 'critical',\n },\n {\n pattern: /\\|\\s*bash\\b/i,\n name: 'pipe to bash',\n severity: 'critical',\n },\n {\n pattern: /\\|\\s*sh\\b/i,\n name: 'pipe to sh',\n severity: 'critical',\n },\n {\n pattern: /curl.*\\|\\s*(bash|sh)\\b/i,\n name: 'curl pipe to shell',\n severity: 'critical',\n },\n {\n pattern: /wget.*\\|\\s*(bash|sh)\\b/i,\n name: 'wget pipe to shell',\n severity: 'critical',\n },\n {\n pattern: /eval\\s+['\"$]/,\n name: 'eval with dynamic input',\n severity: 'critical',\n },\n\n // CRITICAL: Destructive Operations\n {\n pattern: /\\brm\\s+-rf\\s+\\/(?!\\S)/,\n name: 'rm -rf /',\n severity: 'critical',\n },\n {\n pattern: />\\s*\\/dev\\/sd[a-z]/i,\n name: 'write to block device',\n severity: 'critical',\n },\n {\n pattern: />\\s*\\/dev\\/nvme/i,\n name: 'write to NVMe device',\n severity: 'critical',\n },\n {\n pattern: />\\s*\\/etc\\/(passwd|shadow|sudoers)/i,\n name: 'overwrite critical system files',\n severity: 'critical',\n },\n {\n pattern: />\\s*\\/boot\\//i,\n name: 'overwrite boot files',\n severity: 'critical',\n },\n\n // CRITICAL: Fork Bomb & DoS\n {\n pattern: /:\\(\\)\\s*\\{\\s*:\\s*\\|\\s*:\\s*&\\s*\\}\\s*;?\\s*:/,\n name: 'fork bomb pattern ()',\n severity: 'critical',\n },\n {\n pattern: /\\.\\s*\\(\\)\\s*\\{\\s*\\.\\s*\\|\\s*\\.\\s*&\\s*\\}\\s*;?\\s*\\./,\n name: 'fork bomb pattern (.)',\n severity: 'critical',\n },\n\n // HIGH: Reverse Shell & Data Exfiltration\n {\n pattern: /\\$\\(\\s*<\\s*\\/dev\\/tcp/i,\n name: 'TCP socket connection',\n severity: 'high',\n },\n {\n pattern: /\\/dev\\/tcp\\//i,\n name: 'direct TCP device access',\n severity: 'high',\n },\n {\n pattern: /\\/dev\\/udp\\//i,\n name: 'direct UDP device access',\n severity: 'high',\n },\n {\n pattern: /mkfifo.*nc/i,\n name: 'named pipe with netcat',\n severity: 'high',\n },\n {\n pattern: /bash\\s+-i\\s+>&\\s*\\/dev\\/tcp/i,\n name: 'interactive bash reverse shell',\n severity: 'high',\n },\n\n // HIGH: Data Encoding & Obfuscation\n {\n pattern: /base64\\s+(-d|--decode).*\\|\\s*(bash|sh|eval)/i,\n name: 'base64 decode and execute',\n severity: 'high',\n },\n {\n pattern: /xxd\\s+-r.*\\|\\s*(bash|sh|eval)/i,\n name: 'hex decode and execute',\n severity: 'high',\n },\n\n // HIGH: System & Security Manipulation\n {\n pattern: /HISTFILE=/i,\n name: 'history file manipulation',\n severity: 'high',\n },\n {\n pattern: /unset\\s+HISTFILE/i,\n name: 'disable command history',\n severity: 'high',\n },\n {\n pattern: /history\\s+-c/i,\n name: 'clear command history',\n severity: 'high',\n },\n {\n pattern: /LD_PRELOAD=/i,\n name: 'library preload injection',\n severity: 'high',\n },\n {\n pattern: /LD_LIBRARY_PATH=/i,\n name: 'library path injection',\n severity: 'high',\n },\n {\n pattern: /DYLD_INSERT_LIBRARIES=/i,\n name: 'macOS library injection',\n severity: 'high',\n },\n\n // MEDIUM: Suspicious Command Patterns\n {\n pattern: /\\$\\{[^}]*\\}/,\n name: 'variable expansion',\n severity: 'medium',\n },\n]\n\n/**\n * Check if a command matches any dangerous pattern and return detailed information\n */\nexport function detectDangerousPatterns(\n command: string,\n): DangerousPatternMatch[] {\n const matches: DangerousPatternMatch[] = []\n\n for (const { pattern, name, severity } of DANGEROUS_PATTERN_METADATA) {\n const match = command.match(pattern)\n if (match) {\n matches.push({\n pattern: pattern.toString(),\n name,\n match: match[0],\n severity,\n })\n }\n }\n\n return matches\n}\n\n/**\n * Check if a command matches any dangerous pattern (backward compatible)\n */\nexport function matchesDangerousPattern(command: string): boolean {\n const criticalMatches = detectDangerousPatterns(command).filter(\n m => m.severity === 'critical',\n )\n return criticalMatches.length > 0\n}\n\nexport const PROMPT = `Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.\n\nBefore executing the command, please follow these steps:\n\n1. Directory Verification:\n - If the command will create new directories or files, first use the LS tool to verify the parent directory exists and is the correct location\n - For example, before running \"mkdir foo/bar\", first use LS to check that \"foo\" exists and is the intended parent directory\n\n2. Security Check:\n - For security and to limit the threat of a prompt injection attack, some commands are limited or banned. If you use a disallowed command, you will receive an error message explaining the restriction. Explain the error to the User.\n - Verify that the command is not one of the banned commands: ${BANNED_COMMANDS.join(', ')}.\n\n3. Command Execution:\n - After ensuring proper quoting, execute the command.\n - Capture the output of the command.\n\n4. Output Processing:\n - If the output exceeds ${MAX_OUTPUT_LENGTH} characters, output will be truncated before being returned to you.\n - Prepare the output for display to the user.\n\n5. Return Result:\n - Provide the processed output of the command.\n - If any errors occurred during execution, include those in the output.\n\nUsage notes:\n - The command argument is required.\n - You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). If not specified, commands will timeout after 30 minutes.\n - You can set run_in_background to true to run the command in the background. This is useful for long-running processes like dev servers, build watches, or monitoring tasks. When running in background, you will receive a shell_id that can be used with BashOutputTool and KillShellTool to monitor and manage the task.\n - VERY IMPORTANT: You MUST avoid using search commands like \\`find\\` and \\`grep\\`. Instead use ${GREP_TOOL_NAME}, ${GLOB_TOOL_NAME}, or ${TASK_TOOL_NAME} to search. You MUST avoid read tools like \\`cat\\`, \\`head\\`, \\`tail\\`, and \\`ls\\`, and use ${FileReadTool.name} and ${LSTool.name} to read files.\n - When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings).\n - IMPORTANT: All commands share the same shell session. Shell state (environment variables, virtual environments, current directory, etc.) persist between commands. For example, if you set an environment variable as part of a command, the environment variable will persist for subsequent commands.\n - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \\`cd\\`. You may use \\`cd\\` if the User explicitly requests it.\n <good-example>\n pytest /foo/bar/tests\n </good-example>\n <bad-example>\n cd /foo/bar && pytest tests\n </bad-example>\n\n# Committing changes with git\n\nWhen the user asks you to create a new git commit, follow these steps carefully:\n\n1. Start with a single message that contains exactly three tool_use blocks that do the following (it is VERY IMPORTANT that you send these tool_use blocks in a single message, otherwise it will feel slow to the user!):\n - Run a git status command to see all untracked files.\n - Run a git diff command to see both staged and unstaged changes that will be committed.\n - Run a git log command to see recent commit messages, so that you can follow this repository's commit message style.\n\n2. Use the git context at the start of this conversation to determine which files are relevant to your commit. Add relevant untracked files to the staging area. Do not commit files that were already modified at the start of this conversation, if they are not relevant to your commit.\n\n3. Analyze all staged changes (both previously staged and newly added) and draft a commit message. Wrap your analysis process in <commit_analysis> tags:\n\n<commit_analysis>\n- List the files that have been changed or added\n- Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.)\n- Brainstorm the purpose or motivation behind these changes\n- Do not use tools to explore code, beyond what is available in the git context\n- Assess the impact of these changes on the overall project\n- Check for any sensitive information that shouldn't be committed\n- Draft a concise (1-2 sentences) commit message that focuses on the \"why\" rather than the \"what\"\n- Ensure your language is clear, concise, and to the point\n- Ensure the message accurately reflects the changes and their purpose (i.e. \"add\" means a wholly new feature, \"update\" means an enhancement to an existing feature, \"fix\" means a bug fix, etc.)\n- Ensure the message is not generic (avoid words like \"Update\" or \"Fix\" without context)\n- Review the draft message to ensure it accurately reflects the changes and their purpose\n</commit_analysis>\n\n4. Create the commit with a message ending with:\n\uD83E\uDD16 Generated with ${PRODUCT_NAME} & {MODEL_NAME}\nCo-Authored-By: ${PRODUCT_NAME} <noreply@${PRODUCT_NAME}.com>\n\n- In order to ensure good formatting, ALWAYS pass the commit message via a HEREDOC, a la this example:\n<example>\ngit commit -m \"$(cat <<'EOF'\n Commit message here.\n\n \uD83E\uDD16 Generated with ${PRODUCT_NAME} & {MODEL_NAME}\n Co-Authored-By: ${PRODUCT_NAME} <noreply@${PRODUCT_NAME}.com>\n EOF\n )\"\n</example>\n\n5. If the commit fails due to pre-commit hook changes, retry the commit ONCE to include these automated changes. If it fails again, it usually means a pre-commit hook is preventing the commit. If the commit succeeds but you notice that files were modified by the pre-commit hook, you MUST amend your commit to include them.\n\n6. Finally, run git status to make sure the commit succeeded.\n\nImportant notes:\n- When possible, combine the \"git add\" and \"git commit\" commands into a single \"git commit -am\" command, to speed things up\n- However, be careful not to stage files (e.g. with \\`git add .\\`) for commits that aren't part of the change, they may have untracked files they want to keep around, but not commit.\n- NEVER update the git config\n- DO NOT push to the remote repository\n- IMPORTANT: Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported.\n- If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit\n- Ensure your commit message is meaningful and concise. It should explain the purpose of the changes, not just describe them.\n- Return an empty response - the user will see the git output directly\n\n# Creating pull requests\nUse the gh command via the Bash tool for ALL GitHub-related tasks including working with issues, pull requests, checks, and releases. If given a Github URL use the gh command to get the information needed.\n\nIMPORTANT: When the user asks you to create a pull request, follow these steps carefully:\n\n1. Understand the current state of the branch. Remember to send a single message that contains multiple tool_use blocks (it is VERY IMPORTANT that you do this in a single message, otherwise it will feel slow to the user!):\n - Run a git status command to see all untracked files.\n - Run a git diff command to see both staged and unstaged changes that will be committed.\n - Check if the current branch tracks a remote branch and is up to date with the remote, so you know if you need to push to the remote\n - Run a git log command and \\`git diff main...HEAD\\` to understand the full commit history for the current branch (from the time it diverged from the \\`main\\` branch.)\n\n2. Create new branch if needed\n\n3. Commit changes if needed\n\n4. Push to remote with -u flag if needed\n\n5. Analyze all changes that will be included in the pull request, making sure to look at all relevant commits (not just the latest commit, but all commits that will be included in the pull request!), and draft a pull request summary. Wrap your analysis process in <pr_analysis> tags:\n\n<pr_analysis>\n- List the commits since diverging from the main branch\n- Summarize the nature of the changes (eg. new feature, enhancement to an existing feature, bug fix, refactoring, test, docs, etc.)\n- Brainstorm the purpose or motivation behind these changes\n- Assess the impact of these changes on the overall project\n- Do not use tools to explore code, beyond what is available in the git context\n- Check for any sensitive information that shouldn't be committed\n- Draft a concise (1-2 bullet points) pull request summary that focuses on the \"why\" rather than the \"what\"\n- Ensure the summary accurately reflects all changes since diverging from the main branch\n- Ensure your language is clear, concise, and to the point\n- Ensure the summary accurately reflects the changes and their purpose (ie. \"add\" means a wholly new feature, \"update\" means an enhancement to an existing feature, \"fix\" means a bug fix, etc.)\n- Ensure the summary is not generic (avoid words like \"Update\" or \"Fix\" without context)\n- Review the draft summary to ensure it accurately reflects the changes and their purpose\n</pr_analysis>\n\n6. Create PR using gh pr create with the format below. Use a HEREDOC to pass the body to ensure correct formatting.\n<example>\ngh pr create --title \"the pr title\" --body \"$(cat <<'EOF'\n## Summary\n<1-3 bullet points>\n\n## Test plan\n[Checklist of TODOs for testing the pull request...]\n\n\uD83E\uDD16 Generated with [${PRODUCT_NAME}](${PRODUCT_URL}) & {MODEL_NAME}\nEOF\n)\"\n</example>\n\nImportant:\n- Return an empty response - the user will see the gh output directly\n- Never update git config`\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,12 +1,15 @@
1
1
  import { NotebookEditTool } from "../NotebookEditTool/NotebookEditTool.js";
2
- const DESCRIPTION = `This is a tool for editing files. For moving or renaming files, you should generally use the Bash tool with the 'mv' command instead. For larger edits, use the Write tool to overwrite files. For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} instead.
2
+ import { FileWriteTool } from "../FileWriteTool/FileWriteTool.js";
3
+ import { FileReadTool } from "../FileReadTool/FileReadTool.js";
4
+ import { LSTool } from "../lsTool/lsTool.js";
5
+ const DESCRIPTION = `This is a tool for editing files. For moving or renaming files, you should generally use the Bash tool with the 'mv' command instead. For larger edits, use the ${FileWriteTool.name} tool to overwrite files. For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} instead.
3
6
 
4
7
  Before using this tool:
5
8
 
6
- 1. Use the View tool to understand the file's contents and context
9
+ 1. Use the ${FileReadTool.name} tool to understand the file's contents and context
7
10
 
8
11
  2. Verify the directory path is correct (only applicable when creating new files):
9
- - Use the LS tool to verify the parent directory exists and is the correct location
12
+ - Use the ${LSTool.name} tool to verify the parent directory exists and is the correct location
10
13
 
11
14
  To make a file edit, provide the following:
12
15
  1. file_path: The absolute path to the file to modify (must be absolute, not relative)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/FileEditTool/prompt.ts"],
4
- "sourcesContent": ["import { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\n\nexport const DESCRIPTION = `This is a tool for editing files. For moving or renaming files, you should generally use the Bash tool with the 'mv' command instead. For larger edits, use the Write tool to overwrite files. For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} instead.\n\nBefore using this tool:\n\n1. Use the View tool to understand the file's contents and context\n\n2. Verify the directory path is correct (only applicable when creating new files):\n - Use the LS tool to verify the parent directory exists and is the correct location\n\nTo make a file edit, provide the following:\n1. file_path: The absolute path to the file to modify (must be absolute, not relative)\n2. old_string: The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)\n3. new_string: The edited text to replace the old_string\n\nThe tool will replace ONE occurrence of old_string with new_string in the specified file.\n\nCRITICAL REQUIREMENTS FOR USING THIS TOOL:\n\n1. UNIQUENESS: The old_string MUST uniquely identify the specific instance you want to change. This means:\n - Include AT LEAST 3-5 lines of context BEFORE the change point\n - Include AT LEAST 3-5 lines of context AFTER the change point\n - Include all whitespace, indentation, and surrounding code exactly as it appears in the file\n\n2. SINGLE INSTANCE: This tool can only change ONE instance at a time. If you need to change multiple instances:\n - Make separate calls to this tool for each instance\n - Each call must uniquely identify its specific instance using extensive context\n\n3. VERIFICATION: Before using this tool:\n - Check how many instances of the target text exist in the file\n - If multiple instances exist, gather enough context to uniquely identify each one\n - Plan separate tool calls for each instance\n\nWARNING: If you do not follow these requirements:\n - The tool will fail if old_string matches multiple locations\n - The tool will fail if old_string doesn't match exactly (including whitespace)\n - You may change the wrong instance if you don't include enough context\n\nWhen making edits:\n - Ensure the edit results in idiomatic, correct code\n - Do not leave the code in a broken state\n - Always use absolute file paths (starting with /)\n\nIf you want to create a new file, use:\n - A new file path, including dir name if needed\n - An empty old_string\n - The new file's contents as new_string\n\nRemember: when making multiple file edits in a row to the same file, you should prefer to send all edits in a single message with multiple calls to this tool, rather than multiple messages with a single call each.\n`\n"],
5
- "mappings": "AAAA,SAAS,wBAAwB;AAE1B,MAAM,cAAc,gPAAgP,iBAAiB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["import { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\nimport { FileWriteTool } from '@tools/FileWriteTool/FileWriteTool'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { LSTool } from '@tools/lsTool/lsTool'\n\nexport const DESCRIPTION = `This is a tool for editing files. For moving or renaming files, you should generally use the Bash tool with the 'mv' command instead. For larger edits, use the ${FileWriteTool.name} tool to overwrite files. For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} instead.\n\nBefore using this tool:\n\n1. Use the ${FileReadTool.name} tool to understand the file's contents and context\n\n2. Verify the directory path is correct (only applicable when creating new files):\n - Use the ${LSTool.name} tool to verify the parent directory exists and is the correct location\n\nTo make a file edit, provide the following:\n1. file_path: The absolute path to the file to modify (must be absolute, not relative)\n2. old_string: The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)\n3. new_string: The edited text to replace the old_string\n\nThe tool will replace ONE occurrence of old_string with new_string in the specified file.\n\nCRITICAL REQUIREMENTS FOR USING THIS TOOL:\n\n1. UNIQUENESS: The old_string MUST uniquely identify the specific instance you want to change. This means:\n - Include AT LEAST 3-5 lines of context BEFORE the change point\n - Include AT LEAST 3-5 lines of context AFTER the change point\n - Include all whitespace, indentation, and surrounding code exactly as it appears in the file\n\n2. SINGLE INSTANCE: This tool can only change ONE instance at a time. If you need to change multiple instances:\n - Make separate calls to this tool for each instance\n - Each call must uniquely identify its specific instance using extensive context\n\n3. VERIFICATION: Before using this tool:\n - Check how many instances of the target text exist in the file\n - If multiple instances exist, gather enough context to uniquely identify each one\n - Plan separate tool calls for each instance\n\nWARNING: If you do not follow these requirements:\n - The tool will fail if old_string matches multiple locations\n - The tool will fail if old_string doesn't match exactly (including whitespace)\n - You may change the wrong instance if you don't include enough context\n\nWhen making edits:\n - Ensure the edit results in idiomatic, correct code\n - Do not leave the code in a broken state\n - Always use absolute file paths (starting with /)\n\nIf you want to create a new file, use:\n - A new file path, including dir name if needed\n - An empty old_string\n - The new file's contents as new_string\n\nRemember: when making multiple file edits in a row to the same file, you should prefer to send all edits in a single message with multiple calls to this tool, rather than multiple messages with a single call each.\n`\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AAEhB,MAAM,cAAc,mKAAmK,cAAc,IAAI,2EAA2E,iBAAiB,IAAI;AAAA;AAAA;AAAA;AAAA,aAInS,aAAa,IAAI;AAAA;AAAA;AAAA,eAGf,OAAO,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -1,11 +1,13 @@
1
+ import { FileReadTool } from "../FileReadTool/FileReadTool.js";
2
+ import { LSTool } from "../lsTool/lsTool.js";
1
3
  const PROMPT = `Write a file to the local filesystem. Overwrites the existing file if there is one.
2
4
 
3
5
  Before using this tool:
4
6
 
5
- 1. Use the ReadFile tool to understand the file's contents and context
7
+ 1. Use the ${FileReadTool.name} tool to understand the file's contents and context
6
8
 
7
9
  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`;
10
+ - Use the ${LSTool.name} tool to verify the parent directory exists and is the correct location`;
9
11
  const DESCRIPTION = "Write a file to the local filesystem.";
10
12
  export {
11
13
  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 = `Write a file to the local filesystem. Overwrites the existing file if there is one.\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,aAIT,aAAa,IAAI;AAAA;AAAA;AAAA,eAGf,OAAO,IAAI;AAEnB,MAAM,cAAc;",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,11 @@
1
1
  import { NotebookEditTool } from "../NotebookEditTool/NotebookEditTool.js";
2
- const DESCRIPTION = `This is a tool for making multiple edits to a single file in one operation. It is built on top of the Edit tool and allows you to perform multiple find-and-replace operations efficiently. Prefer this tool over the Edit tool when you need to make multiple edits to the same file.
2
+ import { FileReadTool } from "../FileReadTool/FileReadTool.js";
3
+ import { FileEditTool } from "../FileEditTool/FileEditTool.js";
4
+ const DESCRIPTION = `This is a tool for making multiple edits to a single file in one operation. It is built on top of the ${FileEditTool.name} tool and allows you to perform multiple find-and-replace operations efficiently. Prefer this tool over the ${FileEditTool.name} tool when you need to make multiple edits to the same file.
3
5
 
4
6
  Before using this tool:
5
7
 
6
- 1. Use the Read tool to understand the file's contents and context
8
+ 1. Use the ${FileReadTool.name} tool to understand the file's contents and context
7
9
  2. Verify the directory path is correct
8
10
 
9
11
  To make multiple file edits, provide the following:
@@ -21,7 +23,7 @@ IMPORTANT:
21
23
  - For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} instead
22
24
 
23
25
  CRITICAL REQUIREMENTS:
24
- 1. All edits follow the same requirements as the single Edit tool
26
+ 1. All edits follow the same requirements as the single ${FileEditTool.name} tool
25
27
  2. The edits are atomic - either all succeed or none are applied
26
28
  3. Plan your edits carefully to avoid conflicts between sequential operations
27
29
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/tools/MultiEditTool/prompt.ts"],
4
- "sourcesContent": ["import { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\n\nexport const DESCRIPTION = `This is a tool for making multiple edits to a single file in one operation. It is built on top of the Edit tool and allows you to perform multiple find-and-replace operations efficiently. Prefer this tool over the Edit tool when you need to make multiple edits to the same file.\n\nBefore using this tool:\n\n1. Use the Read tool to understand the file's contents and context\n2. Verify the directory path is correct\n\nTo make multiple file edits, provide the following:\n1. file_path: The absolute path to the file to modify (must be absolute, not relative)\n2. edits: An array of edit operations to perform, where each edit contains:\n - old_string: The text to replace (must match the file contents exactly, including all whitespace and indentation)\n - new_string: The edited text to replace the old_string\n - replace_all: Replace all occurences of old_string. This parameter is optional and defaults to false.\n\nIMPORTANT:\n- All edits are applied in sequence, in the order they are provided\n- Each edit operates on the result of the previous edit\n- All edits must be valid for the operation to succeed - if any edit fails, none will be applied\n- This tool is ideal when you need to make several changes to different parts of the same file\n- For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} instead\n\nCRITICAL REQUIREMENTS:\n1. All edits follow the same requirements as the single Edit tool\n2. The edits are atomic - either all succeed or none are applied\n3. Plan your edits carefully to avoid conflicts between sequential operations\n\nWARNING:\n- The tool will fail if edits.old_string doesn't match the file contents exactly (including whitespace)\n- The tool will fail if edits.old_string and edits.new_string are the same\n- Since edits are applied in sequence, ensure that earlier edits don't affect the text that later edits are trying to find\n\nWhen making edits:\n- Ensure all edits result in idiomatic, correct code\n- Do not leave the code in a broken state\n- Always use absolute file paths (starting with /)\n- Use replace_all for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.\n\nIf you want to create a new file, use:\n- A new file path, including dir name if needed\n- First edit: empty old_string and the new file's contents as new_string\n- Subsequent edits: normal edit operations on the created content`\n\nexport const PROMPT = DESCRIPTION\n"],
5
- "mappings": "AAAA,SAAS,wBAAwB;AAE1B,MAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kDAmBuB,iBAAiB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBhE,MAAM,SAAS;",
4
+ "sourcesContent": ["import { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\nimport { FileReadTool } from '@tools/FileReadTool/FileReadTool'\nimport { FileEditTool } from '@tools/FileEditTool/FileEditTool'\n\nexport const DESCRIPTION = `This is a tool for making multiple edits to a single file in one operation. It is built on top of the ${FileEditTool.name} tool and allows you to perform multiple find-and-replace operations efficiently. Prefer this tool over the ${FileEditTool.name} tool when you need to make multiple edits to the same file.\n\nBefore using this tool:\n\n1. Use the ${FileReadTool.name} tool to understand the file's contents and context\n2. Verify the directory path is correct\n\nTo make multiple file edits, provide the following:\n1. file_path: The absolute path to the file to modify (must be absolute, not relative)\n2. edits: An array of edit operations to perform, where each edit contains:\n - old_string: The text to replace (must match the file contents exactly, including all whitespace and indentation)\n - new_string: The edited text to replace the old_string\n - replace_all: Replace all occurences of old_string. This parameter is optional and defaults to false.\n\nIMPORTANT:\n- All edits are applied in sequence, in the order they are provided\n- Each edit operates on the result of the previous edit\n- All edits must be valid for the operation to succeed - if any edit fails, none will be applied\n- This tool is ideal when you need to make several changes to different parts of the same file\n- For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} instead\n\nCRITICAL REQUIREMENTS:\n1. All edits follow the same requirements as the single ${FileEditTool.name} tool\n2. The edits are atomic - either all succeed or none are applied\n3. Plan your edits carefully to avoid conflicts between sequential operations\n\nWARNING:\n- The tool will fail if edits.old_string doesn't match the file contents exactly (including whitespace)\n- The tool will fail if edits.old_string and edits.new_string are the same\n- Since edits are applied in sequence, ensure that earlier edits don't affect the text that later edits are trying to find\n\nWhen making edits:\n- Ensure all edits result in idiomatic, correct code\n- Do not leave the code in a broken state\n- Always use absolute file paths (starting with /)\n- Use replace_all for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.\n\nIf you want to create a new file, use:\n- A new file path, including dir name if needed\n- First edit: empty old_string and the new file's contents as new_string\n- Subsequent edits: normal edit operations on the created content`\n\nexport const PROMPT = DESCRIPTION\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB;AACjC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAEtB,MAAM,cAAc,yGAAyG,aAAa,IAAI,+GAA+G,aAAa,IAAI;AAAA;AAAA;AAAA;AAAA,aAIxQ,aAAa,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kDAeoB,iBAAiB,IAAI;AAAA;AAAA;AAAA,0DAGb,aAAa,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBpE,MAAM,SAAS;",
6
6
  "names": []
7
7
  }
@@ -16,6 +16,7 @@ import { DESCRIPTION, PROMPT } from "./prompt.js";
16
16
  import { hasWritePermission } from "../../utils/permissions/filesystem.js";
17
17
  import { emitReminderEvent } from "../../services/systemReminder.js";
18
18
  import { recordFileEdit } from "../../services/fileFreshness.js";
19
+ import { FileEditTool } from "../FileEditTool/FileEditTool.js";
19
20
  const inputSchema = z.strictObject({
20
21
  notebook_path: z.string().describe(
21
22
  "The absolute path to the Jupyter notebook file to edit (must be absolute, not relative)"
@@ -98,7 +99,7 @@ const NotebookEditTool = {
98
99
  if (extname(fullPath) !== ".ipynb") {
99
100
  return {
100
101
  result: false,
101
- message: "File must be a Jupyter notebook (.ipynb file). For editing other file types, use the FileEdit tool."
102
+ message: `File must be a Jupyter notebook (.ipynb file). For editing other file types, use the ${FileEditTool.name} tool.`
102
103
  };
103
104
  }
104
105
  if (cell_number < 0) {