centaurus-cli 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/types.js +0 -1
- package/dist/ai/types.js.map +1 -1
- package/dist/cli-adapter.js +5047 -5158
- package/dist/cli-adapter.js.map +1 -1
- package/dist/commands/CommandParser.js +372 -315
- package/dist/commands/CommandParser.js.map +1 -1
- package/dist/config/build-config.js +11 -42
- package/dist/config/build-config.js.map +1 -1
- package/dist/config/defaultConfig.js +94 -82
- package/dist/config/defaultConfig.js.map +1 -1
- package/dist/config/manager.js +144 -160
- package/dist/config/manager.js.map +1 -1
- package/dist/config/mcp-config-manager.js +411 -364
- package/dist/config/mcp-config-manager.js.map +1 -1
- package/dist/config/models.js +118 -185
- package/dist/config/models.js.map +1 -1
- package/dist/config/slash-commands.js +186 -184
- package/dist/config/slash-commands.js.map +1 -1
- package/dist/config/types.js +33 -26
- package/dist/config/types.js.map +1 -1
- package/dist/context/command-detector.js +63 -67
- package/dist/context/command-detector.js.map +1 -1
- package/dist/context/context-manager.js +533 -518
- package/dist/context/context-manager.js.map +1 -1
- package/dist/context/handlers/docker-handler.js +518 -576
- package/dist/context/handlers/docker-handler.js.map +1 -1
- package/dist/context/handlers/ssh-handler.js +1050 -1109
- package/dist/context/handlers/ssh-handler.js.map +1 -1
- package/dist/context/handlers/wsl-handler.js +558 -630
- package/dist/context/handlers/wsl-handler.js.map +1 -1
- package/dist/context/index.js +42 -6
- package/dist/context/index.js.map +1 -1
- package/dist/context/subshell-handler.js +0 -4
- package/dist/context/subshell-handler.js.map +1 -1
- package/dist/context/types.js +20 -31
- package/dist/context/types.js.map +1 -1
- package/dist/hooks/useConnectivity.js +13 -10
- package/dist/hooks/useConnectivity.js.map +1 -1
- package/dist/hooks/useTerminalDimensions.js +67 -79
- package/dist/hooks/useTerminalDimensions.js.map +1 -1
- package/dist/index.js +228 -251
- package/dist/index.js.map +1 -1
- package/dist/mcp/mcp-command-handler.js +297 -260
- package/dist/mcp/mcp-command-handler.js.map +1 -1
- package/dist/mcp/mcp-server-manager.js +139 -155
- package/dist/mcp/mcp-server-manager.js.map +1 -1
- package/dist/mcp/mcp-tool-wrapper.js +74 -94
- package/dist/mcp/mcp-tool-wrapper.js.map +1 -1
- package/dist/services/ai-autocomplete-agent.js +169 -181
- package/dist/services/ai-autocomplete-agent.js.map +1 -1
- package/dist/services/ai-context-injector.js +180 -93
- package/dist/services/ai-context-injector.js.map +1 -1
- package/dist/services/ai-service-client.js +513 -456
- package/dist/services/ai-service-client.js.map +1 -1
- package/dist/services/api-client.js +443 -441
- package/dist/services/api-client.js.map +1 -1
- package/dist/services/auth-handler.js +162 -198
- package/dist/services/auth-handler.js.map +1 -1
- package/dist/services/background-task-manager.js +258 -282
- package/dist/services/background-task-manager.js.map +1 -1
- package/dist/services/checkpoint-manager.js +1526 -1512
- package/dist/services/checkpoint-manager.js.map +1 -1
- package/dist/services/clipboard-service.js +151 -200
- package/dist/services/clipboard-service.js.map +1 -1
- package/dist/services/connectivity-manager.js +63 -65
- package/dist/services/connectivity-manager.js.map +1 -1
- package/dist/services/conversation-manager.js +118 -121
- package/dist/services/conversation-manager.js.map +1 -1
- package/dist/services/environment-context-injector.js +160 -187
- package/dist/services/environment-context-injector.js.map +1 -1
- package/dist/services/fast-context-agent.js +203 -243
- package/dist/services/fast-context-agent.js.map +1 -1
- package/dist/services/input-detection-agent.js +190 -202
- package/dist/services/input-detection-agent.js.map +1 -1
- package/dist/services/input-requirement-detector.js +155 -189
- package/dist/services/input-requirement-detector.js.map +1 -1
- package/dist/services/local-chat-storage.js +342 -365
- package/dist/services/local-chat-storage.js.map +1 -1
- package/dist/services/monitored-shell-manager.js +225 -233
- package/dist/services/monitored-shell-manager.js.map +1 -1
- package/dist/services/ollama-service.js +293 -310
- package/dist/services/ollama-service.js.map +1 -1
- package/dist/services/rules-storage.js +142 -0
- package/dist/services/rules-storage.js.map +1 -0
- package/dist/services/session-quota-manager.js +219 -235
- package/dist/services/session-quota-manager.js.map +1 -1
- package/dist/services/shell-input-agent.js +299 -334
- package/dist/services/shell-input-agent.js.map +1 -1
- package/dist/services/sub-agent-manager.js +459 -501
- package/dist/services/sub-agent-manager.js.map +1 -1
- package/dist/services/warpify-detector.js +133 -183
- package/dist/services/warpify-detector.js.map +1 -1
- package/dist/services/workflow-storage.js +202 -217
- package/dist/services/workflow-storage.js.map +1 -1
- package/dist/test-ssh-handler.js +148 -193
- package/dist/test-ssh-handler.js.map +1 -1
- package/dist/tools/add-mcp.js +161 -0
- package/dist/tools/add-mcp.js.map +1 -0
- package/dist/tools/background-command.js +240 -273
- package/dist/tools/background-command.js.map +1 -1
- package/dist/tools/command.js +447 -440
- package/dist/tools/command.js.map +1 -1
- package/dist/tools/create-image.js +172 -202
- package/dist/tools/create-image.js.map +1 -1
- package/dist/tools/enter-remote-session.js +169 -215
- package/dist/tools/enter-remote-session.js.map +1 -1
- package/dist/tools/fast-context.js +60 -67
- package/dist/tools/fast-context.js.map +1 -1
- package/dist/tools/file-ops.js +601 -572
- package/dist/tools/file-ops.js.map +1 -1
- package/dist/tools/find-files.js +262 -303
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/get-diff.js +423 -406
- package/dist/tools/get-diff.js.map +1 -1
- package/dist/tools/grep-search.js +966 -948
- package/dist/tools/grep-search.js.map +1 -1
- package/dist/tools/inspect-symbol.js +308 -323
- package/dist/tools/inspect-symbol.js.map +1 -1
- package/dist/tools/plan-mode.js +459 -503
- package/dist/tools/plan-mode.js.map +1 -1
- package/dist/tools/read-binary-file.js +160 -190
- package/dist/tools/read-binary-file.js.map +1 -1
- package/dist/tools/registry.js +100 -84
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/reproduce_issue.js +170 -151
- package/dist/tools/reproduce_issue.js.map +1 -1
- package/dist/tools/sub-agent.js +223 -228
- package/dist/tools/sub-agent.js.map +1 -1
- package/dist/tools/task-complete.js +28 -27
- package/dist/tools/task-complete.js.map +1 -1
- package/dist/tools/types.js +0 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/tools/validation.js +96 -118
- package/dist/tools/validation.js.map +1 -1
- package/dist/tools/web-search.js +194 -194
- package/dist/tools/web-search.js.map +1 -1
- package/dist/tools/workflow-tool.js +77 -82
- package/dist/tools/workflow-tool.js.map +1 -1
- package/dist/types/index.js +0 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/rule.js +1 -0
- package/dist/types/rule.js.map +1 -0
- package/dist/types/workflow.js +0 -7
- package/dist/types/workflow.js.map +1 -1
- package/dist/ui/components/AgentTimer.js +24 -25
- package/dist/ui/components/AgentTimer.js.map +1 -1
- package/dist/ui/components/App.js +3266 -3263
- package/dist/ui/components/App.js.map +1 -1
- package/dist/ui/components/AuthScreen.js +22 -34
- package/dist/ui/components/AuthScreen.js.map +1 -1
- package/dist/ui/components/AuthWelcomeScreen.js +30 -24
- package/dist/ui/components/AuthWelcomeScreen.js.map +1 -1
- package/dist/ui/components/Breadcrumbs.js +53 -82
- package/dist/ui/components/Breadcrumbs.js.map +1 -1
- package/dist/ui/components/CircularSelectInput.js +59 -67
- package/dist/ui/components/CircularSelectInput.js.map +1 -1
- package/dist/ui/components/ClipboardFileAutocomplete.js +78 -39
- package/dist/ui/components/ClipboardFileAutocomplete.js.map +1 -1
- package/dist/ui/components/CodeBlock.js +24 -42
- package/dist/ui/components/CodeBlock.js.map +1 -1
- package/dist/ui/components/ConfigViewer.js +18 -25
- package/dist/ui/components/ConfigViewer.js.map +1 -1
- package/dist/ui/components/ConfirmPrompt.js +49 -71
- package/dist/ui/components/ConfirmPrompt.js.map +1 -1
- package/dist/ui/components/ConnectionStatusMessage.js +32 -83
- package/dist/ui/components/ConnectionStatusMessage.js.map +1 -1
- package/dist/ui/components/ContextWindowIndicator.js +34 -49
- package/dist/ui/components/ContextWindowIndicator.js.map +1 -1
- package/dist/ui/components/DetailedPlanReviewScreen.js +104 -106
- package/dist/ui/components/DetailedPlanReviewScreen.js.map +1 -1
- package/dist/ui/components/DiffViewer.js +68 -121
- package/dist/ui/components/DiffViewer.js.map +1 -1
- package/dist/ui/components/ErrorBoundary.js +40 -48
- package/dist/ui/components/ErrorBoundary.js.map +1 -1
- package/dist/ui/components/FileCreationPreview.js +29 -60
- package/dist/ui/components/FileCreationPreview.js.map +1 -1
- package/dist/ui/components/FileOperation.js +34 -29
- package/dist/ui/components/FileOperation.js.map +1 -1
- package/dist/ui/components/FileTagAutocomplete.js +55 -25
- package/dist/ui/components/FileTagAutocomplete.js.map +1 -1
- package/dist/ui/components/FontRecommendation.js.map +1 -1
- package/dist/ui/components/GitDiffBreadcrumb.js +29 -0
- package/dist/ui/components/GitDiffBreadcrumb.js.map +1 -0
- package/dist/ui/components/InputBox.js +1620 -2150
- package/dist/ui/components/InputBox.js.map +1 -1
- package/dist/ui/components/InteractiveShell.js +234 -352
- package/dist/ui/components/InteractiveShell.js.map +1 -1
- package/dist/ui/components/KeyboardHelp.js +34 -35
- package/dist/ui/components/KeyboardHelp.js.map +1 -1
- package/dist/ui/components/LoadingIndicator.js +22 -25
- package/dist/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/ui/components/MCPAddScreen.js +40 -51
- package/dist/ui/components/MCPAddScreen.js.map +1 -1
- package/dist/ui/components/MCPListScreen.js +40 -48
- package/dist/ui/components/MCPListScreen.js.map +1 -1
- package/dist/ui/components/MCPServerListScreen.js +49 -56
- package/dist/ui/components/MCPServerListScreen.js.map +1 -1
- package/dist/ui/components/MarkdownRenderer.js +69 -96
- package/dist/ui/components/MarkdownRenderer.js.map +1 -1
- package/dist/ui/components/MessageBox.js +66 -48
- package/dist/ui/components/MessageBox.js.map +1 -1
- package/dist/ui/components/MessageDisplay.js +150 -142
- package/dist/ui/components/MessageDisplay.js.map +1 -1
- package/dist/ui/components/MonitorModeAIPanel.js +46 -65
- package/dist/ui/components/MonitorModeAIPanel.js.map +1 -1
- package/dist/ui/components/MultiLineInput.js +243 -277
- package/dist/ui/components/MultiLineInput.js.map +1 -1
- package/dist/ui/components/PasswordPrompt.js +37 -18
- package/dist/ui/components/PasswordPrompt.js.map +1 -1
- package/dist/ui/components/PlanAcceptedMessage.js +27 -38
- package/dist/ui/components/PlanAcceptedMessage.js.map +1 -1
- package/dist/ui/components/PlanReviewScreen.js +46 -50
- package/dist/ui/components/PlanReviewScreen.js.map +1 -1
- package/dist/ui/components/RulesEditorScreen.js +81 -0
- package/dist/ui/components/RulesEditorScreen.js.map +1 -0
- package/dist/ui/components/SelectPrompt.js +19 -8
- package/dist/ui/components/SelectPrompt.js.map +1 -1
- package/dist/ui/components/ShimmerText.js +44 -0
- package/dist/ui/components/ShimmerText.js.map +1 -0
- package/dist/ui/components/SlashCommandAutocomplete.js +49 -22
- package/dist/ui/components/SlashCommandAutocomplete.js.map +1 -1
- package/dist/ui/components/StatusBar.js +56 -87
- package/dist/ui/components/StatusBar.js.map +1 -1
- package/dist/ui/components/StreamingMessageDisplay.js +116 -99
- package/dist/ui/components/StreamingMessageDisplay.js.map +1 -1
- package/dist/ui/components/TaskCompletedMessage.js +28 -23
- package/dist/ui/components/TaskCompletedMessage.js.map +1 -1
- package/dist/ui/components/TaskProgressIndicator.js +44 -70
- package/dist/ui/components/TaskProgressIndicator.js.map +1 -1
- package/dist/ui/components/ThinkingDisplay.js +44 -41
- package/dist/ui/components/ThinkingDisplay.js.map +1 -1
- package/dist/ui/components/ToolExecutionMessage.js +772 -1326
- package/dist/ui/components/ToolExecutionMessage.js.map +1 -1
- package/dist/ui/components/ToolExecutionStatus.js +53 -84
- package/dist/ui/components/ToolExecutionStatus.js.map +1 -1
- package/dist/ui/components/ToolResult.js +22 -15
- package/dist/ui/components/ToolResult.js.map +1 -1
- package/dist/ui/components/VersionUpdatePrompt.js +88 -120
- package/dist/ui/components/VersionUpdatePrompt.js.map +1 -1
- package/dist/ui/components/WelcomeBanner.js +176 -26
- package/dist/ui/components/WelcomeBanner.js.map +1 -1
- package/dist/ui/components/WorkflowCreatorScreen.js +94 -161
- package/dist/ui/components/WorkflowCreatorScreen.js.map +1 -1
- package/dist/utils/ansi-encoder.js +30 -61
- package/dist/utils/ansi-encoder.js.map +1 -1
- package/dist/utils/chat-formatter.js +327 -305
- package/dist/utils/chat-formatter.js.map +1 -1
- package/dist/utils/command-history.js +152 -174
- package/dist/utils/command-history.js.map +1 -1
- package/dist/utils/context-sanitizer.js +49 -112
- package/dist/utils/context-sanitizer.js.map +1 -1
- package/dist/utils/conversation-logger.js +292 -324
- package/dist/utils/conversation-logger.js.map +1 -1
- package/dist/utils/custom-commands-manager.js +126 -131
- package/dist/utils/custom-commands-manager.js.map +1 -1
- package/dist/utils/editor-utils.js +732 -837
- package/dist/utils/editor-utils.js.map +1 -1
- package/dist/utils/file.js +174 -213
- package/dist/utils/file.js.map +1 -1
- package/dist/utils/git-stats.js +169 -0
- package/dist/utils/git-stats.js.map +1 -0
- package/dist/utils/input-classifier.js +960 -482
- package/dist/utils/input-classifier.js.map +1 -1
- package/dist/utils/logger.js +48 -73
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/markdown-parser.js +277 -310
- package/dist/utils/markdown-parser.js.map +1 -1
- package/dist/utils/rule-reference-resolver.js +54 -0
- package/dist/utils/rule-reference-resolver.js.map +1 -0
- package/dist/utils/shell.js +144 -156
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/state.js +23 -22
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/syntax-checker.js +279 -327
- package/dist/utils/syntax-checker.js.map +1 -1
- package/dist/utils/terminal-output.js +199 -302
- package/dist/utils/terminal-output.js.map +1 -1
- package/dist/utils/text-clipboard.js +47 -70
- package/dist/utils/text-clipboard.js.map +1 -1
- package/dist/utils/unicode-sanitizer.js +134 -197
- package/dist/utils/unicode-sanitizer.js.map +1 -1
- package/dist/utils/version-checker.js +46 -56
- package/dist/utils/version-checker.js.map +1 -1
- package/package.json +6 -4
- package/dist/ai/types.d.ts +0 -20
- package/dist/ai/types.d.ts.map +0 -1
- package/dist/cli-adapter.d.ts +0 -514
- package/dist/cli-adapter.d.ts.map +0 -1
- package/dist/commands/CommandParser.d.ts +0 -27
- package/dist/commands/CommandParser.d.ts.map +0 -1
- package/dist/config/build-config.d.ts +0 -42
- package/dist/config/build-config.d.ts.map +0 -1
- package/dist/config/defaultConfig.d.ts +0 -79
- package/dist/config/defaultConfig.d.ts.map +0 -1
- package/dist/config/manager.d.ts +0 -62
- package/dist/config/manager.d.ts.map +0 -1
- package/dist/config/mcp-config-manager.d.ts +0 -79
- package/dist/config/mcp-config-manager.d.ts.map +0 -1
- package/dist/config/models.d.ts +0 -83
- package/dist/config/models.d.ts.map +0 -1
- package/dist/config/slash-commands.d.ts +0 -23
- package/dist/config/slash-commands.d.ts.map +0 -1
- package/dist/config/types.d.ts +0 -35
- package/dist/config/types.d.ts.map +0 -1
- package/dist/context/command-detector.d.ts +0 -50
- package/dist/context/command-detector.d.ts.map +0 -1
- package/dist/context/context-manager.d.ts +0 -157
- package/dist/context/context-manager.d.ts.map +0 -1
- package/dist/context/handlers/docker-handler.d.ts +0 -130
- package/dist/context/handlers/docker-handler.d.ts.map +0 -1
- package/dist/context/handlers/ssh-handler.d.ts +0 -201
- package/dist/context/handlers/ssh-handler.d.ts.map +0 -1
- package/dist/context/handlers/wsl-handler.d.ts +0 -146
- package/dist/context/handlers/wsl-handler.d.ts.map +0 -1
- package/dist/context/index.d.ts +0 -8
- package/dist/context/index.d.ts.map +0 -1
- package/dist/context/subshell-handler.d.ts +0 -165
- package/dist/context/subshell-handler.d.ts.map +0 -1
- package/dist/context/types.d.ts +0 -70
- package/dist/context/types.d.ts.map +0 -1
- package/dist/hooks/useConnectivity.d.ts +0 -2
- package/dist/hooks/useConnectivity.d.ts.map +0 -1
- package/dist/hooks/useTerminalDimensions.d.ts +0 -41
- package/dist/hooks/useTerminalDimensions.d.ts.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/mcp/mcp-command-handler.d.ts +0 -47
- package/dist/mcp/mcp-command-handler.d.ts.map +0 -1
- package/dist/mcp/mcp-server-manager.d.ts +0 -30
- package/dist/mcp/mcp-server-manager.d.ts.map +0 -1
- package/dist/mcp/mcp-tool-wrapper.d.ts +0 -12
- package/dist/mcp/mcp-tool-wrapper.d.ts.map +0 -1
- package/dist/services/ai-autocomplete-agent.d.ts +0 -39
- package/dist/services/ai-autocomplete-agent.d.ts.map +0 -1
- package/dist/services/ai-context-injector.d.ts +0 -41
- package/dist/services/ai-context-injector.d.ts.map +0 -1
- package/dist/services/ai-service-client.d.ts +0 -128
- package/dist/services/ai-service-client.d.ts.map +0 -1
- package/dist/services/api-client.d.ts +0 -353
- package/dist/services/api-client.d.ts.map +0 -1
- package/dist/services/auth-handler.d.ts +0 -30
- package/dist/services/auth-handler.d.ts.map +0 -1
- package/dist/services/background-task-manager.d.ts +0 -114
- package/dist/services/background-task-manager.d.ts.map +0 -1
- package/dist/services/checkpoint-manager.d.ts +0 -204
- package/dist/services/checkpoint-manager.d.ts.map +0 -1
- package/dist/services/clipboard-service.d.ts +0 -37
- package/dist/services/clipboard-service.d.ts.map +0 -1
- package/dist/services/connectivity-manager.d.ts +0 -18
- package/dist/services/connectivity-manager.d.ts.map +0 -1
- package/dist/services/conversation-manager.d.ts +0 -73
- package/dist/services/conversation-manager.d.ts.map +0 -1
- package/dist/services/environment-context-injector.d.ts +0 -69
- package/dist/services/environment-context-injector.d.ts.map +0 -1
- package/dist/services/fast-context-agent.d.ts +0 -12
- package/dist/services/fast-context-agent.d.ts.map +0 -1
- package/dist/services/input-detection-agent.d.ts +0 -40
- package/dist/services/input-detection-agent.d.ts.map +0 -1
- package/dist/services/input-requirement-detector.d.ts +0 -28
- package/dist/services/input-requirement-detector.d.ts.map +0 -1
- package/dist/services/local-chat-storage.d.ts +0 -182
- package/dist/services/local-chat-storage.d.ts.map +0 -1
- package/dist/services/monitored-shell-manager.d.ts +0 -120
- package/dist/services/monitored-shell-manager.d.ts.map +0 -1
- package/dist/services/ollama-service.d.ts +0 -197
- package/dist/services/ollama-service.d.ts.map +0 -1
- package/dist/services/session-quota-manager.d.ts +0 -101
- package/dist/services/session-quota-manager.d.ts.map +0 -1
- package/dist/services/shell-input-agent.d.ts +0 -89
- package/dist/services/shell-input-agent.d.ts.map +0 -1
- package/dist/services/sub-agent-manager.d.ts +0 -140
- package/dist/services/sub-agent-manager.d.ts.map +0 -1
- package/dist/services/warpify-detector.d.ts +0 -43
- package/dist/services/warpify-detector.d.ts.map +0 -1
- package/dist/services/workflow-storage.d.ts +0 -72
- package/dist/services/workflow-storage.d.ts.map +0 -1
- package/dist/test-ssh-handler.d.ts +0 -8
- package/dist/test-ssh-handler.d.ts.map +0 -1
- package/dist/tools/background-command.d.ts +0 -11
- package/dist/tools/background-command.d.ts.map +0 -1
- package/dist/tools/command.d.ts +0 -3
- package/dist/tools/command.d.ts.map +0 -1
- package/dist/tools/create-image.d.ts +0 -10
- package/dist/tools/create-image.d.ts.map +0 -1
- package/dist/tools/enter-remote-session.d.ts +0 -48
- package/dist/tools/enter-remote-session.d.ts.map +0 -1
- package/dist/tools/fast-context.d.ts +0 -3
- package/dist/tools/fast-context.d.ts.map +0 -1
- package/dist/tools/file-ops.d.ts +0 -7
- package/dist/tools/file-ops.d.ts.map +0 -1
- package/dist/tools/find-files.d.ts +0 -49
- package/dist/tools/find-files.d.ts.map +0 -1
- package/dist/tools/get-diff.d.ts +0 -14
- package/dist/tools/get-diff.d.ts.map +0 -1
- package/dist/tools/grep-search.d.ts +0 -155
- package/dist/tools/grep-search.d.ts.map +0 -1
- package/dist/tools/inspect-symbol.d.ts +0 -32
- package/dist/tools/inspect-symbol.d.ts.map +0 -1
- package/dist/tools/plan-mode.d.ts +0 -140
- package/dist/tools/plan-mode.d.ts.map +0 -1
- package/dist/tools/read-binary-file.d.ts +0 -10
- package/dist/tools/read-binary-file.d.ts.map +0 -1
- package/dist/tools/registry.d.ts +0 -31
- package/dist/tools/registry.d.ts.map +0 -1
- package/dist/tools/reproduce_issue.d.ts +0 -2
- package/dist/tools/reproduce_issue.d.ts.map +0 -1
- package/dist/tools/sub-agent.d.ts +0 -9
- package/dist/tools/sub-agent.d.ts.map +0 -1
- package/dist/tools/task-complete.d.ts +0 -3
- package/dist/tools/task-complete.d.ts.map +0 -1
- package/dist/tools/types.d.ts +0 -40
- package/dist/tools/types.d.ts.map +0 -1
- package/dist/tools/validation.d.ts +0 -47
- package/dist/tools/validation.d.ts.map +0 -1
- package/dist/tools/web-search.d.ts +0 -24
- package/dist/tools/web-search.d.ts.map +0 -1
- package/dist/tools/workflow-tool.d.ts +0 -11
- package/dist/tools/workflow-tool.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -123
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/workflow.d.ts +0 -110
- package/dist/types/workflow.d.ts.map +0 -1
- package/dist/ui/components/AgentTimer.d.ts +0 -7
- package/dist/ui/components/AgentTimer.d.ts.map +0 -1
- package/dist/ui/components/App.d.ts +0 -197
- package/dist/ui/components/App.d.ts.map +0 -1
- package/dist/ui/components/AuthScreen.d.ts +0 -8
- package/dist/ui/components/AuthScreen.d.ts.map +0 -1
- package/dist/ui/components/AuthWelcomeScreen.d.ts +0 -8
- package/dist/ui/components/AuthWelcomeScreen.d.ts.map +0 -1
- package/dist/ui/components/Breadcrumbs.d.ts +0 -13
- package/dist/ui/components/Breadcrumbs.d.ts.map +0 -1
- package/dist/ui/components/CircularSelectInput.d.ts +0 -24
- package/dist/ui/components/CircularSelectInput.d.ts.map +0 -1
- package/dist/ui/components/ClipboardFileAutocomplete.d.ts +0 -10
- package/dist/ui/components/ClipboardFileAutocomplete.d.ts.map +0 -1
- package/dist/ui/components/CodeBlock.d.ts +0 -9
- package/dist/ui/components/CodeBlock.d.ts.map +0 -1
- package/dist/ui/components/ConfigViewer.d.ts +0 -11
- package/dist/ui/components/ConfigViewer.d.ts.map +0 -1
- package/dist/ui/components/ConfirmPrompt.d.ts +0 -13
- package/dist/ui/components/ConfirmPrompt.d.ts.map +0 -1
- package/dist/ui/components/ConnectionStatusMessage.d.ts +0 -17
- package/dist/ui/components/ConnectionStatusMessage.d.ts.map +0 -1
- package/dist/ui/components/ContextWindowIndicator.d.ts +0 -8
- package/dist/ui/components/ContextWindowIndicator.d.ts.map +0 -1
- package/dist/ui/components/DetailedPlanReviewScreen.d.ts +0 -17
- package/dist/ui/components/DetailedPlanReviewScreen.d.ts.map +0 -1
- package/dist/ui/components/DiffViewer.d.ts +0 -9
- package/dist/ui/components/DiffViewer.d.ts.map +0 -1
- package/dist/ui/components/ErrorBoundary.d.ts +0 -17
- package/dist/ui/components/ErrorBoundary.d.ts.map +0 -1
- package/dist/ui/components/FileCreationPreview.d.ts +0 -8
- package/dist/ui/components/FileCreationPreview.d.ts.map +0 -1
- package/dist/ui/components/FileOperation.d.ts +0 -10
- package/dist/ui/components/FileOperation.d.ts.map +0 -1
- package/dist/ui/components/FileTagAutocomplete.d.ts +0 -11
- package/dist/ui/components/FileTagAutocomplete.d.ts.map +0 -1
- package/dist/ui/components/FontRecommendation.d.ts +0 -1
- package/dist/ui/components/FontRecommendation.d.ts.map +0 -1
- package/dist/ui/components/InputBox.d.ts +0 -42
- package/dist/ui/components/InputBox.d.ts.map +0 -1
- package/dist/ui/components/InteractiveShell.d.ts +0 -30
- package/dist/ui/components/InteractiveShell.d.ts.map +0 -1
- package/dist/ui/components/KeyboardHelp.d.ts +0 -7
- package/dist/ui/components/KeyboardHelp.d.ts.map +0 -1
- package/dist/ui/components/LoadingIndicator.d.ts +0 -3
- package/dist/ui/components/LoadingIndicator.d.ts.map +0 -1
- package/dist/ui/components/MCPAddScreen.d.ts +0 -13
- package/dist/ui/components/MCPAddScreen.d.ts.map +0 -1
- package/dist/ui/components/MCPListScreen.d.ts +0 -17
- package/dist/ui/components/MCPListScreen.d.ts.map +0 -1
- package/dist/ui/components/MCPServerListScreen.d.ts +0 -16
- package/dist/ui/components/MCPServerListScreen.d.ts.map +0 -1
- package/dist/ui/components/MarkdownRenderer.d.ts +0 -8
- package/dist/ui/components/MarkdownRenderer.d.ts.map +0 -1
- package/dist/ui/components/MessageBox.d.ts +0 -10
- package/dist/ui/components/MessageBox.d.ts.map +0 -1
- package/dist/ui/components/MessageDisplay.d.ts +0 -14
- package/dist/ui/components/MessageDisplay.d.ts.map +0 -1
- package/dist/ui/components/MonitorModeAIPanel.d.ts +0 -23
- package/dist/ui/components/MonitorModeAIPanel.d.ts.map +0 -1
- package/dist/ui/components/MultiLineInput.d.ts +0 -13
- package/dist/ui/components/MultiLineInput.d.ts.map +0 -1
- package/dist/ui/components/PasswordPrompt.d.ts +0 -9
- package/dist/ui/components/PasswordPrompt.d.ts.map +0 -1
- package/dist/ui/components/PlanAcceptedMessage.d.ts +0 -20
- package/dist/ui/components/PlanAcceptedMessage.d.ts.map +0 -1
- package/dist/ui/components/PlanReviewScreen.d.ts +0 -14
- package/dist/ui/components/PlanReviewScreen.d.ts.map +0 -1
- package/dist/ui/components/SelectPrompt.d.ts +0 -12
- package/dist/ui/components/SelectPrompt.d.ts.map +0 -1
- package/dist/ui/components/SlashCommandAutocomplete.d.ts +0 -13
- package/dist/ui/components/SlashCommandAutocomplete.d.ts.map +0 -1
- package/dist/ui/components/StatusBar.d.ts +0 -14
- package/dist/ui/components/StatusBar.d.ts.map +0 -1
- package/dist/ui/components/StreamingMessageDisplay.d.ts +0 -15
- package/dist/ui/components/StreamingMessageDisplay.d.ts.map +0 -1
- package/dist/ui/components/TaskCompletedMessage.d.ts +0 -14
- package/dist/ui/components/TaskCompletedMessage.d.ts.map +0 -1
- package/dist/ui/components/TaskProgressIndicator.d.ts +0 -18
- package/dist/ui/components/TaskProgressIndicator.d.ts.map +0 -1
- package/dist/ui/components/ThinkingDisplay.d.ts +0 -15
- package/dist/ui/components/ThinkingDisplay.d.ts.map +0 -1
- package/dist/ui/components/ToolExecutionMessage.d.ts +0 -8
- package/dist/ui/components/ToolExecutionMessage.d.ts.map +0 -1
- package/dist/ui/components/ToolExecutionStatus.d.ts +0 -10
- package/dist/ui/components/ToolExecutionStatus.d.ts.map +0 -1
- package/dist/ui/components/ToolResult.d.ts +0 -10
- package/dist/ui/components/ToolResult.d.ts.map +0 -1
- package/dist/ui/components/VersionUpdatePrompt.d.ts +0 -9
- package/dist/ui/components/VersionUpdatePrompt.d.ts.map +0 -1
- package/dist/ui/components/WelcomeBanner.d.ts +0 -3
- package/dist/ui/components/WelcomeBanner.d.ts.map +0 -1
- package/dist/ui/components/WorkflowCreatorScreen.d.ts +0 -25
- package/dist/ui/components/WorkflowCreatorScreen.d.ts.map +0 -1
- package/dist/utils/ansi-encoder.d.ts +0 -7
- package/dist/utils/ansi-encoder.d.ts.map +0 -1
- package/dist/utils/chat-formatter.d.ts +0 -12
- package/dist/utils/chat-formatter.d.ts.map +0 -1
- package/dist/utils/command-history.d.ts +0 -24
- package/dist/utils/command-history.d.ts.map +0 -1
- package/dist/utils/context-sanitizer.d.ts +0 -50
- package/dist/utils/context-sanitizer.d.ts.map +0 -1
- package/dist/utils/conversation-logger.d.ts +0 -142
- package/dist/utils/conversation-logger.d.ts.map +0 -1
- package/dist/utils/custom-commands-manager.d.ts +0 -59
- package/dist/utils/custom-commands-manager.d.ts.map +0 -1
- package/dist/utils/editor-utils.d.ts +0 -101
- package/dist/utils/editor-utils.d.ts.map +0 -1
- package/dist/utils/file.d.ts +0 -61
- package/dist/utils/file.d.ts.map +0 -1
- package/dist/utils/input-classifier.d.ts +0 -25
- package/dist/utils/input-classifier.d.ts.map +0 -1
- package/dist/utils/logger.d.ts +0 -17
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/markdown-parser.d.ts +0 -60
- package/dist/utils/markdown-parser.d.ts.map +0 -1
- package/dist/utils/shell.d.ts +0 -47
- package/dist/utils/shell.d.ts.map +0 -1
- package/dist/utils/state.d.ts +0 -13
- package/dist/utils/state.d.ts.map +0 -1
- package/dist/utils/syntax-checker.d.ts +0 -24
- package/dist/utils/syntax-checker.d.ts.map +0 -1
- package/dist/utils/terminal-output.d.ts +0 -25
- package/dist/utils/terminal-output.d.ts.map +0 -1
- package/dist/utils/text-clipboard.d.ts +0 -12
- package/dist/utils/text-clipboard.d.ts.map +0 -1
- package/dist/utils/unicode-sanitizer.d.ts +0 -44
- package/dist/utils/unicode-sanitizer.d.ts.map +0 -1
- package/dist/utils/version-checker.d.ts +0 -14
- package/dist/utils/version-checker.d.ts.map +0 -1
|
@@ -1,1005 +1,1023 @@
|
|
|
1
|
-
import { spawn } from
|
|
2
|
-
import { execFile } from
|
|
3
|
-
import { promisify } from
|
|
4
|
-
import * as path from
|
|
5
|
-
import * as fs from
|
|
6
|
-
import * as readline from
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { execFile } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as readline from "readline";
|
|
7
7
|
const execFileAsync = promisify(execFile);
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* Backend priority:
|
|
15
|
-
* 1. ripgrep (fastest, cross-platform)
|
|
16
|
-
* 2. grep (Linux/macOS native)
|
|
17
|
-
* 3. PowerShell Select-String (Windows, good Unicode support)
|
|
18
|
-
* 4. Pure Node.js (universal fallback, no dependencies)
|
|
19
|
-
*
|
|
20
|
-
* Features:
|
|
21
|
-
* - Streaming output to handle large repos
|
|
22
|
-
* - Early abort when MAX_MATCHES reached
|
|
23
|
-
* - Proper encoding handling (UTF-8 with fallback)
|
|
24
|
-
* - CRLF normalization
|
|
25
|
-
* - Windows path support (drive letters, UNC)
|
|
26
|
-
*/
|
|
27
|
-
export class GrepSearchTool {
|
|
28
|
-
static DEFAULT_MAX_MATCHES = 50;
|
|
29
|
-
static MAX_LINE_LENGTH = 300;
|
|
30
|
-
static CONTEXT_LINES = 2;
|
|
31
|
-
static SEARCH_TIMEOUT = 30000;
|
|
32
|
-
// Cache tool availability to avoid repeated checks
|
|
33
|
-
static toolCache = new Map();
|
|
34
|
-
/**
|
|
35
|
-
* Check if a command is available
|
|
36
|
-
*/
|
|
37
|
-
async hasCommand(cmd, args = ['--version']) {
|
|
38
|
-
const cacheKey = `${cmd}:${args.join(',')}`;
|
|
39
|
-
if (GrepSearchTool.toolCache.has(cacheKey)) {
|
|
40
|
-
return GrepSearchTool.toolCache.get(cacheKey);
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
await Promise.race([
|
|
44
|
-
execFileAsync(cmd, args, { timeout: 2000 }),
|
|
45
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 2000))
|
|
46
|
-
]);
|
|
47
|
-
GrepSearchTool.toolCache.set(cacheKey, true);
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
GrepSearchTool.toolCache.set(cacheKey, false);
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
8
|
+
function hasUnescapedChar(value, targetChar) {
|
|
9
|
+
let escaped = false;
|
|
10
|
+
for (const ch of value) {
|
|
11
|
+
if (escaped) {
|
|
12
|
+
escaped = false;
|
|
13
|
+
continue;
|
|
54
14
|
}
|
|
55
|
-
|
|
56
|
-
|
|
15
|
+
if (ch === "\\") {
|
|
16
|
+
escaped = true;
|
|
17
|
+
continue;
|
|
57
18
|
}
|
|
58
|
-
|
|
59
|
-
|
|
19
|
+
if (ch === targetChar) {
|
|
20
|
+
return true;
|
|
60
21
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
function looksLikeRegexPattern(value) {
|
|
26
|
+
const query = value.trim();
|
|
27
|
+
if (!query) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (hasUnescapedChar(query, "|")) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
if (/\\[dDsSwWbBAZzGQEnrtvf0(){}[\].+*?^$|\\]/.test(query)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (query.startsWith("^") || /(^|[^\\])\$$/.test(query)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (/(^|[^\\])\[[^\]]+\]/.test(query)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if (/(^|[^\\])\((\?:|\?=|\?!|\?<=|\?<!)/.test(query)) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (/(^|[^\\])\{[0-9]+(,[0-9]*)?\}/.test(query)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
function usesRegexFeaturesUnsupportedByGrep(value) {
|
|
51
|
+
const query = value.trim();
|
|
52
|
+
if (!query) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (/\\[bBkK]/.test(query)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (/\\[pPkK]\{/.test(query)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (/\(\?(?::|=|!|<=|<!|<[^>]+>)/.test(query)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (/(?:\*\?|\+\?|\?\?|\{\d+(?:,\d*)?\}\?)/.test(query)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
function normalizeRegexForGrep(pattern) {
|
|
70
|
+
let out = "";
|
|
71
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
72
|
+
const ch = pattern[i];
|
|
73
|
+
if (ch !== "\\") {
|
|
74
|
+
out += ch;
|
|
75
|
+
continue;
|
|
69
76
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
async getPowerShellExe() {
|
|
74
|
-
if (await this.hasCommand('pwsh', ['-Version']))
|
|
75
|
-
return 'pwsh';
|
|
76
|
-
return 'powershell';
|
|
77
|
+
if (i + 1 >= pattern.length) {
|
|
78
|
+
out += "\\";
|
|
79
|
+
break;
|
|
77
80
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
const next = pattern[i + 1];
|
|
82
|
+
switch (next) {
|
|
83
|
+
case "d":
|
|
84
|
+
out += "[0-9]";
|
|
85
|
+
break;
|
|
86
|
+
case "D":
|
|
87
|
+
out += "[^0-9]";
|
|
88
|
+
break;
|
|
89
|
+
case "s":
|
|
90
|
+
out += "[[:space:]]";
|
|
91
|
+
break;
|
|
92
|
+
case "S":
|
|
93
|
+
out += "[^[:space:]]";
|
|
94
|
+
break;
|
|
95
|
+
case "w":
|
|
96
|
+
out += "[[:alnum:]_]";
|
|
97
|
+
break;
|
|
98
|
+
case "W":
|
|
99
|
+
out += "[^[:alnum:]_]";
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
out += `\\${next}`;
|
|
103
|
+
break;
|
|
83
104
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
i++;
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
function escapePowerShellSingleQuoted(value) {
|
|
110
|
+
return value.replace(/'/g, "''");
|
|
111
|
+
}
|
|
112
|
+
class GrepSearchTool {
|
|
113
|
+
static DEFAULT_MAX_MATCHES = 50;
|
|
114
|
+
static MAX_LINE_LENGTH = 300;
|
|
115
|
+
static CONTEXT_LINES = 2;
|
|
116
|
+
static SEARCH_TIMEOUT = 3e4;
|
|
117
|
+
// Cache tool availability to avoid repeated checks
|
|
118
|
+
static toolCache = /* @__PURE__ */ new Map();
|
|
119
|
+
/**
|
|
120
|
+
* Check if a command is available
|
|
121
|
+
*/
|
|
122
|
+
async hasCommand(cmd, args = ["--version"]) {
|
|
123
|
+
const cacheKey = `${cmd}:${args.join(",")}`;
|
|
124
|
+
if (GrepSearchTool.toolCache.has(cacheKey)) {
|
|
125
|
+
return GrepSearchTool.toolCache.get(cacheKey);
|
|
92
126
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
127
|
+
try {
|
|
128
|
+
await Promise.race([
|
|
129
|
+
execFileAsync(cmd, args, { timeout: 2e3 }),
|
|
130
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 2e3))
|
|
131
|
+
]);
|
|
132
|
+
GrepSearchTool.toolCache.set(cacheKey, true);
|
|
133
|
+
return true;
|
|
134
|
+
} catch {
|
|
135
|
+
GrepSearchTool.toolCache.set(cacheKey, false);
|
|
136
|
+
return false;
|
|
98
137
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (m2)
|
|
111
|
-
return { file: m2[1], lineNum: +m2[2], content: m2[3] };
|
|
112
|
-
// Unix / relative path: /path/to/file:line:content or path/to/file:line:content
|
|
113
|
-
const m3 = line.match(/^(.+?):(\d+):(.*)$/);
|
|
114
|
-
if (m3)
|
|
115
|
-
return { file: m3[1], lineNum: +m3[2], content: m3[3] };
|
|
116
|
-
return null;
|
|
138
|
+
}
|
|
139
|
+
async hasRipgrep() {
|
|
140
|
+
return this.hasCommand("rg", ["--version"]);
|
|
141
|
+
}
|
|
142
|
+
async hasGrep() {
|
|
143
|
+
return this.hasCommand("grep", ["--version"]);
|
|
144
|
+
}
|
|
145
|
+
async hasPowerShell() {
|
|
146
|
+
if (await this.hasCommand("pwsh", ["-Version"])) return true;
|
|
147
|
+
if (process.platform === "win32") {
|
|
148
|
+
return this.hasCommand("powershell", ["-Version"]);
|
|
117
149
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get the PowerShell executable name
|
|
154
|
+
*/
|
|
155
|
+
async getPowerShellExe() {
|
|
156
|
+
if (await this.hasCommand("pwsh", ["-Version"])) return "pwsh";
|
|
157
|
+
return "powershell";
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Normalize file path to forward slashes
|
|
161
|
+
*/
|
|
162
|
+
normalizePath(filePath) {
|
|
163
|
+
return filePath.replace(/\\/g, "/");
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Truncate long lines
|
|
167
|
+
*/
|
|
168
|
+
truncateLine(line) {
|
|
169
|
+
if (line.length <= GrepSearchTool.MAX_LINE_LENGTH) {
|
|
170
|
+
return line;
|
|
171
|
+
}
|
|
172
|
+
return line.substring(0, GrepSearchTool.MAX_LINE_LENGTH) + " [truncated]";
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Normalize line endings (CRLF -> LF)
|
|
176
|
+
*/
|
|
177
|
+
normalizeLineEndings(text) {
|
|
178
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Parse file:line:content format robustly, handling Windows paths
|
|
182
|
+
* E.g., "C:\foo\bar.ts:42:content" or "/foo/bar.ts:42:content"
|
|
183
|
+
*/
|
|
184
|
+
parseGrepLine(line) {
|
|
185
|
+
const m1 = line.match(/^([A-Za-z]:\\.+?):(\d+):(.*)$/);
|
|
186
|
+
if (m1) return { file: m1[1], lineNum: +m1[2], content: m1[3] };
|
|
187
|
+
const m2 = line.match(/^(\\\\.+?):(\d+):(.*)$/);
|
|
188
|
+
if (m2) return { file: m2[1], lineNum: +m2[2], content: m2[3] };
|
|
189
|
+
const m3 = line.match(/^(.+?):(\d+):(.*)$/);
|
|
190
|
+
if (m3) return { file: m3[1], lineNum: +m3[2], content: m3[3] };
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Create regex from pattern
|
|
195
|
+
*/
|
|
196
|
+
createSearchRegex(pattern, isRegex, caseInsensitive) {
|
|
197
|
+
const flags = caseInsensitive ? "gi" : "g";
|
|
198
|
+
if (isRegex) {
|
|
199
|
+
try {
|
|
200
|
+
return new RegExp(pattern, flags);
|
|
201
|
+
} catch {
|
|
132
202
|
return new RegExp(this.escapeRegex(pattern), flags);
|
|
203
|
+
}
|
|
133
204
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
205
|
+
return new RegExp(this.escapeRegex(pattern), flags);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Escape regex special characters
|
|
209
|
+
*/
|
|
210
|
+
escapeRegex(str) {
|
|
211
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if file matches include patterns
|
|
215
|
+
*/
|
|
216
|
+
matchesIncludesFilter(filePath, includes) {
|
|
217
|
+
if (!includes || includes.length === 0) {
|
|
218
|
+
return true;
|
|
139
219
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
220
|
+
const fileName = path.basename(filePath);
|
|
221
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
222
|
+
for (const pattern of includes) {
|
|
223
|
+
if (pattern.startsWith("*.")) {
|
|
224
|
+
const ext = pattern.substring(1);
|
|
225
|
+
if (fileName.endsWith(ext)) {
|
|
226
|
+
return true;
|
|
146
227
|
}
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const ext = pattern.substring(1);
|
|
152
|
-
if (fileName.endsWith(ext)) {
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
else if (pattern.includes('**')) {
|
|
157
|
-
const ext = pattern.replace('**/', '').replace('*', '');
|
|
158
|
-
if (fileName.endsWith(ext) || normalizedPath.includes(ext)) {
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
else if (fileName === pattern || normalizedPath.includes(pattern)) {
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
228
|
+
} else if (pattern.includes("**")) {
|
|
229
|
+
const ext = pattern.replace("**/", "").replace("*", "");
|
|
230
|
+
if (fileName.endsWith(ext) || normalizedPath.includes(ext)) {
|
|
231
|
+
return true;
|
|
165
232
|
}
|
|
166
|
-
|
|
233
|
+
} else if (fileName === pattern || normalizedPath.includes(pattern)) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
167
236
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return true; // Assume binary on error
|
|
186
|
-
}
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Check if file is likely binary
|
|
241
|
+
*/
|
|
242
|
+
async isBinaryFile(filePath) {
|
|
243
|
+
try {
|
|
244
|
+
const fd = await fs.promises.open(filePath, "r");
|
|
245
|
+
const buffer = Buffer.alloc(8192);
|
|
246
|
+
const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
|
|
247
|
+
await fd.close();
|
|
248
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
249
|
+
if (buffer[i] === 0) return true;
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
} catch {
|
|
253
|
+
return true;
|
|
187
254
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
args.push('--', params.Query, params.SearchPath);
|
|
209
|
-
const matches = [];
|
|
210
|
-
let truncated = false;
|
|
211
|
-
let currentFile = null;
|
|
212
|
-
let contextQueue = [];
|
|
213
|
-
let pendingMatch = null;
|
|
214
|
-
let contextAfterCount = 0;
|
|
215
|
-
// RACECONDITION FIX: Guard against double-resolution
|
|
216
|
-
let finished = false;
|
|
217
|
-
const child = spawn('rg', args, {
|
|
218
|
-
cwd,
|
|
219
|
-
env: { ...process.env, LANG: 'en_US.UTF-8' }
|
|
220
|
-
});
|
|
221
|
-
const rl = readline.createInterface({ input: child.stdout });
|
|
222
|
-
// Robust cleanup function
|
|
223
|
-
const done = (result) => {
|
|
224
|
-
if (finished)
|
|
225
|
-
return;
|
|
226
|
-
finished = true;
|
|
227
|
-
clearTimeout(timer);
|
|
228
|
-
// TERMINATION FIX: Use simple kill() for Windows compatibility
|
|
229
|
-
child.kill();
|
|
230
|
-
rl.close();
|
|
231
|
-
resolve(result);
|
|
232
|
-
};
|
|
233
|
-
const fail = (err) => {
|
|
234
|
-
if (finished)
|
|
235
|
-
return;
|
|
236
|
-
finished = true;
|
|
237
|
-
clearTimeout(timer);
|
|
238
|
-
child.kill();
|
|
239
|
-
rl.close();
|
|
240
|
-
reject(err);
|
|
241
|
-
};
|
|
242
|
-
const timer = setTimeout(() => {
|
|
243
|
-
done({
|
|
244
|
-
matches,
|
|
245
|
-
totalMatches: matches.length,
|
|
246
|
-
truncated: true,
|
|
247
|
-
truncationReason: 'timeout',
|
|
248
|
-
searchPattern: params.Query
|
|
249
|
-
});
|
|
250
|
-
}, GrepSearchTool.SEARCH_TIMEOUT);
|
|
251
|
-
rl.on('line', (line) => {
|
|
252
|
-
if (finished)
|
|
253
|
-
return;
|
|
254
|
-
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
255
|
-
if (matches.length >= maxMatches) {
|
|
256
|
-
truncated = true;
|
|
257
|
-
// ABORT FIX: Immediately stop and resolve
|
|
258
|
-
done({
|
|
259
|
-
matches,
|
|
260
|
-
totalMatches: matches.length,
|
|
261
|
-
truncated,
|
|
262
|
-
truncationReason: 'max_matches',
|
|
263
|
-
searchPattern: params.Query
|
|
264
|
-
});
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
try {
|
|
268
|
-
const event = JSON.parse(line);
|
|
269
|
-
if (event.type === 'begin' && event.data?.path?.text) {
|
|
270
|
-
currentFile = this.normalizePath(event.data.path.text);
|
|
271
|
-
contextQueue = [];
|
|
272
|
-
pendingMatch = null;
|
|
273
|
-
contextAfterCount = 0;
|
|
274
|
-
}
|
|
275
|
-
else if (event.type === 'context' && event.data?.lines?.text) {
|
|
276
|
-
const ctxLine = {
|
|
277
|
-
lineNumber: event.data.line_number,
|
|
278
|
-
text: this.truncateLine(this.normalizeLineEndings(event.data.lines.text).trimEnd())
|
|
279
|
-
};
|
|
280
|
-
if (pendingMatch && contextAfterCount < GrepSearchTool.CONTEXT_LINES) {
|
|
281
|
-
pendingMatch.contextAfter.push(ctxLine);
|
|
282
|
-
contextAfterCount++;
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
contextQueue.push(ctxLine);
|
|
286
|
-
if (contextQueue.length > GrepSearchTool.CONTEXT_LINES) {
|
|
287
|
-
contextQueue.shift();
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
else if (event.type === 'match' && event.data) {
|
|
292
|
-
// Finalize previous match
|
|
293
|
-
if (pendingMatch) {
|
|
294
|
-
matches.push(pendingMatch);
|
|
295
|
-
}
|
|
296
|
-
const lineText = event.data.lines?.text || '';
|
|
297
|
-
const submatches = event.data.submatches || [];
|
|
298
|
-
pendingMatch = {
|
|
299
|
-
file: currentFile || this.normalizePath(event.data.path?.text || ''),
|
|
300
|
-
line: event.data.line_number,
|
|
301
|
-
column: submatches.length > 0 ? submatches[0].start + 1 : undefined,
|
|
302
|
-
byteOffset: event.data.absolute_offset,
|
|
303
|
-
match: this.truncateLine(this.normalizeLineEndings(lineText).trimEnd()),
|
|
304
|
-
matchIndices: submatches.map((s) => [s.start, s.end]),
|
|
305
|
-
contextBefore: [...contextQueue],
|
|
306
|
-
contextAfter: []
|
|
307
|
-
};
|
|
308
|
-
contextQueue = [];
|
|
309
|
-
contextAfterCount = 0;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
catch {
|
|
313
|
-
// Ignore parse errors
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
child.on('close', (code) => {
|
|
317
|
-
if (finished)
|
|
318
|
-
return;
|
|
319
|
-
// Finalize last match
|
|
320
|
-
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
321
|
-
if (pendingMatch && matches.length < maxMatches) {
|
|
322
|
-
matches.push(pendingMatch);
|
|
323
|
-
}
|
|
324
|
-
done({
|
|
325
|
-
matches,
|
|
326
|
-
totalMatches: matches.length,
|
|
327
|
-
truncated,
|
|
328
|
-
truncationReason: truncated ? 'max_matches' : undefined,
|
|
329
|
-
searchPattern: params.Query
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
child.on('error', (err) => {
|
|
333
|
-
fail(err);
|
|
334
|
-
});
|
|
255
|
+
}
|
|
256
|
+
// ========================================================================
|
|
257
|
+
// RIPGREP BACKEND (Streaming)
|
|
258
|
+
// ========================================================================
|
|
259
|
+
async searchWithRipgrepStreaming(params, cwd) {
|
|
260
|
+
return new Promise((resolve, reject) => {
|
|
261
|
+
const args = [
|
|
262
|
+
"--json",
|
|
263
|
+
`--context=${GrepSearchTool.CONTEXT_LINES}`
|
|
264
|
+
];
|
|
265
|
+
if (params.CaseInsensitive) {
|
|
266
|
+
args.push("--ignore-case");
|
|
267
|
+
}
|
|
268
|
+
if (!params.IsRegex) {
|
|
269
|
+
args.push("--fixed-strings");
|
|
270
|
+
}
|
|
271
|
+
if (params.Includes && params.Includes.length > 0) {
|
|
272
|
+
params.Includes.forEach((pattern) => {
|
|
273
|
+
args.push("--glob", pattern);
|
|
335
274
|
});
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
finished = true;
|
|
374
|
-
clearTimeout(timer);
|
|
375
|
-
child.kill();
|
|
376
|
-
rl.close();
|
|
377
|
-
resolve(result);
|
|
378
|
-
};
|
|
379
|
-
const fail = (err) => {
|
|
380
|
-
if (finished)
|
|
381
|
-
return;
|
|
382
|
-
finished = true;
|
|
383
|
-
clearTimeout(timer);
|
|
384
|
-
child.kill();
|
|
385
|
-
rl.close();
|
|
386
|
-
reject(err);
|
|
387
|
-
};
|
|
388
|
-
const timer = setTimeout(() => {
|
|
389
|
-
done({
|
|
390
|
-
matches,
|
|
391
|
-
totalMatches: matches.length,
|
|
392
|
-
truncated: true,
|
|
393
|
-
truncationReason: 'timeout',
|
|
394
|
-
searchPattern: params.Query
|
|
395
|
-
});
|
|
396
|
-
}, GrepSearchTool.SEARCH_TIMEOUT);
|
|
397
|
-
rl.on('line', (line) => {
|
|
398
|
-
if (finished)
|
|
399
|
-
return;
|
|
400
|
-
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
401
|
-
if (matches.length >= maxMatches) {
|
|
402
|
-
truncated = true;
|
|
403
|
-
done({
|
|
404
|
-
matches,
|
|
405
|
-
totalMatches: matches.length,
|
|
406
|
-
truncated,
|
|
407
|
-
truncationReason: 'max_matches',
|
|
408
|
-
searchPattern: params.Query
|
|
409
|
-
});
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
const normalizedLine = this.normalizeLineEndings(line);
|
|
413
|
-
// Skip separator lines
|
|
414
|
-
if (normalizedLine === '--')
|
|
415
|
-
return;
|
|
416
|
-
const parsed = this.parseGrepLine(normalizedLine);
|
|
417
|
-
if (parsed) {
|
|
418
|
-
// For grep, we treat each line as a match (context handling is simpler)
|
|
419
|
-
matches.push({
|
|
420
|
-
file: this.normalizePath(parsed.file),
|
|
421
|
-
line: parsed.lineNum,
|
|
422
|
-
match: this.truncateLine(parsed.content),
|
|
423
|
-
contextBefore: [],
|
|
424
|
-
contextAfter: []
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
child.on('close', () => {
|
|
429
|
-
if (finished)
|
|
430
|
-
return;
|
|
431
|
-
done({
|
|
432
|
-
matches,
|
|
433
|
-
totalMatches: matches.length,
|
|
434
|
-
truncated,
|
|
435
|
-
truncationReason: truncated ? 'max_matches' : undefined,
|
|
436
|
-
searchPattern: params.Query
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
child.on('error', (err) => fail(err));
|
|
275
|
+
}
|
|
276
|
+
args.push("--", params.Query, params.SearchPath);
|
|
277
|
+
const matches = [];
|
|
278
|
+
let truncated = false;
|
|
279
|
+
let currentFile = null;
|
|
280
|
+
let contextQueue = [];
|
|
281
|
+
let pendingMatch = null;
|
|
282
|
+
let contextAfterCount = 0;
|
|
283
|
+
let finished = false;
|
|
284
|
+
const child = spawn("rg", args, {
|
|
285
|
+
cwd,
|
|
286
|
+
env: { ...process.env, LANG: "en_US.UTF-8" }
|
|
287
|
+
});
|
|
288
|
+
const rl = readline.createInterface({ input: child.stdout });
|
|
289
|
+
const done = (result) => {
|
|
290
|
+
if (finished) return;
|
|
291
|
+
finished = true;
|
|
292
|
+
clearTimeout(timer);
|
|
293
|
+
child.kill();
|
|
294
|
+
rl.close();
|
|
295
|
+
resolve(result);
|
|
296
|
+
};
|
|
297
|
+
const fail = (err) => {
|
|
298
|
+
if (finished) return;
|
|
299
|
+
finished = true;
|
|
300
|
+
clearTimeout(timer);
|
|
301
|
+
child.kill();
|
|
302
|
+
rl.close();
|
|
303
|
+
reject(err);
|
|
304
|
+
};
|
|
305
|
+
const timer = setTimeout(() => {
|
|
306
|
+
done({
|
|
307
|
+
matches,
|
|
308
|
+
totalMatches: matches.length,
|
|
309
|
+
truncated: true,
|
|
310
|
+
truncationReason: "timeout",
|
|
311
|
+
searchPattern: params.Query
|
|
440
312
|
});
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
// PATH FIX: Use $_.Path instead of $_.Filename for absolute paths
|
|
456
|
-
if (isDirectory) {
|
|
457
|
-
// Search directory recursively
|
|
458
|
-
const includePattern = params.Includes && params.Includes.length > 0
|
|
459
|
-
? params.Includes.map(p => `'${p}'`).join(',')
|
|
460
|
-
: "'*'";
|
|
461
|
-
const max = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
462
|
-
psCommand = params.IsRegex
|
|
463
|
-
? `Get-ChildItem -Path '${searchPath}' -Recurse -File -Include ${includePattern} -ErrorAction SilentlyContinue | Select-String -Pattern '${patternArg}'${params.CaseInsensitive ? '' : ' -CaseSensitive'} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }`
|
|
464
|
-
: `Get-ChildItem -Path '${searchPath}' -Recurse -File -Include ${includePattern} -ErrorAction SilentlyContinue | Select-String -Pattern '${patternArg}' -SimpleMatch${params.CaseInsensitive ? '' : ' -CaseSensitive'} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }`;
|
|
465
|
-
}
|
|
466
|
-
else {
|
|
467
|
-
// Search single file
|
|
468
|
-
const max = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
469
|
-
psCommand = params.IsRegex
|
|
470
|
-
? `Select-String -Path '${searchPath}' -Pattern '${patternArg}'${params.CaseInsensitive ? '' : ' -CaseSensitive'} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }`
|
|
471
|
-
: `Select-String -Path '${searchPath}' -Pattern '${patternArg}' -SimpleMatch${params.CaseInsensitive ? '' : ' -CaseSensitive'} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }`;
|
|
313
|
+
}, GrepSearchTool.SEARCH_TIMEOUT);
|
|
314
|
+
rl.on("line", (line) => {
|
|
315
|
+
if (finished) return;
|
|
316
|
+
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
317
|
+
if (matches.length >= maxMatches) {
|
|
318
|
+
truncated = true;
|
|
319
|
+
done({
|
|
320
|
+
matches,
|
|
321
|
+
totalMatches: matches.length,
|
|
322
|
+
truncated,
|
|
323
|
+
truncationReason: "max_matches",
|
|
324
|
+
searchPattern: params.Query
|
|
325
|
+
});
|
|
326
|
+
return;
|
|
472
327
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
return;
|
|
485
|
-
finished = true;
|
|
486
|
-
clearTimeout(timer);
|
|
487
|
-
child.kill();
|
|
488
|
-
resolve(result);
|
|
328
|
+
try {
|
|
329
|
+
const event = JSON.parse(line);
|
|
330
|
+
if (event.type === "begin" && event.data?.path?.text) {
|
|
331
|
+
currentFile = this.normalizePath(event.data.path.text);
|
|
332
|
+
contextQueue = [];
|
|
333
|
+
pendingMatch = null;
|
|
334
|
+
contextAfterCount = 0;
|
|
335
|
+
} else if (event.type === "context" && event.data?.lines?.text) {
|
|
336
|
+
const ctxLine = {
|
|
337
|
+
lineNumber: event.data.line_number,
|
|
338
|
+
text: this.truncateLine(this.normalizeLineEndings(event.data.lines.text).trimEnd())
|
|
489
339
|
};
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
340
|
+
if (pendingMatch && contextAfterCount < GrepSearchTool.CONTEXT_LINES) {
|
|
341
|
+
pendingMatch.contextAfter.push(ctxLine);
|
|
342
|
+
contextAfterCount++;
|
|
343
|
+
} else {
|
|
344
|
+
contextQueue.push(ctxLine);
|
|
345
|
+
if (contextQueue.length > GrepSearchTool.CONTEXT_LINES) {
|
|
346
|
+
contextQueue.shift();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} else if (event.type === "match" && event.data) {
|
|
350
|
+
if (pendingMatch) {
|
|
351
|
+
matches.push(pendingMatch);
|
|
352
|
+
}
|
|
353
|
+
const lineText = event.data.lines?.text || "";
|
|
354
|
+
const submatches = event.data.submatches || [];
|
|
355
|
+
pendingMatch = {
|
|
356
|
+
file: currentFile || this.normalizePath(event.data.path?.text || ""),
|
|
357
|
+
line: event.data.line_number,
|
|
358
|
+
column: submatches.length > 0 ? submatches[0].start + 1 : void 0,
|
|
359
|
+
byteOffset: event.data.absolute_offset,
|
|
360
|
+
match: this.truncateLine(this.normalizeLineEndings(lineText).trimEnd()),
|
|
361
|
+
matchIndices: submatches.map((s) => [s.start, s.end]),
|
|
362
|
+
contextBefore: [...contextQueue],
|
|
363
|
+
contextAfter: []
|
|
497
364
|
};
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
truncated: true,
|
|
503
|
-
truncationReason: 'timeout',
|
|
504
|
-
searchPattern: params.Query
|
|
505
|
-
});
|
|
506
|
-
}, GrepSearchTool.SEARCH_TIMEOUT);
|
|
507
|
-
let stdout = '';
|
|
508
|
-
let stderr = '';
|
|
509
|
-
child.stdout.on('data', (data) => {
|
|
510
|
-
stdout += data.toString();
|
|
511
|
-
});
|
|
512
|
-
child.stderr.on('data', (data) => {
|
|
513
|
-
stderr += data.toString();
|
|
514
|
-
});
|
|
515
|
-
child.on('close', (code) => {
|
|
516
|
-
if (finished)
|
|
517
|
-
return;
|
|
518
|
-
const lines = this.normalizeLineEndings(stdout).split('\n').filter(l => l.trim());
|
|
519
|
-
for (const line of lines) {
|
|
520
|
-
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
521
|
-
if (matches.length >= maxMatches) {
|
|
522
|
-
truncated = true;
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
const parsed = this.parseGrepLine(line);
|
|
526
|
-
if (parsed) {
|
|
527
|
-
matches.push({
|
|
528
|
-
file: this.normalizePath(parsed.file),
|
|
529
|
-
line: parsed.lineNum,
|
|
530
|
-
match: this.truncateLine(parsed.content),
|
|
531
|
-
contextBefore: [],
|
|
532
|
-
contextAfter: []
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
done({
|
|
537
|
-
matches,
|
|
538
|
-
totalMatches: matches.length,
|
|
539
|
-
truncated,
|
|
540
|
-
truncationReason: truncated ? 'max_matches' : undefined,
|
|
541
|
-
searchPattern: params.Query
|
|
542
|
-
});
|
|
543
|
-
});
|
|
544
|
-
child.on('error', (err) => fail(err));
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
// ========================================================================
|
|
548
|
-
// PURE NODE.JS BACKEND (Universal Fallback)
|
|
549
|
-
// ========================================================================
|
|
550
|
-
async searchWithNodeJS(params, cwd) {
|
|
551
|
-
const matches = [];
|
|
552
|
-
let truncated = false;
|
|
553
|
-
let filesSearched = 0;
|
|
554
|
-
let encodingFallback = false;
|
|
555
|
-
const searchPath = path.resolve(cwd, params.SearchPath);
|
|
556
|
-
const regex = this.createSearchRegex(params.Query, params.IsRegex ?? false, params.CaseInsensitive ?? false);
|
|
557
|
-
// Collect files to search
|
|
558
|
-
const filesToSearch = [];
|
|
559
|
-
const stats = await fs.promises.stat(searchPath).catch(() => null);
|
|
560
|
-
if (!stats) {
|
|
561
|
-
return { matches: [], totalMatches: 0, truncated: false, searchPattern: params.Query };
|
|
562
|
-
}
|
|
563
|
-
if (stats.isFile()) {
|
|
564
|
-
filesToSearch.push(searchPath);
|
|
365
|
+
contextQueue = [];
|
|
366
|
+
contextAfterCount = 0;
|
|
367
|
+
}
|
|
368
|
+
} catch {
|
|
565
369
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
// Search each file
|
|
370
|
+
});
|
|
371
|
+
child.on("close", (code) => {
|
|
372
|
+
if (finished) return;
|
|
570
373
|
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
truncated = true;
|
|
574
|
-
break;
|
|
575
|
-
}
|
|
576
|
-
// Skip binary files
|
|
577
|
-
if (await this.isBinaryFile(filePath)) {
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
filesSearched++;
|
|
581
|
-
try {
|
|
582
|
-
let content;
|
|
583
|
-
const buffer = await fs.promises.readFile(filePath);
|
|
584
|
-
// Try UTF-8 first
|
|
585
|
-
try {
|
|
586
|
-
content = buffer.toString('utf8');
|
|
587
|
-
// Check for replacement characters indicating decode failure
|
|
588
|
-
if (content.includes('\uFFFD')) {
|
|
589
|
-
content = buffer.toString('latin1');
|
|
590
|
-
encodingFallback = true;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
catch {
|
|
594
|
-
content = buffer.toString('latin1');
|
|
595
|
-
encodingFallback = true;
|
|
596
|
-
}
|
|
597
|
-
const lines = this.normalizeLineEndings(content).split('\n');
|
|
598
|
-
for (let i = 0; i < lines.length; i++) {
|
|
599
|
-
if (matches.length >= maxMatches) {
|
|
600
|
-
truncated = true;
|
|
601
|
-
break;
|
|
602
|
-
}
|
|
603
|
-
const line = lines[i];
|
|
604
|
-
regex.lastIndex = 0; // Reset regex state
|
|
605
|
-
// LOOP FIX: Capture all matches in the line, not just the first
|
|
606
|
-
let matchResult;
|
|
607
|
-
while ((matchResult = regex.exec(line)) !== null) {
|
|
608
|
-
// Prevent infinite loops with zero-width matches
|
|
609
|
-
if (matchResult.index === regex.lastIndex) {
|
|
610
|
-
regex.lastIndex++;
|
|
611
|
-
}
|
|
612
|
-
const relativePath = path.relative(cwd, filePath);
|
|
613
|
-
// Collect context
|
|
614
|
-
const contextBefore = [];
|
|
615
|
-
const contextAfter = [];
|
|
616
|
-
for (let j = Math.max(0, i - GrepSearchTool.CONTEXT_LINES); j < i; j++) {
|
|
617
|
-
contextBefore.push({
|
|
618
|
-
lineNumber: j + 1,
|
|
619
|
-
text: this.truncateLine(lines[j])
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
for (let j = i + 1; j <= Math.min(lines.length - 1, i + GrepSearchTool.CONTEXT_LINES); j++) {
|
|
623
|
-
contextAfter.push({
|
|
624
|
-
lineNumber: j + 1,
|
|
625
|
-
text: this.truncateLine(lines[j])
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
matches.push({
|
|
629
|
-
file: this.normalizePath(relativePath),
|
|
630
|
-
line: i + 1,
|
|
631
|
-
column: matchResult.index + 1,
|
|
632
|
-
match: this.truncateLine(line),
|
|
633
|
-
contextBefore,
|
|
634
|
-
contextAfter
|
|
635
|
-
});
|
|
636
|
-
if (matches.length >= maxMatches) {
|
|
637
|
-
truncated = true;
|
|
638
|
-
break;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
if (truncated)
|
|
642
|
-
break;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
catch {
|
|
646
|
-
// Skip files that can't be read
|
|
647
|
-
}
|
|
374
|
+
if (pendingMatch && matches.length < maxMatches) {
|
|
375
|
+
matches.push(pendingMatch);
|
|
648
376
|
}
|
|
649
|
-
|
|
377
|
+
done({
|
|
378
|
+
matches,
|
|
379
|
+
totalMatches: matches.length,
|
|
380
|
+
truncated,
|
|
381
|
+
truncationReason: truncated ? "max_matches" : void 0,
|
|
382
|
+
searchPattern: params.Query
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
child.on("error", (err) => {
|
|
386
|
+
fail(err);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
// ========================================================================
|
|
391
|
+
// GREP BACKEND (Streaming)
|
|
392
|
+
// ========================================================================
|
|
393
|
+
async searchWithGrepStreaming(params, cwd) {
|
|
394
|
+
return new Promise((resolve, reject) => {
|
|
395
|
+
const query = params.IsRegex ? normalizeRegexForGrep(params.Query) : params.Query;
|
|
396
|
+
const args = [
|
|
397
|
+
"-n",
|
|
398
|
+
// line numbers
|
|
399
|
+
"-r",
|
|
400
|
+
// recursive
|
|
401
|
+
`--context=${GrepSearchTool.CONTEXT_LINES}`
|
|
402
|
+
];
|
|
403
|
+
if (params.CaseInsensitive) {
|
|
404
|
+
args.push("-i");
|
|
405
|
+
}
|
|
406
|
+
if (!params.IsRegex) {
|
|
407
|
+
args.push("-F");
|
|
408
|
+
} else {
|
|
409
|
+
args.push("-E");
|
|
410
|
+
}
|
|
411
|
+
if (params.Includes && params.Includes.length > 0) {
|
|
412
|
+
params.Includes.forEach((pattern) => {
|
|
413
|
+
args.push("--include", pattern);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
args.push(query, params.SearchPath);
|
|
417
|
+
const matches = [];
|
|
418
|
+
let truncated = false;
|
|
419
|
+
let finished = false;
|
|
420
|
+
const child = spawn("grep", args, {
|
|
421
|
+
cwd,
|
|
422
|
+
env: { ...process.env, LANG: "en_US.UTF-8" }
|
|
423
|
+
});
|
|
424
|
+
const rl = readline.createInterface({ input: child.stdout });
|
|
425
|
+
const done = (result) => {
|
|
426
|
+
if (finished) return;
|
|
427
|
+
finished = true;
|
|
428
|
+
clearTimeout(timer);
|
|
429
|
+
child.kill();
|
|
430
|
+
rl.close();
|
|
431
|
+
resolve(result);
|
|
432
|
+
};
|
|
433
|
+
const fail = (err) => {
|
|
434
|
+
if (finished) return;
|
|
435
|
+
finished = true;
|
|
436
|
+
clearTimeout(timer);
|
|
437
|
+
child.kill();
|
|
438
|
+
rl.close();
|
|
439
|
+
reject(err);
|
|
440
|
+
};
|
|
441
|
+
const timer = setTimeout(() => {
|
|
442
|
+
done({
|
|
443
|
+
matches,
|
|
444
|
+
totalMatches: matches.length,
|
|
445
|
+
truncated: true,
|
|
446
|
+
truncationReason: "timeout",
|
|
447
|
+
searchPattern: params.Query
|
|
448
|
+
});
|
|
449
|
+
}, GrepSearchTool.SEARCH_TIMEOUT);
|
|
450
|
+
rl.on("line", (line) => {
|
|
451
|
+
if (finished) return;
|
|
452
|
+
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
453
|
+
if (matches.length >= maxMatches) {
|
|
454
|
+
truncated = true;
|
|
455
|
+
done({
|
|
650
456
|
matches,
|
|
651
457
|
totalMatches: matches.length,
|
|
652
458
|
truncated,
|
|
653
|
-
truncationReason:
|
|
654
|
-
searchPattern: params.Query
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Recursively collect files from directory
|
|
661
|
-
*/
|
|
662
|
-
async collectFiles(dir, files, includes, maxFiles = 10000) {
|
|
663
|
-
if (files.length >= maxFiles)
|
|
664
|
-
return;
|
|
665
|
-
try {
|
|
666
|
-
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
667
|
-
for (const entry of entries) {
|
|
668
|
-
if (files.length >= maxFiles)
|
|
669
|
-
break;
|
|
670
|
-
const fullPath = path.join(dir, entry.name);
|
|
671
|
-
// Skip common non-code directories
|
|
672
|
-
if (entry.isDirectory()) {
|
|
673
|
-
if (['node_modules', '.git', 'dist', 'build', '__pycache__', '.vscode', '.idea'].includes(entry.name)) {
|
|
674
|
-
continue;
|
|
675
|
-
}
|
|
676
|
-
await this.collectFiles(fullPath, files, includes, maxFiles);
|
|
677
|
-
}
|
|
678
|
-
else if (entry.isFile()) {
|
|
679
|
-
if (this.matchesIncludesFilter(fullPath, includes)) {
|
|
680
|
-
files.push(fullPath);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
459
|
+
truncationReason: "max_matches",
|
|
460
|
+
searchPattern: params.Query
|
|
461
|
+
});
|
|
462
|
+
return;
|
|
684
463
|
}
|
|
685
|
-
|
|
686
|
-
|
|
464
|
+
const normalizedLine = this.normalizeLineEndings(line);
|
|
465
|
+
if (normalizedLine === "--") return;
|
|
466
|
+
const parsed = this.parseGrepLine(normalizedLine);
|
|
467
|
+
if (parsed) {
|
|
468
|
+
matches.push({
|
|
469
|
+
file: this.normalizePath(parsed.file),
|
|
470
|
+
line: parsed.lineNum,
|
|
471
|
+
match: this.truncateLine(parsed.content),
|
|
472
|
+
contextBefore: [],
|
|
473
|
+
contextAfter: []
|
|
474
|
+
});
|
|
687
475
|
}
|
|
476
|
+
});
|
|
477
|
+
child.on("close", () => {
|
|
478
|
+
if (finished) return;
|
|
479
|
+
done({
|
|
480
|
+
matches,
|
|
481
|
+
totalMatches: matches.length,
|
|
482
|
+
truncated,
|
|
483
|
+
truncationReason: truncated ? "max_matches" : void 0,
|
|
484
|
+
searchPattern: params.Query
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
child.on("error", (err) => fail(err));
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
// ========================================================================
|
|
491
|
+
// POWERSHELL BACKEND (Windows)
|
|
492
|
+
// ========================================================================
|
|
493
|
+
async searchWithPowerShell(params, cwd) {
|
|
494
|
+
const psExe = await this.getPowerShellExe();
|
|
495
|
+
const patternArg = escapePowerShellSingleQuoted(params.Query);
|
|
496
|
+
const searchPath = path.resolve(cwd, params.SearchPath);
|
|
497
|
+
const escapedSearchPath = escapePowerShellSingleQuoted(searchPath);
|
|
498
|
+
let psCommand;
|
|
499
|
+
const stats = await fs.promises.stat(searchPath).catch(() => null);
|
|
500
|
+
const isDirectory = stats?.isDirectory() ?? false;
|
|
501
|
+
if (isDirectory) {
|
|
502
|
+
const includePattern = params.Includes && params.Includes.length > 0 ? params.Includes.map((p) => `'${escapePowerShellSingleQuoted(p)}'`).join(",") : "'*'";
|
|
503
|
+
const max = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
504
|
+
psCommand = params.IsRegex ? `Get-ChildItem -Path '${escapedSearchPath}' -Recurse -File -Include ${includePattern} -ErrorAction SilentlyContinue | Select-String -Pattern '${patternArg}'${params.CaseInsensitive ? "" : " -CaseSensitive"} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }` : `Get-ChildItem -Path '${escapedSearchPath}' -Recurse -File -Include ${includePattern} -ErrorAction SilentlyContinue | Select-String -Pattern '${patternArg}' -SimpleMatch${params.CaseInsensitive ? "" : " -CaseSensitive"} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }`;
|
|
505
|
+
} else {
|
|
506
|
+
const max = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
507
|
+
psCommand = params.IsRegex ? `Select-String -Path '${escapedSearchPath}' -Pattern '${patternArg}'${params.CaseInsensitive ? "" : " -CaseSensitive"} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }` : `Select-String -Path '${escapedSearchPath}' -Pattern '${patternArg}' -SimpleMatch${params.CaseInsensitive ? "" : " -CaseSensitive"} -Context ${GrepSearchTool.CONTEXT_LINES} | Select-Object -First ${max} | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }`;
|
|
508
|
+
}
|
|
509
|
+
return new Promise((resolve, reject) => {
|
|
510
|
+
const matches = [];
|
|
511
|
+
let truncated = false;
|
|
512
|
+
let finished = false;
|
|
513
|
+
const child = spawn(psExe, ["-NoProfile", "-Command", psCommand], {
|
|
514
|
+
cwd,
|
|
515
|
+
env: { ...process.env }
|
|
516
|
+
});
|
|
517
|
+
const done = (result) => {
|
|
518
|
+
if (finished) return;
|
|
519
|
+
finished = true;
|
|
520
|
+
clearTimeout(timer);
|
|
521
|
+
child.kill();
|
|
522
|
+
resolve(result);
|
|
523
|
+
};
|
|
524
|
+
const fail = (err) => {
|
|
525
|
+
if (finished) return;
|
|
526
|
+
finished = true;
|
|
527
|
+
clearTimeout(timer);
|
|
528
|
+
child.kill();
|
|
529
|
+
reject(err);
|
|
530
|
+
};
|
|
531
|
+
const timer = setTimeout(() => {
|
|
532
|
+
done({
|
|
533
|
+
matches,
|
|
534
|
+
totalMatches: matches.length,
|
|
535
|
+
truncated: true,
|
|
536
|
+
truncationReason: "timeout",
|
|
537
|
+
searchPattern: params.Query
|
|
538
|
+
});
|
|
539
|
+
}, GrepSearchTool.SEARCH_TIMEOUT);
|
|
540
|
+
let stdout = "";
|
|
541
|
+
let stderr = "";
|
|
542
|
+
child.stdout.on("data", (data) => {
|
|
543
|
+
stdout += data.toString();
|
|
544
|
+
});
|
|
545
|
+
child.stderr.on("data", (data) => {
|
|
546
|
+
stderr += data.toString();
|
|
547
|
+
});
|
|
548
|
+
child.on("close", (code) => {
|
|
549
|
+
if (finished) return;
|
|
550
|
+
const lines = this.normalizeLineEndings(stdout).split("\n").filter((l) => l.trim());
|
|
551
|
+
for (const line of lines) {
|
|
552
|
+
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
553
|
+
if (matches.length >= maxMatches) {
|
|
554
|
+
truncated = true;
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
const parsed = this.parseGrepLine(line);
|
|
558
|
+
if (parsed) {
|
|
559
|
+
matches.push({
|
|
560
|
+
file: this.normalizePath(parsed.file),
|
|
561
|
+
line: parsed.lineNum,
|
|
562
|
+
match: this.truncateLine(parsed.content),
|
|
563
|
+
contextBefore: [],
|
|
564
|
+
contextAfter: []
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
done({
|
|
569
|
+
matches,
|
|
570
|
+
totalMatches: matches.length,
|
|
571
|
+
truncated,
|
|
572
|
+
truncationReason: truncated ? "max_matches" : void 0,
|
|
573
|
+
searchPattern: params.Query
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
child.on("error", (err) => fail(err));
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
// ========================================================================
|
|
580
|
+
// PURE NODE.JS BACKEND (Universal Fallback)
|
|
581
|
+
// ========================================================================
|
|
582
|
+
async searchWithNodeJS(params, cwd) {
|
|
583
|
+
const matches = [];
|
|
584
|
+
let truncated = false;
|
|
585
|
+
let filesSearched = 0;
|
|
586
|
+
let encodingFallback = false;
|
|
587
|
+
const searchPath = path.resolve(cwd, params.SearchPath);
|
|
588
|
+
const regex = this.createSearchRegex(params.Query, params.IsRegex ?? false, params.CaseInsensitive ?? false);
|
|
589
|
+
const filesToSearch = [];
|
|
590
|
+
const stats = await fs.promises.stat(searchPath).catch(() => null);
|
|
591
|
+
if (!stats) {
|
|
592
|
+
return { matches: [], totalMatches: 0, truncated: false, searchPattern: params.Query };
|
|
593
|
+
}
|
|
594
|
+
if (stats.isFile()) {
|
|
595
|
+
filesToSearch.push(searchPath);
|
|
596
|
+
} else if (stats.isDirectory()) {
|
|
597
|
+
await this.collectFiles(searchPath, filesToSearch, params.Includes);
|
|
688
598
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
599
|
+
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
600
|
+
for (const filePath of filesToSearch) {
|
|
601
|
+
if (matches.length >= maxMatches) {
|
|
602
|
+
truncated = true;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
if (await this.isBinaryFile(filePath)) {
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
filesSearched++;
|
|
609
|
+
try {
|
|
610
|
+
let content;
|
|
611
|
+
const buffer = await fs.promises.readFile(filePath);
|
|
695
612
|
try {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
613
|
+
content = buffer.toString("utf8");
|
|
614
|
+
if (content.includes("\uFFFD")) {
|
|
615
|
+
content = buffer.toString("latin1");
|
|
616
|
+
encodingFallback = true;
|
|
617
|
+
}
|
|
618
|
+
} catch {
|
|
619
|
+
content = buffer.toString("latin1");
|
|
620
|
+
encodingFallback = true;
|
|
621
|
+
}
|
|
622
|
+
const lines = this.normalizeLineEndings(content).split("\n");
|
|
623
|
+
for (let i = 0; i < lines.length; i++) {
|
|
624
|
+
if (matches.length >= maxMatches) {
|
|
625
|
+
truncated = true;
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
const line = lines[i];
|
|
629
|
+
regex.lastIndex = 0;
|
|
630
|
+
let matchResult;
|
|
631
|
+
while ((matchResult = regex.exec(line)) !== null) {
|
|
632
|
+
if (matchResult.index === regex.lastIndex) {
|
|
633
|
+
regex.lastIndex++;
|
|
700
634
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
635
|
+
const relativePath = path.relative(cwd, filePath);
|
|
636
|
+
const contextBefore = [];
|
|
637
|
+
const contextAfter = [];
|
|
638
|
+
for (let j = Math.max(0, i - GrepSearchTool.CONTEXT_LINES); j < i; j++) {
|
|
639
|
+
contextBefore.push({
|
|
640
|
+
lineNumber: j + 1,
|
|
641
|
+
text: this.truncateLine(lines[j])
|
|
642
|
+
});
|
|
704
643
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
644
|
+
for (let j = i + 1; j <= Math.min(lines.length - 1, i + GrepSearchTool.CONTEXT_LINES); j++) {
|
|
645
|
+
contextAfter.push({
|
|
646
|
+
lineNumber: j + 1,
|
|
647
|
+
text: this.truncateLine(lines[j])
|
|
648
|
+
});
|
|
708
649
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
650
|
+
matches.push({
|
|
651
|
+
file: this.normalizePath(relativePath),
|
|
652
|
+
line: i + 1,
|
|
653
|
+
column: matchResult.index + 1,
|
|
654
|
+
match: this.truncateLine(line),
|
|
655
|
+
contextBefore,
|
|
656
|
+
contextAfter
|
|
657
|
+
});
|
|
658
|
+
if (matches.length >= maxMatches) {
|
|
659
|
+
truncated = true;
|
|
660
|
+
break;
|
|
713
661
|
}
|
|
662
|
+
}
|
|
663
|
+
if (truncated) break;
|
|
714
664
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
665
|
+
} catch {
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
matches,
|
|
670
|
+
totalMatches: matches.length,
|
|
671
|
+
truncated,
|
|
672
|
+
truncationReason: truncated ? "max_matches" : void 0,
|
|
673
|
+
searchPattern: params.Query,
|
|
674
|
+
filesSearched,
|
|
675
|
+
encodingFallback
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Recursively collect files from directory
|
|
680
|
+
*/
|
|
681
|
+
async collectFiles(dir, files, includes, maxFiles = 1e4) {
|
|
682
|
+
if (files.length >= maxFiles) return;
|
|
683
|
+
try {
|
|
684
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
685
|
+
for (const entry of entries) {
|
|
686
|
+
if (files.length >= maxFiles) break;
|
|
687
|
+
const fullPath = path.join(dir, entry.name);
|
|
688
|
+
if (entry.isDirectory()) {
|
|
689
|
+
if (["node_modules", ".git", "dist", "build", "__pycache__", ".vscode", ".idea"].includes(entry.name)) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
await this.collectFiles(fullPath, files, includes, maxFiles);
|
|
693
|
+
} else if (entry.isFile()) {
|
|
694
|
+
if (this.matchesIncludesFilter(fullPath, includes)) {
|
|
695
|
+
files.push(fullPath);
|
|
696
|
+
}
|
|
724
697
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
698
|
+
}
|
|
699
|
+
} catch {
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
// ========================================================================
|
|
703
|
+
// MAIN EXECUTE METHOD
|
|
704
|
+
// ========================================================================
|
|
705
|
+
async executeWithPreferredBackends(params, cwd) {
|
|
706
|
+
let result;
|
|
707
|
+
let backend;
|
|
708
|
+
try {
|
|
709
|
+
if (await this.hasRipgrep()) {
|
|
710
|
+
backend = "ripgrep";
|
|
711
|
+
result = await this.searchWithRipgrepStreaming(params, cwd);
|
|
712
|
+
} else if (await this.hasGrep()) {
|
|
713
|
+
if (params.IsRegex && usesRegexFeaturesUnsupportedByGrep(params.Query)) {
|
|
714
|
+
backend = "nodejs";
|
|
715
|
+
result = await this.searchWithNodeJS(params, cwd);
|
|
716
|
+
} else {
|
|
717
|
+
backend = "grep";
|
|
718
|
+
result = await this.searchWithGrepStreaming(params, cwd);
|
|
734
719
|
}
|
|
735
|
-
|
|
720
|
+
} else if (process.platform === "win32" && await this.hasPowerShell()) {
|
|
721
|
+
backend = "powershell";
|
|
722
|
+
result = await this.searchWithPowerShell(params, cwd);
|
|
723
|
+
} else {
|
|
724
|
+
backend = "nodejs";
|
|
725
|
+
result = await this.searchWithNodeJS(params, cwd);
|
|
726
|
+
}
|
|
727
|
+
} catch (error) {
|
|
728
|
+
try {
|
|
729
|
+
backend = "nodejs";
|
|
730
|
+
result = await this.searchWithNodeJS(params, cwd);
|
|
731
|
+
} catch (fallbackError) {
|
|
732
|
+
throw new Error(`Search failed: ${error.message}. Fallback also failed: ${fallbackError.message}`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (params.Includes && params.Includes.length > 0 && backend === "nodejs") {
|
|
736
|
+
const filteredMatches = result.matches.filter(
|
|
737
|
+
(match) => this.matchesIncludesFilter(match.file, params.Includes)
|
|
738
|
+
);
|
|
739
|
+
return {
|
|
740
|
+
...result,
|
|
741
|
+
matches: filteredMatches,
|
|
742
|
+
totalMatches: filteredMatches.length,
|
|
743
|
+
backend
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
return { ...result, backend };
|
|
747
|
+
}
|
|
748
|
+
async execute(params, cwd) {
|
|
749
|
+
const shouldRetryAsRegex = params.IsRegex === void 0 && looksLikeRegexPattern(params.Query);
|
|
750
|
+
const primaryParams = {
|
|
751
|
+
...params,
|
|
752
|
+
IsRegex: params.IsRegex ?? false
|
|
753
|
+
};
|
|
754
|
+
const primaryResult = await this.executeWithPreferredBackends(primaryParams, cwd);
|
|
755
|
+
if (shouldRetryAsRegex && primaryResult.matches.length === 0) {
|
|
756
|
+
const regexParams = {
|
|
757
|
+
...params,
|
|
758
|
+
IsRegex: true
|
|
759
|
+
};
|
|
760
|
+
const regexResult = await this.executeWithPreferredBackends(regexParams, cwd);
|
|
761
|
+
if (regexResult.matches.length > 0) {
|
|
762
|
+
return regexResult;
|
|
763
|
+
}
|
|
736
764
|
}
|
|
765
|
+
return primaryResult;
|
|
766
|
+
}
|
|
737
767
|
}
|
|
738
|
-
// ============================================================================
|
|
739
|
-
// FORMATTING FUNCTION
|
|
740
|
-
// ============================================================================
|
|
741
768
|
function formatGrepResults(result) {
|
|
742
|
-
|
|
743
|
-
|
|
769
|
+
if (result.matches.length === 0) {
|
|
770
|
+
return `No matches found for pattern "${result.searchPattern}"`;
|
|
771
|
+
}
|
|
772
|
+
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
773
|
+
result.matches.forEach((match) => uniqueFiles.add(match.file));
|
|
774
|
+
const fileCount = uniqueFiles.size;
|
|
775
|
+
const output = [];
|
|
776
|
+
output.push(`Found ${result.uniqueMatchesCount} match${result.uniqueMatchesCount === 1 ? "" : "es"} in ${fileCount} file${fileCount === 1 ? "" : "s"} for pattern "${result.searchPattern}"`);
|
|
777
|
+
if (result.truncated) {
|
|
778
|
+
const reason = result.truncationReason === "timeout" ? "(timed out)" : "";
|
|
779
|
+
output.push(`(showing first ${result.matches.length} matches${reason ? " " + reason : ""})`);
|
|
780
|
+
}
|
|
781
|
+
const matchesByFile = /* @__PURE__ */ new Map();
|
|
782
|
+
result.matches.forEach((match) => {
|
|
783
|
+
if (!matchesByFile.has(match.file)) {
|
|
784
|
+
matchesByFile.set(match.file, []);
|
|
744
785
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
output.push(
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
matchesByFile.get(match.file)?.push(match);
|
|
786
|
+
matchesByFile.get(match.file)?.push(match);
|
|
787
|
+
});
|
|
788
|
+
matchesByFile.forEach((matches, file) => {
|
|
789
|
+
output.push(`
|
|
790
|
+
${file}`);
|
|
791
|
+
matches.forEach((match) => {
|
|
792
|
+
match.contextBefore.forEach((ctx) => {
|
|
793
|
+
output.push(`${ctx.lineNumber}: ${ctx.text}`);
|
|
794
|
+
});
|
|
795
|
+
const columnInfo = match.column ? ` (col ${match.column})` : "";
|
|
796
|
+
output.push(`${match.line}:> ${match.match}${columnInfo}`);
|
|
797
|
+
match.contextAfter.forEach((ctx) => {
|
|
798
|
+
output.push(`${ctx.lineNumber}: ${ctx.text}`);
|
|
799
|
+
});
|
|
760
800
|
});
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
matches.forEach(match => {
|
|
764
|
-
match.contextBefore.forEach(ctx => {
|
|
765
|
-
output.push(`${ctx.lineNumber}: ${ctx.text}`);
|
|
766
|
-
});
|
|
767
|
-
const columnInfo = match.column ? ` (col ${match.column})` : '';
|
|
768
|
-
output.push(`${match.line}:> ${match.match}${columnInfo}`);
|
|
769
|
-
match.contextAfter.forEach(ctx => {
|
|
770
|
-
output.push(`${ctx.lineNumber}: ${ctx.text}`);
|
|
771
|
-
});
|
|
772
|
-
});
|
|
773
|
-
});
|
|
774
|
-
return output.join('\n');
|
|
801
|
+
});
|
|
802
|
+
return output.join("\n");
|
|
775
803
|
}
|
|
776
|
-
// ============================================================================
|
|
777
|
-
// REMOTE SEARCH HELPER
|
|
778
|
-
// ============================================================================
|
|
779
|
-
/**
|
|
780
|
-
* Execute grep search on a remote system via ContextManager
|
|
781
|
-
* Used for SSH/Docker/WSL environments
|
|
782
|
-
*/
|
|
783
804
|
async function searchRemote(params, contextManager) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
if (match) {
|
|
814
|
-
// Truncate long lines
|
|
815
|
-
let content = match[3];
|
|
816
|
-
if (content.length > 300) {
|
|
817
|
-
content = content.substring(0, 300) + ' [truncated]';
|
|
818
|
-
}
|
|
819
|
-
matches.push({
|
|
820
|
-
file: match[1],
|
|
821
|
-
line: parseInt(match[2], 10),
|
|
822
|
-
match: content,
|
|
823
|
-
contextBefore: [],
|
|
824
|
-
contextAfter: [],
|
|
825
|
-
});
|
|
826
|
-
}
|
|
805
|
+
const maxMatches = params.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
806
|
+
const remoteQuery = params.IsRegex ? normalizeRegexForGrep(params.Query) : params.Query;
|
|
807
|
+
const escapedQuery = remoteQuery.replace(/'/g, "'\\''");
|
|
808
|
+
const checkResult = await contextManager.executeCommand(`test -d "${params.SearchPath}" && echo "dir" || echo "file"`, 5e3);
|
|
809
|
+
const isDirectory = checkResult.stdout.trim() === "dir";
|
|
810
|
+
let grepArgs = isDirectory ? "-rn" : "-Hn";
|
|
811
|
+
if (params.CaseInsensitive) {
|
|
812
|
+
grepArgs += "i";
|
|
813
|
+
}
|
|
814
|
+
if (params.IsRegex) {
|
|
815
|
+
grepArgs += "E";
|
|
816
|
+
} else {
|
|
817
|
+
grepArgs += "F";
|
|
818
|
+
}
|
|
819
|
+
let includeArgs = "";
|
|
820
|
+
if (isDirectory && params.Includes && params.Includes.length > 0) {
|
|
821
|
+
includeArgs = params.Includes.map((p) => `--include='${p}'`).join(" ");
|
|
822
|
+
}
|
|
823
|
+
const command = `grep ${grepArgs} ${includeArgs} '${escapedQuery}' "${params.SearchPath}" 2>/dev/null | head -n ${maxMatches}`;
|
|
824
|
+
try {
|
|
825
|
+
const result = await contextManager.executeCommand(command, 3e4);
|
|
826
|
+
const matches = [];
|
|
827
|
+
const lines = result.stdout.split("\n").filter((line) => line.trim());
|
|
828
|
+
for (const line of lines) {
|
|
829
|
+
const match = line.match(/^(.+?):(\d+):(.+)$/);
|
|
830
|
+
if (match) {
|
|
831
|
+
let content = match[3];
|
|
832
|
+
if (content.length > 300) {
|
|
833
|
+
content = content.substring(0, 300) + " [truncated]";
|
|
827
834
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
}
|
|
837
|
-
catch (error) {
|
|
838
|
-
// If grep fails (command not found, permission denied, etc.), return empty result
|
|
839
|
-
return {
|
|
840
|
-
matches: [],
|
|
841
|
-
totalMatches: 0,
|
|
842
|
-
truncated: false,
|
|
843
|
-
searchPattern: params.Query,
|
|
844
|
-
backend: 'remote',
|
|
845
|
-
};
|
|
835
|
+
matches.push({
|
|
836
|
+
file: match[1],
|
|
837
|
+
line: parseInt(match[2], 10),
|
|
838
|
+
match: content,
|
|
839
|
+
contextBefore: [],
|
|
840
|
+
contextAfter: []
|
|
841
|
+
});
|
|
842
|
+
}
|
|
846
843
|
}
|
|
844
|
+
return {
|
|
845
|
+
matches,
|
|
846
|
+
totalMatches: matches.length,
|
|
847
|
+
truncated: matches.length >= maxMatches,
|
|
848
|
+
truncationReason: matches.length >= maxMatches ? "max_matches" : void 0,
|
|
849
|
+
searchPattern: params.Query,
|
|
850
|
+
backend: "remote"
|
|
851
|
+
};
|
|
852
|
+
} catch (error) {
|
|
853
|
+
return {
|
|
854
|
+
matches: [],
|
|
855
|
+
totalMatches: 0,
|
|
856
|
+
truncated: false,
|
|
857
|
+
searchPattern: params.Query,
|
|
858
|
+
backend: "remote"
|
|
859
|
+
};
|
|
860
|
+
}
|
|
847
861
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
line: context
|
|
871
|
-
|
|
872
|
-
NOT for: Finding files/folders by NAME. Use find_files for that instead.
|
|
862
|
+
const grepSearchTool = {
|
|
863
|
+
schema: {
|
|
864
|
+
name: "grep_search",
|
|
865
|
+
description: `Search FILE CONTENTS for text patterns using ripgrep.
|
|
866
|
+
|
|
867
|
+
USE THIS TOOL WHEN:
|
|
868
|
+
- Searching for text/code INSIDE files (function names, variable names, error messages, etc.)
|
|
869
|
+
- Looking for where something is defined or used in the codebase
|
|
870
|
+
- Finding all files that contain a specific string or pattern
|
|
871
|
+
|
|
872
|
+
MULTI-WORD SEARCH: When you search for "control system", this tool will automatically:
|
|
873
|
+
1. Search for the exact phrase "control system"
|
|
874
|
+
2. ALSO search for files containing just "control" or just "system"
|
|
875
|
+
3. Merge and deduplicate all results
|
|
876
|
+
|
|
877
|
+
Results are returned with file paths and line numbers:
|
|
878
|
+
filename
|
|
879
|
+
line: context
|
|
880
|
+
line:> match (col X)
|
|
881
|
+
line: context
|
|
882
|
+
|
|
883
|
+
NOT for: Finding files/folders by NAME. Use find_files for that instead.
|
|
873
884
|
Total results are capped at 50 matches. Use Includes to filter by file type.`,
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
},
|
|
881
|
-
Query: {
|
|
882
|
-
type: 'string',
|
|
883
|
-
description: 'The search term or pattern. For multi-word queries like "control system", individual words will also be searched.',
|
|
884
|
-
},
|
|
885
|
-
SearchPath: {
|
|
886
|
-
type: 'string',
|
|
887
|
-
description: 'The path to search. This can be a directory or a file.',
|
|
888
|
-
},
|
|
889
|
-
Includes: {
|
|
890
|
-
type: 'array',
|
|
891
|
-
description: 'Glob patterns to filter files (e.g. ["*.ts"]).',
|
|
892
|
-
items: { type: 'string' }
|
|
893
|
-
},
|
|
894
|
-
MaxResults: {
|
|
895
|
-
type: 'number',
|
|
896
|
-
description: 'Optional: Maximum number of results to return. Default is 50. Increase this if you need to see more matches.',
|
|
897
|
-
},
|
|
898
|
-
CaseInsensitive: {
|
|
899
|
-
type: 'boolean',
|
|
900
|
-
description: 'If true, performs a case-insensitive search. Default: false (case-sensitive)',
|
|
901
|
-
},
|
|
902
|
-
IsRegex: {
|
|
903
|
-
type: 'boolean',
|
|
904
|
-
description: 'If true, treats Query as a regular expression. Default: false',
|
|
905
|
-
},
|
|
906
|
-
},
|
|
907
|
-
required: ['reason_text', 'Query', 'SearchPath'],
|
|
885
|
+
parameters: {
|
|
886
|
+
type: "object",
|
|
887
|
+
properties: {
|
|
888
|
+
reason_text: {
|
|
889
|
+
type: "string",
|
|
890
|
+
description: "REQUIRED: A brief explanation of what you are searching for and why."
|
|
908
891
|
},
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
892
|
+
Query: {
|
|
893
|
+
type: "string",
|
|
894
|
+
description: 'The search term or pattern. For multi-word queries like "control system", individual words will also be searched.'
|
|
895
|
+
},
|
|
896
|
+
SearchPath: {
|
|
897
|
+
type: "string",
|
|
898
|
+
description: "The path to search. This can be a directory or a file."
|
|
899
|
+
},
|
|
900
|
+
Includes: {
|
|
901
|
+
type: "array",
|
|
902
|
+
description: 'Glob patterns to filter files (e.g. ["*.ts"]).',
|
|
903
|
+
items: { type: "string" }
|
|
904
|
+
},
|
|
905
|
+
MaxResults: {
|
|
906
|
+
type: "number",
|
|
907
|
+
description: "Optional: Maximum number of results to return. Default is 50. Increase this if you need to see more matches."
|
|
908
|
+
},
|
|
909
|
+
CaseInsensitive: {
|
|
910
|
+
type: "boolean",
|
|
911
|
+
description: "If true, performs a case-insensitive search. Default: false (case-sensitive)"
|
|
912
|
+
},
|
|
913
|
+
IsRegex: {
|
|
914
|
+
type: "boolean",
|
|
915
|
+
description: "If true, treats Query as regex. If omitted, the tool starts with literal search and auto-retries regex for regex-like patterns (e.g. a|b, loadChat\\()."
|
|
931
916
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
917
|
+
},
|
|
918
|
+
required: ["reason_text", "Query", "SearchPath"]
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
async execute(args, context) {
|
|
922
|
+
const tool = new GrepSearchTool();
|
|
923
|
+
const startTime = Date.now();
|
|
924
|
+
const query = args.Query;
|
|
925
|
+
const queryLooksRegexLike = looksLikeRegexPattern(query);
|
|
926
|
+
const words = query.trim().split(/\s+/).filter((w) => w.length > 2);
|
|
927
|
+
const allMatches = [];
|
|
928
|
+
const seenMatches = /* @__PURE__ */ new Set();
|
|
929
|
+
const matchesPerQuery = {};
|
|
930
|
+
const queriesExecuted = [];
|
|
931
|
+
let rawTotalMatches = 0;
|
|
932
|
+
let backend = "ripgrep";
|
|
933
|
+
let truncated = false;
|
|
934
|
+
let truncationReason;
|
|
935
|
+
let encodingFallback = false;
|
|
936
|
+
const queries = [query];
|
|
937
|
+
if (words.length > 1 && args.IsRegex !== true && !queryLooksRegexLike) {
|
|
938
|
+
for (const word of words) {
|
|
939
|
+
if (word !== query) {
|
|
940
|
+
queries.push(word);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
const contextManager = context.contextManager;
|
|
945
|
+
const currentContext = contextManager?.getCurrentContext();
|
|
946
|
+
const isRemote = currentContext && currentContext.type !== "local";
|
|
947
|
+
try {
|
|
948
|
+
for (const searchQuery of queries) {
|
|
949
|
+
queriesExecuted.push(searchQuery);
|
|
950
|
+
const params = {
|
|
951
|
+
Query: searchQuery,
|
|
952
|
+
SearchPath: args.SearchPath,
|
|
953
|
+
Includes: args.Includes,
|
|
954
|
+
MaxResults: args.MaxResults,
|
|
955
|
+
CaseInsensitive: args.CaseInsensitive ?? false,
|
|
956
|
+
IsRegex: args.IsRegex
|
|
957
|
+
};
|
|
936
958
|
try {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
Includes: args.Includes,
|
|
943
|
-
MaxResults: args.MaxResults,
|
|
944
|
-
CaseInsensitive: args.CaseInsensitive ?? false,
|
|
945
|
-
IsRegex: args.IsRegex,
|
|
946
|
-
};
|
|
947
|
-
try {
|
|
948
|
-
// Use remote search for SSH/Docker/WSL, local search otherwise
|
|
949
|
-
const result = isRemote
|
|
950
|
-
? await searchRemote(params, contextManager)
|
|
951
|
-
: await tool.execute(params, context.cwd);
|
|
952
|
-
backend = result.backend;
|
|
953
|
-
matchesPerQuery[searchQuery] = result.totalMatches;
|
|
954
|
-
rawTotalMatches += result.totalMatches;
|
|
955
|
-
if (result.truncated) {
|
|
956
|
-
truncated = true;
|
|
957
|
-
truncationReason = result.truncationReason;
|
|
958
|
-
}
|
|
959
|
-
if (result.encodingFallback) {
|
|
960
|
-
encodingFallback = true;
|
|
961
|
-
}
|
|
962
|
-
// Add unique matches with improved dedupe key
|
|
963
|
-
for (const match of result.matches) {
|
|
964
|
-
// Use file:line:column:matchText for more accurate deduplication
|
|
965
|
-
const key = `${match.file}:${match.line}:${match.column ?? 0}:${match.match.substring(0, 50)}`;
|
|
966
|
-
const maxMatches = args.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
967
|
-
if (!seenMatches.has(key) && allMatches.length < maxMatches) {
|
|
968
|
-
seenMatches.add(key);
|
|
969
|
-
allMatches.push(match);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
catch {
|
|
974
|
-
matchesPerQuery[searchQuery] = 0;
|
|
975
|
-
continue;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
const endTime = Date.now();
|
|
979
|
-
const uniqueMatchesCount = allMatches.length;
|
|
980
|
-
const max = args.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
981
|
-
const finalResult = {
|
|
982
|
-
matches: allMatches,
|
|
983
|
-
totalMatches: rawTotalMatches,
|
|
984
|
-
uniqueMatchesCount,
|
|
985
|
-
truncated: truncated || allMatches.length >= max,
|
|
986
|
-
truncationReason: allMatches.length >= max ? 'max_matches' : truncationReason,
|
|
987
|
-
searchPattern: queries.length > 1 ? `"${query}" (+ individual words)` : query,
|
|
988
|
-
metadata: {
|
|
989
|
-
backend,
|
|
990
|
-
searchDurationMs: endTime - startTime,
|
|
991
|
-
queriesExecuted,
|
|
992
|
-
matchesPerQuery,
|
|
993
|
-
encodingFallback
|
|
994
|
-
},
|
|
995
|
-
formattedOutput: '',
|
|
959
|
+
let result = isRemote ? await searchRemote(params, contextManager) : await tool.execute(params, context.cwd);
|
|
960
|
+
if (isRemote && args.IsRegex === void 0 && looksLikeRegexPattern(searchQuery) && result.matches.length === 0) {
|
|
961
|
+
const regexRetryParams = {
|
|
962
|
+
...params,
|
|
963
|
+
IsRegex: true
|
|
996
964
|
};
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
965
|
+
const regexRetryResult = await searchRemote(regexRetryParams, contextManager);
|
|
966
|
+
if (regexRetryResult.matches.length > 0) {
|
|
967
|
+
result = regexRetryResult;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
backend = result.backend;
|
|
971
|
+
matchesPerQuery[searchQuery] = result.totalMatches;
|
|
972
|
+
rawTotalMatches += result.totalMatches;
|
|
973
|
+
if (result.truncated) {
|
|
974
|
+
truncated = true;
|
|
975
|
+
truncationReason = result.truncationReason;
|
|
976
|
+
}
|
|
977
|
+
if (result.encodingFallback) {
|
|
978
|
+
encodingFallback = true;
|
|
979
|
+
}
|
|
980
|
+
for (const match of result.matches) {
|
|
981
|
+
const key = `${match.file}:${match.line}:${match.column ?? 0}:${match.match.substring(0, 50)}`;
|
|
982
|
+
const maxMatches = args.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
983
|
+
if (!seenMatches.has(key) && allMatches.length < maxMatches) {
|
|
984
|
+
seenMatches.add(key);
|
|
985
|
+
allMatches.push(match);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
} catch {
|
|
989
|
+
matchesPerQuery[searchQuery] = 0;
|
|
990
|
+
continue;
|
|
1002
991
|
}
|
|
1003
|
-
|
|
992
|
+
}
|
|
993
|
+
const endTime = Date.now();
|
|
994
|
+
const uniqueMatchesCount = allMatches.length;
|
|
995
|
+
const max = args.MaxResults || GrepSearchTool.DEFAULT_MAX_MATCHES;
|
|
996
|
+
const finalResult = {
|
|
997
|
+
matches: allMatches,
|
|
998
|
+
totalMatches: rawTotalMatches,
|
|
999
|
+
uniqueMatchesCount,
|
|
1000
|
+
truncated: truncated || allMatches.length >= max,
|
|
1001
|
+
truncationReason: allMatches.length >= max ? "max_matches" : truncationReason,
|
|
1002
|
+
searchPattern: queries.length > 1 ? `"${query}" (+ individual words)` : query,
|
|
1003
|
+
metadata: {
|
|
1004
|
+
backend,
|
|
1005
|
+
searchDurationMs: endTime - startTime,
|
|
1006
|
+
queriesExecuted,
|
|
1007
|
+
matchesPerQuery,
|
|
1008
|
+
encodingFallback
|
|
1009
|
+
},
|
|
1010
|
+
formattedOutput: ""
|
|
1011
|
+
};
|
|
1012
|
+
finalResult.formattedOutput = formatGrepResults(finalResult);
|
|
1013
|
+
return finalResult.formattedOutput;
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
throw new Error(`Grep search failed: ${error.message}`);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
export {
|
|
1020
|
+
GrepSearchTool,
|
|
1021
|
+
grepSearchTool
|
|
1004
1022
|
};
|
|
1005
1023
|
//# sourceMappingURL=grep-search.js.map
|