@within-7/minto 0.1.5 → 0.1.7
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.
- package/dist/commands/agents/AgentsCommand.js +2342 -0
- package/dist/commands/agents/AgentsCommand.js.map +7 -0
- package/dist/commands/agents/constants.js +58 -0
- package/dist/commands/agents/constants.js.map +7 -0
- package/dist/commands/agents/index.js +37 -0
- package/dist/commands/agents/index.js.map +7 -0
- package/dist/commands/agents/types.js +10 -0
- package/dist/commands/agents/types.js.map +7 -0
- package/dist/commands/agents/utils/fileOperations.js +185 -0
- package/dist/commands/agents/utils/fileOperations.js.map +7 -0
- package/dist/commands/agents/utils/index.js +21 -0
- package/dist/commands/agents/utils/index.js.map +7 -0
- package/dist/commands/bug.js +2 -2
- package/dist/commands/bug.js.map +2 -2
- package/dist/commands/compact.js +5 -5
- package/dist/commands/compact.js.map +2 -2
- package/dist/commands/ctx_viz.js +55 -22
- package/dist/commands/ctx_viz.js.map +2 -2
- package/dist/commands/mcp-interactive.js +11 -11
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/model.js +94 -32
- package/dist/commands/model.js.map +3 -3
- package/dist/commands/plugin/AddMarketplaceForm.js +49 -21
- package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
- package/dist/commands/plugin/ConfirmDialog.js +38 -26
- package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
- package/dist/commands/plugin/InstalledPluginsByMarketplace.js +24 -8
- package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
- package/dist/commands/plugin/InstalledPluginsManager.js +3 -1
- package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
- package/dist/commands/plugin/MainMenu.js +16 -7
- package/dist/commands/plugin/MainMenu.js.map +2 -2
- package/dist/commands/plugin/MarketplaceManager.js +84 -39
- package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
- package/dist/commands/plugin/MarketplaceSelector.js +7 -3
- package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
- package/dist/commands/plugin/PlaceholderScreen.js +16 -2
- package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
- package/dist/commands/plugin/PluginBrowser.js +4 -2
- package/dist/commands/plugin/PluginBrowser.js.map +2 -2
- package/dist/commands/plugin/PluginDetailsInstall.js +12 -6
- package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
- package/dist/commands/plugin/PluginDetailsManage.js +14 -5
- package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
- package/dist/commands/plugin/example-usage.js.map +2 -2
- package/dist/commands/plugin/utils.js.map +2 -2
- package/dist/commands/plugin.js +226 -46
- package/dist/commands/plugin.js.map +2 -2
- package/dist/commands/refreshCommands.js +6 -3
- package/dist/commands/refreshCommands.js.map +2 -2
- package/dist/commands/resume.js +2 -1
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/setup.js +19 -5
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/terminalSetup.js +2 -2
- package/dist/commands/terminalSetup.js.map +1 -1
- package/dist/commands.js +14 -30
- package/dist/commands.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/QuestionView.js +10 -1
- package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
- package/dist/components/BackgroundTasksPanel.js +5 -1
- package/dist/components/BackgroundTasksPanel.js.map +2 -2
- package/dist/components/Config.js +17 -4
- package/dist/components/Config.js.map +2 -2
- package/dist/components/ConsoleOAuthFlow.js.map +2 -2
- package/dist/components/CustomSelect/select-option.js +4 -1
- package/dist/components/CustomSelect/select-option.js.map +2 -2
- package/dist/components/Help.js +6 -8
- package/dist/components/Help.js.map +2 -2
- package/dist/components/Logo.js +1 -1
- package/dist/components/Logo.js.map +2 -2
- package/dist/components/ModelListManager.js.map +2 -2
- package/dist/components/ModelSelector/ModelSelector.js +2030 -0
- package/dist/components/ModelSelector/ModelSelector.js.map +7 -0
- package/dist/components/ModelSelector/ScreenContainer.js +27 -0
- package/dist/components/ModelSelector/ScreenContainer.js.map +7 -0
- package/dist/components/ModelSelector/constants.js +37 -0
- package/dist/components/ModelSelector/constants.js.map +7 -0
- package/dist/components/ModelSelector/hooks/index.js +5 -0
- package/dist/components/ModelSelector/hooks/index.js.map +7 -0
- package/dist/components/ModelSelector/hooks/useEscapeNavigation.js +21 -0
- package/dist/components/ModelSelector/hooks/useEscapeNavigation.js.map +7 -0
- package/dist/components/ModelSelector/index.js +17 -0
- package/dist/components/ModelSelector/index.js.map +7 -0
- package/dist/components/ModelSelector/types.js +1 -0
- package/dist/components/ModelSelector/types.js.map +7 -0
- package/dist/components/PressEnterToContinue.js +1 -1
- package/dist/components/PressEnterToContinue.js.map +2 -2
- package/dist/components/ProjectOnboarding.js +1 -1
- package/dist/components/ProjectOnboarding.js.map +2 -2
- package/dist/components/PromptInput.js +88 -37
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/QuitSummary.js +17 -10
- package/dist/components/QuitSummary.js.map +2 -2
- package/dist/components/SentryErrorBoundary.js.map +2 -2
- package/dist/components/StreamingBashOutput.js.map +2 -2
- package/dist/components/StructuredDiff.js.map +2 -2
- package/dist/components/SubagentProgress.js.map +2 -2
- package/dist/components/TaskCard.js.map +2 -2
- package/dist/components/TextInput.js.map +1 -1
- package/dist/components/TodoItem.js.map +1 -1
- package/dist/components/binary-feedback/BinaryFeedbackOption.js +1 -3
- package/dist/components/binary-feedback/BinaryFeedbackOption.js.map +2 -2
- package/dist/components/messages/AssistantLocalCommandOutputMessage.js.map +1 -1
- package/dist/components/messages/AssistantToolUseMessage.js +3 -1
- package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
- package/dist/components/messages/TaskProgressMessage.js.map +2 -2
- package/dist/components/messages/TaskToolMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/utils.js.map +2 -2
- package/dist/components/permissions/FileEditPermissionRequest/FileEditToolDiff.js.map +2 -2
- package/dist/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.js.map +2 -2
- package/dist/components/permissions/hooks.js.map +2 -2
- package/dist/constants/modelCapabilities.js +1 -1
- package/dist/constants/modelCapabilities.js.map +2 -2
- package/dist/constants/prompts.js.map +1 -1
- package/dist/constants/timing.js +34 -0
- package/dist/constants/timing.js.map +7 -0
- package/dist/entrypoints/cli.js +128 -33
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/entrypoints/mcp.js +13 -18
- package/dist/entrypoints/mcp.js.map +2 -2
- package/dist/hooks/useCanUseTool.js.map +2 -2
- package/dist/hooks/useCancelRequest.js.map +1 -1
- package/dist/hooks/useHistorySearch.js.map +2 -2
- package/dist/hooks/useLogStartupTime.js.map +2 -2
- package/dist/hooks/usePermissionRequestLogging.js.map +2 -2
- package/dist/hooks/useTextInput.js.map +1 -1
- package/dist/hooks/useUnifiedCompletion.js +493 -394
- package/dist/hooks/useUnifiedCompletion.js.map +2 -2
- package/dist/index.js.map +2 -2
- package/dist/permissions.js +4 -7
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +6 -1
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +72 -36
- package/dist/screens/REPL.js.map +2 -2
- package/dist/screens/ResumeConversation.js +2 -1
- package/dist/screens/ResumeConversation.js.map +2 -2
- package/dist/services/adapters/base.js.map +2 -2
- package/dist/services/adapters/chatCompletions.js.map +2 -2
- package/dist/services/adapters/responsesAPI.js +3 -1
- package/dist/services/adapters/responsesAPI.js.map +2 -2
- package/dist/services/claude.js +327 -328
- package/dist/services/claude.js.map +2 -2
- package/dist/services/customCommands.js +6 -1
- package/dist/services/customCommands.js.map +2 -2
- package/dist/services/fileFreshness.js.map +2 -2
- package/dist/services/gpt5ConnectionTest.js +20 -7
- package/dist/services/gpt5ConnectionTest.js.map +2 -2
- package/dist/services/hookExecutor.js +6 -12
- package/dist/services/hookExecutor.js.map +2 -2
- package/dist/services/mcpClient.js +29 -2
- package/dist/services/mcpClient.js.map +2 -2
- package/dist/services/mentionProcessor.js +23 -10
- package/dist/services/mentionProcessor.js.map +2 -2
- package/dist/services/modelAdapterFactory.js.map +2 -2
- package/dist/services/oauth.js.map +2 -2
- package/dist/services/openai.js +109 -72
- package/dist/services/openai.js.map +3 -3
- package/dist/services/responseStateManager.js.map +2 -2
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/tools/ArchitectTool/ArchitectTool.js +10 -9
- package/dist/tools/ArchitectTool/ArchitectTool.js.map +2 -2
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +14 -8
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
- package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +8 -1
- package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
- package/dist/tools/BashOutputTool/BashOutputTool.js.map +2 -2
- package/dist/tools/BashTool/BashTool.js.map +2 -2
- package/dist/tools/FileReadTool/FileReadTool.js +23 -4
- package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
- package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
- package/dist/tools/GlobTool/GlobTool.js +11 -2
- package/dist/tools/GlobTool/GlobTool.js.map +2 -2
- package/dist/tools/GrepTool/GrepTool.js +7 -5
- package/dist/tools/GrepTool/GrepTool.js.map +2 -2
- package/dist/tools/MCPTool/MCPTool.js +11 -12
- package/dist/tools/MCPTool/MCPTool.js.map +2 -2
- package/dist/tools/MultiEditTool/MultiEditTool.js +4 -1
- package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
- package/dist/tools/NotebookReadTool/NotebookReadTool.js +11 -5
- package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
- package/dist/tools/SkillTool/SkillTool.js +18 -6
- package/dist/tools/SkillTool/SkillTool.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +37 -51
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/tools/TaskTool/prompt.js.map +2 -2
- package/dist/tools/ThinkTool/ThinkTool.js +6 -1
- package/dist/tools/ThinkTool/ThinkTool.js.map +2 -2
- package/dist/tools/TodoWriteTool/TodoWriteTool.js +29 -5
- package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
- package/dist/tools/URLFetcherTool/URLFetcherTool.js +5 -2
- package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
- package/dist/tools/URLFetcherTool/cache.js +6 -3
- package/dist/tools/URLFetcherTool/cache.js.map +2 -2
- package/dist/tools/URLFetcherTool/htmlToMarkdown.js +3 -1
- package/dist/tools/URLFetcherTool/htmlToMarkdown.js.map +2 -2
- package/dist/tools/WebSearchTool/WebSearchTool.js +6 -1
- package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/searchProviders.js +15 -6
- package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
- package/dist/tools.js +4 -1
- package/dist/tools.js.map +2 -2
- package/dist/types/core.js +1 -0
- package/dist/types/core.js.map +7 -0
- package/dist/types/hooks.js +1 -4
- package/dist/types/hooks.js.map +2 -2
- package/dist/types/marketplace.js +8 -2
- package/dist/types/marketplace.js.map +2 -2
- package/dist/types/plugin.js +9 -6
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/BackgroundShellManager.js +76 -10
- package/dist/utils/BackgroundShellManager.js.map +2 -2
- package/dist/utils/PersistentShell.js +7 -2
- package/dist/utils/PersistentShell.js.map +2 -2
- package/dist/utils/advancedFuzzyMatcher.js +4 -1
- package/dist/utils/advancedFuzzyMatcher.js.map +2 -2
- package/dist/utils/agentLoader.js +69 -35
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentStorage.js.map +2 -2
- package/dist/utils/async.js +163 -0
- package/dist/utils/async.js.map +7 -0
- package/dist/utils/autoUpdater.js +8 -2
- package/dist/utils/autoUpdater.js.map +2 -2
- package/dist/utils/commands.js +23 -11
- package/dist/utils/commands.js.map +2 -2
- package/dist/utils/commonUnixCommands.js +3 -1
- package/dist/utils/commonUnixCommands.js.map +2 -2
- package/dist/utils/compressionMode.js.map +2 -2
- package/dist/utils/config.js +30 -14
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/debugLogger.js.map +2 -2
- package/dist/utils/env.js.map +2 -2
- package/dist/utils/envConfig.js +82 -0
- package/dist/utils/envConfig.js.map +7 -0
- package/dist/utils/errorHandling.js +89 -0
- package/dist/utils/errorHandling.js.map +7 -0
- package/dist/utils/expertChatStorage.js.map +2 -2
- package/dist/utils/fuzzyMatcher.js +13 -7
- package/dist/utils/fuzzyMatcher.js.map +2 -2
- package/dist/utils/hookManager.js +14 -4
- package/dist/utils/hookManager.js.map +2 -2
- package/dist/utils/log.js.map +2 -2
- package/dist/utils/marketplaceManager.js +44 -9
- package/dist/utils/marketplaceManager.js.map +2 -2
- package/dist/utils/messageContextManager.js.map +1 -1
- package/dist/utils/messages.js +6 -3
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/model.js +3 -1
- package/dist/utils/model.js.map +2 -2
- package/dist/utils/pluginInstaller.js +3 -15
- package/dist/utils/pluginInstaller.js.map +2 -2
- package/dist/utils/pluginLoader.js +41 -13
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/pluginRegistry.js.map +2 -2
- package/dist/utils/pluginValidator.js +71 -49
- package/dist/utils/pluginValidator.js.map +2 -2
- package/dist/utils/ptyCompat.js.map +2 -2
- package/dist/utils/roundConverter.js.map +2 -2
- package/dist/utils/secureFile.js +43 -14
- package/dist/utils/secureFile.js.map +2 -2
- package/dist/utils/sessionState.js.map +2 -2
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/teamConfig.js +7 -4
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/theme.js.map +2 -2
- package/dist/utils/thinking.js.map +2 -2
- package/dist/utils/unaryLogging.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/FileWriteTool/FileWriteTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Hunk } from 'diff'\nimport { existsSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { EOL } from 'os'\nimport { dirname, extname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport type { Tool } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n detectRepoLineEndings,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { PROMPT } from './prompt'\nimport { hasWritePermission } from '@utils/permissions/filesystem'\nimport { getPatch } from '@utils/diff'\nimport { PROJECT_FILE } from '@constants/product'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_LINES_TO_RENDER_FOR_ASSISTANT = 16000\nconst TRUNCATED_MESSAGE =\n '<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with Grep in order to find the line numbers of what you are looking for.</NOTE>'\n\nconst inputSchema = z.strictObject({\n file_path: z\n .string()\n .describe(\n 'The absolute path to the file to write (must be absolute, not relative)',\n ),\n content: z.string().describe('The content to write to the file'),\n})\n\nexport const FileWriteTool = {\n name: 'Replace',\n async description() {\n return 'Write a file to the local filesystem.'\n },\n userFacingName: () => 'Write',\n async prompt() {\n return PROMPT\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // FileWriteTool modifies state/files, not safe for concurrent execution\n },\n needsPermissions({ file_path }) {\n return !hasWritePermission(file_path)\n },\n renderToolUseMessage(input, { verbose }) {\n return `file_path: ${verbose ? input.file_path : relative(getCwd(), input.file_path)}`\n },\n renderToolUseRejectedMessage({ file_path, content }: any = {}
|
|
5
|
-
"mappings": "AACA,SAAS,YAAY,WAAW,cAAc,gBAAgB;AAC9D,SAAS,KAAK,YAAY;AAC1B,SAAS,WAAW;AACpB,SAAS,SAAS,SAAS,YAAY,UAAU,SAAS,WAAW;AACrE,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,kCAAkC;AAC3C,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,sCAAsC;AAE/C,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,0BAA0B;AACnC,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAClC,SAAS,sBAAsB;AAE/B,MAAM,sBAAsB;AAC5B,MAAM,oCAAoC;AAC1C,MAAM,oBACJ;AAEF,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,WAAW,EACR,OAAO,EACP;AAAA,IACC;AAAA,EACF;AAAA,EACF,SAAS,EAAE,OAAO,EAAE,SAAS,kCAAkC;AACjE,CAAC;AAEM,MAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,MAAM;AAAA,EACtB,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA;AAAA,EACA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB,EAAE,UAAU,GAAG;AAC9B,WAAO,CAAC,mBAAmB,SAAS;AAAA,EACtC;AAAA,EACA,qBAAqB,OAAO,EAAE,QAAQ,GAAG;AACvC,WAAO,cAAc,UAAU,MAAM,YAAY,SAAS,OAAO,GAAG,MAAM,SAAS,CAAC;AAAA,EACtF;AAAA,EACA,
|
|
4
|
+
"sourcesContent": ["import { Hunk } from 'diff'\nimport { existsSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { EOL } from 'os'\nimport { dirname, extname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport type { Tool } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n detectRepoLineEndings,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { PROMPT } from './prompt'\nimport { hasWritePermission } from '@utils/permissions/filesystem'\nimport { getPatch } from '@utils/diff'\nimport { PROJECT_FILE } from '@constants/product'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_LINES_TO_RENDER_FOR_ASSISTANT = 16000\nconst TRUNCATED_MESSAGE =\n '<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with Grep in order to find the line numbers of what you are looking for.</NOTE>'\n\nconst inputSchema = z.strictObject({\n file_path: z\n .string()\n .describe(\n 'The absolute path to the file to write (must be absolute, not relative)',\n ),\n content: z.string().describe('The content to write to the file'),\n})\n\nexport const FileWriteTool = {\n name: 'Replace',\n async description() {\n return 'Write a file to the local filesystem.'\n },\n userFacingName: () => 'Write',\n async prompt() {\n return PROMPT\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // FileWriteTool modifies state/files, not safe for concurrent execution\n },\n needsPermissions({ file_path }) {\n return !hasWritePermission(file_path)\n },\n renderToolUseMessage(input, { verbose }) {\n return `file_path: ${verbose ? input.file_path : relative(getCwd(), input.file_path)}`\n },\n renderToolUseRejectedMessage(\n { file_path, content }: any = {},\n { columns, verbose }: any = {},\n ) {\n try {\n if (!file_path) {\n return <FallbackToolUseRejectedMessage />\n }\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const oldFileExists = existsSync(fullFilePath)\n const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'\n const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null\n const type = oldContent ? 'update' : 'create'\n const patch = getPatch({\n filePath: file_path,\n fileContents: oldContent ?? '',\n oldStr: oldContent ?? '',\n newStr: content,\n })\n\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF{' '}\n <Text color={getTheme().error}>\n User rejected {type === 'update' ? 'update' : 'write'} to{' '}\n </Text>\n <Text bold>\n {verbose ? file_path : relative(getCwd(), file_path)}\n </Text>\n </Text>\n {intersperse(\n patch.map(_ => (\n <Box flexDirection=\"column\" paddingLeft={5} key={_.newStart}>\n <StructuredDiff patch={_} dim={true} width={columns - 12} />\n </Box>\n )),\n i => (\n <Box paddingLeft={5} key={`ellipsis-${i}`}>\n <Text color={getTheme().secondaryText}>...</Text>\n </Box>\n ),\n )}\n </Box>\n )\n } catch (e) {\n // Handle the case where while we were showing the diff, the user manually made the change.\n // TODO: Find a way to show the diff in this case\n logError(e)\n return (\n <Box flexDirection=\"column\">\n <Text>{' '}\u23BF (No changes)</Text>\n </Box>\n )\n }\n },\n renderToolResultMessage({ filePath, content, structuredPatch, type }) {\n const verbose = false // Default to false since verbose is no longer passed\n switch (type) {\n case 'create': {\n const contentWithFallback = content || '(No content)'\n const numLines = content.split(EOL).length\n\n return (\n <Box flexDirection=\"column\">\n <Text>\n {' '}\u23BF Wrote {numLines} lines to{' '}\n <Text bold>\n {verbose ? filePath : relative(getCwd(), filePath)}\n </Text>\n </Text>\n <Box flexDirection=\"column\" paddingLeft={5}>\n <HighlightedCode\n code={\n verbose\n ? contentWithFallback\n : contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')\n }\n language={extname(filePath).slice(1)}\n />\n {!verbose && numLines > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{numLines - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n </Box>\n )\n }\n case 'update':\n return (\n <FileEditToolUpdatedMessage\n filePath={filePath}\n structuredPatch={structuredPatch}\n verbose={verbose}\n />\n )\n }\n },\n async validateInput({ file_path }, { readFileTimestamps }) {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n if (!existsSync(fullFilePath)) {\n return { result: true }\n }\n\n const readTimestamp = readFileTimestamps[fullFilePath]\n if (!readTimestamp) {\n return {\n result: false,\n message:\n 'File has not been read yet. Read it first before writing to it.',\n }\n }\n\n // Check if file exists and get its last modified time\n const stats = statSync(fullFilePath)\n const lastWriteTime = stats.mtimeMs\n if (lastWriteTime > readTimestamp) {\n return {\n result: false,\n message:\n 'File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.',\n }\n }\n\n return { result: true }\n },\n async *call({ file_path, content }, { readFileTimestamps }) {\n const fullFilePath = isAbsolute(file_path)\n ? file_path\n : resolve(getCwd(), file_path)\n const dir = dirname(fullFilePath)\n const oldFileExists = existsSync(fullFilePath)\n const enc = oldFileExists ? detectFileEncoding(fullFilePath) : 'utf-8'\n const oldContent = oldFileExists ? readFileSync(fullFilePath, enc) : null\n\n const endings = oldFileExists\n ? detectLineEndings(fullFilePath)\n : await detectRepoLineEndings(getCwd())\n\n mkdirSync(dir, { recursive: true })\n writeTextContent(fullFilePath, content, enc, endings!)\n\n // Record Agent edit operation for file freshness tracking\n recordFileEdit(fullFilePath, content)\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = statSync(fullFilePath).mtimeMs\n\n // Log when writing to CLAUDE.md\n if (fullFilePath.endsWith(`${sep}${PROJECT_FILE}`)) {\n }\n\n // Emit file edited event for system reminders\n emitReminderEvent('file:edited', {\n filePath: fullFilePath,\n content,\n oldContent: oldContent || '',\n timestamp: Date.now(),\n operation: oldFileExists ? 'update' : 'create',\n })\n\n if (oldContent) {\n const patch = getPatch({\n filePath: file_path,\n fileContents: oldContent,\n oldStr: oldContent,\n newStr: content,\n })\n\n const data = {\n type: 'update' as const,\n filePath: file_path,\n content,\n structuredPatch: patch,\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n const data = {\n type: 'create' as const,\n filePath: file_path,\n content,\n structuredPatch: [],\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant({ filePath, content, type }) {\n switch (type) {\n case 'create':\n return `File created successfully at: ${filePath}`\n case 'update':\n return `The file ${filePath} has been updated. Here's the result of running \\`cat -n\\` on a snippet of the edited file:\n${addLineNumbers({\n content:\n content.split(/\\r?\\n/).length > MAX_LINES_TO_RENDER_FOR_ASSISTANT\n ? content\n .split(/\\r?\\n/)\n .slice(0, MAX_LINES_TO_RENDER_FOR_ASSISTANT)\n .join('\\n') + TRUNCATED_MESSAGE\n : content,\n startLine: 1,\n})}`\n }\n },\n} satisfies Tool<\n typeof inputSchema,\n {\n type: 'create' | 'update'\n filePath: string\n content: string\n structuredPatch: Hunk[]\n }\n>\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,YAAY,WAAW,cAAc,gBAAgB;AAC9D,SAAS,KAAK,YAAY;AAC1B,SAAS,WAAW;AACpB,SAAS,SAAS,SAAS,YAAY,UAAU,SAAS,WAAW;AACrE,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,kCAAkC;AAC3C,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,sCAAsC;AAE/C,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,0BAA0B;AACnC,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAClC,SAAS,sBAAsB;AAE/B,MAAM,sBAAsB;AAC5B,MAAM,oCAAoC;AAC1C,MAAM,oBACJ;AAEF,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,WAAW,EACR,OAAO,EACP;AAAA,IACC;AAAA,EACF;AAAA,EACF,SAAS,EAAE,OAAO,EAAE,SAAS,kCAAkC;AACjE,CAAC;AAEM,MAAM,gBAAgB;AAAA,EAC3B,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,MAAM;AAAA,EACtB,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA;AAAA,EACA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB,EAAE,UAAU,GAAG;AAC9B,WAAO,CAAC,mBAAmB,SAAS;AAAA,EACtC;AAAA,EACA,qBAAqB,OAAO,EAAE,QAAQ,GAAG;AACvC,WAAO,cAAc,UAAU,MAAM,YAAY,SAAS,OAAO,GAAG,MAAM,SAAS,CAAC;AAAA,EACtF;AAAA,EACA,6BACE,EAAE,WAAW,QAAQ,IAAS,CAAC,GAC/B,EAAE,SAAS,QAAQ,IAAS,CAAC,GAC7B;AACA,QAAI;AACF,UAAI,CAAC,WAAW;AACd,eAAO,oCAAC,oCAA+B;AAAA,MACzC;AACA,YAAM,eAAe,WAAW,SAAS,IACrC,YACA,QAAQ,OAAO,GAAG,SAAS;AAC/B,YAAM,gBAAgB,WAAW,YAAY;AAC7C,YAAM,MAAM,gBAAgB,mBAAmB,YAAY,IAAI;AAC/D,YAAM,aAAa,gBAAgB,aAAa,cAAc,GAAG,IAAI;AACrE,YAAM,OAAO,aAAa,WAAW;AACrC,YAAM,QAAQ,SAAS;AAAA,QACrB,UAAU;AAAA,QACV,cAAc,cAAc;AAAA,QAC5B,QAAQ,cAAc;AAAA,QACtB,QAAQ;AAAA,MACV,CAAC;AAED,aACE,oCAAC,OAAI,eAAc,YACjB,oCAAC,YACE,MAAK,UAAE,KACR,oCAAC,QAAK,OAAO,SAAS,EAAE,SAAO,kBACd,SAAS,WAAW,WAAW,SAAQ,OAAI,GAC5D,GACA,oCAAC,QAAK,MAAI,QACP,UAAU,YAAY,SAAS,OAAO,GAAG,SAAS,CACrD,CACF,GACC;AAAA,QACC,MAAM,IAAI,OACR,oCAAC,OAAI,eAAc,UAAS,aAAa,GAAG,KAAK,EAAE,YACjD,oCAAC,kBAAe,OAAO,GAAG,KAAK,MAAM,OAAO,UAAU,IAAI,CAC5D,CACD;AAAA,QACD,OACE,oCAAC,OAAI,aAAa,GAAG,KAAK,YAAY,CAAC,MACrC,oCAAC,QAAK,OAAO,SAAS,EAAE,iBAAe,KAAG,CAC5C;AAAA,MAEJ,CACF;AAAA,IAEJ,SAAS,GAAG;AAGV,eAAS,CAAC;AACV,aACE,oCAAC,OAAI,eAAc,YACjB,oCAAC,YAAM,MAAK,qBAAc,CAC5B;AAAA,IAEJ;AAAA,EACF;AAAA,EACA,wBAAwB,EAAE,UAAU,SAAS,iBAAiB,KAAK,GAAG;AACpE,UAAM,UAAU;AAChB,YAAQ,MAAM;AAAA,MACZ,KAAK,UAAU;AACb,cAAM,sBAAsB,WAAW;AACvC,cAAM,WAAW,QAAQ,MAAM,GAAG,EAAE;AAEpC,eACE,oCAAC,OAAI,eAAc,YACjB,oCAAC,YACE,MAAK,iBAAS,UAAS,aAAU,KAClC,oCAAC,QAAK,MAAI,QACP,UAAU,WAAW,SAAS,OAAO,GAAG,QAAQ,CACnD,CACF,GACA,oCAAC,OAAI,eAAc,UAAS,aAAa,KACvC;AAAA,UAAC;AAAA;AAAA,YACC,MACE,UACI,sBACA,oBACG,MAAM,IAAI,EACV,MAAM,GAAG,mBAAmB,EAC5B,OAAO,OAAK,EAAE,KAAK,MAAM,EAAE,EAC3B,KAAK,IAAI;AAAA,YAElB,UAAU,QAAQ,QAAQ,EAAE,MAAM,CAAC;AAAA;AAAA,QACrC,GACC,CAAC,WAAW,WAAW,uBACtB,oCAAC,QAAK,OAAO,SAAS,EAAE,iBAAe,UAC9B,WAAW,qBAAoB,SACxC,CAEJ,CACF;AAAA,MAEJ;AAAA,MACA,KAAK;AACH,eACE;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA;AAAA,QACF;AAAA,IAEN;AAAA,EACF;AAAA,EACA,MAAM,cAAc,EAAE,UAAU,GAAG,EAAE,mBAAmB,GAAG;AACzD,UAAM,eAAe,WAAW,SAAS,IACrC,YACA,QAAQ,OAAO,GAAG,SAAS;AAC/B,QAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,aAAO,EAAE,QAAQ,KAAK;AAAA,IACxB;AAEA,UAAM,gBAAgB,mBAAmB,YAAY;AACrD,QAAI,CAAC,eAAe;AAClB,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SACE;AAAA,MACJ;AAAA,IACF;AAGA,UAAM,QAAQ,SAAS,YAAY;AACnC,UAAM,gBAAgB,MAAM;AAC5B,QAAI,gBAAgB,eAAe;AACjC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SACE;AAAA,MACJ;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,KAAK;AAAA,EACxB;AAAA,EACA,OAAO,KAAK,EAAE,WAAW,QAAQ,GAAG,EAAE,mBAAmB,GAAG;AAC1D,UAAM,eAAe,WAAW,SAAS,IACrC,YACA,QAAQ,OAAO,GAAG,SAAS;AAC/B,UAAM,MAAM,QAAQ,YAAY;AAChC,UAAM,gBAAgB,WAAW,YAAY;AAC7C,UAAM,MAAM,gBAAgB,mBAAmB,YAAY,IAAI;AAC/D,UAAM,aAAa,gBAAgB,aAAa,cAAc,GAAG,IAAI;AAErE,UAAM,UAAU,gBACZ,kBAAkB,YAAY,IAC9B,MAAM,sBAAsB,OAAO,CAAC;AAExC,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAClC,qBAAiB,cAAc,SAAS,KAAK,OAAQ;AAGrD,mBAAe,cAAc,OAAO;AAGpC,uBAAmB,YAAY,IAAI,SAAS,YAAY,EAAE;AAG1D,QAAI,aAAa,SAAS,GAAG,GAAG,GAAG,YAAY,EAAE,GAAG;AAAA,IACpD;AAGA,sBAAkB,eAAe;AAAA,MAC/B,UAAU;AAAA,MACV;AAAA,MACA,YAAY,cAAc;AAAA,MAC1B,WAAW,KAAK,IAAI;AAAA,MACpB,WAAW,gBAAgB,WAAW;AAAA,IACxC,CAAC;AAED,QAAI,YAAY;AACd,YAAM,QAAQ,SAAS;AAAA,QACrB,UAAU;AAAA,QACV,cAAc;AAAA,QACd,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AAED,YAAMA,QAAO;AAAA,QACX,MAAM;AAAA,QACN,UAAU;AAAA,QACV;AAAA,QACA,iBAAiB;AAAA,MACnB;AACA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAAA;AAAA,QACA,oBAAoB,KAAK,yBAAyBA,KAAI;AAAA,MACxD;AACA;AAAA,IACF;AAEA,UAAM,OAAO;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA,iBAAiB,CAAC;AAAA,IACpB;AACA,UAAM;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA,oBAAoB,KAAK,yBAAyB,IAAI;AAAA,IACxD;AAAA,EACF;AAAA,EACA,yBAAyB,EAAE,UAAU,SAAS,KAAK,GAAG;AACpD,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO,iCAAiC,QAAQ;AAAA,MAClD,KAAK;AACH,eAAO,YAAY,QAAQ;AAAA,EACjC,eAAe;AAAA,UACf,SACE,QAAQ,MAAM,OAAO,EAAE,SAAS,oCAC5B,QACG,MAAM,OAAO,EACb,MAAM,GAAG,iCAAiC,EAC1C,KAAK,IAAI,IAAI,oBAChB;AAAA,UACN,WAAW;AAAA,QACb,CAAC,CAAC;AAAA,IACE;AAAA,EACF;AACF;",
|
|
6
6
|
"names": ["data"]
|
|
7
7
|
}
|
|
@@ -47,10 +47,19 @@ const GlobTool = {
|
|
|
47
47
|
return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
|
|
48
48
|
},
|
|
49
49
|
renderToolResultMessage(output) {
|
|
50
|
+
if (!output) {
|
|
51
|
+
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Search completed")));
|
|
52
|
+
}
|
|
50
53
|
if (typeof output === "string") {
|
|
51
|
-
|
|
54
|
+
try {
|
|
55
|
+
output = JSON.parse(output);
|
|
56
|
+
} catch {
|
|
57
|
+
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Search completed")));
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
|
-
|
|
60
|
+
const numFiles = output?.numFiles ?? 0;
|
|
61
|
+
const durationMs = output?.durationMs ?? 0;
|
|
62
|
+
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Found "), /* @__PURE__ */ React.createElement(Text, { bold: true }, numFiles, " "), /* @__PURE__ */ React.createElement(Text, null, numFiles === 0 || numFiles > 1 ? "files" : "file")), /* @__PURE__ */ React.createElement(Cost, { costUSD: 0, durationMs, debug: false }));
|
|
54
63
|
},
|
|
55
64
|
async *call({ pattern, path }, { abortController }) {
|
|
56
65
|
const start = Date.now();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/GlobTool/GlobTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n output = JSON.parse(output) as Output\n }\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{
|
|
5
|
-
"mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,SAAS;AAClB,SAAS,YAAY;AACrB,SAAS,sCAAsC;AAE/C,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,aAAa,4BAA4B;AAClD,SAAS,YAAY,UAAU,eAAe;AAC9C,SAAS,yBAAyB;AAElC,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,SAAS,EAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,EACtE,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AASM,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EACA;AAAA,EACA,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB,EAAE,KAAK,GAAG;AACzB,WAAO,CAAC,kBAAkB,QAAQ,OAAO,CAAC;AAAA,EAC5C;AAAA,EACA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,EAAE,SAAS,KAAK,GAAG,EAAE,QAAQ,GAAG;AACnD,UAAM,eAAe,OACjB,WAAW,IAAI,IACb,OACA,QAAQ,OAAO,GAAG,IAAI,IACxB;AACJ,UAAM,eAAe,eACjB,SAAS,OAAO,GAAG,YAAY,IAC/B;AACJ,WAAO,aAAa,OAAO,IAAI,gBAAgB,UAAU,YAAY,UAAU,eAAe,YAAY,MAAM,EAAE;AAAA,EACpH;AAAA,EACA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ;AAE9B,QAAI,OAAO,WAAW,UAAU;AAC9B,
|
|
4
|
+
"sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Search completed</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numFiles = output?.numFiles ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ pattern, path }, { abortController }) {\n const start = Date.now()\n const { files, truncated } = await glob(\n pattern,\n path ?? getCwd(),\n { limit: 100, offset: 0 },\n abortController.signal,\n )\n const output: Output = {\n filenames: files,\n durationMs: Date.now() - start,\n numFiles: files.length,\n truncated,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n let result = output.filenames.join('\\n')\n if (output.filenames.length === 0) {\n result = 'No files found'\n }\n // Only add truncation message if results were actually truncated\n else if (output.truncated) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n} satisfies Tool<typeof inputSchema, Output>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,10 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { Cost } from "../../components/Cost.js";
|
|
6
6
|
import { FallbackToolUseRejectedMessage } from "../../components/FallbackToolUseRejectedMessage.js";
|
|
7
7
|
import { getCwd } from "../../utils/state.js";
|
|
8
|
-
import {
|
|
9
|
-
getAbsolutePath,
|
|
10
|
-
getAbsoluteAndRelativePaths
|
|
11
|
-
} from "../../utils/file.js";
|
|
8
|
+
import { getAbsolutePath, getAbsoluteAndRelativePaths } from "../../utils/file.js";
|
|
12
9
|
import { ripGrep } from "../../utils/ripgrep.js";
|
|
13
10
|
import { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from "./prompt.js";
|
|
14
11
|
import { hasReadPermission } from "../../utils/permissions/filesystem.js";
|
|
@@ -54,10 +51,15 @@ const GrepTool = {
|
|
|
54
51
|
return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
|
|
55
52
|
},
|
|
56
53
|
renderToolResultMessage(output) {
|
|
54
|
+
if (!output) {
|
|
55
|
+
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Search completed")));
|
|
56
|
+
}
|
|
57
57
|
if (typeof output === "string") {
|
|
58
58
|
output = output;
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
const numFiles = output?.numFiles ?? 0;
|
|
61
|
+
const durationMs = output?.durationMs ?? 0;
|
|
62
|
+
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0Found "), /* @__PURE__ */ React.createElement(Text, { bold: true }, numFiles, " "), /* @__PURE__ */ React.createElement(Text, null, numFiles === 0 || numFiles > 1 ? "files" : "file")), /* @__PURE__ */ React.createElement(Cost, { costUSD: 0, durationMs, debug: false }));
|
|
61
63
|
},
|
|
62
64
|
renderResultForAssistant({ numFiles, filenames }) {
|
|
63
65
|
if (numFiles === 0) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/GrepTool/GrepTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { stat } from 'fs/promises'\nimport { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport {
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,SAAS;AAClB,SAAS,YAAY;AACrB,SAAS,sCAAsC;AAE/C,SAAS,cAAc;AACvB
|
|
4
|
+
"sourcesContent": ["import { stat } from 'fs/promises'\nimport { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { getAbsolutePath, getAbsoluteAndRelativePaths } from '@utils/file'\nimport { ripGrep } from '@utils/ripgrep'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n include: z\n .string()\n .optional()\n .describe(\n 'File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\")',\n ),\n})\n\nconst MAX_RESULTS = 100\n\ntype Input = typeof inputSchema\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n}\n\nexport const GrepTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GrepTool is read-only, safe for concurrent execution\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path, include }, { verbose }) {\n const { absolutePath, relativePath } = getAbsoluteAndRelativePaths(path)\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}${include ? `, include: \"${include}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Search completed</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n // Convert string to Output type using tmpDeserializeOldLogResult if needed\n output = output as unknown as Output\n }\n\n const numFiles = output?.numFiles ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n renderResultForAssistant({ numFiles, filenames }) {\n if (numFiles === 0) {\n return 'No files found'\n }\n let result = `Found ${numFiles} file${numFiles === 1 ? '' : 's'}\\n${filenames.slice(0, MAX_RESULTS).join('\\n')}`\n if (numFiles > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n async *call({ pattern, path, include }, { abortController }) {\n const start = Date.now()\n const absolutePath = getAbsolutePath(path) || getCwd()\n\n const args = ['-li', pattern]\n if (include) {\n args.push('--glob', include)\n }\n\n const results = await ripGrep(args, absolutePath, abortController.signal)\n\n const stats = await Promise.all(results.map(_ => stat(_)))\n const matches = results\n // Sort by modification time\n .map((_, i) => [_, stats[i]!] as const)\n .sort((a, b) => {\n if (process.env.NODE_ENV === 'test') {\n // In tests, we always want to sort by filename, so that results are deterministic\n return a[0].localeCompare(b[0])\n }\n const timeComparison = (b[1].mtimeMs ?? 0) - (a[1].mtimeMs ?? 0)\n if (timeComparison === 0) {\n // Sort by filename as a tiebreaker\n return a[0].localeCompare(b[0])\n }\n return timeComparison\n })\n .map(_ => _[0])\n\n const output = {\n filenames: matches,\n durationMs: Date.now() - start,\n numFiles: matches.length,\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n} satisfies Tool<Input, Output>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -48,8 +48,14 @@ const MCPTool = {
|
|
|
48
48
|
},
|
|
49
49
|
renderToolResultMessage(output) {
|
|
50
50
|
const verbose = false;
|
|
51
|
+
if (!output) {
|
|
52
|
+
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "(No content)")));
|
|
53
|
+
}
|
|
51
54
|
if (Array.isArray(output)) {
|
|
52
55
|
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, output.map((item, i) => {
|
|
56
|
+
if (!item) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
53
59
|
if (item.type === "image") {
|
|
54
60
|
return /* @__PURE__ */ React.createElement(
|
|
55
61
|
Box,
|
|
@@ -62,20 +68,13 @@ const MCPTool = {
|
|
|
62
68
|
/* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, null, "[Image]"))
|
|
63
69
|
);
|
|
64
70
|
}
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
key: i,
|
|
70
|
-
content: item.text,
|
|
71
|
-
lines: lines2,
|
|
72
|
-
verbose
|
|
73
|
-
}
|
|
74
|
-
);
|
|
71
|
+
const text = item.text || "";
|
|
72
|
+
const lines2 = text.split("\n").length;
|
|
73
|
+
return /* @__PURE__ */ React.createElement(OutputLine, { key: i, content: text, lines: lines2, verbose });
|
|
75
74
|
}));
|
|
76
75
|
}
|
|
77
|
-
if (
|
|
78
|
-
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "
|
|
76
|
+
if (typeof output !== "string") {
|
|
77
|
+
return /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React.createElement(Text, { color: getTheme().secondaryText }, "MCP operation completed")));
|
|
79
78
|
}
|
|
80
79
|
const lines = output.split("\n").length;
|
|
81
80
|
return /* @__PURE__ */ React.createElement(OutputLine, { content: output, lines, verbose });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/MCPTool/MCPTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { type Tool } from '@tool'\nimport { getTheme } from '@utils/theme'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { OutputLine } from '@tools/BashTool/OutputLine'\n\n// Allow any input object since MCP tools define their own schemas\nconst inputSchema = z.object({}).passthrough()\n\nexport const MCPTool = {\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // MCPTool can modify state through MCP calls, not safe for concurrent execution\n },\n // Overridden in mcpClient.ts\n name: 'mcp',\n // Overridden in mcpClient.ts\n async description() {\n return DESCRIPTION\n },\n // Overridden in mcpClient.ts\n async prompt() {\n return PROMPT\n },\n inputSchema,\n // Overridden in mcpClient.ts\n async *call() {\n yield {\n type: 'result',\n data: '',\n resultForAssistant: '',\n }\n },\n needsPermissions() {\n return true\n },\n renderToolUseMessage(input) {\n return Object.entries(input)\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n // Overridden in mcpClient.ts\n userFacingName: () => 'mcp',\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n const verbose = false // Set default value for verbose\n if (Array.isArray(output)) {\n return (\n <Box flexDirection=\"column\">\n {output.map((item, i) => {\n if (item.type === 'image') {\n return (\n <Box\n key={i}\n justifyContent=\"space-between\"\n overflowX=\"hidden\"\n width=\"100%\"\n >\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>[Image]</Text>\n </Box>\n </Box>\n )\n }\n const
|
|
5
|
-
"mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAE/C,SAAS,gBAAgB;AACzB,SAAS,aAAa,cAAc;AACpC,SAAS,kBAAkB;AAG3B,MAAM,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAEtC,MAAM,UAAU;AAAA,EACrB,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM;AAAA;AAAA,EAEN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA;AAAA;AAAA,EAEA,OAAO,OAAO;AACZ,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,oBAAoB;AAAA,IACtB;AAAA,EACF;AAAA,EACA,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,OAAO;AAC1B,WAAO,OAAO,QAAQ,KAAK,EACxB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE,EACxD,KAAK,IAAI;AAAA,EACd;AAAA;AAAA,EAEA,gBAAgB,MAAM;AAAA,EACtB,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ;AAC9B,UAAM,UAAU;
|
|
4
|
+
"sourcesContent": ["import { Box, Text } from 'ink'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { type Tool } from '@tool'\nimport { getTheme } from '@utils/theme'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { OutputLine } from '@tools/BashTool/OutputLine'\n\n// Allow any input object since MCP tools define their own schemas\nconst inputSchema = z.object({}).passthrough()\n\nexport const MCPTool = {\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // MCPTool can modify state through MCP calls, not safe for concurrent execution\n },\n // Overridden in mcpClient.ts\n name: 'mcp',\n // Overridden in mcpClient.ts\n async description() {\n return DESCRIPTION\n },\n // Overridden in mcpClient.ts\n async prompt() {\n return PROMPT\n },\n inputSchema,\n // Overridden in mcpClient.ts\n async *call() {\n yield {\n type: 'result',\n data: '',\n resultForAssistant: '',\n }\n },\n needsPermissions() {\n return true\n },\n renderToolUseMessage(input) {\n return Object.entries(input)\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n // Overridden in mcpClient.ts\n userFacingName: () => 'mcp',\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n const verbose = false // Set default value for verbose\n\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text color={getTheme().secondaryText}>(No content)</Text>\n </Box>\n </Box>\n )\n }\n\n if (Array.isArray(output)) {\n return (\n <Box flexDirection=\"column\">\n {output.map((item, i) => {\n // Guard against null/undefined items in array\n if (!item) {\n return null\n }\n if (item.type === 'image') {\n return (\n <Box\n key={i}\n justifyContent=\"space-between\"\n overflowX=\"hidden\"\n width=\"100%\"\n >\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>[Image]</Text>\n </Box>\n </Box>\n )\n }\n // Guard against missing text property\n const text = item.text || ''\n const lines = text.split('\\n').length\n return (\n <OutputLine key={i} content={text} lines={lines} verbose={verbose} />\n )\n })}\n </Box>\n )\n }\n\n // Guard against non-string output\n if (typeof output !== 'string') {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text color={getTheme().secondaryText}>MCP operation completed</Text>\n </Box>\n </Box>\n )\n }\n\n const lines = output.split('\\n').length\n return <OutputLine content={output} lines={lines} verbose={verbose} />\n },\n renderResultForAssistant(content) {\n return content\n },\n} satisfies Tool<typeof inputSchema, string>\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAE/C,SAAS,gBAAgB;AACzB,SAAS,aAAa,cAAc;AACpC,SAAS,kBAAkB;AAG3B,MAAM,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAEtC,MAAM,UAAU;AAAA,EACrB,MAAM,YAAY;AAChB,WAAO;AAAA,EACT;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM;AAAA;AAAA,EAEN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA;AAAA;AAAA,EAEA,OAAO,OAAO;AACZ,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,oBAAoB;AAAA,IACtB;AAAA,EACF;AAAA,EACA,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,OAAO;AAC1B,WAAO,OAAO,QAAQ,KAAK,EACxB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,UAAU,KAAK,CAAC,EAAE,EACxD,KAAK,IAAI;AAAA,EACd;AAAA;AAAA,EAEA,gBAAgB,MAAM;AAAA,EACtB,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ;AAC9B,UAAM,UAAU;AAGhB,QAAI,CAAC,QAAQ;AACX,aACE,oCAAC,OAAI,gBAAe,iBAAgB,WAAU,UAAS,OAAM,UAC3D,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,QAAK,OAAO,SAAS,EAAE,iBAAe,cAAY,CACrD,CACF;AAAA,IAEJ;AAEA,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,aACE,oCAAC,OAAI,eAAc,YAChB,OAAO,IAAI,CAAC,MAAM,MAAM;AAEvB,YAAI,CAAC,MAAM;AACT,iBAAO;AAAA,QACT;AACA,YAAI,KAAK,SAAS,SAAS;AACzB,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,gBAAe;AAAA,cACf,WAAU;AAAA,cACV,OAAM;AAAA;AAAA,YAEN,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,YAAK,SAAO,CACf;AAAA,UACF;AAAA,QAEJ;AAEA,cAAM,OAAO,KAAK,QAAQ;AAC1B,cAAMA,SAAQ,KAAK,MAAM,IAAI,EAAE;AAC/B,eACE,oCAAC,cAAW,KAAK,GAAG,SAAS,MAAM,OAAOA,QAAO,SAAkB;AAAA,MAEvE,CAAC,CACH;AAAA,IAEJ;AAGA,QAAI,OAAO,WAAW,UAAU;AAC9B,aACE,oCAAC,OAAI,gBAAe,iBAAgB,WAAU,UAAS,OAAM,UAC3D,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,qBAAoB,GAC1B,oCAAC,QAAK,OAAO,SAAS,EAAE,iBAAe,yBAAuB,CAChE,CACF;AAAA,IAEJ;AAEA,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE;AACjC,WAAO,oCAAC,cAAW,SAAS,QAAQ,OAAc,SAAkB;AAAA,EACtE;AAAA,EACA,yBAAyB,SAAS;AAChC,WAAO;AAAA,EACT;AACF;",
|
|
6
6
|
"names": ["lines"]
|
|
7
7
|
}
|
|
@@ -15,7 +15,10 @@ import { getTheme } from "../../utils/theme.js";
|
|
|
15
15
|
import { NotebookEditTool } from "../NotebookEditTool/NotebookEditTool.js";
|
|
16
16
|
function applyContentEdit(content, oldString, newString, replaceAll = false) {
|
|
17
17
|
if (replaceAll) {
|
|
18
|
-
const regex = new RegExp(
|
|
18
|
+
const regex = new RegExp(
|
|
19
|
+
oldString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
|
|
20
|
+
"g"
|
|
21
|
+
);
|
|
19
22
|
const matches = content.match(regex);
|
|
20
23
|
const occurrences = matches ? matches.length : 0;
|
|
21
24
|
const newContent = content.replace(regex, newString);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/MultiEditTool/MultiEditTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { dirname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { Tool, ValidationResult } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n findSimilarFile,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\n// Local content-based edit function for MultiEditTool\nfunction applyContentEdit(\n content: string,\n oldString: string,\n newString: string,\n replaceAll: boolean = false\n): { newContent: string; occurrences: number } {\n if (replaceAll) {\n const regex = new RegExp(oldString.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'g')\n const matches = content.match(regex)\n const occurrences = matches ? matches.length : 0\n const newContent = content.replace(regex, newString)\n return { newContent, occurrences }\n } else {\n if (content.includes(oldString)) {\n const newContent = content.replace(oldString, newString)\n return { newContent, occurrences: 1 }\n } else {\n throw new Error(`String not found: ${oldString.substring(0, 50)}...`)\n }\n }\n}\nimport { hasWritePermission } from '@utils/permissions/filesystem'\nimport { PROJECT_FILE } from '@constants/product'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\nimport { getPatch } from '@utils/diff'\n\nconst EditSchema = z.object({\n old_string: z.string().describe('The text to replace'),\n new_string: z.string().describe('The text to replace it with'),\n replace_all: z\n .boolean()\n .optional()\n .default(false)\n .describe('Replace all occurences of old_string (default false)'),\n})\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to modify'),\n edits: z\n .array(EditSchema)\n .min(1)\n .describe('Array of edit operations to perform sequentially on the file'),\n})\n\nexport type In = typeof inputSchema\n\n// Number of lines of context to include before/after the change in our result message\nconst N_LINES_SNIPPET = 4\n\nexport const MultiEditTool = {\n name: 'MultiEdit',\n async description() {\n return 'A tool for making multiple edits to a single file atomically'\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n userFacingName() {\n return 'Multi-Edit'\n },\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // MultiEdit modifies files, not safe for concurrent execution\n },\n needsPermissions(input?: z.infer<typeof inputSchema>) {\n if (!input) return true\n return !hasWritePermission(input.file_path)\n },\n renderResultForAssistant(content) {\n return content\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, edits } = input\n const workingDir = getCwd()\n const relativePath = isAbsolute(file_path)\n ? relative(workingDir, file_path)\n : file_path\n\n if (verbose) {\n const editSummary = edits\n .map(\n (edit, index) =>\n `${index + 1}. Replace \"${edit.old_string.substring(0, 50)}${edit.old_string.length > 50 ? '...' : ''}\" with \"${edit.new_string.substring(0, 50)}${edit.new_string.length > 50 ? '...' : ''}\"`,\n )\n .join('\\n')\n return `Multiple edits to ${relativePath}:\\n${editSummary}`\n }\n\n return `Making ${edits.length} edits to ${relativePath}`\n },\n renderToolUseRejectedMessage() {\n return (\n <Box>\n <Text color={getTheme().error}>\u26A0 Edit request rejected</Text>\n </Box>\n )\n },\n renderToolResultMessage(output) {\n if (typeof output === 'string') {\n const isError = output.includes('Error:')\n return (\n <Box flexDirection=\"column\">\n <Text color={isError ? getTheme().error : getTheme().success}>\n {output}\n </Text>\n </Box>\n )\n }\n\n return (\n <FileEditToolUpdatedMessage\n filePath={output.filePath}\n structuredPatch={output.structuredPatch}\n verbose={false}\n />\n )\n },\n async validateInput(\n { file_path, edits }: z.infer<typeof inputSchema>,\n context?: { readFileTimestamps?: Record<string, number> },\n ): Promise<ValidationResult> {\n const workingDir = getCwd()\n const normalizedPath = isAbsolute(file_path)\n ? resolve(file_path)\n : resolve(workingDir, file_path)\n\n // Check if it's a notebook file\n if (normalizedPath.endsWith('.ipynb')) {\n return {\n result: false,\n errorCode: 1,\n message: `For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} tool instead.`,\n }\n }\n\n // For new files, check parent directory exists\n if (!existsSync(normalizedPath)) {\n const parentDir = dirname(normalizedPath)\n if (!existsSync(parentDir)) {\n return {\n result: false,\n errorCode: 2,\n message: `Parent directory does not exist: ${parentDir}`,\n }\n }\n\n // For new files, ensure first edit creates the file (empty old_string)\n if (edits.length === 0 || edits[0].old_string !== '') {\n return {\n result: false,\n errorCode: 6,\n message:\n 'For new files, the first edit must have an empty old_string to create the file content.',\n }\n }\n } else {\n // For existing files, apply file protection mechanisms\n const readFileTimestamps = context?.readFileTimestamps || {}\n const readTimestamp = readFileTimestamps[normalizedPath]\n\n if (!readTimestamp) {\n return {\n result: false,\n errorCode: 7,\n message:\n 'File has not been read yet. Read it first before editing it.',\n meta: {\n filePath: normalizedPath,\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n // Check if file has been modified since last read\n const stats = statSync(normalizedPath)\n const lastWriteTime = stats.mtimeMs\n if (lastWriteTime > readTimestamp) {\n return {\n result: false,\n errorCode: 8,\n message:\n 'File has been modified since read, either by the user or by a linter. Read it again before attempting to edit it.',\n meta: {\n filePath: normalizedPath,\n lastWriteTime,\n readTimestamp,\n },\n }\n }\n\n // Pre-validate that all old_strings exist in the file\n const encoding = detectFileEncoding(normalizedPath)\n if (encoding === 'binary') {\n return {\n result: false,\n errorCode: 9,\n message: 'Cannot edit binary files.',\n }\n }\n\n const currentContent = readFileSync(normalizedPath, 'utf-8')\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i]\n if (\n edit.old_string !== '' &&\n !currentContent.includes(edit.old_string)\n ) {\n return {\n result: false,\n errorCode: 10,\n message: `Edit ${i + 1}: String to replace not found in file: \"${edit.old_string.substring(0, 100)}${edit.old_string.length > 100 ? '...' : ''}\"`,\n meta: {\n editIndex: i + 1,\n oldString: edit.old_string.substring(0, 200),\n },\n }\n }\n }\n }\n\n // Validate each edit\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i]\n if (edit.old_string === edit.new_string) {\n return {\n result: false,\n errorCode: 3,\n message: `Edit ${i + 1}: old_string and new_string cannot be the same`,\n }\n }\n }\n\n return { result: true }\n },\n async *call({ file_path, edits }, { readFileTimestamps }) {\n const startTime = Date.now()\n const workingDir = getCwd()\n const filePath = isAbsolute(file_path)\n ? resolve(file_path)\n : resolve(workingDir, file_path)\n\n try {\n // Read current file content (or empty for new files)\n let currentContent = ''\n let fileExists = existsSync(filePath)\n\n if (fileExists) {\n const encoding = detectFileEncoding(filePath)\n if (encoding === 'binary') {\n yield {\n type: 'result',\n data: 'Error: Cannot edit binary files',\n resultForAssistant: 'Error: Cannot edit binary files',\n }\n return\n }\n currentContent = readFileSync(filePath, 'utf-8')\n } else {\n // For new files, ensure parent directory exists\n const parentDir = dirname(filePath)\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true })\n }\n }\n\n // Apply all edits sequentially\n let modifiedContent = currentContent\n const appliedEdits = []\n\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i]\n const { old_string, new_string, replace_all } = edit\n\n try {\n const result = applyContentEdit(\n modifiedContent,\n old_string,\n new_string,\n replace_all,\n )\n modifiedContent = result.newContent\n appliedEdits.push({\n editIndex: i + 1,\n success: true,\n old_string: old_string.substring(0, 100),\n new_string: new_string.substring(0, 100),\n occurrences: result.occurrences,\n })\n } catch (error) {\n // If any edit fails, abort the entire operation\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error'\n yield {\n type: 'result',\n data: `Error in edit ${i + 1}: ${errorMessage}`,\n resultForAssistant: `Error in edit ${i + 1}: ${errorMessage}`,\n }\n return\n }\n }\n\n // Write the modified content\n const lineEndings = fileExists ? detectLineEndings(currentContent) : 'LF'\n const encoding = fileExists ? detectFileEncoding(filePath) : 'utf8'\n writeTextContent(filePath, modifiedContent, encoding, lineEndings)\n\n // Record Agent edit operation for file freshness tracking\n recordFileEdit(filePath, modifiedContent)\n\n // Update readFileTimestamps to prevent stale file warnings\n readFileTimestamps[filePath] = Date.now()\n\n // Emit file edited event for system reminders\n emitReminderEvent('file:edited', {\n filePath,\n edits: edits.map(e => ({\n oldString: e.old_string,\n newString: e.new_string,\n })),\n originalContent: currentContent,\n newContent: modifiedContent,\n timestamp: Date.now(),\n operation: fileExists ? 'update' : 'create',\n })\n\n // Generate result data\n const relativePath = relative(workingDir, filePath)\n const summary = `Successfully applied ${edits.length} edits to ${relativePath}`\n\n const structuredPatch = getPatch({\n filePath: file_path,\n fileContents: currentContent,\n oldStr: currentContent,\n newStr: modifiedContent,\n })\n\n const resultData = {\n filePath: file_path,\n wasNewFile: !fileExists,\n editsApplied: appliedEdits,\n totalEdits: edits.length,\n summary,\n structuredPatch,\n }\n\n // Log the operation\n \n\n yield {\n type: 'result',\n data: resultData,\n resultForAssistant: summary,\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error occurred'\n const errorResult = `Error applying multi-edit: ${errorMessage}`\n\n logError(error)\n\n yield {\n type: 'result',\n data: errorResult,\n resultForAssistant: errorResult,\n }\n }\n },\n} satisfies Tool<typeof inputSchema, any>\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY,WAAW,cAAc,gBAAgB;AAC9D,SAAS,KAAK,YAAY;AAC1B,SAAS,SAAS,YAAY,UAAU,eAAoB;AAC5D,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,kCAAkC;AAI3C;AAAA,EAEE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AACP,SAAS,gBAAgB;AACzB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,wBAAwB;AAEjC,SAAS,iBACP,SACA,WACA,WACA,aAAsB,OACuB;AAC7C,MAAI,YAAY;AACd,UAAM,QAAQ,IAAI,
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, statSync } from 'fs'\nimport { Box, Text } from 'ink'\nimport { dirname, isAbsolute, relative, resolve, sep } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileEditToolUpdatedMessage } from '@components/FileEditToolUpdatedMessage'\nimport { StructuredDiff } from '@components/StructuredDiff'\nimport { Tool, ValidationResult } from '@tool'\nimport { intersperse } from '@utils/array'\nimport {\n addLineNumbers,\n detectFileEncoding,\n detectLineEndings,\n findSimilarFile,\n writeTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getCwd } from '@utils/state'\nimport { getTheme } from '@utils/theme'\nimport { NotebookEditTool } from '@tools/NotebookEditTool/NotebookEditTool'\n// Local content-based edit function for MultiEditTool\nfunction applyContentEdit(\n content: string,\n oldString: string,\n newString: string,\n replaceAll: boolean = false,\n): { newContent: string; occurrences: number } {\n if (replaceAll) {\n const regex = new RegExp(\n oldString.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'),\n 'g',\n )\n const matches = content.match(regex)\n const occurrences = matches ? matches.length : 0\n const newContent = content.replace(regex, newString)\n return { newContent, occurrences }\n } else {\n if (content.includes(oldString)) {\n const newContent = content.replace(oldString, newString)\n return { newContent, occurrences: 1 }\n } else {\n throw new Error(`String not found: ${oldString.substring(0, 50)}...`)\n }\n }\n}\nimport { hasWritePermission } from '@utils/permissions/filesystem'\nimport { PROJECT_FILE } from '@constants/product'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport { recordFileEdit } from '@services/fileFreshness'\nimport { getPatch } from '@utils/diff'\n\nconst EditSchema = z.object({\n old_string: z.string().describe('The text to replace'),\n new_string: z.string().describe('The text to replace it with'),\n replace_all: z\n .boolean()\n .optional()\n .default(false)\n .describe('Replace all occurences of old_string (default false)'),\n})\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to modify'),\n edits: z\n .array(EditSchema)\n .min(1)\n .describe('Array of edit operations to perform sequentially on the file'),\n})\n\nexport type In = typeof inputSchema\n\n// Number of lines of context to include before/after the change in our result message\nconst N_LINES_SNIPPET = 4\n\nexport const MultiEditTool = {\n name: 'MultiEdit',\n async description() {\n return 'A tool for making multiple edits to a single file atomically'\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n userFacingName() {\n return 'Multi-Edit'\n },\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return false\n },\n isConcurrencySafe() {\n return false // MultiEdit modifies files, not safe for concurrent execution\n },\n needsPermissions(input?: z.infer<typeof inputSchema>) {\n if (!input) return true\n return !hasWritePermission(input.file_path)\n },\n renderResultForAssistant(content) {\n return content\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, edits } = input\n const workingDir = getCwd()\n const relativePath = isAbsolute(file_path)\n ? relative(workingDir, file_path)\n : file_path\n\n if (verbose) {\n const editSummary = edits\n .map(\n (edit, index) =>\n `${index + 1}. Replace \"${edit.old_string.substring(0, 50)}${edit.old_string.length > 50 ? '...' : ''}\" with \"${edit.new_string.substring(0, 50)}${edit.new_string.length > 50 ? '...' : ''}\"`,\n )\n .join('\\n')\n return `Multiple edits to ${relativePath}:\\n${editSummary}`\n }\n\n return `Making ${edits.length} edits to ${relativePath}`\n },\n renderToolUseRejectedMessage() {\n return (\n <Box>\n <Text color={getTheme().error}>\u26A0 Edit request rejected</Text>\n </Box>\n )\n },\n renderToolResultMessage(output) {\n if (typeof output === 'string') {\n const isError = output.includes('Error:')\n return (\n <Box flexDirection=\"column\">\n <Text color={isError ? getTheme().error : getTheme().success}>\n {output}\n </Text>\n </Box>\n )\n }\n\n return (\n <FileEditToolUpdatedMessage\n filePath={output.filePath}\n structuredPatch={output.structuredPatch}\n verbose={false}\n />\n )\n },\n async validateInput(\n { file_path, edits }: z.infer<typeof inputSchema>,\n context?: { readFileTimestamps?: Record<string, number> },\n ): Promise<ValidationResult> {\n const workingDir = getCwd()\n const normalizedPath = isAbsolute(file_path)\n ? resolve(file_path)\n : resolve(workingDir, file_path)\n\n // Check if it's a notebook file\n if (normalizedPath.endsWith('.ipynb')) {\n return {\n result: false,\n errorCode: 1,\n message: `For Jupyter notebooks (.ipynb files), use the ${NotebookEditTool.name} tool instead.`,\n }\n }\n\n // For new files, check parent directory exists\n if (!existsSync(normalizedPath)) {\n const parentDir = dirname(normalizedPath)\n if (!existsSync(parentDir)) {\n return {\n result: false,\n errorCode: 2,\n message: `Parent directory does not exist: ${parentDir}`,\n }\n }\n\n // For new files, ensure first edit creates the file (empty old_string)\n if (edits.length === 0 || edits[0].old_string !== '') {\n return {\n result: false,\n errorCode: 6,\n message:\n 'For new files, the first edit must have an empty old_string to create the file content.',\n }\n }\n } else {\n // For existing files, apply file protection mechanisms\n const readFileTimestamps = context?.readFileTimestamps || {}\n const readTimestamp = readFileTimestamps[normalizedPath]\n\n if (!readTimestamp) {\n return {\n result: false,\n errorCode: 7,\n message:\n 'File has not been read yet. Read it first before editing it.',\n meta: {\n filePath: normalizedPath,\n isFilePathAbsolute: String(isAbsolute(file_path)),\n },\n }\n }\n\n // Check if file has been modified since last read\n const stats = statSync(normalizedPath)\n const lastWriteTime = stats.mtimeMs\n if (lastWriteTime > readTimestamp) {\n return {\n result: false,\n errorCode: 8,\n message:\n 'File has been modified since read, either by the user or by a linter. Read it again before attempting to edit it.',\n meta: {\n filePath: normalizedPath,\n lastWriteTime,\n readTimestamp,\n },\n }\n }\n\n // Pre-validate that all old_strings exist in the file\n const encoding = detectFileEncoding(normalizedPath)\n if (encoding === 'binary') {\n return {\n result: false,\n errorCode: 9,\n message: 'Cannot edit binary files.',\n }\n }\n\n const currentContent = readFileSync(normalizedPath, 'utf-8')\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i]\n if (\n edit.old_string !== '' &&\n !currentContent.includes(edit.old_string)\n ) {\n return {\n result: false,\n errorCode: 10,\n message: `Edit ${i + 1}: String to replace not found in file: \"${edit.old_string.substring(0, 100)}${edit.old_string.length > 100 ? '...' : ''}\"`,\n meta: {\n editIndex: i + 1,\n oldString: edit.old_string.substring(0, 200),\n },\n }\n }\n }\n }\n\n // Validate each edit\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i]\n if (edit.old_string === edit.new_string) {\n return {\n result: false,\n errorCode: 3,\n message: `Edit ${i + 1}: old_string and new_string cannot be the same`,\n }\n }\n }\n\n return { result: true }\n },\n async *call({ file_path, edits }, { readFileTimestamps }) {\n const startTime = Date.now()\n const workingDir = getCwd()\n const filePath = isAbsolute(file_path)\n ? resolve(file_path)\n : resolve(workingDir, file_path)\n\n try {\n // Read current file content (or empty for new files)\n let currentContent = ''\n let fileExists = existsSync(filePath)\n\n if (fileExists) {\n const encoding = detectFileEncoding(filePath)\n if (encoding === 'binary') {\n yield {\n type: 'result',\n data: 'Error: Cannot edit binary files',\n resultForAssistant: 'Error: Cannot edit binary files',\n }\n return\n }\n currentContent = readFileSync(filePath, 'utf-8')\n } else {\n // For new files, ensure parent directory exists\n const parentDir = dirname(filePath)\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true })\n }\n }\n\n // Apply all edits sequentially\n let modifiedContent = currentContent\n const appliedEdits = []\n\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i]\n const { old_string, new_string, replace_all } = edit\n\n try {\n const result = applyContentEdit(\n modifiedContent,\n old_string,\n new_string,\n replace_all,\n )\n modifiedContent = result.newContent\n appliedEdits.push({\n editIndex: i + 1,\n success: true,\n old_string: old_string.substring(0, 100),\n new_string: new_string.substring(0, 100),\n occurrences: result.occurrences,\n })\n } catch (error) {\n // If any edit fails, abort the entire operation\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error'\n yield {\n type: 'result',\n data: `Error in edit ${i + 1}: ${errorMessage}`,\n resultForAssistant: `Error in edit ${i + 1}: ${errorMessage}`,\n }\n return\n }\n }\n\n // Write the modified content\n const lineEndings = fileExists ? detectLineEndings(currentContent) : 'LF'\n const encoding = fileExists ? detectFileEncoding(filePath) : 'utf8'\n writeTextContent(filePath, modifiedContent, encoding, lineEndings)\n\n // Record Agent edit operation for file freshness tracking\n recordFileEdit(filePath, modifiedContent)\n\n // Update readFileTimestamps to prevent stale file warnings\n readFileTimestamps[filePath] = Date.now()\n\n // Emit file edited event for system reminders\n emitReminderEvent('file:edited', {\n filePath,\n edits: edits.map(e => ({\n oldString: e.old_string,\n newString: e.new_string,\n })),\n originalContent: currentContent,\n newContent: modifiedContent,\n timestamp: Date.now(),\n operation: fileExists ? 'update' : 'create',\n })\n\n // Generate result data\n const relativePath = relative(workingDir, filePath)\n const summary = `Successfully applied ${edits.length} edits to ${relativePath}`\n\n const structuredPatch = getPatch({\n filePath: file_path,\n fileContents: currentContent,\n oldStr: currentContent,\n newStr: modifiedContent,\n })\n\n const resultData = {\n filePath: file_path,\n wasNewFile: !fileExists,\n editsApplied: appliedEdits,\n totalEdits: edits.length,\n summary,\n structuredPatch,\n }\n\n // Log the operation\n\n yield {\n type: 'result',\n data: resultData,\n resultForAssistant: summary,\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error occurred'\n const errorResult = `Error applying multi-edit: ${errorMessage}`\n\n logError(error)\n\n yield {\n type: 'result',\n data: errorResult,\n resultForAssistant: errorResult,\n }\n }\n },\n} satisfies Tool<typeof inputSchema, any>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["encoding"]
|
|
7
7
|
}
|
|
@@ -89,11 +89,15 @@ const NotebookReadTool = {
|
|
|
89
89
|
};
|
|
90
90
|
},
|
|
91
91
|
renderResultForAssistant(data) {
|
|
92
|
+
if (!data || !Array.isArray(data)) {
|
|
93
|
+
return "No cells found in notebook";
|
|
94
|
+
}
|
|
92
95
|
return data.map((cell, index) => {
|
|
93
|
-
|
|
94
|
-
${cell.
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
if (!cell) return `Cell ${index + 1}: (empty)`;
|
|
97
|
+
let content = `Cell ${index + 1} (${cell.cellType || "unknown"}):
|
|
98
|
+
${cell.source || ""}`;
|
|
99
|
+
if (cell.outputs && Array.isArray(cell.outputs) && cell.outputs.length > 0) {
|
|
100
|
+
const outputText = cell.outputs.filter((output) => output != null).map((output) => output.text || "").filter(Boolean).join("\n");
|
|
97
101
|
if (outputText) {
|
|
98
102
|
content += `
|
|
99
103
|
Output:
|
|
@@ -136,7 +140,9 @@ function processOutput(output) {
|
|
|
136
140
|
case "display_data":
|
|
137
141
|
return {
|
|
138
142
|
output_type: output.output_type,
|
|
139
|
-
text: processOutputText(
|
|
143
|
+
text: processOutputText(
|
|
144
|
+
output.data?.["text/plain"]
|
|
145
|
+
),
|
|
140
146
|
image: output.data && extractImage(output.data)
|
|
141
147
|
};
|
|
142
148
|
case "error":
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/NotebookReadTool/NotebookReadTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import type {\n ImageBlockParam,\n TextBlockParam,\n} from '@anthropic-ai/sdk/resources/index.mjs'\n\nimport { existsSync, readFileSync } from 'fs'\nimport { Text } from 'ink'\nimport { extname, isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport {\n NotebookCellSource,\n NotebookContent,\n NotebookCell,\n NotebookOutputImage,\n NotebookCellSourceOutput,\n NotebookCellOutput,\n NotebookCellType,\n} from '@minto-types/notebook'\nimport { formatOutput } from '@tools/BashTool/utils'\nimport { getCwd } from '@utils/state'\nimport { findSimilarFile } from '@utils/file'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n notebook_path: z\n .string()\n .describe(\n 'The absolute path to the Jupyter notebook file to read (must be absolute, not relative)',\n ),\n})\n\ntype In = typeof inputSchema\ntype Out = NotebookCellSource[]\n\
|
|
5
|
-
"mappings": "AAKA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AACrB,SAAS,SAAS,YAAY,UAAU,eAAe;AACvD,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAW/C,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,uBAAuB;AAChC,SAAS,aAAa,cAAc;AACpC,SAAS,yBAAyB;AAElC,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,eAAe,EACZ,OAAO,EACP;AAAA,IACC;AAAA,EACF;AACJ,CAAC;
|
|
4
|
+
"sourcesContent": ["import type {\n ImageBlockParam,\n TextBlockParam,\n} from '@anthropic-ai/sdk/resources/index.mjs'\n\nimport { existsSync, readFileSync } from 'fs'\nimport { Text } from 'ink'\nimport { extname, isAbsolute, relative, resolve } from 'path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport {\n NotebookCellSource,\n NotebookContent,\n NotebookCell,\n NotebookOutputImage,\n NotebookCellSourceOutput,\n NotebookCellOutput,\n NotebookCellType,\n} from '@minto-types/notebook'\nimport { formatOutput } from '@tools/BashTool/utils'\nimport { getCwd } from '@utils/state'\nimport { findSimilarFile } from '@utils/file'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n notebook_path: z\n .string()\n .describe(\n 'The absolute path to the Jupyter notebook file to read (must be absolute, not relative)',\n ),\n})\n\ntype In = typeof inputSchema\ntype Out = NotebookCellSource[]\n\nexport const NotebookReadTool = {\n name: 'ReadNotebook',\n async description() {\n return DESCRIPTION\n },\n async prompt() {\n return PROMPT\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // NotebookReadTool is read-only, safe for concurrent execution\n },\n inputSchema,\n userFacingName() {\n return 'Read Notebook'\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ notebook_path }) {\n return !hasReadPermission(notebook_path)\n },\n async validateInput({ notebook_path }) {\n const fullFilePath = isAbsolute(notebook_path)\n ? notebook_path\n : resolve(getCwd(), notebook_path)\n\n if (!existsSync(fullFilePath)) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n if (extname(fullFilePath) !== '.ipynb') {\n return {\n result: false,\n message: 'File must be a Jupyter notebook (.ipynb file).',\n }\n }\n\n return { result: true }\n },\n renderToolUseMessage(input, { verbose }) {\n return `notebook_path: ${verbose ? input.notebook_path : relative(getCwd(), input.notebook_path)}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n\n renderToolResultMessage(content) {\n if (!content) {\n return <Text>No cells found in notebook</Text>\n }\n if (content.length < 1 || !content[0]) {\n return <Text>No cells found in notebook</Text>\n }\n return <Text>Read {content.length} cells</Text>\n },\n async *call({ notebook_path }) {\n const fullPath = isAbsolute(notebook_path)\n ? notebook_path\n : resolve(getCwd(), notebook_path)\n\n const content = readFileSync(fullPath, 'utf-8')\n const notebook = JSON.parse(content) as NotebookContent\n const language = notebook.metadata.language_info?.name ?? 'python'\n const cells = notebook.cells.map((cell, index) =>\n processCell(cell, index, language),\n )\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(cells),\n data: cells,\n }\n },\n renderResultForAssistant(data: NotebookCellSource[]) {\n // Guard against undefined or null data\n if (!data || !Array.isArray(data)) {\n return 'No cells found in notebook'\n }\n\n // Convert the complex structure to a string representation for the assistant\n return data\n .map((cell, index) => {\n if (!cell) return `Cell ${index + 1}: (empty)`\n let content = `Cell ${index + 1} (${cell.cellType || 'unknown'}):\\n${cell.source || ''}`\n if (cell.outputs && Array.isArray(cell.outputs) && cell.outputs.length > 0) {\n const outputText = cell.outputs\n .filter(output => output != null)\n .map(output => output.text || '')\n .filter(Boolean)\n .join('\\n')\n if (outputText) {\n content += `\\nOutput:\\n${outputText}`\n }\n }\n return content\n })\n .join('\\n\\n')\n },\n} satisfies Tool<In, Out>\n\nfunction processOutputText(text: string | string[] | undefined): string {\n if (!text) return ''\n const rawText = Array.isArray(text) ? text.join('') : text\n const { truncatedContent } = formatOutput(rawText)\n return truncatedContent\n}\n\nfunction extractImage(\n data: Record<string, unknown>,\n): NotebookOutputImage | undefined {\n if (typeof data['image/png'] === 'string') {\n return {\n image_data: data['image/png'] as string,\n media_type: 'image/png',\n }\n }\n if (typeof data['image/jpeg'] === 'string') {\n return {\n image_data: data['image/jpeg'] as string,\n media_type: 'image/jpeg',\n }\n }\n return undefined\n}\n\nfunction processOutput(output: NotebookCellOutput) {\n switch (output.output_type) {\n case 'stream':\n return {\n output_type: output.output_type,\n text: processOutputText(output.text),\n }\n case 'execute_result':\n case 'display_data':\n return {\n output_type: output.output_type,\n text: processOutputText(\n output.data?.['text/plain'] as string | string[] | undefined,\n ),\n image: output.data && extractImage(output.data),\n }\n case 'error':\n return {\n output_type: output.output_type,\n text: processOutputText(\n `${output.ename}: ${output.evalue}\\n${output.traceback.join('\\n')}`,\n ),\n }\n }\n}\n\nfunction processCell(\n cell: NotebookCell,\n index: number,\n language: string,\n): NotebookCellSource {\n const cellData: NotebookCellSource = {\n cell: index,\n cellType: cell.cell_type,\n source: Array.isArray(cell.source) ? cell.source.join('') : cell.source,\n language,\n execution_count: cell.execution_count,\n }\n\n if (cell.outputs?.length) {\n cellData.outputs = cell.outputs.map(processOutput)\n }\n\n return cellData\n}\n\nfunction cellContentToToolResult(cell: NotebookCellSource): TextBlockParam {\n const metadata = []\n if (cell.cellType !== 'code') {\n metadata.push(`<cell_type>${cell.cellType}</cell_type>`)\n }\n if (cell.language !== 'python' && cell.cellType === 'code') {\n metadata.push(`<language>${cell.language}</language>`)\n }\n const cellContent = `<cell ${cell.cell}>${metadata.join('')}${cell.source}</cell ${cell.cell}>`\n return {\n text: cellContent,\n type: 'text',\n }\n}\n\nfunction cellOutputToToolResult(output: NotebookCellSourceOutput) {\n const outputs: (TextBlockParam | ImageBlockParam)[] = []\n if (output.text) {\n outputs.push({\n text: `\\n${output.text}`,\n type: 'text',\n })\n }\n if (output.image) {\n outputs.push({\n type: 'image',\n source: {\n data: output.image.image_data,\n media_type: output.image.media_type,\n type: 'base64',\n },\n })\n }\n return outputs\n}\n\nfunction getToolResultFromCell(cell: NotebookCellSource) {\n const contentResult = cellContentToToolResult(cell)\n const outputResults = cell.outputs?.flatMap(cellOutputToToolResult)\n return [contentResult, ...(outputResults ?? [])]\n}\n\nexport function isNotebookCellType(\n value: string | null,\n): value is NotebookCellType {\n return value === 'code' || value === 'markdown'\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
} from "../../utils/skillLoader.js";
|
|
12
12
|
import { DESCRIPTION, PROMPT } from "./prompt.js";
|
|
13
13
|
const inputSchema = z.strictObject({
|
|
14
|
-
action: z.enum(["list", "load", "search"]).describe(
|
|
14
|
+
action: z.enum(["list", "load", "search"]).describe(
|
|
15
|
+
"Action to perform: list all skills, load a specific skill, or search skills"
|
|
16
|
+
),
|
|
15
17
|
name: z.string().optional().describe('Name of skill to load (required for "load" action)'),
|
|
16
18
|
query: z.string().optional().describe('Search query (required for "search" action)')
|
|
17
19
|
});
|
|
@@ -44,7 +46,9 @@ const SkillTool = {
|
|
|
44
46
|
if (output.skills.length === 0) {
|
|
45
47
|
return "No skills available. Install plugins with skills to use this feature.";
|
|
46
48
|
}
|
|
47
|
-
const skillsList = output.skills.map(
|
|
49
|
+
const skillsList = output.skills.map(
|
|
50
|
+
(skill) => `- **${skill.name}** (${skill.plugin}): ${skill.description}`
|
|
51
|
+
).join("\n");
|
|
48
52
|
return `Available skills (${output.skills.length}):
|
|
49
53
|
|
|
50
54
|
${skillsList}
|
|
@@ -55,7 +59,9 @@ Use the "load" action to get full skill content.`;
|
|
|
55
59
|
if (output.skills.length === 0) {
|
|
56
60
|
return `No skills found matching "${output.query}".`;
|
|
57
61
|
}
|
|
58
|
-
const skillsList = output.skills.map(
|
|
62
|
+
const skillsList = output.skills.map(
|
|
63
|
+
(skill) => `- **${skill.name}** (${skill.plugin}): ${skill.description}`
|
|
64
|
+
).join("\n");
|
|
59
65
|
return `Skills matching "${output.query}" (${output.skills.length}):
|
|
60
66
|
|
|
61
67
|
${skillsList}
|
|
@@ -95,14 +101,20 @@ ${skill.content}
|
|
|
95
101
|
return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
|
|
96
102
|
},
|
|
97
103
|
renderToolResultMessage(output) {
|
|
104
|
+
if (!output || !output.type) {
|
|
105
|
+
return /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, null, "Skill operation completed"));
|
|
106
|
+
}
|
|
98
107
|
if (output.type === "list") {
|
|
99
|
-
|
|
108
|
+
const skills = output.skills || [];
|
|
109
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: "green" }, "\u2713 Found ", skills.length, " skill", skills.length !== 1 ? "s" : ""), skills.slice(0, 10).map((skill, i) => /* @__PURE__ */ React.createElement(Box, { key: i, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, skill?.name || "Unknown"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " (", skill?.plugin || "Unknown", ")")), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", skill?.description || ""))), skills.length > 10 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ... and ", skills.length - 10, " more"));
|
|
100
110
|
}
|
|
101
111
|
if (output.type === "search") {
|
|
102
|
-
|
|
112
|
+
const skills = output.skills || [];
|
|
113
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: "green" }, "\u2713 Found ", skills.length, " skill", skills.length !== 1 ? "s" : "", ' matching "', output.query || "", '"'), skills.slice(0, 10).map((skill, i) => /* @__PURE__ */ React.createElement(Box, { key: i, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { bold: true }, skill?.name || "Unknown"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " (", skill?.plugin || "Unknown", ")")), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", skill?.description || ""))), skills.length > 10 && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ... and ", skills.length - 10, " more"));
|
|
103
114
|
}
|
|
104
115
|
if (output.type === "load") {
|
|
105
|
-
|
|
116
|
+
const skill = output.skill || { name: "", description: "", plugin: "", content: "" };
|
|
117
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: "green" }, "\u2713 Loaded skill: ", skill.name || "Unknown"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " Plugin: ", skill.plugin || "Unknown"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", "Content: ", (skill.content || "").length, " characters"));
|
|
106
118
|
}
|
|
107
119
|
return /* @__PURE__ */ React.createElement(Box, { paddingLeft: 2 }, /* @__PURE__ */ React.createElement(Text, null, "Skill operation completed"));
|
|
108
120
|
},
|