@within-7/minto 0.4.1 → 0.4.2
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/Tool.js +7 -0
- package/dist/Tool.js.map +2 -2
- package/dist/commands/agents/AgentsCommand.js +1 -1
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/agents/constants.js +2 -2
- package/dist/commands/agents/constants.js.map +2 -2
- package/dist/commands/clear.js +4 -3
- package/dist/commands/clear.js.map +2 -2
- package/dist/commands/compact.js +2 -2
- package/dist/commands/compact.js.map +1 -1
- package/dist/commands/context.js +3 -1
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/login.js +128 -0
- package/dist/commands/login.js.map +7 -0
- package/dist/commands/memory.js +33 -82
- package/dist/commands/memory.js.map +2 -2
- package/dist/commands/quit.js +3 -1
- package/dist/commands/quit.js.map +2 -2
- package/dist/commands/resume.js +39 -239
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/tasks.js +1 -1
- package/dist/commands/tasks.js.map +2 -2
- package/dist/commands/terminalSetup.js +6 -2
- package/dist/commands/terminalSetup.js.map +2 -2
- package/dist/commands.js +2 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/AgentDetailView.js +126 -0
- package/dist/components/AgentDetailView.js.map +7 -0
- package/dist/components/AgentThinkingBlock.js +1 -1
- package/dist/components/AgentThinkingBlock.js.map +2 -2
- package/dist/components/AgentViewBanner.js +22 -0
- package/dist/components/AgentViewBanner.js.map +7 -0
- package/dist/components/HeaderBar.js +1 -1
- package/dist/components/HeaderBar.js.map +2 -2
- package/dist/components/Help.js +8 -1
- package/dist/components/Help.js.map +2 -2
- package/dist/components/HotkeyHelpPanel.js +26 -8
- package/dist/components/HotkeyHelpPanel.js.map +2 -2
- package/dist/components/IdleNotificationBar.js +10 -0
- package/dist/components/IdleNotificationBar.js.map +7 -0
- package/dist/components/ModelSelector/ModelSelector.js +55 -20
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/PromptInput.js +186 -115
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/RewindPanel.js +272 -0
- package/dist/components/RewindPanel.js.map +7 -0
- package/dist/components/Spinner.js +10 -21
- package/dist/components/Spinner.js.map +2 -2
- package/dist/components/StreamingTextPreview.js +29 -0
- package/dist/components/StreamingTextPreview.js.map +7 -0
- package/dist/components/SubagentBlock.js +3 -2
- package/dist/components/SubagentBlock.js.map +2 -2
- package/dist/components/SubagentProgress.js +4 -4
- package/dist/components/SubagentProgress.js.map +2 -2
- package/dist/components/TabbedListView/SearchInput.js +1 -1
- package/dist/components/TabbedListView/SearchInput.js.map +2 -2
- package/dist/components/TabbedListView/TabbedListView.js +87 -41
- package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
- package/dist/components/TaskCard.js +4 -4
- package/dist/components/TaskCard.js.map +2 -2
- package/dist/components/TeamMemberPanel.js +107 -0
- package/dist/components/TeamMemberPanel.js.map +7 -0
- package/dist/components/ThinkingSelector.js +84 -0
- package/dist/components/ThinkingSelector.js.map +7 -0
- package/dist/components/TitledDivider.js +26 -0
- package/dist/components/TitledDivider.js.map +7 -0
- package/dist/components/TodoPanel.js +31 -30
- package/dist/components/TodoPanel.js.map +2 -2
- package/dist/components/TokenWarning.js +28 -7
- package/dist/components/TokenWarning.js.map +2 -2
- package/dist/components/messages/AssistantTextMessage.js +5 -2
- package/dist/components/messages/AssistantTextMessage.js.map +2 -2
- package/dist/components/messages/AssistantToolUseMessage.js +9 -1
- package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
- package/dist/components/messages/DefaultToolResultFallback.js +11 -0
- package/dist/components/messages/DefaultToolResultFallback.js.map +7 -0
- package/dist/components/messages/ParallelTasksGroupView.js +14 -6
- package/dist/components/messages/ParallelTasksGroupView.js.map +2 -2
- package/dist/components/messages/TaskInModuleView.js +27 -27
- package/dist/components/messages/TaskInModuleView.js.map +2 -2
- package/dist/components/messages/UserGuidanceMessage.js +26 -0
- package/dist/components/messages/UserGuidanceMessage.js.map +7 -0
- package/dist/components/messages/UserPromptMessage.js +2 -1
- package/dist/components/messages/UserPromptMessage.js.map +2 -2
- package/dist/components/messages/UserTeamNotificationMessage.js +91 -0
- package/dist/components/messages/UserTeamNotificationMessage.js.map +7 -0
- package/dist/components/messages/UserTextMessage.js +8 -0
- package/dist/components/messages/UserTextMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolRejectMessage.js +4 -2
- package/dist/components/messages/UserToolResultMessage/UserToolRejectMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolResultMessage.js +18 -1
- package/dist/components/messages/UserToolResultMessage/UserToolResultMessage.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js +12 -1
- package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js.map +2 -2
- package/dist/components/permissions/PermissionRequest.js +4 -0
- package/dist/components/permissions/PermissionRequest.js.map +2 -2
- package/dist/components/permissions/PlanApprovalRequest.js +164 -0
- package/dist/components/permissions/PlanApprovalRequest.js.map +7 -0
- package/dist/constants/agentTeams.js +17 -0
- package/dist/constants/agentTeams.js.map +7 -0
- package/dist/constants/macros.js +2 -1
- package/dist/constants/macros.js.map +2 -2
- package/dist/constants/prompts/agentPrompt.js +1 -0
- package/dist/constants/prompts/agentPrompt.js.map +2 -2
- package/dist/constants/prompts/autoMemory.js +39 -0
- package/dist/constants/prompts/autoMemory.js.map +7 -0
- package/dist/constants/prompts/codeConventions.js +1 -13
- package/dist/constants/prompts/codeConventions.js.map +2 -2
- package/dist/constants/prompts/doingTasks.js +21 -2
- package/dist/constants/prompts/doingTasks.js.map +2 -2
- package/dist/constants/prompts/envInfo.js +6 -7
- package/dist/constants/prompts/envInfo.js.map +2 -2
- package/dist/constants/prompts/index.js +27 -5
- package/dist/constants/prompts/index.js.map +2 -2
- package/dist/constants/prompts/taskManagement.js +2 -43
- package/dist/constants/prompts/taskManagement.js.map +2 -2
- package/dist/constants/prompts/teamOverlays.js +50 -0
- package/dist/constants/prompts/teamOverlays.js.map +7 -0
- package/dist/constants/prompts/toneAndStyle.js +4 -29
- package/dist/constants/prompts/toneAndStyle.js.map +2 -2
- package/dist/constants/prompts/toolUsagePolicy.js +7 -22
- package/dist/constants/prompts/toolUsagePolicy.js.map +2 -2
- package/dist/constants/toolInputExamples.js +2 -2
- package/dist/constants/toolInputExamples.js.map +2 -2
- package/dist/context.js +39 -6
- package/dist/context.js.map +2 -2
- package/dist/core/backupManager.js +1 -1
- package/dist/core/backupManager.js.map +2 -2
- package/dist/core/permissions/rules/planModeRule.js +1 -1
- package/dist/core/permissions/rules/planModeRule.js.map +1 -1
- package/dist/core/permissions/rules/safeModeRule.js +1 -1
- package/dist/core/permissions/rules/safeModeRule.js.map +1 -1
- package/dist/engine/AgentEngine.js +902 -0
- package/dist/engine/AgentEngine.js.map +7 -0
- package/dist/engine/EngineRegistry.js +89 -0
- package/dist/engine/EngineRegistry.js.map +7 -0
- package/dist/engine/foregroundAdapter.js +191 -0
- package/dist/engine/foregroundAdapter.js.map +7 -0
- package/dist/engine/index.js +15 -0
- package/dist/engine/index.js.map +7 -0
- package/dist/engine/types.js +1 -0
- package/dist/engine/types.js.map +7 -0
- package/dist/entrypoints/cli.js +410 -79
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/hooks/useAgentEngine.js +129 -0
- package/dist/hooks/useAgentEngine.js.map +7 -0
- package/dist/hooks/useAgentTokenStats.js +0 -16
- package/dist/hooks/useAgentTokenStats.js.map +2 -2
- package/dist/hooks/useCanUseTool.js +47 -2
- package/dist/hooks/useCanUseTool.js.map +2 -2
- package/dist/hooks/useDeferredLoading.js +4 -1
- package/dist/hooks/useDeferredLoading.js.map +2 -2
- package/dist/hooks/useIdleNotifications.js +66 -0
- package/dist/hooks/useIdleNotifications.js.map +7 -0
- package/dist/hooks/useSessionTracking.js +9 -7
- package/dist/hooks/useSessionTracking.js.map +2 -2
- package/dist/hooks/useTeamMembers.js +51 -0
- package/dist/hooks/useTeamMembers.js.map +7 -0
- package/dist/i18n/locales/en.js +77 -12
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +77 -12
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/messages.js.map +2 -2
- package/dist/permissions.js +113 -7
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +135 -37
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +504 -361
- package/dist/screens/REPL.js.map +3 -3
- package/dist/screens/ResumeConversation.js +199 -14
- package/dist/screens/ResumeConversation.js.map +2 -2
- package/dist/services/adapters/base.js.map +1 -1
- package/dist/services/agentTeams/backends/headless.js +108 -0
- package/dist/services/agentTeams/backends/headless.js.map +7 -0
- package/dist/services/agentTeams/backends/inProcess.js +102 -0
- package/dist/services/agentTeams/backends/inProcess.js.map +7 -0
- package/dist/services/agentTeams/backends/resolver.js +18 -0
- package/dist/services/agentTeams/backends/resolver.js.map +7 -0
- package/dist/services/agentTeams/backends/tmux.js +168 -0
- package/dist/services/agentTeams/backends/tmux.js.map +7 -0
- package/dist/services/agentTeams/backends/types.js +1 -0
- package/dist/services/agentTeams/backends/types.js.map +7 -0
- package/dist/services/agentTeams/heartbeat.js +88 -0
- package/dist/services/agentTeams/heartbeat.js.map +7 -0
- package/dist/services/agentTeams/index.js +42 -2
- package/dist/services/agentTeams/index.js.map +2 -2
- package/dist/services/agentTeams/injectionChannel.js +105 -0
- package/dist/services/agentTeams/injectionChannel.js.map +7 -0
- package/dist/services/agentTeams/mailbox.js +410 -30
- package/dist/services/agentTeams/mailbox.js.map +2 -2
- package/dist/services/agentTeams/messageFormatter.js +80 -0
- package/dist/services/agentTeams/messageFormatter.js.map +7 -0
- package/dist/services/agentTeams/permissionDelegation.js +71 -0
- package/dist/services/agentTeams/permissionDelegation.js.map +7 -0
- package/dist/services/agentTeams/teamEvents.js +45 -0
- package/dist/services/agentTeams/teamEvents.js.map +7 -0
- package/dist/services/agentTeams/teamManager.js +251 -34
- package/dist/services/agentTeams/teamManager.js.map +2 -2
- package/dist/services/agentTeams/teamTaskStore.js +290 -61
- package/dist/services/agentTeams/teamTaskStore.js.map +2 -2
- package/dist/services/agentTeams/teammateSpawner.js +99 -18
- package/dist/services/agentTeams/teammateSpawner.js.map +2 -2
- package/dist/services/hookExecutor.js +51 -8
- package/dist/services/hookExecutor.js.map +2 -2
- package/dist/services/llm/anthropicProvider.js +56 -59
- package/dist/services/llm/anthropicProvider.js.map +2 -2
- package/dist/services/llm/dispatch.js +24 -5
- package/dist/services/llm/dispatch.js.map +2 -2
- package/dist/services/llm/openaiProvider.js +115 -136
- package/dist/services/llm/openaiProvider.js.map +3 -3
- package/dist/services/llm/types.js +89 -15
- package/dist/services/llm/types.js.map +2 -2
- package/dist/services/mcpClient.js +80 -4
- package/dist/services/mcpClient.js.map +2 -2
- package/dist/services/mintoAuth.js +299 -0
- package/dist/services/mintoAuth.js.map +7 -0
- package/dist/services/oauth.js +3 -3
- package/dist/services/oauth.js.map +2 -2
- package/dist/services/openai.js +91 -20
- package/dist/services/openai.js.map +2 -2
- package/dist/services/plugins/pluginRuntime.js +11 -5
- package/dist/services/plugins/pluginRuntime.js.map +2 -2
- package/dist/services/plugins/pluginValidation.js +4 -2
- package/dist/services/plugins/pluginValidation.js.map +2 -2
- package/dist/services/sandbox/sandboxController.js +11 -3
- package/dist/services/sandbox/sandboxController.js.map +2 -2
- package/dist/services/sessionMemoryInjector.js +77 -0
- package/dist/services/sessionMemoryInjector.js.map +7 -0
- package/dist/services/systemReminder.js +130 -8
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/services/taskStore.js +199 -8
- package/dist/services/taskStore.js.map +3 -3
- package/dist/services/topicDetector.js +169 -0
- package/dist/services/topicDetector.js.map +7 -0
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +0 -13
- package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
- package/dist/tools/BashTool/BashTool.js +51 -28
- package/dist/tools/BashTool/BashTool.js.map +2 -2
- package/dist/tools/BashTool/prompt.js +95 -118
- package/dist/tools/BashTool/prompt.js.map +2 -2
- package/dist/tools/BashTool/utils.js +39 -1
- package/dist/tools/BashTool/utils.js.map +2 -2
- package/dist/tools/EnterWorktreeTool/EnterWorktreeTool.js +121 -0
- package/dist/tools/EnterWorktreeTool/EnterWorktreeTool.js.map +7 -0
- package/dist/tools/EnterWorktreeTool/prompt.js +22 -0
- package/dist/tools/EnterWorktreeTool/prompt.js.map +7 -0
- package/dist/tools/FileEditTool/FileEditTool.js +9 -4
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileEditTool/prompt.js +3 -7
- package/dist/tools/FileEditTool/prompt.js.map +2 -2
- package/dist/tools/FileReadTool/FileReadTool.js +125 -3
- package/dist/tools/FileReadTool/FileReadTool.js.map +2 -2
- package/dist/tools/FileReadTool/prompt.js +1 -2
- package/dist/tools/FileReadTool/prompt.js.map +2 -2
- package/dist/tools/FileWriteTool/prompt.js +3 -5
- package/dist/tools/FileWriteTool/prompt.js.map +2 -2
- package/dist/tools/GlobTool/GlobTool.js +3 -2
- package/dist/tools/GlobTool/GlobTool.js.map +2 -2
- package/dist/tools/GrepTool/GrepTool.js +16 -5
- package/dist/tools/GrepTool/GrepTool.js.map +2 -2
- package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +2 -2
- package/dist/tools/MCPSearchTool/MCPSearchTool.js +172 -0
- package/dist/tools/MCPSearchTool/MCPSearchTool.js.map +7 -0
- package/dist/tools/MCPSearchTool/prompt.js +77 -0
- package/dist/tools/MCPSearchTool/prompt.js.map +7 -0
- package/dist/tools/MultiEditTool/prompt.js +4 -7
- package/dist/tools/MultiEditTool/prompt.js.map +2 -2
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js +12 -8
- package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js +54 -1
- package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +2 -2
- package/dist/tools/PlanModeTool/prompt.js +23 -74
- package/dist/tools/PlanModeTool/prompt.js.map +2 -2
- package/dist/tools/SendMessageTool/SendMessageTool.js +341 -0
- package/dist/tools/SendMessageTool/SendMessageTool.js.map +7 -0
- package/dist/tools/SendMessageTool/prompt.js +44 -0
- package/dist/tools/SendMessageTool/prompt.js.map +7 -0
- package/dist/tools/TaskCreateTool/prompt.js +15 -4
- package/dist/tools/TaskCreateTool/prompt.js.map +2 -2
- package/dist/tools/TaskListTool/prompt.js +18 -3
- package/dist/tools/TaskListTool/prompt.js.map +2 -2
- package/dist/tools/TaskOutputTool/prompt.js +4 -3
- package/dist/tools/TaskOutputTool/prompt.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +762 -98
- package/dist/tools/TaskTool/TaskTool.js.map +3 -3
- package/dist/tools/TaskTool/constants.js +8 -2
- package/dist/tools/TaskTool/constants.js.map +2 -2
- package/dist/tools/TaskTool/prompt.js +74 -70
- package/dist/tools/TaskTool/prompt.js.map +2 -2
- package/dist/tools/TaskUpdateTool/TaskUpdateTool.js +15 -1
- package/dist/tools/TaskUpdateTool/TaskUpdateTool.js.map +2 -2
- package/dist/tools/TeamCreateTool/TeamCreateTool.js +129 -0
- package/dist/tools/TeamCreateTool/TeamCreateTool.js.map +7 -0
- package/dist/tools/TeamCreateTool/prompt.js +58 -0
- package/dist/tools/TeamCreateTool/prompt.js.map +7 -0
- package/dist/tools/TeamDeleteTool/TeamDeleteTool.js +151 -0
- package/dist/tools/TeamDeleteTool/TeamDeleteTool.js.map +7 -0
- package/dist/tools/TeamDeleteTool/prompt.js +16 -0
- package/dist/tools/TeamDeleteTool/prompt.js.map +7 -0
- package/dist/tools/URLFetcherTool/URLFetcherTool.js +106 -15
- package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
- package/dist/tools/URLFetcherTool/prompt.js +3 -2
- package/dist/tools/URLFetcherTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/WebSearchTool.js +2 -1
- package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js +5 -4
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/tools.js +100 -20
- package/dist/tools.js.map +2 -2
- package/dist/types/PermissionMode.js +35 -6
- package/dist/types/PermissionMode.js.map +2 -2
- package/dist/types/hooks.js +2 -0
- package/dist/types/hooks.js.map +2 -2
- package/dist/types/plugin.js +2 -0
- package/dist/types/plugin.js.map +3 -3
- package/dist/utils/CircuitBreaker.js +15 -9
- package/dist/utils/CircuitBreaker.js.map +2 -2
- package/dist/utils/agentLoader.js +249 -112
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/animationManager.js +40 -3
- package/dist/utils/animationManager.js.map +2 -2
- package/dist/utils/ask.js +7 -6
- package/dist/utils/ask.js.map +2 -2
- package/dist/utils/atomicWrite.js +23 -0
- package/dist/utils/atomicWrite.js.map +7 -0
- package/dist/utils/autoCompactCore.js +73 -56
- package/dist/utils/autoCompactCore.js.map +2 -2
- package/dist/utils/autoMemoryPaths.js +89 -0
- package/dist/utils/autoMemoryPaths.js.map +7 -0
- package/dist/utils/config.js +63 -38
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/configSchema.js +13 -8
- package/dist/utils/configSchema.js.map +2 -2
- package/dist/utils/credentials/index.js +14 -0
- package/dist/utils/credentials/index.js.map +2 -2
- package/dist/utils/dualPath.js +24 -0
- package/dist/utils/dualPath.js.map +7 -0
- package/dist/utils/exit.js +66 -7
- package/dist/utils/exit.js.map +2 -2
- package/dist/utils/externalEditor.js +155 -0
- package/dist/utils/externalEditor.js.map +7 -0
- package/dist/utils/fileLock.js +67 -0
- package/dist/utils/fileLock.js.map +7 -0
- package/dist/utils/format.js +24 -14
- package/dist/utils/format.js.map +2 -2
- package/dist/utils/globalErrorHandler.js +5 -96
- package/dist/utils/globalErrorHandler.js.map +3 -3
- package/dist/utils/groupHandlers/parallelTasksHandler.js +5 -3
- package/dist/utils/groupHandlers/parallelTasksHandler.js.map +2 -2
- package/dist/utils/groupHandlers/taskHandler.js +2 -2
- package/dist/utils/groupHandlers/taskHandler.js.map +2 -2
- package/dist/utils/hookManager.js +64 -6
- package/dist/utils/hookManager.js.map +2 -2
- package/dist/utils/log.js +6 -2
- package/dist/utils/log.js.map +2 -2
- package/dist/utils/markdown.js +237 -19
- package/dist/utils/markdown.js.map +2 -2
- package/dist/utils/messageContextManager.js +18 -5
- package/dist/utils/messageContextManager.js.map +2 -2
- package/dist/utils/messageGroupManager.js +1 -1
- package/dist/utils/messageGroupManager.js.map +2 -2
- package/dist/utils/messages.js +104 -46
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/model.js +2 -2
- package/dist/utils/model.js.map +2 -2
- package/dist/utils/pasteCache.js +8 -4
- package/dist/utils/pasteCache.js.map +2 -2
- package/dist/utils/pluginLoader.js +18 -0
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/secureKeyStorage.js +36 -7
- package/dist/utils/secureKeyStorage.js.map +2 -2
- package/dist/utils/simpleMode.js +7 -0
- package/dist/utils/simpleMode.js.map +7 -0
- package/dist/utils/streamingState.js +11 -1
- package/dist/utils/streamingState.js.map +2 -2
- package/dist/utils/taskDisplayUtils.js +2 -1
- package/dist/utils/taskDisplayUtils.js.map +2 -2
- package/dist/utils/teamConfig.js +2 -2
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/thinking.js +6 -2
- package/dist/utils/thinking.js.map +3 -3
- package/dist/utils/tokenProgress.js +55 -0
- package/dist/utils/tokenProgress.js.map +7 -0
- package/dist/utils/toolRiskClassification.js +26 -17
- package/dist/utils/toolRiskClassification.js.map +2 -2
- package/dist/utils/tooling/toolError.js +12 -0
- package/dist/utils/tooling/toolError.js.map +7 -0
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +10 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/FileReadTool/FileReadTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { ImageBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { statSync } from 'node:fs'\nimport { Box, Text } from 'ink'\nimport * as path from 'node:path'\nimport { extname, relative } from 'node:path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport type { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport {\n addLineNumbers,\n findSimilarFile,\n normalizeFilePath,\n readTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport {\n recordFileRead,\n generateFileModificationReminder,\n} from '@services/fileFreshness'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\nimport { secureFileService } from '@utils/secureFile'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_OUTPUT_SIZE = 0.25 * 1024 * 1024 // 0.25MB in bytes\n\n// Common image extensions\nconst IMAGE_EXTENSIONS = new Set([\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.webp',\n])\n\n// Maximum dimensions for images\nconst MAX_WIDTH = 2000\nconst MAX_HEIGHT = 2000\nconst MAX_IMAGE_SIZE = 3.75 * 1024 * 1024 // 5MB in bytes, with base64 encoding\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once',\n ),\n limit: z\n .number()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once.',\n ),\n})\n\nexport const FileReadTool = {\n name: 'Read',\n async description() {\n return DESCRIPTION\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // FileRead is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Read'\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ file_path }) {\n return !hasReadPermission(file_path || getCwd())\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, ...rest } = input\n const entries = [\n ['file_path', verbose ? file_path : relative(getCwd(), file_path)],\n ...Object.entries(rest),\n ]\n return entries\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output || !output.type) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n\n const verbose = false // Set default value for verbose\n // TODO: Render recursively\n switch (output.type) {\n case 'image':\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>Read image</Text>\n </Box>\n </Box>\n )\n case 'text': {\n // Guard against missing file property\n if (!output.file) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n const { filePath, content, numLines } = output.file\n const contentWithFallback = content || '(No content)'\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Box flexDirection=\"column\">\n <HighlightedCode\n code={\n verbose\n ? contentWithFallback\n : contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')\n }\n language={extname(filePath || '').slice(1)}\n />\n {!verbose && (numLines || 0) > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{(numLines || 0) - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n )\n }\n default:\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n async validateInput({ file_path, offset, limit }) {\n const fullFilePath = normalizeFilePath(file_path)\n\n // Use secure file service to check if file exists and get file info\n const fileCheck = secureFileService.safeGetFileInfo(fullFilePath)\n if (!fileCheck.success) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n const stats = fileCheck.stats!\n const fileSize = stats.size\n const ext = path.extname(fullFilePath).toLowerCase()\n\n // Skip size check for image files - they have their own size limits\n if (!IMAGE_EXTENSIONS.has(ext)) {\n // If file is too large and no offset/limit provided\n if (fileSize > MAX_OUTPUT_SIZE && !offset && !limit) {\n return {\n result: false,\n message: formatFileSizeError(fileSize),\n meta: { fileSize },\n }\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, offset = 1, limit = undefined },\n { readFileTimestamps },\n ) {\n const ext = path.extname(file_path).toLowerCase()\n const fullFilePath = normalizeFilePath(file_path)\n\n // Record file read for freshness tracking\n recordFileRead(fullFilePath)\n\n // Emit file read event for system reminders\n emitReminderEvent('file:read', {\n filePath: fullFilePath,\n extension: ext,\n timestamp: Date.now(),\n })\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = Date.now()\n\n // Check for file modifications and generate reminder if needed\n const modificationReminder = generateFileModificationReminder(fullFilePath)\n if (modificationReminder) {\n emitReminderEvent('file:modified', {\n filePath: fullFilePath,\n reminder: modificationReminder,\n timestamp: Date.now(),\n })\n }\n\n // If it's an image file, process and return base64 encoded contents\n if (IMAGE_EXTENSIONS.has(ext)) {\n const data = await readImage(fullFilePath, ext)\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n // Handle offset properly - if offset is 0, don't subtract 1\n const lineOffset = offset === 0 ? 0 : offset - 1\n const { content, lineCount, totalLines } = readTextContent(\n fullFilePath,\n lineOffset,\n limit,\n )\n\n // Emit file:empty reminder if file exists but has no content\n if (content.length === 0 && totalLines === 0) {\n emitReminderEvent('file:empty', {\n filePath: fullFilePath,\n timestamp: Date.now(),\n })\n }\n\n // Emit file:truncated reminder if content was limited\n if (limit && totalLines > lineCount) {\n emitReminderEvent('file:truncated', {\n filePath: fullFilePath,\n limit: lineCount,\n totalLines,\n timestamp: Date.now(),\n })\n }\n\n // Add size validation after reading for non-image files\n if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {\n throw new Error(formatFileSizeError(content.length))\n }\n\n const data = {\n type: 'text' as const,\n file: {\n filePath: file_path,\n content: content,\n numLines: lineCount,\n startLine: offset,\n totalLines,\n },\n }\n\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant(data) {\n // Guard against undefined or null data\n if (!data || !data.type) {\n return 'File read completed'\n }\n\n switch (data.type) {\n case 'image':\n // Guard against missing file property\n if (!data.file) {\n return 'Image read completed'\n }\n return [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: data.file.base64 || '',\n media_type: data.file.type || 'image/png',\n },\n },\n ]\n case 'text':\n // Guard against missing file property\n if (!data.file) {\n return 'File read completed'\n }\n return addLineNumbers(data.file)\n default:\n return 'File read completed'\n }\n },\n} satisfies Tool\n\nconst formatFileSizeError = (sizeInBytes: number) =>\n `File content (${Math.round(sizeInBytes / 1024)}KB) exceeds maximum allowed size (${Math.round(MAX_OUTPUT_SIZE / 1024)}KB). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.`\n\nfunction createImageResponse(\n buffer: Buffer,\n ext: string,\n): {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n} {\n return {\n type: 'image',\n file: {\n base64: buffer.toString('base64'),\n type: `image/${ext.slice(1)}` as ImageBlockParam.Source['media_type'],\n },\n }\n}\n\nasync function readImage(\n filePath: string,\n ext: string,\n): Promise<{\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n}> {\n try {\n const stats = statSync(filePath)\n const sharp = (\n (await import('sharp')) as unknown as { default: typeof import('sharp') }\n ).default\n\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n const image = sharp(fileReadResult.content as Buffer)\n const metadata = await image.metadata()\n\n if (!metadata.width || !metadata.height) {\n if (stats.size > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n }\n\n // Calculate dimensions while maintaining aspect ratio\n let width = metadata.width || 0\n let height = metadata.height || 0\n\n // Check if the original file just works\n if (\n stats.size <= MAX_IMAGE_SIZE &&\n width <= MAX_WIDTH &&\n height <= MAX_HEIGHT\n ) {\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n\n if (width > MAX_WIDTH) {\n height = Math.round((height * MAX_WIDTH) / width)\n width = MAX_WIDTH\n }\n\n if (height > MAX_HEIGHT) {\n width = Math.round((width * MAX_HEIGHT) / height)\n height = MAX_HEIGHT\n }\n\n // Resize image and convert to buffer\n const resizedImageBuffer = await image\n .resize(width, height, {\n fit: 'inside',\n withoutEnlargement: true,\n })\n .toBuffer()\n\n // If still too large after resize, compress quality\n if (resizedImageBuffer.length > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n\n return createImageResponse(resizedImageBuffer, ext)\n } catch (e) {\n logError(e)\n // If any error occurs during processing, return original image\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,gBAAgB;AACzB,SAAS,KAAK,YAAY;AAC1B,YAAY,UAAU;AACtB,SAAS,SAAS,gBAAgB;AAClC,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAC/C,SAAS,uBAAuB;AAEhC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa,cAAc;AACpC,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAElC,MAAM,sBAAsB;AAC5B,MAAM,kBAAkB,OAAO,OAAO;AAGtC,MAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,MAAM,YAAY;AAClB,MAAM,aAAa;AACnB,MAAM,iBAAiB,OAAO,OAAO;
|
|
4
|
+
"sourcesContent": ["import { ImageBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'\nimport { statSync } from 'node:fs'\nimport { Box, Text } from 'ink'\nimport * as path from 'node:path'\nimport { extname, relative } from 'node:path'\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { HighlightedCode } from '@components/HighlightedCode'\nimport type { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport {\n addLineNumbers,\n findSimilarFile,\n normalizeFilePath,\n readTextContent,\n} from '@utils/file'\nimport { logError } from '@utils/log'\nimport { getTheme } from '@utils/theme'\nimport { emitReminderEvent } from '@services/systemReminder'\nimport {\n recordFileRead,\n generateFileModificationReminder,\n} from '@services/fileFreshness'\nimport { DESCRIPTION, PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\nimport { secureFileService } from '@utils/secureFile'\n\nconst MAX_LINES_TO_RENDER = 5\nconst MAX_OUTPUT_SIZE = 0.25 * 1024 * 1024 // 0.25MB in bytes\n\n// Common image extensions\nconst IMAGE_EXTENSIONS = new Set([\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.bmp',\n '.webp',\n])\n\n// Maximum dimensions for images\nconst MAX_WIDTH = 2000\nconst MAX_HEIGHT = 2000\nconst MAX_IMAGE_SIZE = 3.75 * 1024 * 1024 // 5MB in bytes, with base64 encoding\n\n// PDF file extension\nconst PDF_EXTENSION = '.pdf'\n\nconst inputSchema = z.strictObject({\n file_path: z.string().describe('The absolute path to the file to read'),\n offset: z\n .number()\n .optional()\n .describe(\n 'The line number to start reading from. Only provide if the file is too large to read at once',\n ),\n limit: z\n .number()\n .optional()\n .describe(\n 'The number of lines to read. Only provide if the file is too large to read at once.',\n ),\n pages: z\n .string()\n .optional()\n .describe(\n 'Page range for PDF files (e.g., \"1-5\", \"3\", \"10-20\"). Only applicable to PDF files. Maximum 20 pages per request.',\n ),\n})\n\nexport const FileReadTool = {\n name: 'Read',\n async description() {\n return DESCRIPTION\n },\n async prompt() {\n return PROMPT\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // FileRead is read-only, safe for concurrent execution\n },\n userFacingName() {\n return 'Read'\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ file_path }) {\n return !hasReadPermission(file_path || getCwd())\n },\n renderToolUseMessage(input, { verbose }) {\n const { file_path, ...rest } = input\n const entries = [\n ['file_path', verbose ? file_path : relative(getCwd(), file_path)],\n ...Object.entries(rest),\n ]\n return entries\n .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)\n .join(', ')\n },\n renderToolResultMessage(output) {\n // Guard against undefined or null output\n if (!output || !output.type) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n\n const verbose = false // Set default value for verbose\n // TODO: Render recursively\n switch (output.type) {\n case 'image':\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>Read image</Text>\n </Box>\n </Box>\n )\n case 'text': {\n // Guard against missing file property\n if (!output.file) {\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n const { filePath, content, numLines } = output.file\n const contentWithFallback = content || '(No content)'\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Box flexDirection=\"column\">\n <HighlightedCode\n code={\n verbose\n ? contentWithFallback\n : contentWithFallback\n .split('\\n')\n .slice(0, MAX_LINES_TO_RENDER)\n .filter(_ => _.trim() !== '')\n .join('\\n')\n }\n language={extname(filePath || '').slice(1)}\n />\n {!verbose && (numLines || 0) > MAX_LINES_TO_RENDER && (\n <Text color={getTheme().secondaryText}>\n ... (+{(numLines || 0) - MAX_LINES_TO_RENDER} lines)\n </Text>\n )}\n </Box>\n </Box>\n </Box>\n )\n }\n default:\n return (\n <Box justifyContent=\"space-between\" overflowX=\"hidden\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF </Text>\n <Text>File read completed</Text>\n </Box>\n </Box>\n )\n }\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n async validateInput({ file_path, offset, limit, pages }) {\n const fullFilePath = normalizeFilePath(file_path)\n\n // Use secure file service to check if file exists and get file info\n const fileCheck = secureFileService.safeGetFileInfo(fullFilePath)\n if (!fileCheck.success) {\n // Try to find a similar file with a different extension\n const similarFilename = findSimilarFile(fullFilePath)\n let message = 'File does not exist.'\n\n // If we found a similar file, suggest it to the assistant\n if (similarFilename) {\n message += ` Did you mean ${similarFilename}?`\n }\n\n return {\n result: false,\n message,\n }\n }\n\n const stats = fileCheck.stats!\n const fileSize = stats.size\n const ext = path.extname(fullFilePath).toLowerCase()\n\n // Skip size check for image files - they have their own size limits\n if (!IMAGE_EXTENSIONS.has(ext) && ext !== PDF_EXTENSION) {\n // If file is too large and no offset/limit provided\n if (fileSize > MAX_OUTPUT_SIZE && !offset && !limit) {\n return {\n result: false,\n message: formatFileSizeError(fileSize),\n meta: { fileSize },\n }\n }\n }\n\n // Validate pages parameter for PDF files\n if (pages && ext !== PDF_EXTENSION) {\n return {\n result: false,\n message:\n 'The \"pages\" parameter is only applicable to PDF files (.pdf).',\n }\n }\n\n // Large PDFs require the pages parameter\n if (ext === PDF_EXTENSION && fileSize > MAX_OUTPUT_SIZE * 4 && !pages) {\n return {\n result: false,\n message: `PDF file is large (${Math.round(fileSize / 1024)}KB). Please provide the \"pages\" parameter to read specific page ranges (e.g., pages: \"1-5\"). Maximum 20 pages per request.`,\n }\n }\n\n return { result: true }\n },\n async *call(\n { file_path, offset = 1, limit = undefined, pages = undefined },\n { readFileTimestamps },\n ) {\n const ext = path.extname(file_path).toLowerCase()\n const fullFilePath = normalizeFilePath(file_path)\n\n // Record file read for freshness tracking\n recordFileRead(fullFilePath)\n\n // Emit file read event for system reminders\n emitReminderEvent('file:read', {\n filePath: fullFilePath,\n extension: ext,\n timestamp: Date.now(),\n })\n\n // Update read timestamp, to invalidate stale writes\n readFileTimestamps[fullFilePath] = Date.now()\n\n // Check for file modifications and generate reminder if needed\n const modificationReminder = generateFileModificationReminder(fullFilePath)\n if (modificationReminder) {\n emitReminderEvent('file:modified', {\n filePath: fullFilePath,\n reminder: modificationReminder,\n timestamp: Date.now(),\n })\n }\n\n // If it's an image file, process and return base64 encoded contents\n if (IMAGE_EXTENSIONS.has(ext)) {\n const data = await readImage(fullFilePath, ext)\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n // Handle PDF files with optional page range\n if (ext === PDF_EXTENSION) {\n const pdfContent = await readPdfContent(fullFilePath, pages)\n const data = {\n type: 'text' as const,\n file: {\n filePath: file_path,\n content: pdfContent.text,\n numLines: pdfContent.text.split('\\n').length,\n startLine: 1,\n totalLines: pdfContent.text.split('\\n').length,\n },\n }\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n return\n }\n\n // Handle offset properly - if offset is 0, don't subtract 1\n const lineOffset = offset === 0 ? 0 : offset - 1\n const { content, lineCount, totalLines } = readTextContent(\n fullFilePath,\n lineOffset,\n limit,\n )\n\n // Emit file:empty reminder if file exists but has no content\n if (content.length === 0 && totalLines === 0) {\n emitReminderEvent('file:empty', {\n filePath: fullFilePath,\n timestamp: Date.now(),\n })\n }\n\n // Emit file:truncated reminder if content was limited\n if (limit && totalLines > lineCount) {\n emitReminderEvent('file:truncated', {\n filePath: fullFilePath,\n limit: lineCount,\n totalLines,\n timestamp: Date.now(),\n })\n }\n\n // Add size validation after reading for non-image files\n if (!IMAGE_EXTENSIONS.has(ext) && content.length > MAX_OUTPUT_SIZE) {\n throw new Error(formatFileSizeError(content.length))\n }\n\n const data = {\n type: 'text' as const,\n file: {\n filePath: file_path,\n content: content,\n numLines: lineCount,\n startLine: offset,\n totalLines,\n },\n }\n\n yield {\n type: 'result',\n data,\n resultForAssistant: this.renderResultForAssistant(data),\n }\n },\n renderResultForAssistant(data) {\n // Guard against undefined or null data\n if (!data || !data.type) {\n return 'File read completed'\n }\n\n switch (data.type) {\n case 'image':\n // Guard against missing file property\n if (!data.file) {\n return 'Image read completed'\n }\n return [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: data.file.base64 || '',\n media_type: data.file.type || 'image/png',\n },\n },\n ]\n case 'text':\n // Guard against missing file property\n if (!data.file) {\n return 'File read completed'\n }\n return addLineNumbers(data.file)\n default:\n return 'File read completed'\n }\n },\n} satisfies Tool\n\nconst formatFileSizeError = (sizeInBytes: number) =>\n `File content (${Math.round(sizeInBytes / 1024)}KB) exceeds maximum allowed size (${Math.round(MAX_OUTPUT_SIZE / 1024)}KB). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.`\n\nfunction createImageResponse(\n buffer: Buffer,\n ext: string,\n): {\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n} {\n return {\n type: 'image',\n file: {\n base64: buffer.toString('base64'),\n type: `image/${ext.slice(1)}` as ImageBlockParam.Source['media_type'],\n },\n }\n}\n\nasync function readImage(\n filePath: string,\n ext: string,\n): Promise<{\n type: 'image'\n file: { base64: string; type: ImageBlockParam.Source['media_type'] }\n}> {\n try {\n const stats = statSync(filePath)\n const sharp = (\n (await import('sharp')) as unknown as { default: typeof import('sharp') }\n ).default\n\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n const image = sharp(fileReadResult.content as Buffer)\n const metadata = await image.metadata()\n\n if (!metadata.width || !metadata.height) {\n if (stats.size > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n }\n\n // Calculate dimensions while maintaining aspect ratio\n let width = metadata.width || 0\n let height = metadata.height || 0\n\n // Check if the original file just works\n if (\n stats.size <= MAX_IMAGE_SIZE &&\n width <= MAX_WIDTH &&\n height <= MAX_HEIGHT\n ) {\n // Use secure file service to read the file\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n\n if (width > MAX_WIDTH) {\n height = Math.round((height * MAX_WIDTH) / width)\n width = MAX_WIDTH\n }\n\n if (height > MAX_HEIGHT) {\n width = Math.round((width * MAX_HEIGHT) / height)\n height = MAX_HEIGHT\n }\n\n // Resize image and convert to buffer\n const resizedImageBuffer = await image\n .resize(width, height, {\n fit: 'inside',\n withoutEnlargement: true,\n })\n .toBuffer()\n\n // If still too large after resize, compress quality\n if (resizedImageBuffer.length > MAX_IMAGE_SIZE) {\n const compressedBuffer = await image.jpeg({ quality: 80 }).toBuffer()\n return createImageResponse(compressedBuffer, 'jpeg')\n }\n\n return createImageResponse(resizedImageBuffer, ext)\n } catch (e) {\n logError(e)\n // If any error occurs during processing, return original image\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: MAX_IMAGE_SIZE,\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read image file: ${fileReadResult.error}`)\n }\n\n return createImageResponse(fileReadResult.content as Buffer, ext)\n }\n}\n\n/**\n * Parse a page range string like \"1-5\", \"3\", \"10-20\" into start/end numbers.\n * Maximum 20 pages per request.\n */\nfunction parsePagesRange(pages: string): { start: number; end: number } {\n const trimmed = pages.trim()\n const rangeMatch = trimmed.match(/^(\\d+)\\s*-\\s*(\\d+)$/)\n if (rangeMatch) {\n const start = parseInt(rangeMatch[1], 10)\n const end = parseInt(rangeMatch[2], 10)\n if (start < 1 || end < start) {\n throw new Error(\n `Invalid page range \"${pages}\": start must be >= 1 and end must be >= start`,\n )\n }\n if (end - start + 1 > 20) {\n throw new Error(\n `Page range \"${pages}\" exceeds maximum of 20 pages per request`,\n )\n }\n return { start, end }\n }\n const singleMatch = trimmed.match(/^(\\d+)$/)\n if (singleMatch) {\n const page = parseInt(singleMatch[1], 10)\n if (page < 1) {\n throw new Error(`Invalid page number \"${pages}\": must be >= 1`)\n }\n return { start: page, end: page }\n }\n throw new Error(\n `Invalid pages format \"${pages}\". Use \"N\" for a single page or \"N-M\" for a range.`,\n )\n}\n\n/**\n * Read PDF content with optional page range.\n * Uses pdf-parse if available, otherwise falls back to basic text extraction.\n */\nasync function readPdfContent(\n filePath: string,\n pages?: string,\n): Promise<{ text: string; numPages?: number }> {\n const pageRange = pages ? parsePagesRange(pages) : undefined\n\n try {\n // Try dynamic import of pdf-parse\n // Dynamic import \u2014 pdf-parse may not be installed\n const pdfParseModule = await (import('pdf-parse' as string) as Promise<any>)\n const pdfParse = pdfParseModule.default ?? pdfParseModule\n\n const fileReadResult = secureFileService.safeReadFile(filePath, {\n encoding: 'buffer' as BufferEncoding,\n maxFileSize: 50 * 1024 * 1024, // 50MB max for PDFs\n })\n\n if (!fileReadResult.success) {\n throw new Error(`Failed to read PDF file: ${fileReadResult.error}`)\n }\n\n const options: any = {}\n if (pageRange) {\n // pdf-parse uses 1-indexed page rendering callback\n options.pagerender = (pageData: any) => {\n const pageNum = pageData.pageIndex + 1\n if (pageNum >= pageRange.start && pageNum <= pageRange.end) {\n return pageData.getTextContent().then((textContent: any) => {\n return textContent.items.map((item: any) => item.str).join(' ')\n })\n }\n return Promise.resolve('') // Skip pages outside range\n }\n }\n\n const result = await pdfParse(fileReadResult.content as Buffer, options)\n\n let text = result.text || ''\n // Add page info header\n const header = pageRange\n ? `[PDF: ${filePath} | Pages ${pageRange.start}-${pageRange.end} of ${result.numpages}]`\n : `[PDF: ${filePath} | ${result.numpages} pages]`\n\n text = `${header}\\n\\n${text}`\n\n return { text, numPages: result.numpages }\n } catch (importError: any) {\n // pdf-parse not available \u2014 provide basic fallback\n if (\n importError?.code === 'MODULE_NOT_FOUND' ||\n importError?.message?.includes('Cannot find module')\n ) {\n // Read raw text content as a fallback (may contain some readable text)\n const { content, lineCount, totalLines } = readTextContent(\n filePath,\n 0,\n undefined,\n )\n if (content.trim()) {\n const header = `[PDF: ${filePath} | Raw text extraction (install pdf-parse for better results)]`\n return { text: `${header}\\n\\n${content}` }\n }\n return {\n text: `[PDF file: ${filePath}]\\n\\nPDF text extraction requires the pdf-parse package. Install it with:\\n bun add pdf-parse\\n\\nAlternatively, convert the PDF to text before reading.`,\n }\n }\n throw importError\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["data", "fileReadResult"]
|
|
7
7
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { NotebookReadTool } from "../NotebookReadTool/NotebookReadTool.js";
|
|
2
1
|
const MAX_LINES_TO_READ = 2e3;
|
|
3
2
|
const MAX_LINE_LENGTH = 2e3;
|
|
4
3
|
const DESCRIPTION = "Read a file from the local filesystem.";
|
|
@@ -13,7 +12,7 @@ Usage:
|
|
|
13
12
|
- Results are returned using cat -n format, with line numbers starting at 1
|
|
14
13
|
- This tool allows reading images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as the LLM is multimodal.
|
|
15
14
|
- This tool can read PDF files (.pdf). PDFs are processed page by page, extracting both text and visual content for analysis.
|
|
16
|
-
- For Jupyter notebooks (.ipynb files), use the
|
|
15
|
+
- For Jupyter notebooks (.ipynb files), use the NotebookRead instead, which returns all cells with their outputs, combining code, text, and visualizations.
|
|
17
16
|
- This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.
|
|
18
17
|
- You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.
|
|
19
18
|
- You will regularly be asked to read screenshots. If the user provides a path to a screenshot, ALWAYS use this tool to view the file at the path. This tool will work with all temporary file paths.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/FileReadTool/prompt.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "AAAA,
|
|
4
|
+
"sourcesContent": ["const MAX_LINES_TO_READ = 2000\nconst MAX_LINE_LENGTH = 2000\n\nexport const DESCRIPTION = 'Read a file from the local filesystem.'\nexport const PROMPT = `Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to ${MAX_LINES_TO_READ} lines starting from the beginning of the file\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than ${MAX_LINE_LENGTH} characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool allows reading images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as the LLM is multimodal.\n- This tool can read PDF files (.pdf). PDFs are processed page by page, extracting both text and visual content for analysis.\n- For Jupyter notebooks (.ipynb files), use the NotebookRead instead, which returns all cells with their outputs, combining code, text, and visualizations.\n- This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.\n- You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.\n- You will regularly be asked to read screenshots. If the user provides a path to a screenshot, ALWAYS use this tool to view the file at the path. This tool will work with all temporary file paths.\n- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`\n"],
|
|
5
|
+
"mappings": "AAAA,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;AAEjB,MAAM,cAAc;AACpB,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,+BAKS,iBAAiB;AAAA;AAAA,0BAEtB,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import { FileReadTool } from "../FileReadTool/FileReadTool.js";
|
|
2
|
-
import { LSTool } from "../lsTool/lsTool.js";
|
|
3
1
|
const PROMPT = `Writes a file to the local filesystem.
|
|
4
2
|
|
|
5
3
|
Usage:
|
|
6
4
|
- This tool will overwrite the existing file if there is one at the provided path.
|
|
7
|
-
- If this is an existing file, you MUST use the
|
|
5
|
+
- If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.
|
|
8
6
|
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
|
|
9
7
|
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
|
|
10
8
|
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
|
|
11
9
|
|
|
12
10
|
Before using this tool:
|
|
13
11
|
|
|
14
|
-
1. Use the
|
|
12
|
+
1. Use the Read tool to understand the file's contents and context
|
|
15
13
|
|
|
16
14
|
2. Directory Verification (only applicable when creating new files):
|
|
17
|
-
- Use the
|
|
15
|
+
- Use the LS tool to verify the parent directory exists and is the correct location`;
|
|
18
16
|
const DESCRIPTION = "Write a file to the local filesystem.";
|
|
19
17
|
export {
|
|
20
18
|
DESCRIPTION,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/FileWriteTool/prompt.ts"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["export const PROMPT = `Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\n\nBefore using this tool:\n\n1. Use the Read tool to understand the file's contents and context\n\n2. Directory Verification (only applicable when creating new files):\n - Use the LS tool to verify the parent directory exists and is the correct location`\n\nexport const DESCRIPTION = 'Write a file to the local filesystem.'\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBf,MAAM,cAAc;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,7 @@ import React from "react";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { Cost } from "../../components/Cost.js";
|
|
5
5
|
import { FallbackToolUseRejectedMessage } from "../../components/FallbackToolUseRejectedMessage.js";
|
|
6
|
+
import { DefaultToolResultFallback } from "../../components/messages/DefaultToolResultFallback.js";
|
|
6
7
|
import { CollapsibleHint } from "../../components/CollapsibleHint.js";
|
|
7
8
|
import { getCwd } from "../../utils/state.js";
|
|
8
9
|
import { glob } from "../../utils/file.js";
|
|
@@ -49,13 +50,13 @@ const GlobTool = {
|
|
|
49
50
|
},
|
|
50
51
|
renderToolResultMessage(output, { verbose }) {
|
|
51
52
|
if (!output) {
|
|
52
|
-
return /* @__PURE__ */ React.createElement(
|
|
53
|
+
return /* @__PURE__ */ React.createElement(DefaultToolResultFallback, { toolName: "Search" });
|
|
53
54
|
}
|
|
54
55
|
if (typeof output === "string") {
|
|
55
56
|
try {
|
|
56
57
|
output = JSON.parse(output);
|
|
57
58
|
} catch {
|
|
58
|
-
return /* @__PURE__ */ React.createElement(
|
|
59
|
+
return /* @__PURE__ */ React.createElement(DefaultToolResultFallback, { toolName: "Search" });
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
const numFiles = output?.numFiles ?? 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/GlobTool/GlobTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return
|
|
5
|
-
"mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,SAAS;AAClB,SAAS,YAAY;AACrB,SAAS,sCAAsC;AAC/C,SAAS,uBAAuB;AAEhC,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,EAAE,QAAQ,GAAG;AAE3C,QAAI,CAAC,QAAQ;AACX,
|
|
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 { DefaultToolResultFallback } from '@components/messages/DefaultToolResultFallback'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { glob } from '@utils/file'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { isAbsolute, relative, resolve } from 'path'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z.string().describe('The glob pattern to match files against'),\n path: z\n .string()\n .optional()\n .describe(\n 'The directory to search in. Defaults to the current working directory.',\n ),\n})\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n truncated: boolean\n}\n\nexport const GlobTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // GlobTool is read-only, safe for concurrent execution\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ pattern, path }, { verbose }) {\n const absolutePath = path\n ? isAbsolute(path)\n ? path\n : resolve(getCwd(), path)\n : undefined\n const relativePath = absolutePath\n ? relative(getCwd(), absolutePath)\n : undefined\n return `pattern: \"${pattern}\"${relativePath || verbose ? `, path: \"${verbose ? absolutePath : relativePath}\"` : ''}`\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return <DefaultToolResultFallback toolName=\"Search\" />\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 <DefaultToolResultFallback toolName=\"Search\" />\n }\n }\n\n const numFiles = output?.numFiles ?? 0\n const durationMs = output?.durationMs ?? 0\n // Show expand hint when there are many files and not in verbose mode\n const showExpandHint = !verbose && numFiles > 5\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ pattern, path }, { abortController }) {\n const start = Date.now()\n const { files, truncated } = await glob(\n pattern,\n path ?? getCwd(),\n { limit: 100, offset: 0 },\n abortController.signal,\n )\n const output: Output = {\n filenames: files,\n durationMs: Date.now() - start,\n numFiles: files.length,\n truncated,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n let result = output.filenames.join('\\n')\n if (output.filenames.length === 0) {\n result = 'No files found'\n }\n // Only add truncation message if results were actually truncated\n else if (output.truncated) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n },\n} satisfies Tool\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,6 +4,7 @@ import React from "react";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { Cost } from "../../components/Cost.js";
|
|
6
6
|
import { FallbackToolUseRejectedMessage } from "../../components/FallbackToolUseRejectedMessage.js";
|
|
7
|
+
import { DefaultToolResultFallback } from "../../components/messages/DefaultToolResultFallback.js";
|
|
7
8
|
import { CollapsibleHint } from "../../components/CollapsibleHint.js";
|
|
8
9
|
import { getCwd } from "../../utils/state.js";
|
|
9
10
|
import { getAbsolutePath, getAbsoluteAndRelativePaths } from "../../utils/file.js";
|
|
@@ -120,7 +121,7 @@ const GrepTool = {
|
|
|
120
121
|
},
|
|
121
122
|
renderToolResultMessage(output, { verbose }) {
|
|
122
123
|
if (!output) {
|
|
123
|
-
return /* @__PURE__ */ React.createElement(
|
|
124
|
+
return /* @__PURE__ */ React.createElement(DefaultToolResultFallback, { toolName: "Search" });
|
|
124
125
|
}
|
|
125
126
|
if (typeof output === "string") {
|
|
126
127
|
output = output;
|
|
@@ -367,14 +368,24 @@ ${filenames.slice(0, MAX_RESULTS).join("\n")}`;
|
|
|
367
368
|
}
|
|
368
369
|
}
|
|
369
370
|
}
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
371
|
+
const STAT_CONCURRENCY = 50;
|
|
372
|
+
const stats = new Array(
|
|
373
|
+
matchedFiles.length
|
|
374
|
+
).fill(null);
|
|
375
|
+
for (let i = 0; i < matchedFiles.length; i += STAT_CONCURRENCY) {
|
|
376
|
+
const batch = matchedFiles.slice(i, i + STAT_CONCURRENCY);
|
|
377
|
+
const batchResults = await Promise.all(
|
|
378
|
+
batch.map((f) => stat(f).catch(() => null))
|
|
379
|
+
);
|
|
380
|
+
batchResults.forEach((r, j) => {
|
|
381
|
+
stats[i + j] = r;
|
|
382
|
+
});
|
|
383
|
+
}
|
|
373
384
|
let matches = matchedFiles.map((file, i) => [file, stats[i]]).filter(([, s]) => s !== null).sort((a, b) => {
|
|
374
385
|
if (process.env.NODE_ENV === "test") {
|
|
375
386
|
return a[0].localeCompare(b[0]);
|
|
376
387
|
}
|
|
377
|
-
const timeComparison = (b[1]?.mtimeMs ?? 0) - (a[1]?.mtimeMs ?? 0);
|
|
388
|
+
const timeComparison = Number(b[1]?.mtimeMs ?? 0) - Number(a[1]?.mtimeMs ?? 0);
|
|
378
389
|
if (timeComparison === 0) {
|
|
379
390
|
return a[0].localeCompare(b[0]);
|
|
380
391
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/GrepTool/GrepTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { stat } from 'fs/promises'\nimport { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { getAbsolutePath, getAbsoluteAndRelativePaths } from '@utils/file'\nimport { ripGrepStreaming, ripGrepStreamingWithContent } from '@utils/ripgrep'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe(\n 'File or directory to search in (rg PATH). Defaults to current working directory.',\n ),\n // Backward compatible: 'include' is mapped to 'glob'\n include: z\n .string()\n .optional()\n .describe(\n 'File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\") - DEPRECATED: use glob instead',\n ),\n glob: z\n .string()\n .optional()\n .describe(\n 'Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\") - maps to rg --glob',\n ),\n type: z\n .string()\n .optional()\n .describe(\n 'File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types.',\n ),\n output_mode: z\n .enum(['content', 'files_with_matches', 'count'])\n .optional()\n .describe(\n 'Output mode: \"content\" shows matching lines, \"files_with_matches\" shows file paths (default), \"count\" shows match counts',\n ),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.',\n ),\n context: z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before and after each match (rg -C). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-A': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show after each match (rg -A). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-B': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before each match (rg -B). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-C': z.number().optional().describe('Alias for context.'),\n '-n': z\n .boolean()\n .optional()\n .describe(\n 'Show line numbers in output (rg -n). Requires output_mode: \"content\", ignored otherwise. Defaults to true.',\n ),\n '-i': z.boolean().optional().describe('Case insensitive search (rg -i)'),\n head_limit: z\n .number()\n .optional()\n .describe(\n 'Limit output to first N lines/entries, equivalent to \"| head -N\". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 0 (unlimited).',\n ),\n offset: z\n .number()\n .optional()\n .describe(\n 'Skip first N lines/entries before applying head_limit, equivalent to \"| tail -n +N | head -N\". Works across all output modes. Defaults to 0.',\n ),\n})\n\nconst MAX_RESULTS = 100\n\ntype Input = typeof inputSchema\ntype OutputMode = 'content' | 'files_with_matches' | 'count'\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n // For content mode\n content?: string\n numMatches?: number\n // For count mode\n counts?: Array<{ file: string; count: number }>\n // Output mode used\n outputMode: OutputMode\n}\n\nexport const GrepTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Grep is read-only, safe for concurrent execution\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage(input, { verbose }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode,\n multiline,\n context,\n '-i': caseInsensitive,\n } = input\n const { absolutePath, relativePath } = getAbsoluteAndRelativePaths(path)\n const effectiveGlob = glob || include\n\n const parts: string[] = [`pattern: \"${pattern}\"`]\n if (relativePath || verbose) {\n parts.push(`path: \"${verbose ? absolutePath : relativePath}\"`)\n }\n if (effectiveGlob) {\n parts.push(`glob: \"${effectiveGlob}\"`)\n }\n if (type) {\n parts.push(`type: \"${type}\"`)\n }\n if (output_mode && output_mode !== 'files_with_matches') {\n parts.push(`mode: \"${output_mode}\"`)\n }\n if (multiline) {\n parts.push('multiline')\n }\n if (context) {\n parts.push(`context: ${context}`)\n }\n if (caseInsensitive === false) {\n parts.push('case-sensitive')\n }\n\n return parts.join(', ')\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \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 numMatches = output?.numMatches ?? 0\n const durationMs = output?.durationMs ?? 0\n const outputMode = output?.outputMode ?? 'files_with_matches'\n\n // Different display based on output mode\n if (outputMode === 'content') {\n const showExpandHint = !verbose && numMatches > 10\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else if (outputMode === 'count') {\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Counted </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else {\n // files_with_matches (default)\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n }\n },\n renderResultForAssistant(output: Output) {\n const { numFiles, filenames, content, counts, numMatches, outputMode } =\n output\n\n if (outputMode === 'content') {\n if (!content || numMatches === 0) {\n return 'No matches found'\n }\n let result = content\n if (numMatches && numMatches > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else if (outputMode === 'count') {\n if (!counts || counts.length === 0) {\n return 'No matches found'\n }\n const totalMatches = counts.reduce((sum, c) => sum + c.count, 0)\n let result = `Found ${totalMatches} match${totalMatches === 1 ? '' : 'es'} in ${numFiles} file${numFiles === 1 ? '' : 's'}\\n`\n result += counts\n .slice(0, MAX_RESULTS)\n .map(c => `${c.file}:${c.count}`)\n .join('\\n')\n if (counts.length > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else {\n // files_with_matches (default)\n if (numFiles === 0) {\n return 'No files found'\n }\n let result = `Found ${numFiles} file${numFiles === 1 ? '' : 's'}\\n${filenames.slice(0, MAX_RESULTS).join('\\n')}`\n if (numFiles > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n }\n },\n async *call(input, { abortController }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode = 'files_with_matches',\n multiline,\n context,\n '-A': afterContext,\n '-B': beforeContext,\n '-C': contextAlias,\n '-n': lineNumbers = true,\n '-i': caseInsensitive = true,\n head_limit = 0,\n offset = 0,\n } = input\n\n const start = Date.now()\n const absolutePath = getAbsolutePath(path) || getCwd()\n\n // Use glob if provided, fall back to include for backward compatibility\n const effectiveGlob = glob || include\n\n // Determine effective context value (context takes precedence over -C alias)\n const effectiveContext = context ?? contextAlias\n\n // Build ripgrep arguments based on output mode\n const args: string[] = []\n\n // Case sensitivity\n if (caseInsensitive) {\n args.push('-i')\n }\n\n // Multiline mode\n if (multiline) {\n args.push('-U', '--multiline-dotall')\n }\n\n // File type filter\n if (type) {\n args.push('--type', type)\n }\n\n // Glob filter\n if (effectiveGlob) {\n args.push('--glob', effectiveGlob)\n }\n\n // Output mode specific flags\n if (output_mode === 'files_with_matches') {\n args.push('-l')\n } else if (output_mode === 'count') {\n args.push('-c')\n } else if (output_mode === 'content') {\n // For content mode, add context and line number flags\n if (lineNumbers) {\n args.push('-n')\n }\n if (effectiveContext !== undefined && effectiveContext > 0) {\n args.push('-C', String(effectiveContext))\n } else {\n if (beforeContext !== undefined && beforeContext > 0) {\n args.push('-B', String(beforeContext))\n }\n if (afterContext !== undefined && afterContext > 0) {\n args.push('-A', String(afterContext))\n }\n }\n }\n\n // Add the pattern\n args.push(pattern)\n\n // Different processing based on output mode\n if (output_mode === 'content') {\n // Content mode: collect full output with context\n const contentLines: string[] = []\n const filesWithMatches = new Set<string>()\n let matchCount = 0\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n contentLines.push(chunk.line)\n // Track files (lines starting with filename:linenum:)\n const fileMatch = chunk.line.match(/^([^:]+):(\\d+):/)\n if (fileMatch) {\n filesWithMatches.add(fileMatch[1])\n matchCount++\n }\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchCount} match${matchCount === 1 ? '' : 'es'} in ${filesWithMatches.size} file${filesWithMatches.size === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Apply offset and head_limit\n let resultLines = contentLines\n if (offset > 0) {\n resultLines = resultLines.slice(offset)\n }\n if (head_limit > 0) {\n resultLines = resultLines.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: Array.from(filesWithMatches),\n content: resultLines.join('\\n'),\n numMatches: matchCount,\n numFiles: filesWithMatches.size,\n durationMs: Date.now() - start,\n outputMode: 'content',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else if (output_mode === 'count') {\n // Count mode: collect file:count pairs\n const counts: Array<{ file: string; count: number }> = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n // Format: filename:count\n const match = chunk.line.match(/^(.+):(\\d+)$/)\n if (match) {\n counts.push({ file: match[1], count: parseInt(match[2], 10) })\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found matches in ${counts.length} file${counts.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n }\n\n // Sort by count descending, then by filename\n counts.sort((a, b) => {\n const countDiff = b.count - a.count\n if (countDiff !== 0) return countDiff\n return a.file.localeCompare(b.file)\n })\n\n // Apply offset and head_limit\n let resultCounts = counts\n if (offset > 0) {\n resultCounts = resultCounts.slice(offset)\n }\n if (head_limit > 0) {\n resultCounts = resultCounts.slice(0, head_limit)\n }\n\n const totalMatches = resultCounts.reduce((sum, c) => sum + c.count, 0)\n\n const output: Output = {\n filenames: resultCounts.map(c => c.file),\n counts: resultCounts,\n numMatches: totalMatches,\n numFiles: resultCounts.length,\n durationMs: Date.now() - start,\n outputMode: 'count',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else {\n // files_with_matches mode (default): list matching files\n const matchedFiles: string[] = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreaming(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'match') {\n matchedFiles.push(chunk.file)\n\n // Yield progress update periodically for real-time feedback\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchedFiles.length} file${matchedFiles.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Sort by modification time\n const stats = await Promise.all(\n matchedFiles.map(_ => stat(_).catch(() => null)),\n )\n let matches = matchedFiles\n .map((file, i) => [file, stats[i]] as const)\n .filter(([, s]) => s !== null)\n .sort((a, b) => {\n if (process.env.NODE_ENV === 'test') {\n // In tests, we always want to sort by filename, so that results are deterministic\n return a[0].localeCompare(b[0])\n }\n const timeComparison = (b[1]?.mtimeMs ?? 0) - (a[1]?.mtimeMs ?? 0)\n if (timeComparison === 0) {\n // Sort by filename as a tiebreaker\n return a[0].localeCompare(b[0])\n }\n return timeComparison\n })\n .map(_ => _[0])\n\n // Apply offset and head_limit\n if (offset > 0) {\n matches = matches.slice(offset)\n }\n if (head_limit > 0) {\n matches = matches.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: matches,\n durationMs: Date.now() - start,\n numFiles: matches.length,\n outputMode: 'files_with_matches',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n }\n },\n} satisfies Tool\n"],
|
|
5
|
-
"mappings": "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
|
|
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 { DefaultToolResultFallback } from '@components/messages/DefaultToolResultFallback'\nimport { CollapsibleHint } from '@components/CollapsibleHint'\nimport { Tool } from '@tool'\nimport { getCwd } from '@utils/state'\nimport { getAbsolutePath, getAbsoluteAndRelativePaths } from '@utils/file'\nimport { ripGrepStreaming, ripGrepStreamingWithContent } from '@utils/ripgrep'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { hasReadPermission } from '@utils/permissions/filesystem'\n\nconst inputSchema = z.strictObject({\n pattern: z\n .string()\n .describe('The regular expression pattern to search for in file contents'),\n path: z\n .string()\n .optional()\n .describe(\n 'File or directory to search in (rg PATH). Defaults to current working directory.',\n ),\n // Backward compatible: 'include' is mapped to 'glob'\n include: z\n .string()\n .optional()\n .describe(\n 'File pattern to include in the search (e.g. \"*.js\", \"*.{ts,tsx}\") - DEPRECATED: use glob instead',\n ),\n glob: z\n .string()\n .optional()\n .describe(\n 'Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\") - maps to rg --glob',\n ),\n type: z\n .string()\n .optional()\n .describe(\n 'File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types.',\n ),\n output_mode: z\n .enum(['content', 'files_with_matches', 'count'])\n .optional()\n .describe(\n 'Output mode: \"content\" shows matching lines, \"files_with_matches\" shows file paths (default), \"count\" shows match counts',\n ),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.',\n ),\n context: z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before and after each match (rg -C). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-A': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show after each match (rg -A). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-B': z\n .number()\n .optional()\n .describe(\n 'Number of lines to show before each match (rg -B). Requires output_mode: \"content\", ignored otherwise.',\n ),\n '-C': z.number().optional().describe('Alias for context.'),\n '-n': z\n .boolean()\n .optional()\n .describe(\n 'Show line numbers in output (rg -n). Requires output_mode: \"content\", ignored otherwise. Defaults to true.',\n ),\n '-i': z.boolean().optional().describe('Case insensitive search (rg -i)'),\n head_limit: z\n .number()\n .optional()\n .describe(\n 'Limit output to first N lines/entries, equivalent to \"| head -N\". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 0 (unlimited).',\n ),\n offset: z\n .number()\n .optional()\n .describe(\n 'Skip first N lines/entries before applying head_limit, equivalent to \"| tail -n +N | head -N\". Works across all output modes. Defaults to 0.',\n ),\n})\n\nconst MAX_RESULTS = 100\n\ntype Input = typeof inputSchema\ntype OutputMode = 'content' | 'files_with_matches' | 'count'\n\ntype Output = {\n durationMs: number\n numFiles: number\n filenames: string[]\n // For content mode\n content?: string\n numMatches?: number\n // For count mode\n counts?: Array<{ file: string; count: number }>\n // Output mode used\n outputMode: OutputMode\n}\n\nexport const GrepTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'Search'\n },\n inputSchema,\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Grep is read-only, safe for concurrent execution\n },\n async isEnabled() {\n return true\n },\n needsPermissions({ path }) {\n return !hasReadPermission(path || getCwd())\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage(input, { verbose }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode,\n multiline,\n context,\n '-i': caseInsensitive,\n } = input\n const { absolutePath, relativePath } = getAbsoluteAndRelativePaths(path)\n const effectiveGlob = glob || include\n\n const parts: string[] = [`pattern: \"${pattern}\"`]\n if (relativePath || verbose) {\n parts.push(`path: \"${verbose ? absolutePath : relativePath}\"`)\n }\n if (effectiveGlob) {\n parts.push(`glob: \"${effectiveGlob}\"`)\n }\n if (type) {\n parts.push(`type: \"${type}\"`)\n }\n if (output_mode && output_mode !== 'files_with_matches') {\n parts.push(`mode: \"${output_mode}\"`)\n }\n if (multiline) {\n parts.push('multiline')\n }\n if (context) {\n parts.push(`context: ${context}`)\n }\n if (caseInsensitive === false) {\n parts.push('case-sensitive')\n }\n\n return parts.join(', ')\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output, { verbose }) {\n // Guard against undefined or null output\n if (!output) {\n return <DefaultToolResultFallback toolName=\"Search\" />\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n // Convert string to Output type using tmpDeserializeOldLogResult if needed\n output = output as unknown as Output\n }\n\n const numFiles = output?.numFiles ?? 0\n const numMatches = output?.numMatches ?? 0\n const durationMs = output?.durationMs ?? 0\n const outputMode = output?.outputMode ?? 'files_with_matches'\n\n // Different display based on output mode\n if (outputMode === 'content') {\n const showExpandHint = !verbose && numMatches > 10\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else if (outputMode === 'count') {\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Counted </Text>\n <Text bold>{numMatches} </Text>\n <Text>{numMatches === 1 ? 'match' : 'matches'} in </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 1 ? 'file' : 'files'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n } else {\n // files_with_matches (default)\n const showExpandHint = !verbose && numFiles > 5\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numFiles} </Text>\n <Text>{numFiles === 0 || numFiles > 1 ? 'files' : 'file'}</Text>\n {showExpandHint && <CollapsibleHint canExpand={true} />}\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n }\n },\n renderResultForAssistant(output: Output) {\n const { numFiles, filenames, content, counts, numMatches, outputMode } =\n output\n\n if (outputMode === 'content') {\n if (!content || numMatches === 0) {\n return 'No matches found'\n }\n let result = content\n if (numMatches && numMatches > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else if (outputMode === 'count') {\n if (!counts || counts.length === 0) {\n return 'No matches found'\n }\n const totalMatches = counts.reduce((sum, c) => sum + c.count, 0)\n let result = `Found ${totalMatches} match${totalMatches === 1 ? '' : 'es'} in ${numFiles} file${numFiles === 1 ? '' : 's'}\\n`\n result += counts\n .slice(0, MAX_RESULTS)\n .map(c => `${c.file}:${c.count}`)\n .join('\\n')\n if (counts.length > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n } else {\n // files_with_matches (default)\n if (numFiles === 0) {\n return 'No files found'\n }\n let result = `Found ${numFiles} file${numFiles === 1 ? '' : 's'}\\n${filenames.slice(0, MAX_RESULTS).join('\\n')}`\n if (numFiles > MAX_RESULTS) {\n result +=\n '\\n(Results are truncated. Consider using a more specific path or pattern.)'\n }\n return result\n }\n },\n async *call(input, { abortController }) {\n const {\n pattern,\n path,\n include,\n glob,\n type,\n output_mode = 'files_with_matches',\n multiline,\n context,\n '-A': afterContext,\n '-B': beforeContext,\n '-C': contextAlias,\n '-n': lineNumbers = true,\n '-i': caseInsensitive = true,\n head_limit = 0,\n offset = 0,\n } = input\n\n const start = Date.now()\n const absolutePath = getAbsolutePath(path) || getCwd()\n\n // Use glob if provided, fall back to include for backward compatibility\n const effectiveGlob = glob || include\n\n // Determine effective context value (context takes precedence over -C alias)\n const effectiveContext = context ?? contextAlias\n\n // Build ripgrep arguments based on output mode\n const args: string[] = []\n\n // Case sensitivity\n if (caseInsensitive) {\n args.push('-i')\n }\n\n // Multiline mode\n if (multiline) {\n args.push('-U', '--multiline-dotall')\n }\n\n // File type filter\n if (type) {\n args.push('--type', type)\n }\n\n // Glob filter\n if (effectiveGlob) {\n args.push('--glob', effectiveGlob)\n }\n\n // Output mode specific flags\n if (output_mode === 'files_with_matches') {\n args.push('-l')\n } else if (output_mode === 'count') {\n args.push('-c')\n } else if (output_mode === 'content') {\n // For content mode, add context and line number flags\n if (lineNumbers) {\n args.push('-n')\n }\n if (effectiveContext !== undefined && effectiveContext > 0) {\n args.push('-C', String(effectiveContext))\n } else {\n if (beforeContext !== undefined && beforeContext > 0) {\n args.push('-B', String(beforeContext))\n }\n if (afterContext !== undefined && afterContext > 0) {\n args.push('-A', String(afterContext))\n }\n }\n }\n\n // Add the pattern\n args.push(pattern)\n\n // Different processing based on output mode\n if (output_mode === 'content') {\n // Content mode: collect full output with context\n const contentLines: string[] = []\n const filesWithMatches = new Set<string>()\n let matchCount = 0\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n contentLines.push(chunk.line)\n // Track files (lines starting with filename:linenum:)\n const fileMatch = chunk.line.match(/^([^:]+):(\\d+):/)\n if (fileMatch) {\n filesWithMatches.add(fileMatch[1])\n matchCount++\n }\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchCount} match${matchCount === 1 ? '' : 'es'} in ${filesWithMatches.size} file${filesWithMatches.size === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Apply offset and head_limit\n let resultLines = contentLines\n if (offset > 0) {\n resultLines = resultLines.slice(offset)\n }\n if (head_limit > 0) {\n resultLines = resultLines.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: Array.from(filesWithMatches),\n content: resultLines.join('\\n'),\n numMatches: matchCount,\n numFiles: filesWithMatches.size,\n durationMs: Date.now() - start,\n outputMode: 'content',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else if (output_mode === 'count') {\n // Count mode: collect file:count pairs\n const counts: Array<{ file: string; count: number }> = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreamingWithContent(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'line') {\n // Format: filename:count\n const match = chunk.line.match(/^(.+):(\\d+)$/)\n if (match) {\n counts.push({ file: match[1], count: parseInt(match[2], 10) })\n\n // Progress update\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found matches in ${counts.length} file${counts.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n }\n\n // Sort by count descending, then by filename\n counts.sort((a, b) => {\n const countDiff = b.count - a.count\n if (countDiff !== 0) return countDiff\n return a.file.localeCompare(b.file)\n })\n\n // Apply offset and head_limit\n let resultCounts = counts\n if (offset > 0) {\n resultCounts = resultCounts.slice(offset)\n }\n if (head_limit > 0) {\n resultCounts = resultCounts.slice(0, head_limit)\n }\n\n const totalMatches = resultCounts.reduce((sum, c) => sum + c.count, 0)\n\n const output: Output = {\n filenames: resultCounts.map(c => c.file),\n counts: resultCounts,\n numMatches: totalMatches,\n numFiles: resultCounts.length,\n durationMs: Date.now() - start,\n outputMode: 'count',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n } else {\n // files_with_matches mode (default): list matching files\n const matchedFiles: string[] = []\n let lastProgressUpdate = 0\n const PROGRESS_UPDATE_INTERVAL = 100\n\n for await (const chunk of ripGrepStreaming(\n args,\n absolutePath,\n abortController.signal,\n )) {\n if (chunk.type === 'match') {\n matchedFiles.push(chunk.file)\n\n // Yield progress update periodically for real-time feedback\n const now = Date.now()\n if (now - lastProgressUpdate >= PROGRESS_UPDATE_INTERVAL) {\n lastProgressUpdate = now\n yield {\n type: 'progress',\n content: {\n type: 'streaming',\n toolName: 'Search',\n stdout: `Searching... Found ${matchedFiles.length} file${matchedFiles.length === 1 ? '' : 's'}`,\n stderr: '',\n isStreaming: true,\n },\n }\n }\n }\n }\n\n // Sort by modification time \u2014 batch stat() calls to limit concurrency\n const STAT_CONCURRENCY = 50\n const stats: (Awaited<ReturnType<typeof stat>> | null)[] = new Array(\n matchedFiles.length,\n ).fill(null)\n for (let i = 0; i < matchedFiles.length; i += STAT_CONCURRENCY) {\n const batch = matchedFiles.slice(i, i + STAT_CONCURRENCY)\n const batchResults = await Promise.all(\n batch.map(f => stat(f).catch(() => null)),\n )\n batchResults.forEach((r, j) => {\n stats[i + j] = r\n })\n }\n let matches = matchedFiles\n .map((file, i) => [file, stats[i]] as const)\n .filter(([, s]) => s !== null)\n .sort((a, b) => {\n if (process.env.NODE_ENV === 'test') {\n // In tests, we always want to sort by filename, so that results are deterministic\n return a[0].localeCompare(b[0])\n }\n const timeComparison =\n Number(b[1]?.mtimeMs ?? 0) - Number(a[1]?.mtimeMs ?? 0)\n if (timeComparison === 0) {\n // Sort by filename as a tiebreaker\n return a[0].localeCompare(b[0])\n }\n return timeComparison\n })\n .map(_ => _[0])\n\n // Apply offset and head_limit\n if (offset > 0) {\n matches = matches.slice(offset)\n }\n if (head_limit > 0) {\n matches = matches.slice(0, head_limit)\n }\n\n const output: Output = {\n filenames: matches,\n durationMs: Date.now() - start,\n numFiles: matches.length,\n outputMode: 'files_with_matches',\n }\n\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n }\n },\n} satisfies Tool\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,SAAS;AAClB,SAAS,YAAY;AACrB,SAAS,sCAAsC;AAC/C,SAAS,iCAAiC;AAC1C,SAAS,uBAAuB;AAEhC,SAAS,cAAc;AACvB,SAAS,iBAAiB,mCAAmC;AAC7D,SAAS,kBAAkB,mCAAmC;AAC9D,SAAS,aAAa,4BAA4B;AAClD,SAAS,yBAAyB;AAElC,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,SAAS,EACN,OAAO,EACP,SAAS,+DAA+D;AAAA,EAC3E,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA;AAAA,EAEF,SAAS,EACN,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,aAAa,EACV,KAAK,CAAC,WAAW,sBAAsB,OAAO,CAAC,EAC/C,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,WAAW,EACR,QAAQ,EACR,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,SAAS,EACN,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,EACzD,MAAM,EACH,QAAQ,EACR,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,EACvE,YAAY,EACT,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,QAAQ,EACL,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAED,MAAM,cAAc;AAkBb,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM,cAAc;AAClB,WAAO;AAAA,EACT;AAAA,EACA,iBAAiB;AACf,WAAO;AAAA,EACT;AAAA,EACA;AAAA,EACA,aAAa;AACX,WAAO;AAAA,EACT;AAAA,EACA,oBAAoB;AAClB,WAAO;AAAA,EACT;AAAA,EACA,MAAM,YAAY;AAChB,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,OAAO,EAAE,QAAQ,GAAG;AACvC,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM;AAAA,IACR,IAAI;AACJ,UAAM,EAAE,cAAc,aAAa,IAAI,4BAA4B,IAAI;AACvE,UAAM,gBAAgB,QAAQ;AAE9B,UAAM,QAAkB,CAAC,aAAa,OAAO,GAAG;AAChD,QAAI,gBAAgB,SAAS;AAC3B,YAAM,KAAK,UAAU,UAAU,eAAe,YAAY,GAAG;AAAA,IAC/D;AACA,QAAI,eAAe;AACjB,YAAM,KAAK,UAAU,aAAa,GAAG;AAAA,IACvC;AACA,QAAI,MAAM;AACR,YAAM,KAAK,UAAU,IAAI,GAAG;AAAA,IAC9B;AACA,QAAI,eAAe,gBAAgB,sBAAsB;AACvD,YAAM,KAAK,UAAU,WAAW,GAAG;AAAA,IACrC;AACA,QAAI,WAAW;AACb,YAAM,KAAK,WAAW;AAAA,IACxB;AACA,QAAI,SAAS;AACX,YAAM,KAAK,YAAY,OAAO,EAAE;AAAA,IAClC;AACA,QAAI,oBAAoB,OAAO;AAC7B,YAAM,KAAK,gBAAgB;AAAA,IAC7B;AAEA,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAAA,EACA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ,EAAE,QAAQ,GAAG;AAE3C,QAAI,CAAC,QAAQ;AACX,aAAO,oCAAC,6BAA0B,UAAS,UAAS;AAAA,IACtD;AAGA,QAAI,OAAO,WAAW,UAAU;AAE9B,eAAS;AAAA,IACX;AAEA,UAAM,WAAW,QAAQ,YAAY;AACrC,UAAM,aAAa,QAAQ,cAAc;AACzC,UAAM,aAAa,QAAQ,cAAc;AACzC,UAAM,aAAa,QAAQ,cAAc;AAGzC,QAAI,eAAe,WAAW;AAC5B,YAAM,iBAAiB,CAAC,WAAW,aAAa;AAChD,aACE,oCAAC,OAAI,gBAAe,iBAAgB,OAAM,UACxC,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,2BAA0B,GAChC,oCAAC,QAAK,MAAI,QAAE,YAAW,GAAC,GACxB,oCAAC,YAAM,eAAe,IAAI,UAAU,WAAU,MAAI,GAClD,oCAAC,QAAK,MAAI,QAAE,UAAS,GAAC,GACtB,oCAAC,YAAM,aAAa,IAAI,SAAS,OAAQ,GACxC,kBAAkB,oCAAC,mBAAgB,WAAW,MAAM,CACvD,GACA,oCAAC,QAAK,SAAS,GAAG,YAAwB,OAAO,OAAO,CAC1D;AAAA,IAEJ,WAAW,eAAe,SAAS;AACjC,YAAM,iBAAiB,CAAC,WAAW,WAAW;AAC9C,aACE,oCAAC,OAAI,gBAAe,iBAAgB,OAAM,UACxC,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,6BAA4B,GAClC,oCAAC,QAAK,MAAI,QAAE,YAAW,GAAC,GACxB,oCAAC,YAAM,eAAe,IAAI,UAAU,WAAU,MAAI,GAClD,oCAAC,QAAK,MAAI,QAAE,UAAS,GAAC,GACtB,oCAAC,YAAM,aAAa,IAAI,SAAS,OAAQ,GACxC,kBAAkB,oCAAC,mBAAgB,WAAW,MAAM,CACvD,GACA,oCAAC,QAAK,SAAS,GAAG,YAAwB,OAAO,OAAO,CAC1D;AAAA,IAEJ,OAAO;AAEL,YAAM,iBAAiB,CAAC,WAAW,WAAW;AAC9C,aACE,oCAAC,OAAI,gBAAe,iBAAgB,OAAM,UACxC,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,2BAA0B,GAChC,oCAAC,QAAK,MAAI,QAAE,UAAS,GAAC,GACtB,oCAAC,YAAM,aAAa,KAAK,WAAW,IAAI,UAAU,MAAO,GACxD,kBAAkB,oCAAC,mBAAgB,WAAW,MAAM,CACvD,GACA,oCAAC,QAAK,SAAS,GAAG,YAAwB,OAAO,OAAO,CAC1D;AAAA,IAEJ;AAAA,EACF;AAAA,EACA,yBAAyB,QAAgB;AACvC,UAAM,EAAE,UAAU,WAAW,SAAS,QAAQ,YAAY,WAAW,IACnE;AAEF,QAAI,eAAe,WAAW;AAC5B,UAAI,CAAC,WAAW,eAAe,GAAG;AAChC,eAAO;AAAA,MACT;AACA,UAAI,SAAS;AACb,UAAI,cAAc,aAAa,aAAa;AAC1C,kBACE;AAAA,MACJ;AACA,aAAO;AAAA,IACT,WAAW,eAAe,SAAS;AACjC,UAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,eAAO;AAAA,MACT;AACA,YAAM,eAAe,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAC/D,UAAI,SAAS,SAAS,YAAY,SAAS,iBAAiB,IAAI,KAAK,IAAI,OAAO,QAAQ,QAAQ,aAAa,IAAI,KAAK,GAAG;AAAA;AACzH,gBAAU,OACP,MAAM,GAAG,WAAW,EACpB,IAAI,OAAK,GAAG,EAAE,IAAI,IAAI,EAAE,KAAK,EAAE,EAC/B,KAAK,IAAI;AACZ,UAAI,OAAO,SAAS,aAAa;AAC/B,kBACE;AAAA,MACJ;AACA,aAAO;AAAA,IACT,OAAO;AAEL,UAAI,aAAa,GAAG;AAClB,eAAO;AAAA,MACT;AACA,UAAI,SAAS,SAAS,QAAQ,QAAQ,aAAa,IAAI,KAAK,GAAG;AAAA,EAAK,UAAU,MAAM,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC;AAC9G,UAAI,WAAW,aAAa;AAC1B,kBACE;AAAA,MACJ;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,OAAO,KAAK,OAAO,EAAE,gBAAgB,GAAG;AACtC,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,cAAc;AAAA,MACpB,MAAM,kBAAkB;AAAA,MACxB,aAAa;AAAA,MACb,SAAS;AAAA,IACX,IAAI;AAEJ,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,eAAe,gBAAgB,IAAI,KAAK,OAAO;AAGrD,UAAM,gBAAgB,QAAQ;AAG9B,UAAM,mBAAmB,WAAW;AAGpC,UAAM,OAAiB,CAAC;AAGxB,QAAI,iBAAiB;AACnB,WAAK,KAAK,IAAI;AAAA,IAChB;AAGA,QAAI,WAAW;AACb,WAAK,KAAK,MAAM,oBAAoB;AAAA,IACtC;AAGA,QAAI,MAAM;AACR,WAAK,KAAK,UAAU,IAAI;AAAA,IAC1B;AAGA,QAAI,eAAe;AACjB,WAAK,KAAK,UAAU,aAAa;AAAA,IACnC;AAGA,QAAI,gBAAgB,sBAAsB;AACxC,WAAK,KAAK,IAAI;AAAA,IAChB,WAAW,gBAAgB,SAAS;AAClC,WAAK,KAAK,IAAI;AAAA,IAChB,WAAW,gBAAgB,WAAW;AAEpC,UAAI,aAAa;AACf,aAAK,KAAK,IAAI;AAAA,MAChB;AACA,UAAI,qBAAqB,UAAa,mBAAmB,GAAG;AAC1D,aAAK,KAAK,MAAM,OAAO,gBAAgB,CAAC;AAAA,MAC1C,OAAO;AACL,YAAI,kBAAkB,UAAa,gBAAgB,GAAG;AACpD,eAAK,KAAK,MAAM,OAAO,aAAa,CAAC;AAAA,QACvC;AACA,YAAI,iBAAiB,UAAa,eAAe,GAAG;AAClD,eAAK,KAAK,MAAM,OAAO,YAAY,CAAC;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAGA,SAAK,KAAK,OAAO;AAGjB,QAAI,gBAAgB,WAAW;AAE7B,YAAM,eAAyB,CAAC;AAChC,YAAM,mBAAmB,oBAAI,IAAY;AACzC,UAAI,aAAa;AACjB,UAAI,qBAAqB;AACzB,YAAM,2BAA2B;AAEjC,uBAAiB,SAAS;AAAA,QACxB;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,MAClB,GAAG;AACD,YAAI,MAAM,SAAS,QAAQ;AACzB,uBAAa,KAAK,MAAM,IAAI;AAE5B,gBAAM,YAAY,MAAM,KAAK,MAAM,iBAAiB;AACpD,cAAI,WAAW;AACb,6BAAiB,IAAI,UAAU,CAAC,CAAC;AACjC;AAAA,UACF;AAGA,gBAAM,MAAM,KAAK,IAAI;AACrB,cAAI,MAAM,sBAAsB,0BAA0B;AACxD,iCAAqB;AACrB,kBAAM;AAAA,cACJ,MAAM;AAAA,cACN,SAAS;AAAA,gBACP,MAAM;AAAA,gBACN,UAAU;AAAA,gBACV,QAAQ,sBAAsB,UAAU,SAAS,eAAe,IAAI,KAAK,IAAI,OAAO,iBAAiB,IAAI,QAAQ,iBAAiB,SAAS,IAAI,KAAK,GAAG;AAAA,gBACvJ,QAAQ;AAAA,gBACR,aAAa;AAAA,cACf;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,UAAI,cAAc;AAClB,UAAI,SAAS,GAAG;AACd,sBAAc,YAAY,MAAM,MAAM;AAAA,MACxC;AACA,UAAI,aAAa,GAAG;AAClB,sBAAc,YAAY,MAAM,GAAG,UAAU;AAAA,MAC/C;AAEA,YAAM,SAAiB;AAAA,QACrB,WAAW,MAAM,KAAK,gBAAgB;AAAA,QACtC,SAAS,YAAY,KAAK,IAAI;AAAA,QAC9B,YAAY;AAAA,QACZ,UAAU,iBAAiB;AAAA,QAC3B,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB,YAAY;AAAA,MACd;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyB,MAAM;AAAA,QACxD,MAAM;AAAA,MACR;AAAA,IACF,WAAW,gBAAgB,SAAS;AAElC,YAAM,SAAiD,CAAC;AACxD,UAAI,qBAAqB;AACzB,YAAM,2BAA2B;AAEjC,uBAAiB,SAAS;AAAA,QACxB;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,MAClB,GAAG;AACD,YAAI,MAAM,SAAS,QAAQ;AAEzB,gBAAM,QAAQ,MAAM,KAAK,MAAM,cAAc;AAC7C,cAAI,OAAO;AACT,mBAAO,KAAK,EAAE,MAAM,MAAM,CAAC,GAAG,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC;AAG7D,kBAAM,MAAM,KAAK,IAAI;AACrB,gBAAI,MAAM,sBAAsB,0BAA0B;AACxD,mCAAqB;AACrB,oBAAM;AAAA,gBACJ,MAAM;AAAA,gBACN,SAAS;AAAA,kBACP,MAAM;AAAA,kBACN,UAAU;AAAA,kBACV,QAAQ,iCAAiC,OAAO,MAAM,QAAQ,OAAO,WAAW,IAAI,KAAK,GAAG;AAAA,kBAC5F,QAAQ;AAAA,kBACR,aAAa;AAAA,gBACf;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,aAAO,KAAK,CAAC,GAAG,MAAM;AACpB,cAAM,YAAY,EAAE,QAAQ,EAAE;AAC9B,YAAI,cAAc,EAAG,QAAO;AAC5B,eAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,MACpC,CAAC;AAGD,UAAI,eAAe;AACnB,UAAI,SAAS,GAAG;AACd,uBAAe,aAAa,MAAM,MAAM;AAAA,MAC1C;AACA,UAAI,aAAa,GAAG;AAClB,uBAAe,aAAa,MAAM,GAAG,UAAU;AAAA,MACjD;AAEA,YAAM,eAAe,aAAa,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAErE,YAAM,SAAiB;AAAA,QACrB,WAAW,aAAa,IAAI,OAAK,EAAE,IAAI;AAAA,QACvC,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,UAAU,aAAa;AAAA,QACvB,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB,YAAY;AAAA,MACd;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyB,MAAM;AAAA,QACxD,MAAM;AAAA,MACR;AAAA,IACF,OAAO;AAEL,YAAM,eAAyB,CAAC;AAChC,UAAI,qBAAqB;AACzB,YAAM,2BAA2B;AAEjC,uBAAiB,SAAS;AAAA,QACxB;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,MAClB,GAAG;AACD,YAAI,MAAM,SAAS,SAAS;AAC1B,uBAAa,KAAK,MAAM,IAAI;AAG5B,gBAAM,MAAM,KAAK,IAAI;AACrB,cAAI,MAAM,sBAAsB,0BAA0B;AACxD,iCAAqB;AACrB,kBAAM;AAAA,cACJ,MAAM;AAAA,cACN,SAAS;AAAA,gBACP,MAAM;AAAA,gBACN,UAAU;AAAA,gBACV,QAAQ,sBAAsB,aAAa,MAAM,QAAQ,aAAa,WAAW,IAAI,KAAK,GAAG;AAAA,gBAC7F,QAAQ;AAAA,gBACR,aAAa;AAAA,cACf;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,YAAM,mBAAmB;AACzB,YAAM,QAAqD,IAAI;AAAA,QAC7D,aAAa;AAAA,MACf,EAAE,KAAK,IAAI;AACX,eAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK,kBAAkB;AAC9D,cAAM,QAAQ,aAAa,MAAM,GAAG,IAAI,gBAAgB;AACxD,cAAM,eAAe,MAAM,QAAQ;AAAA,UACjC,MAAM,IAAI,OAAK,KAAK,CAAC,EAAE,MAAM,MAAM,IAAI,CAAC;AAAA,QAC1C;AACA,qBAAa,QAAQ,CAAC,GAAG,MAAM;AAC7B,gBAAM,IAAI,CAAC,IAAI;AAAA,QACjB,CAAC;AAAA,MACH;AACA,UAAI,UAAU,aACX,IAAI,CAAC,MAAM,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC,CAAU,EAC1C,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM,IAAI,EAC5B,KAAK,CAAC,GAAG,MAAM;AACd,YAAI,QAAQ,IAAI,aAAa,QAAQ;AAEnC,iBAAO,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;AAAA,QAChC;AACA,cAAM,iBACJ,OAAO,EAAE,CAAC,GAAG,WAAW,CAAC,IAAI,OAAO,EAAE,CAAC,GAAG,WAAW,CAAC;AACxD,YAAI,mBAAmB,GAAG;AAExB,iBAAO,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;AAAA,QAChC;AACA,eAAO;AAAA,MACT,CAAC,EACA,IAAI,OAAK,EAAE,CAAC,CAAC;AAGhB,UAAI,SAAS,GAAG;AACd,kBAAU,QAAQ,MAAM,MAAM;AAAA,MAChC;AACA,UAAI,aAAa,GAAG;AAClB,kBAAU,QAAQ,MAAM,GAAG,UAAU;AAAA,MACvC;AAEA,YAAM,SAAiB;AAAA,QACrB,WAAW;AAAA,QACX,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB,UAAU,QAAQ;AAAA,QAClB,YAAY;AAAA,MACd;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,oBAAoB,KAAK,yBAAyB,MAAM;AAAA,QACxD,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/tools/ListMcpResourcesTool/ListMcpResourcesTool.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { listMCPResources, McpResource } from '@services/mcpClient'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { getTheme } from '@utils/theme'\n\nconst inputSchema = z.strictObject({\n server: z\n .string()\n .optional()\n .describe(\n 'Optional server name to filter resources from a specific MCP server',\n ),\n})\n\ntype Output = {\n durationMs: number\n resources: McpResource[]\n}\n\nexport const ListMcpResourcesTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'List MCP Resources'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Read-only operation, safe for concurrent execution\n },\n needsPermissions() {\n return false // No file system access needed\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ server }, { verbose }) {\n if (server) {\n return `server: \"${server}\"`\n }\n return verbose ? 'Listing all MCP resources' : ''\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF No resources available</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Listed MCP resources</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numResources = output?.resources?.length ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numResources} </Text>\n <Text>\n {numResources === 0 || numResources > 1 ? 'resources' : 'resource'}\n </Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ server }) {\n const start = Date.now()\n const resources = await listMCPResources(server)\n const output: Output = {\n resources,\n durationMs: Date.now() - start,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n if (output.resources.length === 0) {\n return 'No MCP resources available'\n }\n\n const lines: string[] = []\n lines.push(`Found ${output.resources.length} MCP resource(s):`)\n lines.push('')\n\n // Group by server\n const byServer = output.resources.reduce(\n (acc, r) => {\n if (!acc[r.serverName]) acc[r.serverName] = []\n acc[r.serverName].push(r)\n return acc\n },\n {} as Record<string, McpResource[]>,\n )\n\n for (const [serverName, resources] of Object.entries(byServer) as [string
|
|
5
|
-
"mappings": "AAAA,SAAS,KAAK,YAAY;AAC1B,OAAO,WAAW;AAClB,SAAS,SAAS;AAClB,SAAS,YAAY;AACrB,SAAS,sCAAsC;AAE/C,SAAS,wBAAqC;AAC9C,SAAS,aAAa,4BAA4B;AAGlD,MAAM,cAAc,EAAE,aAAa;AAAA,EACjC,QAAQ,EACL,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAOM,MAAM,uBAAuB;AAAA,EAClC,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,mBAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EACA,MAAM,SAAS;AACb,WAAO;AAAA,EACT;AAAA,EACA,qBAAqB,EAAE,OAAO,GAAG,EAAE,QAAQ,GAAG;AAC5C,QAAI,QAAQ;AACV,aAAO,YAAY,MAAM;AAAA,IAC3B;AACA,WAAO,UAAU,8BAA8B;AAAA,EACjD;AAAA,EACA,+BAA+B;AAC7B,WAAO,oCAAC,oCAA+B;AAAA,EACzC;AAAA,EACA,wBAAwB,QAAQ;AAC9B,QAAI,CAAC,QAAQ;AACX,aACE,oCAAC,OAAI,gBAAe,iBAAgB,OAAM,UACxC,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,2CAA0C,CAClD,CACF;AAAA,IAEJ;AAGA,QAAI,OAAO,WAAW,UAAU;AAC9B,UAAI;AACF,iBAAS,KAAK,MAAM,MAAM;AAAA,MAC5B,QAAQ;AACN,eACE,oCAAC,OAAI,gBAAe,iBAAgB,OAAM,UACxC,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,yCAAwC,CAChD,CACF;AAAA,MAEJ;AAAA,IACF;AAEA,UAAM,eAAe,QAAQ,WAAW,UAAU;AAClD,UAAM,aAAa,QAAQ,cAAc;AAEzC,WACE,oCAAC,OAAI,gBAAe,iBAAgB,OAAM,UACxC,oCAAC,OAAI,eAAc,SACjB,oCAAC,YAAK,2BAA0B,GAChC,oCAAC,QAAK,MAAI,QAAE,cAAa,GAAC,GAC1B,oCAAC,YACE,iBAAiB,KAAK,eAAe,IAAI,cAAc,UAC1D,CACF,GACA,oCAAC,QAAK,SAAS,GAAG,YAAwB,OAAO,OAAO,CAC1D;AAAA,EAEJ;AAAA,EACA,OAAO,KAAK,EAAE,OAAO,GAAG;AACtB,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,YAAY,MAAM,iBAAiB,MAAM;AAC/C,UAAM,SAAiB;AAAA,MACrB;AAAA,MACA,YAAY,KAAK,IAAI,IAAI;AAAA,IAC3B;AACA,UAAM;AAAA,MACJ,MAAM;AAAA,MACN,oBAAoB,KAAK,yBAAyB,MAAM;AAAA,MACxD,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EACA,yBAAyB,QAAQ;AAC/B,QAAI,OAAO,UAAU,WAAW,GAAG;AACjC,aAAO;AAAA,IACT;AAEA,UAAM,QAAkB,CAAC;AACzB,UAAM,KAAK,SAAS,OAAO,UAAU,MAAM,mBAAmB;AAC9D,UAAM,KAAK,EAAE;AAGb,UAAM,WAAW,OAAO,UAAU;AAAA,MAChC,CAAC,KAAK,MAAM;AACV,YAAI,CAAC,IAAI,EAAE,UAAU,EAAG,KAAI,EAAE,UAAU,IAAI,CAAC;AAC7C,YAAI,EAAE,UAAU,EAAE,KAAK,CAAC;AACxB,eAAO;AAAA,MACT;AAAA,MACA,CAAC;AAAA,IACH;AAEA,eAAW,CAAC,YAAY,SAAS,KAAK,OAAO,QAAQ,QAAQ,
|
|
4
|
+
"sourcesContent": ["import { Box, Text } from 'ink'\nimport React from 'react'\nimport { z } from 'zod'\nimport { Cost } from '@components/Cost'\nimport { FallbackToolUseRejectedMessage } from '@components/FallbackToolUseRejectedMessage'\nimport { Tool } from '@tool'\nimport { listMCPResources, McpResource } from '@services/mcpClient'\nimport { DESCRIPTION, TOOL_NAME_FOR_PROMPT } from './prompt'\nimport { getTheme } from '@utils/theme'\n\nconst inputSchema = z.strictObject({\n server: z\n .string()\n .optional()\n .describe(\n 'Optional server name to filter resources from a specific MCP server',\n ),\n})\n\ntype Output = {\n durationMs: number\n resources: McpResource[]\n}\n\nexport const ListMcpResourcesTool = {\n name: TOOL_NAME_FOR_PROMPT,\n async description() {\n return DESCRIPTION\n },\n userFacingName() {\n return 'List MCP Resources'\n },\n inputSchema,\n async isEnabled() {\n return true\n },\n isReadOnly() {\n return true\n },\n isConcurrencySafe() {\n return true // Read-only operation, safe for concurrent execution\n },\n needsPermissions() {\n return false // No file system access needed\n },\n async prompt() {\n return DESCRIPTION\n },\n renderToolUseMessage({ server }, { verbose }) {\n if (server) {\n return `server: \"${server}\"`\n }\n return verbose ? 'Listing all MCP resources' : ''\n },\n renderToolUseRejectedMessage() {\n return <FallbackToolUseRejectedMessage />\n },\n renderToolResultMessage(output) {\n if (!output) {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF No resources available</Text>\n </Box>\n </Box>\n )\n }\n\n // Handle string content for backward compatibility\n if (typeof output === 'string') {\n try {\n output = JSON.parse(output) as Output\n } catch {\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Listed MCP resources</Text>\n </Box>\n </Box>\n )\n }\n }\n\n const numResources = output?.resources?.length ?? 0\n const durationMs = output?.durationMs ?? 0\n\n return (\n <Box justifyContent=\"space-between\" width=\"100%\">\n <Box flexDirection=\"row\">\n <Text> \u23BF Found </Text>\n <Text bold>{numResources} </Text>\n <Text>\n {numResources === 0 || numResources > 1 ? 'resources' : 'resource'}\n </Text>\n </Box>\n <Cost costUSD={0} durationMs={durationMs} debug={false} />\n </Box>\n )\n },\n async *call({ server }) {\n const start = Date.now()\n const resources = await listMCPResources(server)\n const output: Output = {\n resources,\n durationMs: Date.now() - start,\n }\n yield {\n type: 'result',\n resultForAssistant: this.renderResultForAssistant(output),\n data: output,\n }\n },\n renderResultForAssistant(output) {\n if (output.resources.length === 0) {\n return 'No MCP resources available'\n }\n\n const lines: string[] = []\n lines.push(`Found ${output.resources.length} MCP resource(s):`)\n lines.push('')\n\n // Group by server\n const byServer = output.resources.reduce(\n (acc, r) => {\n if (!acc[r.serverName]) acc[r.serverName] = []\n acc[r.serverName].push(r)\n return acc\n },\n {} as Record<string, McpResource[]>,\n )\n\n for (const [serverName, resources] of Object.entries(byServer) as [\n string,\n McpResource[],\n ][]) {\n lines.push(`Server: ${serverName}`)\n for (const r of resources) {\n lines.push(` - ${r.name}`)\n lines.push(` URI: ${r.uri}`)\n if (r.description) {\n lines.push(` Description: ${r.description}`)\n }\n if (r.mimeType) {\n lines.push(` MIME Type: ${r.mimeType}`)\n }\n }\n lines.push('')\n }\n\n return lines.join('\\n')\n },\n} satisfies Tool\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|