snow-ai 0.4.16 → 0.4.17
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/bundle/cli.mjs +477445 -0
- package/bundle/sql-wasm.wasm +0 -0
- package/package.json +31 -26
- package/dist/agents/codebaseIndexAgent.d.ts +0 -102
- package/dist/agents/codebaseIndexAgent.js +0 -641
- package/dist/agents/codebaseReviewAgent.d.ts +0 -61
- package/dist/agents/codebaseReviewAgent.js +0 -301
- package/dist/agents/compactAgent.d.ts +0 -55
- package/dist/agents/compactAgent.js +0 -306
- package/dist/agents/promptOptimizeAgent.d.ts +0 -54
- package/dist/agents/promptOptimizeAgent.js +0 -268
- package/dist/agents/reviewAgent.d.ts +0 -50
- package/dist/agents/reviewAgent.js +0 -265
- package/dist/agents/summaryAgent.d.ts +0 -57
- package/dist/agents/summaryAgent.js +0 -260
- package/dist/api/anthropic.d.ts +0 -44
- package/dist/api/anthropic.js +0 -598
- package/dist/api/chat.d.ts +0 -73
- package/dist/api/chat.js +0 -386
- package/dist/api/embedding.d.ts +0 -34
- package/dist/api/embedding.js +0 -80
- package/dist/api/gemini.d.ts +0 -31
- package/dist/api/gemini.js +0 -445
- package/dist/api/models.d.ts +0 -15
- package/dist/api/models.js +0 -139
- package/dist/api/responses.d.ts +0 -38
- package/dist/api/responses.js +0 -515
- package/dist/api/systemPrompt.d.ts +0 -4
- package/dist/api/systemPrompt.js +0 -408
- package/dist/api/types.d.ts +0 -53
- package/dist/api/types.js +0 -4
- package/dist/app.d.ts +0 -8
- package/dist/app.js +0 -112
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -199
- package/dist/hooks/useAgentPicker.d.ts +0 -14
- package/dist/hooks/useAgentPicker.js +0 -119
- package/dist/hooks/useClipboard.d.ts +0 -4
- package/dist/hooks/useClipboard.js +0 -175
- package/dist/hooks/useCommandHandler.d.ts +0 -35
- package/dist/hooks/useCommandHandler.js +0 -346
- package/dist/hooks/useCommandPanel.d.ts +0 -17
- package/dist/hooks/useCommandPanel.js +0 -114
- package/dist/hooks/useConversation.d.ts +0 -49
- package/dist/hooks/useConversation.js +0 -1052
- package/dist/hooks/useFilePicker.d.ts +0 -18
- package/dist/hooks/useFilePicker.js +0 -224
- package/dist/hooks/useGlobalExit.d.ts +0 -5
- package/dist/hooks/useGlobalExit.js +0 -34
- package/dist/hooks/useGlobalNavigation.d.ts +0 -6
- package/dist/hooks/useGlobalNavigation.js +0 -17
- package/dist/hooks/useHistoryNavigation.d.ts +0 -35
- package/dist/hooks/useHistoryNavigation.js +0 -133
- package/dist/hooks/useInputBuffer.d.ts +0 -6
- package/dist/hooks/useInputBuffer.js +0 -45
- package/dist/hooks/useKeyboardInput.d.ts +0 -80
- package/dist/hooks/useKeyboardInput.js +0 -608
- package/dist/hooks/useSessionManagement.d.ts +0 -10
- package/dist/hooks/useSessionManagement.js +0 -43
- package/dist/hooks/useSessionSave.d.ts +0 -8
- package/dist/hooks/useSessionSave.js +0 -63
- package/dist/hooks/useSnapshotState.d.ts +0 -26
- package/dist/hooks/useSnapshotState.js +0 -28
- package/dist/hooks/useStreamingState.d.ts +0 -33
- package/dist/hooks/useStreamingState.js +0 -105
- package/dist/hooks/useTerminalFocus.d.ts +0 -28
- package/dist/hooks/useTerminalFocus.js +0 -87
- package/dist/hooks/useTerminalSize.d.ts +0 -4
- package/dist/hooks/useTerminalSize.js +0 -20
- package/dist/hooks/useTodoPicker.d.ts +0 -16
- package/dist/hooks/useTodoPicker.js +0 -94
- package/dist/hooks/useToolConfirmation.d.ts +0 -19
- package/dist/hooks/useToolConfirmation.js +0 -61
- package/dist/hooks/useVSCodeState.d.ts +0 -8
- package/dist/hooks/useVSCodeState.js +0 -81
- package/dist/i18n/I18nContext.d.ts +0 -14
- package/dist/i18n/I18nContext.js +0 -24
- package/dist/i18n/index.d.ts +0 -3
- package/dist/i18n/index.js +0 -2
- package/dist/i18n/lang/en.d.ts +0 -2
- package/dist/i18n/lang/en.js +0 -502
- package/dist/i18n/lang/es.d.ts +0 -2
- package/dist/i18n/lang/es.js +0 -502
- package/dist/i18n/lang/ja.d.ts +0 -2
- package/dist/i18n/lang/ja.js +0 -502
- package/dist/i18n/lang/ko.d.ts +0 -2
- package/dist/i18n/lang/ko.js +0 -502
- package/dist/i18n/lang/zh-TW.d.ts +0 -2
- package/dist/i18n/lang/zh-TW.js +0 -502
- package/dist/i18n/lang/zh.d.ts +0 -2
- package/dist/i18n/lang/zh.js +0 -502
- package/dist/i18n/translations.d.ts +0 -2
- package/dist/i18n/translations.js +0 -14
- package/dist/i18n/types.d.ts +0 -478
- package/dist/i18n/types.js +0 -1
- package/dist/mcp/aceCodeSearch.d.ts +0 -247
- package/dist/mcp/aceCodeSearch.js +0 -1058
- package/dist/mcp/bash.d.ts +0 -50
- package/dist/mcp/bash.js +0 -153
- package/dist/mcp/codebaseSearch.d.ts +0 -44
- package/dist/mcp/codebaseSearch.js +0 -275
- package/dist/mcp/filesystem.d.ts +0 -392
- package/dist/mcp/filesystem.js +0 -1445
- package/dist/mcp/ideDiagnostics.d.ts +0 -36
- package/dist/mcp/ideDiagnostics.js +0 -90
- package/dist/mcp/notebook.d.ts +0 -10
- package/dist/mcp/notebook.js +0 -367
- package/dist/mcp/subagent.d.ts +0 -37
- package/dist/mcp/subagent.js +0 -113
- package/dist/mcp/todo.d.ts +0 -46
- package/dist/mcp/todo.js +0 -511
- package/dist/mcp/types/aceCodeSearch.types.d.ts +0 -92
- package/dist/mcp/types/aceCodeSearch.types.js +0 -4
- package/dist/mcp/types/bash.types.d.ts +0 -13
- package/dist/mcp/types/bash.types.js +0 -4
- package/dist/mcp/types/filesystem.types.d.ts +0 -210
- package/dist/mcp/types/filesystem.types.js +0 -27
- package/dist/mcp/types/todo.types.d.ts +0 -27
- package/dist/mcp/types/todo.types.js +0 -4
- package/dist/mcp/types/websearch.types.d.ts +0 -30
- package/dist/mcp/types/websearch.types.js +0 -4
- package/dist/mcp/utils/aceCodeSearch/filesystem.utils.d.ts +0 -34
- package/dist/mcp/utils/aceCodeSearch/filesystem.utils.js +0 -146
- package/dist/mcp/utils/aceCodeSearch/language.utils.d.ts +0 -14
- package/dist/mcp/utils/aceCodeSearch/language.utils.js +0 -418
- package/dist/mcp/utils/aceCodeSearch/search.utils.d.ts +0 -31
- package/dist/mcp/utils/aceCodeSearch/search.utils.js +0 -136
- package/dist/mcp/utils/aceCodeSearch/symbol.utils.d.ts +0 -20
- package/dist/mcp/utils/aceCodeSearch/symbol.utils.js +0 -141
- package/dist/mcp/utils/bash/security.utils.d.ts +0 -20
- package/dist/mcp/utils/bash/security.utils.js +0 -34
- package/dist/mcp/utils/filesystem/batch-operations.utils.d.ts +0 -39
- package/dist/mcp/utils/filesystem/batch-operations.utils.js +0 -182
- package/dist/mcp/utils/filesystem/code-analysis.utils.d.ts +0 -18
- package/dist/mcp/utils/filesystem/code-analysis.utils.js +0 -165
- package/dist/mcp/utils/filesystem/match-finder.utils.d.ts +0 -16
- package/dist/mcp/utils/filesystem/match-finder.utils.js +0 -85
- package/dist/mcp/utils/filesystem/office-parser.utils.d.ts +0 -43
- package/dist/mcp/utils/filesystem/office-parser.utils.js +0 -163
- package/dist/mcp/utils/filesystem/path-fixer.utils.d.ts +0 -7
- package/dist/mcp/utils/filesystem/path-fixer.utils.js +0 -60
- package/dist/mcp/utils/filesystem/similarity.utils.d.ts +0 -22
- package/dist/mcp/utils/filesystem/similarity.utils.js +0 -75
- package/dist/mcp/utils/todo/date.utils.d.ts +0 -9
- package/dist/mcp/utils/todo/date.utils.js +0 -14
- package/dist/mcp/utils/websearch/browser.utils.d.ts +0 -8
- package/dist/mcp/utils/websearch/browser.utils.js +0 -58
- package/dist/mcp/utils/websearch/text.utils.d.ts +0 -16
- package/dist/mcp/utils/websearch/text.utils.js +0 -39
- package/dist/mcp/websearch.d.ts +0 -88
- package/dist/mcp/websearch.js +0 -375
- package/dist/test/logger-test.d.ts +0 -1
- package/dist/test/logger-test.js +0 -7
- package/dist/types/index.d.ts +0 -15
- package/dist/types/index.js +0 -1
- package/dist/ui/components/AgentPickerPanel.d.ts +0 -10
- package/dist/ui/components/AgentPickerPanel.js +0 -74
- package/dist/ui/components/ChatInput.d.ts +0 -46
- package/dist/ui/components/ChatInput.js +0 -384
- package/dist/ui/components/CommandPanel.d.ts +0 -15
- package/dist/ui/components/CommandPanel.js +0 -80
- package/dist/ui/components/DiffViewer.d.ts +0 -11
- package/dist/ui/components/DiffViewer.js +0 -178
- package/dist/ui/components/FileList.d.ts +0 -15
- package/dist/ui/components/FileList.js +0 -360
- package/dist/ui/components/FileRollbackConfirmation.d.ts +0 -8
- package/dist/ui/components/FileRollbackConfirmation.js +0 -108
- package/dist/ui/components/HelpPanel.d.ts +0 -2
- package/dist/ui/components/HelpPanel.js +0 -67
- package/dist/ui/components/MCPInfoPanel.d.ts +0 -2
- package/dist/ui/components/MCPInfoPanel.js +0 -108
- package/dist/ui/components/MCPInfoScreen.d.ts +0 -7
- package/dist/ui/components/MCPInfoScreen.js +0 -115
- package/dist/ui/components/MarkdownRenderer.d.ts +0 -6
- package/dist/ui/components/MarkdownRenderer.js +0 -70
- package/dist/ui/components/Menu.d.ts +0 -17
- package/dist/ui/components/Menu.js +0 -88
- package/dist/ui/components/MessageList.d.ts +0 -56
- package/dist/ui/components/MessageList.js +0 -97
- package/dist/ui/components/PendingMessages.d.ts +0 -13
- package/dist/ui/components/PendingMessages.js +0 -29
- package/dist/ui/components/PendingToolCalls.d.ts +0 -11
- package/dist/ui/components/PendingToolCalls.js +0 -35
- package/dist/ui/components/ScrollableSelectInput.d.ts +0 -29
- package/dist/ui/components/ScrollableSelectInput.js +0 -157
- package/dist/ui/components/SessionListPanel.d.ts +0 -7
- package/dist/ui/components/SessionListPanel.js +0 -175
- package/dist/ui/components/SessionListScreen.d.ts +0 -7
- package/dist/ui/components/SessionListScreen.js +0 -217
- package/dist/ui/components/SessionListScreenWrapper.d.ts +0 -7
- package/dist/ui/components/SessionListScreenWrapper.js +0 -14
- package/dist/ui/components/ShimmerText.d.ts +0 -9
- package/dist/ui/components/ShimmerText.js +0 -30
- package/dist/ui/components/TodoPickerPanel.d.ts +0 -14
- package/dist/ui/components/TodoPickerPanel.js +0 -119
- package/dist/ui/components/TodoTree.d.ts +0 -15
- package/dist/ui/components/TodoTree.js +0 -60
- package/dist/ui/components/ToolConfirmation.d.ts +0 -21
- package/dist/ui/components/ToolConfirmation.js +0 -204
- package/dist/ui/components/ToolResultPreview.d.ts +0 -13
- package/dist/ui/components/ToolResultPreview.js +0 -337
- package/dist/ui/components/UsagePanel.d.ts +0 -2
- package/dist/ui/components/UsagePanel.js +0 -394
- package/dist/ui/contexts/ThemeContext.d.ts +0 -13
- package/dist/ui/contexts/ThemeContext.js +0 -28
- package/dist/ui/pages/ChatScreen.d.ts +0 -6
- package/dist/ui/pages/ChatScreen.js +0 -1519
- package/dist/ui/pages/CodeBaseConfigScreen.d.ts +0 -8
- package/dist/ui/pages/CodeBaseConfigScreen.js +0 -350
- package/dist/ui/pages/ConfigScreen.d.ts +0 -8
- package/dist/ui/pages/ConfigScreen.js +0 -1101
- package/dist/ui/pages/CustomHeadersScreen.d.ts +0 -6
- package/dist/ui/pages/CustomHeadersScreen.js +0 -502
- package/dist/ui/pages/HeadlessModeScreen.d.ts +0 -7
- package/dist/ui/pages/HeadlessModeScreen.js +0 -381
- package/dist/ui/pages/LanguageSettingsScreen.d.ts +0 -7
- package/dist/ui/pages/LanguageSettingsScreen.js +0 -91
- package/dist/ui/pages/MCPConfigScreen.d.ts +0 -6
- package/dist/ui/pages/MCPConfigScreen.js +0 -55
- package/dist/ui/pages/ProxyConfigScreen.d.ts +0 -8
- package/dist/ui/pages/ProxyConfigScreen.js +0 -149
- package/dist/ui/pages/SensitiveCommandConfigScreen.d.ts +0 -7
- package/dist/ui/pages/SensitiveCommandConfigScreen.js +0 -271
- package/dist/ui/pages/SubAgentConfigScreen.d.ts +0 -9
- package/dist/ui/pages/SubAgentConfigScreen.js +0 -435
- package/dist/ui/pages/SubAgentListScreen.d.ts +0 -9
- package/dist/ui/pages/SubAgentListScreen.js +0 -131
- package/dist/ui/pages/SystemPromptConfigScreen.d.ts +0 -6
- package/dist/ui/pages/SystemPromptConfigScreen.js +0 -326
- package/dist/ui/pages/ThemeSettingsScreen.d.ts +0 -7
- package/dist/ui/pages/ThemeSettingsScreen.js +0 -106
- package/dist/ui/pages/WelcomeScreen.d.ts +0 -7
- package/dist/ui/pages/WelcomeScreen.js +0 -217
- package/dist/ui/themes/index.d.ts +0 -23
- package/dist/ui/themes/index.js +0 -140
- package/dist/utils/apiConfig.d.ts +0 -126
- package/dist/utils/apiConfig.js +0 -423
- package/dist/utils/autoCompress.d.ts +0 -15
- package/dist/utils/autoCompress.js +0 -24
- package/dist/utils/chatExporter.d.ts +0 -9
- package/dist/utils/chatExporter.js +0 -118
- package/dist/utils/checkpointManager.d.ts +0 -74
- package/dist/utils/checkpointManager.js +0 -181
- package/dist/utils/codebaseConfig.d.ts +0 -16
- package/dist/utils/codebaseConfig.js +0 -67
- package/dist/utils/codebaseDatabase.d.ts +0 -102
- package/dist/utils/codebaseDatabase.js +0 -333
- package/dist/utils/codebaseSearchEvents.d.ts +0 -16
- package/dist/utils/codebaseSearchEvents.js +0 -13
- package/dist/utils/commandExecutor.d.ts +0 -13
- package/dist/utils/commandExecutor.js +0 -26
- package/dist/utils/commands/agent.d.ts +0 -2
- package/dist/utils/commands/agent.js +0 -12
- package/dist/utils/commands/clear.d.ts +0 -2
- package/dist/utils/commands/clear.js +0 -12
- package/dist/utils/commands/compact.d.ts +0 -2
- package/dist/utils/commands/compact.js +0 -12
- package/dist/utils/commands/export.d.ts +0 -2
- package/dist/utils/commands/export.js +0 -12
- package/dist/utils/commands/help.d.ts +0 -2
- package/dist/utils/commands/help.js +0 -11
- package/dist/utils/commands/home.d.ts +0 -2
- package/dist/utils/commands/home.js +0 -34
- package/dist/utils/commands/ide.d.ts +0 -2
- package/dist/utils/commands/ide.js +0 -32
- package/dist/utils/commands/init.d.ts +0 -2
- package/dist/utils/commands/init.js +0 -93
- package/dist/utils/commands/mcp.d.ts +0 -2
- package/dist/utils/commands/mcp.js +0 -12
- package/dist/utils/commands/resume.d.ts +0 -2
- package/dist/utils/commands/resume.js +0 -12
- package/dist/utils/commands/review.d.ts +0 -2
- package/dist/utils/commands/review.js +0 -81
- package/dist/utils/commands/role.d.ts +0 -2
- package/dist/utils/commands/role.js +0 -37
- package/dist/utils/commands/todoPicker.d.ts +0 -2
- package/dist/utils/commands/todoPicker.js +0 -12
- package/dist/utils/commands/usage.d.ts +0 -2
- package/dist/utils/commands/usage.js +0 -12
- package/dist/utils/commands/yolo.d.ts +0 -2
- package/dist/utils/commands/yolo.js +0 -12
- package/dist/utils/configManager.d.ts +0 -45
- package/dist/utils/configManager.js +0 -303
- package/dist/utils/contextCompressor.d.ts +0 -16
- package/dist/utils/contextCompressor.js +0 -334
- package/dist/utils/devMode.d.ts +0 -13
- package/dist/utils/devMode.js +0 -54
- package/dist/utils/escapeHandler.d.ts +0 -79
- package/dist/utils/escapeHandler.js +0 -153
- package/dist/utils/fileDialog.d.ts +0 -9
- package/dist/utils/fileDialog.js +0 -74
- package/dist/utils/fileUtils.d.ts +0 -40
- package/dist/utils/fileUtils.js +0 -185
- package/dist/utils/historyManager.d.ts +0 -45
- package/dist/utils/historyManager.js +0 -159
- package/dist/utils/incrementalSnapshot.d.ts +0 -109
- package/dist/utils/incrementalSnapshot.js +0 -383
- package/dist/utils/index.d.ts +0 -11
- package/dist/utils/index.js +0 -18
- package/dist/utils/languageConfig.d.ts +0 -21
- package/dist/utils/languageConfig.js +0 -61
- package/dist/utils/logger.d.ts +0 -37
- package/dist/utils/logger.js +0 -122
- package/dist/utils/mcpToolsManager.d.ts +0 -52
- package/dist/utils/mcpToolsManager.js +0 -878
- package/dist/utils/messageFormatter.d.ts +0 -12
- package/dist/utils/messageFormatter.js +0 -115
- package/dist/utils/notebookManager.d.ts +0 -59
- package/dist/utils/notebookManager.js +0 -213
- package/dist/utils/patch-highlight.d.ts +0 -5
- package/dist/utils/patch-highlight.js +0 -23
- package/dist/utils/processManager.d.ts +0 -27
- package/dist/utils/processManager.js +0 -75
- package/dist/utils/proxyUtils.d.ts +0 -15
- package/dist/utils/proxyUtils.js +0 -50
- package/dist/utils/resourceMonitor.d.ts +0 -65
- package/dist/utils/resourceMonitor.js +0 -175
- package/dist/utils/retryUtils.d.ts +0 -49
- package/dist/utils/retryUtils.js +0 -303
- package/dist/utils/sensitiveCommandManager.d.ts +0 -53
- package/dist/utils/sensitiveCommandManager.js +0 -308
- package/dist/utils/sessionConverter.d.ts +0 -7
- package/dist/utils/sessionConverter.js +0 -306
- package/dist/utils/sessionManager.d.ts +0 -53
- package/dist/utils/sessionManager.js +0 -371
- package/dist/utils/subAgentConfig.d.ts +0 -50
- package/dist/utils/subAgentConfig.js +0 -221
- package/dist/utils/subAgentExecutor.d.ts +0 -40
- package/dist/utils/subAgentExecutor.js +0 -434
- package/dist/utils/terminal.d.ts +0 -5
- package/dist/utils/terminal.js +0 -13
- package/dist/utils/textBuffer.d.ts +0 -99
- package/dist/utils/textBuffer.js +0 -547
- package/dist/utils/textUtils.d.ts +0 -37
- package/dist/utils/textUtils.js +0 -102
- package/dist/utils/themeConfig.d.ts +0 -21
- package/dist/utils/themeConfig.js +0 -61
- package/dist/utils/todoPreprocessor.d.ts +0 -5
- package/dist/utils/todoPreprocessor.js +0 -18
- package/dist/utils/todoScanner.d.ts +0 -8
- package/dist/utils/todoScanner.js +0 -148
- package/dist/utils/toolDisplayConfig.d.ts +0 -16
- package/dist/utils/toolDisplayConfig.js +0 -47
- package/dist/utils/toolExecutor.d.ts +0 -37
- package/dist/utils/toolExecutor.js +0 -224
- package/dist/utils/usageLogger.d.ts +0 -11
- package/dist/utils/usageLogger.js +0 -114
- package/dist/utils/vscodeConnection.d.ts +0 -76
- package/dist/utils/vscodeConnection.js +0 -430
- package/dist/utils/workspaceSnapshot.d.ts +0 -63
- package/dist/utils/workspaceSnapshot.js +0 -300
|
@@ -1,1058 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import { spawn } from 'child_process';
|
|
4
|
-
import { AsyncFzf } from 'fzf';
|
|
5
|
-
import { processManager } from '../utils/processManager.js';
|
|
6
|
-
// Utility functions
|
|
7
|
-
import { detectLanguage } from './utils/aceCodeSearch/language.utils.js';
|
|
8
|
-
import { loadExclusionPatterns, shouldExcludeDirectory, readFileWithCache, } from './utils/aceCodeSearch/filesystem.utils.js';
|
|
9
|
-
import { parseFileSymbols, getContext, } from './utils/aceCodeSearch/symbol.utils.js';
|
|
10
|
-
import { isCommandAvailable, parseGrepOutput, } from './utils/aceCodeSearch/search.utils.js';
|
|
11
|
-
export class ACECodeSearchService {
|
|
12
|
-
constructor(basePath = process.cwd()) {
|
|
13
|
-
Object.defineProperty(this, "basePath", {
|
|
14
|
-
enumerable: true,
|
|
15
|
-
configurable: true,
|
|
16
|
-
writable: true,
|
|
17
|
-
value: void 0
|
|
18
|
-
});
|
|
19
|
-
Object.defineProperty(this, "indexCache", {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
configurable: true,
|
|
22
|
-
writable: true,
|
|
23
|
-
value: new Map()
|
|
24
|
-
});
|
|
25
|
-
Object.defineProperty(this, "lastIndexTime", {
|
|
26
|
-
enumerable: true,
|
|
27
|
-
configurable: true,
|
|
28
|
-
writable: true,
|
|
29
|
-
value: 0
|
|
30
|
-
});
|
|
31
|
-
Object.defineProperty(this, "INDEX_CACHE_DURATION", {
|
|
32
|
-
enumerable: true,
|
|
33
|
-
configurable: true,
|
|
34
|
-
writable: true,
|
|
35
|
-
value: 60000
|
|
36
|
-
}); // 1 minute
|
|
37
|
-
Object.defineProperty(this, "fzfIndex", {
|
|
38
|
-
enumerable: true,
|
|
39
|
-
configurable: true,
|
|
40
|
-
writable: true,
|
|
41
|
-
value: void 0
|
|
42
|
-
});
|
|
43
|
-
Object.defineProperty(this, "allIndexedFiles", {
|
|
44
|
-
enumerable: true,
|
|
45
|
-
configurable: true,
|
|
46
|
-
writable: true,
|
|
47
|
-
value: new Set()
|
|
48
|
-
}); // 使用 Set 提高查找性能 O(1)
|
|
49
|
-
Object.defineProperty(this, "fileModTimes", {
|
|
50
|
-
enumerable: true,
|
|
51
|
-
configurable: true,
|
|
52
|
-
writable: true,
|
|
53
|
-
value: new Map()
|
|
54
|
-
}); // Track file modification times
|
|
55
|
-
Object.defineProperty(this, "customExcludes", {
|
|
56
|
-
enumerable: true,
|
|
57
|
-
configurable: true,
|
|
58
|
-
writable: true,
|
|
59
|
-
value: []
|
|
60
|
-
}); // Custom exclusion patterns from config files
|
|
61
|
-
Object.defineProperty(this, "excludesLoaded", {
|
|
62
|
-
enumerable: true,
|
|
63
|
-
configurable: true,
|
|
64
|
-
writable: true,
|
|
65
|
-
value: false
|
|
66
|
-
}); // Track if exclusions have been loaded
|
|
67
|
-
// 文件内容缓存(用于减少重复读取)
|
|
68
|
-
Object.defineProperty(this, "fileContentCache", {
|
|
69
|
-
enumerable: true,
|
|
70
|
-
configurable: true,
|
|
71
|
-
writable: true,
|
|
72
|
-
value: new Map()
|
|
73
|
-
});
|
|
74
|
-
// 正则表达式缓存(用于 shouldExcludeDirectory)
|
|
75
|
-
Object.defineProperty(this, "regexCache", {
|
|
76
|
-
enumerable: true,
|
|
77
|
-
configurable: true,
|
|
78
|
-
writable: true,
|
|
79
|
-
value: new Map()
|
|
80
|
-
});
|
|
81
|
-
this.basePath = path.resolve(basePath);
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Load custom exclusion patterns from .gitignore and .snowignore
|
|
85
|
-
*/
|
|
86
|
-
async loadExclusionPatterns() {
|
|
87
|
-
if (this.excludesLoaded)
|
|
88
|
-
return;
|
|
89
|
-
this.customExcludes = await loadExclusionPatterns(this.basePath);
|
|
90
|
-
this.excludesLoaded = true;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Check if a directory is a Git repository
|
|
94
|
-
*/
|
|
95
|
-
async isGitRepository(directory = this.basePath) {
|
|
96
|
-
try {
|
|
97
|
-
const gitDir = path.join(directory, '.git');
|
|
98
|
-
const stats = await fs.stat(gitDir);
|
|
99
|
-
return stats.isDirectory();
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Build or refresh the code symbol index with incremental updates
|
|
107
|
-
*/
|
|
108
|
-
async buildIndex(forceRefresh = false) {
|
|
109
|
-
const now = Date.now();
|
|
110
|
-
// Use cache if available and not expired
|
|
111
|
-
if (!forceRefresh &&
|
|
112
|
-
this.indexCache.size > 0 &&
|
|
113
|
-
now - this.lastIndexTime < this.INDEX_CACHE_DURATION) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
// Load exclusion patterns
|
|
117
|
-
await this.loadExclusionPatterns();
|
|
118
|
-
// For force refresh, clear everything
|
|
119
|
-
if (forceRefresh) {
|
|
120
|
-
this.indexCache.clear();
|
|
121
|
-
this.fileModTimes.clear();
|
|
122
|
-
this.allIndexedFiles.clear();
|
|
123
|
-
this.fileContentCache.clear();
|
|
124
|
-
}
|
|
125
|
-
const filesToProcess = [];
|
|
126
|
-
const searchInDirectory = async (dirPath) => {
|
|
127
|
-
try {
|
|
128
|
-
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
129
|
-
for (const entry of entries) {
|
|
130
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
131
|
-
if (entry.isDirectory()) {
|
|
132
|
-
// Use configurable exclusion check
|
|
133
|
-
if (shouldExcludeDirectory(entry.name, fullPath, this.basePath, this.customExcludes, this.regexCache)) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
await searchInDirectory(fullPath);
|
|
137
|
-
}
|
|
138
|
-
else if (entry.isFile()) {
|
|
139
|
-
const language = detectLanguage(fullPath);
|
|
140
|
-
if (language) {
|
|
141
|
-
// Check if file needs to be re-indexed
|
|
142
|
-
try {
|
|
143
|
-
const stats = await fs.stat(fullPath);
|
|
144
|
-
const currentMtime = stats.mtimeMs;
|
|
145
|
-
const cachedMtime = this.fileModTimes.get(fullPath);
|
|
146
|
-
// Only process if file is new or modified
|
|
147
|
-
if (cachedMtime === undefined || currentMtime > cachedMtime) {
|
|
148
|
-
filesToProcess.push(fullPath);
|
|
149
|
-
this.fileModTimes.set(fullPath, currentMtime);
|
|
150
|
-
}
|
|
151
|
-
// Track all indexed files (even if not modified)
|
|
152
|
-
this.allIndexedFiles.add(fullPath);
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
// If we can't stat the file, skip it
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
// Skip directories that cannot be accessed
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
await searchInDirectory(this.basePath);
|
|
166
|
-
// Process files in batches for better performance
|
|
167
|
-
const BATCH_SIZE = 10; // 并发处理批次大小
|
|
168
|
-
const batches = [];
|
|
169
|
-
for (let i = 0; i < filesToProcess.length; i += BATCH_SIZE) {
|
|
170
|
-
batches.push(filesToProcess.slice(i, i + BATCH_SIZE));
|
|
171
|
-
}
|
|
172
|
-
// Process batches concurrently
|
|
173
|
-
for (const batch of batches) {
|
|
174
|
-
await Promise.all(batch.map(async (fullPath) => {
|
|
175
|
-
try {
|
|
176
|
-
const content = await readFileWithCache(fullPath, this.fileContentCache);
|
|
177
|
-
const symbols = await parseFileSymbols(fullPath, content, this.basePath);
|
|
178
|
-
if (symbols.length > 0) {
|
|
179
|
-
this.indexCache.set(fullPath, symbols);
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
// Remove entry if no symbols found
|
|
183
|
-
this.indexCache.delete(fullPath);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
// Remove from index if file cannot be read
|
|
188
|
-
this.indexCache.delete(fullPath);
|
|
189
|
-
this.fileModTimes.delete(fullPath);
|
|
190
|
-
}
|
|
191
|
-
}));
|
|
192
|
-
}
|
|
193
|
-
// Clean up deleted files from cache
|
|
194
|
-
for (const cachedPath of Array.from(this.indexCache.keys())) {
|
|
195
|
-
try {
|
|
196
|
-
await fs.access(cachedPath);
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
// File no longer exists, remove from all caches
|
|
200
|
-
this.indexCache.delete(cachedPath);
|
|
201
|
-
this.fileModTimes.delete(cachedPath);
|
|
202
|
-
this.allIndexedFiles.delete(cachedPath);
|
|
203
|
-
this.fileContentCache.delete(cachedPath);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
this.lastIndexTime = now;
|
|
207
|
-
// Rebuild fzf index only if files were processed
|
|
208
|
-
if (filesToProcess.length > 0 || forceRefresh) {
|
|
209
|
-
this.buildFzfIndex();
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Build fzf index for fast fuzzy symbol name matching
|
|
214
|
-
*/
|
|
215
|
-
buildFzfIndex() {
|
|
216
|
-
const symbolNames = [];
|
|
217
|
-
// Collect all unique symbol names
|
|
218
|
-
for (const fileSymbols of this.indexCache.values()) {
|
|
219
|
-
for (const symbol of fileSymbols) {
|
|
220
|
-
symbolNames.push(symbol.name);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
// Remove duplicates and sort
|
|
224
|
-
const uniqueNames = Array.from(new Set(symbolNames));
|
|
225
|
-
// Build fzf index with adaptive algorithm selection
|
|
226
|
-
// Use v1 for >20k symbols, v2 for ≤20k symbols
|
|
227
|
-
const fuzzyAlgorithm = uniqueNames.length > 20000 ? 'v1' : 'v2';
|
|
228
|
-
this.fzfIndex = new AsyncFzf(uniqueNames, {
|
|
229
|
-
fuzzy: fuzzyAlgorithm,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Search for symbols by name with fuzzy matching using fzf
|
|
234
|
-
*/
|
|
235
|
-
async searchSymbols(query, symbolType, language, maxResults = 100) {
|
|
236
|
-
const startTime = Date.now();
|
|
237
|
-
await this.buildIndex();
|
|
238
|
-
const symbols = [];
|
|
239
|
-
// Use fzf for fuzzy matching if available
|
|
240
|
-
if (this.fzfIndex) {
|
|
241
|
-
try {
|
|
242
|
-
// Get fuzzy matches from fzf
|
|
243
|
-
const fzfResults = await this.fzfIndex.find(query);
|
|
244
|
-
// Build a set of matched symbol names for quick lookup
|
|
245
|
-
const matchedNames = new Set(fzfResults.map((r) => r.item));
|
|
246
|
-
// Collect matching symbols with filters
|
|
247
|
-
for (const fileSymbols of this.indexCache.values()) {
|
|
248
|
-
for (const symbol of fileSymbols) {
|
|
249
|
-
// Apply filters
|
|
250
|
-
if (symbolType && symbol.type !== symbolType)
|
|
251
|
-
continue;
|
|
252
|
-
if (language && symbol.language !== language)
|
|
253
|
-
continue;
|
|
254
|
-
// Check if symbol name is in fzf matches
|
|
255
|
-
if (matchedNames.has(symbol.name)) {
|
|
256
|
-
symbols.push({ ...symbol });
|
|
257
|
-
}
|
|
258
|
-
if (symbols.length >= maxResults)
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
if (symbols.length >= maxResults)
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
264
|
-
// Sort by fzf score (already sorted by relevance from fzf.find)
|
|
265
|
-
// Maintain the fzf order by using the original fzfResults order
|
|
266
|
-
const nameOrder = new Map(fzfResults.map((r, i) => [r.item, i]));
|
|
267
|
-
symbols.sort((a, b) => {
|
|
268
|
-
const aOrder = nameOrder.get(a.name);
|
|
269
|
-
const bOrder = nameOrder.get(b.name);
|
|
270
|
-
// Handle undefined cases
|
|
271
|
-
if (aOrder === undefined && bOrder === undefined)
|
|
272
|
-
return 0;
|
|
273
|
-
if (aOrder === undefined)
|
|
274
|
-
return 1;
|
|
275
|
-
if (bOrder === undefined)
|
|
276
|
-
return -1;
|
|
277
|
-
// Both are numbers (TypeScript needs explicit assertion)
|
|
278
|
-
return aOrder - bOrder;
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
// Fall back to manual scoring if fzf fails
|
|
283
|
-
console.debug('fzf search failed, falling back to manual scoring');
|
|
284
|
-
return this.searchSymbolsManual(query, symbolType, language, maxResults, startTime);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
// Fallback to manual scoring if fzf is not available
|
|
289
|
-
return this.searchSymbolsManual(query, symbolType, language, maxResults, startTime);
|
|
290
|
-
}
|
|
291
|
-
const searchTime = Date.now() - startTime;
|
|
292
|
-
return {
|
|
293
|
-
query,
|
|
294
|
-
symbols,
|
|
295
|
-
references: [], // References would be populated by findReferences
|
|
296
|
-
totalResults: symbols.length,
|
|
297
|
-
searchTime,
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Fallback symbol search using manual fuzzy matching
|
|
302
|
-
*/
|
|
303
|
-
async searchSymbolsManual(query, symbolType, language, maxResults = 100, startTime = Date.now()) {
|
|
304
|
-
const queryLower = query.toLowerCase();
|
|
305
|
-
// Fuzzy match scoring
|
|
306
|
-
const calculateScore = (symbolName) => {
|
|
307
|
-
const nameLower = symbolName.toLowerCase();
|
|
308
|
-
// Exact match
|
|
309
|
-
if (nameLower === queryLower)
|
|
310
|
-
return 100;
|
|
311
|
-
// Starts with
|
|
312
|
-
if (nameLower.startsWith(queryLower))
|
|
313
|
-
return 80;
|
|
314
|
-
// Contains
|
|
315
|
-
if (nameLower.includes(queryLower))
|
|
316
|
-
return 60;
|
|
317
|
-
// Camel case match (e.g., "gfc" matches "getFileContent")
|
|
318
|
-
const camelCaseMatch = symbolName
|
|
319
|
-
.split(/(?=[A-Z])/)
|
|
320
|
-
.map(s => s[0]?.toLowerCase() || '')
|
|
321
|
-
.join('');
|
|
322
|
-
if (camelCaseMatch.includes(queryLower))
|
|
323
|
-
return 40;
|
|
324
|
-
// Fuzzy match
|
|
325
|
-
let score = 0;
|
|
326
|
-
let queryIndex = 0;
|
|
327
|
-
for (let i = 0; i < nameLower.length && queryIndex < queryLower.length; i++) {
|
|
328
|
-
if (nameLower[i] === queryLower[queryIndex]) {
|
|
329
|
-
score += 20;
|
|
330
|
-
queryIndex++;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
if (queryIndex === queryLower.length)
|
|
334
|
-
return score;
|
|
335
|
-
return 0;
|
|
336
|
-
};
|
|
337
|
-
// Search through all indexed symbols with score caching
|
|
338
|
-
const symbolsWithScores = [];
|
|
339
|
-
for (const fileSymbols of this.indexCache.values()) {
|
|
340
|
-
for (const symbol of fileSymbols) {
|
|
341
|
-
// Apply filters
|
|
342
|
-
if (symbolType && symbol.type !== symbolType)
|
|
343
|
-
continue;
|
|
344
|
-
if (language && symbol.language !== language)
|
|
345
|
-
continue;
|
|
346
|
-
const score = calculateScore(symbol.name);
|
|
347
|
-
if (score > 0) {
|
|
348
|
-
symbolsWithScores.push({ symbol: { ...symbol }, score });
|
|
349
|
-
}
|
|
350
|
-
if (symbolsWithScores.length >= maxResults * 2)
|
|
351
|
-
break; // 获取更多候选以便排序
|
|
352
|
-
}
|
|
353
|
-
if (symbolsWithScores.length >= maxResults * 2)
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
// Sort by score (避免重复计算)
|
|
357
|
-
symbolsWithScores.sort((a, b) => b.score - a.score);
|
|
358
|
-
// Extract top results
|
|
359
|
-
const symbols = symbolsWithScores
|
|
360
|
-
.slice(0, maxResults)
|
|
361
|
-
.map(item => item.symbol);
|
|
362
|
-
const searchTime = Date.now() - startTime;
|
|
363
|
-
return {
|
|
364
|
-
query,
|
|
365
|
-
symbols,
|
|
366
|
-
references: [], // References would be populated by findReferences
|
|
367
|
-
totalResults: symbols.length,
|
|
368
|
-
searchTime,
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Find all references to a symbol
|
|
373
|
-
*/
|
|
374
|
-
async findReferences(symbolName, maxResults = 100) {
|
|
375
|
-
const references = [];
|
|
376
|
-
// Load exclusion patterns
|
|
377
|
-
await this.loadExclusionPatterns();
|
|
378
|
-
// Escape special regex characters to prevent ReDoS
|
|
379
|
-
const escapedSymbol = symbolName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
380
|
-
const searchInDirectory = async (dirPath) => {
|
|
381
|
-
try {
|
|
382
|
-
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
383
|
-
for (const entry of entries) {
|
|
384
|
-
if (references.length >= maxResults)
|
|
385
|
-
break;
|
|
386
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
387
|
-
if (entry.isDirectory()) {
|
|
388
|
-
// Use configurable exclusion check
|
|
389
|
-
if (shouldExcludeDirectory(entry.name, fullPath, this.basePath, this.customExcludes, this.regexCache)) {
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
await searchInDirectory(fullPath);
|
|
393
|
-
}
|
|
394
|
-
else if (entry.isFile()) {
|
|
395
|
-
const language = detectLanguage(fullPath);
|
|
396
|
-
if (language) {
|
|
397
|
-
try {
|
|
398
|
-
const content = await fs.readFile(fullPath, 'utf-8');
|
|
399
|
-
const lines = content.split('\n');
|
|
400
|
-
// Search for symbol usage with escaped symbol name
|
|
401
|
-
const regex = new RegExp(`\\b${escapedSymbol}\\b`, 'g');
|
|
402
|
-
for (let i = 0; i < lines.length; i++) {
|
|
403
|
-
const line = lines[i];
|
|
404
|
-
if (!line)
|
|
405
|
-
continue;
|
|
406
|
-
// Reset regex for each line
|
|
407
|
-
regex.lastIndex = 0;
|
|
408
|
-
let match;
|
|
409
|
-
while ((match = regex.exec(line)) !== null) {
|
|
410
|
-
if (references.length >= maxResults)
|
|
411
|
-
break;
|
|
412
|
-
// Determine reference type
|
|
413
|
-
let referenceType = 'usage';
|
|
414
|
-
if (line.includes('import') && line.includes(symbolName)) {
|
|
415
|
-
referenceType = 'import';
|
|
416
|
-
}
|
|
417
|
-
else if (new RegExp(`(?:function|class|const|let|var)\\s+${escapedSymbol}`).test(line)) {
|
|
418
|
-
referenceType = 'definition';
|
|
419
|
-
}
|
|
420
|
-
else if (line.includes(':') &&
|
|
421
|
-
line.includes(symbolName)) {
|
|
422
|
-
referenceType = 'type';
|
|
423
|
-
}
|
|
424
|
-
references.push({
|
|
425
|
-
symbol: symbolName,
|
|
426
|
-
filePath: path.relative(this.basePath, fullPath),
|
|
427
|
-
line: i + 1,
|
|
428
|
-
column: match.index + 1,
|
|
429
|
-
context: getContext(lines, i, 1),
|
|
430
|
-
referenceType,
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
catch (error) {
|
|
436
|
-
// Skip files that cannot be read
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
catch (error) {
|
|
443
|
-
// Skip directories that cannot be accessed
|
|
444
|
-
}
|
|
445
|
-
};
|
|
446
|
-
await searchInDirectory(this.basePath);
|
|
447
|
-
return references;
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* Find symbol definition (go to definition)
|
|
451
|
-
*/
|
|
452
|
-
async findDefinition(symbolName, contextFile) {
|
|
453
|
-
await this.buildIndex();
|
|
454
|
-
// Search in the same file first if context is provided
|
|
455
|
-
if (contextFile) {
|
|
456
|
-
const fullPath = path.resolve(this.basePath, contextFile);
|
|
457
|
-
const fileSymbols = this.indexCache.get(fullPath);
|
|
458
|
-
if (fileSymbols) {
|
|
459
|
-
const symbol = fileSymbols.find(s => s.name === symbolName &&
|
|
460
|
-
(s.type === 'function' ||
|
|
461
|
-
s.type === 'class' ||
|
|
462
|
-
s.type === 'variable'));
|
|
463
|
-
if (symbol)
|
|
464
|
-
return symbol;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
// Search in all files
|
|
468
|
-
for (const fileSymbols of this.indexCache.values()) {
|
|
469
|
-
const symbol = fileSymbols.find(s => s.name === symbolName &&
|
|
470
|
-
(s.type === 'function' ||
|
|
471
|
-
s.type === 'class' ||
|
|
472
|
-
s.type === 'variable'));
|
|
473
|
-
if (symbol)
|
|
474
|
-
return symbol;
|
|
475
|
-
}
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Expand glob patterns with braces like "*.{ts,tsx}" into multiple patterns
|
|
480
|
-
*/
|
|
481
|
-
expandGlobBraces(glob) {
|
|
482
|
-
// Match {a,b,c} pattern
|
|
483
|
-
const braceMatch = glob.match(/^(.+)\{([^}]+)\}(.*)$/);
|
|
484
|
-
if (!braceMatch ||
|
|
485
|
-
!braceMatch[1] ||
|
|
486
|
-
!braceMatch[2] ||
|
|
487
|
-
braceMatch[3] === undefined) {
|
|
488
|
-
return [glob];
|
|
489
|
-
}
|
|
490
|
-
const prefix = braceMatch[1];
|
|
491
|
-
const alternatives = braceMatch[2].split(',');
|
|
492
|
-
const suffix = braceMatch[3];
|
|
493
|
-
return alternatives.map(alt => `${prefix}${alt}${suffix}`);
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Strategy 1: Use git grep for fast searching in Git repositories
|
|
497
|
-
*/
|
|
498
|
-
async gitGrepSearch(pattern, fileGlob, maxResults = 100, isRegex = false) {
|
|
499
|
-
return new Promise((resolve, reject) => {
|
|
500
|
-
const args = ['grep', '--untracked', '-n', '--ignore-case'];
|
|
501
|
-
// Use fixed-strings for literal search, extended regex for pattern search
|
|
502
|
-
if (isRegex) {
|
|
503
|
-
args.push('-E'); // Extended regex
|
|
504
|
-
}
|
|
505
|
-
else {
|
|
506
|
-
args.push('--fixed-strings'); // Literal string matching
|
|
507
|
-
}
|
|
508
|
-
args.push(pattern);
|
|
509
|
-
if (fileGlob) {
|
|
510
|
-
// Normalize path separators for Windows compatibility
|
|
511
|
-
let gitGlob = fileGlob.replace(/\\/g, '/');
|
|
512
|
-
// Convert ** to * as git grep has limited ** support
|
|
513
|
-
gitGlob = gitGlob.replace(/\*\*/g, '*');
|
|
514
|
-
// Expand glob patterns with braces (e.g., "source/*.{ts,tsx}" -> ["source/*.ts", "source/*.tsx"])
|
|
515
|
-
const expandedGlobs = this.expandGlobBraces(gitGlob);
|
|
516
|
-
args.push('--', ...expandedGlobs);
|
|
517
|
-
}
|
|
518
|
-
const child = spawn('git', args, {
|
|
519
|
-
cwd: this.basePath,
|
|
520
|
-
windowsHide: true,
|
|
521
|
-
});
|
|
522
|
-
// Register child process for cleanup
|
|
523
|
-
processManager.register(child);
|
|
524
|
-
const stdoutChunks = [];
|
|
525
|
-
const stderrChunks = [];
|
|
526
|
-
child.stdout.on('data', chunk => stdoutChunks.push(chunk));
|
|
527
|
-
child.stderr.on('data', chunk => stderrChunks.push(chunk));
|
|
528
|
-
child.on('error', err => {
|
|
529
|
-
reject(new Error(`Failed to start git grep: ${err.message}`));
|
|
530
|
-
});
|
|
531
|
-
child.on('close', code => {
|
|
532
|
-
const stdoutData = Buffer.concat(stdoutChunks).toString('utf8');
|
|
533
|
-
const stderrData = Buffer.concat(stderrChunks).toString('utf8').trim();
|
|
534
|
-
if (code === 0) {
|
|
535
|
-
const results = parseGrepOutput(stdoutData, this.basePath);
|
|
536
|
-
resolve(results.slice(0, maxResults));
|
|
537
|
-
}
|
|
538
|
-
else if (code === 1) {
|
|
539
|
-
// No matches found
|
|
540
|
-
resolve([]);
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
reject(new Error(`git grep exited with code ${code}: ${stderrData}`));
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Strategy 2: Use system grep (or ripgrep if available) for fast searching
|
|
550
|
-
*/
|
|
551
|
-
async systemGrepSearch(pattern, fileGlob, maxResults = 100) {
|
|
552
|
-
// Prefer ripgrep (rg) over grep if available
|
|
553
|
-
const grepCommand = (await isCommandAvailable('rg')) ? 'rg' : 'grep';
|
|
554
|
-
const isRipgrep = grepCommand === 'rg';
|
|
555
|
-
return new Promise((resolve, reject) => {
|
|
556
|
-
const args = isRipgrep
|
|
557
|
-
? ['-n', '-i', '--no-heading', pattern]
|
|
558
|
-
: ['-r', '-n', '-H', '-E', '-i'];
|
|
559
|
-
// Add exclusion patterns
|
|
560
|
-
const excludeDirs = [
|
|
561
|
-
'node_modules',
|
|
562
|
-
'.git',
|
|
563
|
-
'dist',
|
|
564
|
-
'build',
|
|
565
|
-
'__pycache__',
|
|
566
|
-
'target',
|
|
567
|
-
'.next',
|
|
568
|
-
'.nuxt',
|
|
569
|
-
'coverage',
|
|
570
|
-
];
|
|
571
|
-
if (isRipgrep) {
|
|
572
|
-
// Ripgrep uses --glob for filtering
|
|
573
|
-
excludeDirs.forEach(dir => args.push('--glob', `!${dir}/`));
|
|
574
|
-
if (fileGlob) {
|
|
575
|
-
// Normalize path separators for Windows compatibility
|
|
576
|
-
const normalizedGlob = fileGlob.replace(/\\/g, '/');
|
|
577
|
-
// Expand glob patterns with braces
|
|
578
|
-
const expandedGlobs = this.expandGlobBraces(normalizedGlob);
|
|
579
|
-
expandedGlobs.forEach(glob => args.push('--glob', glob));
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
// System grep uses --exclude-dir
|
|
584
|
-
excludeDirs.forEach(dir => args.push(`--exclude-dir=${dir}`));
|
|
585
|
-
if (fileGlob) {
|
|
586
|
-
// Normalize path separators for Windows compatibility
|
|
587
|
-
const normalizedGlob = fileGlob.replace(/\\/g, '/');
|
|
588
|
-
// Expand glob patterns with braces
|
|
589
|
-
const expandedGlobs = this.expandGlobBraces(normalizedGlob);
|
|
590
|
-
expandedGlobs.forEach(glob => args.push(`--include=${glob}`));
|
|
591
|
-
}
|
|
592
|
-
args.push(pattern, '.');
|
|
593
|
-
}
|
|
594
|
-
const child = spawn(grepCommand, args, {
|
|
595
|
-
cwd: this.basePath,
|
|
596
|
-
windowsHide: true,
|
|
597
|
-
});
|
|
598
|
-
// Register child process for cleanup
|
|
599
|
-
processManager.register(child);
|
|
600
|
-
const stdoutChunks = [];
|
|
601
|
-
const stderrChunks = [];
|
|
602
|
-
child.stdout.on('data', chunk => stdoutChunks.push(chunk));
|
|
603
|
-
child.stderr.on('data', chunk => {
|
|
604
|
-
const stderrStr = chunk.toString();
|
|
605
|
-
// Suppress common harmless stderr messages
|
|
606
|
-
if (!stderrStr.includes('Permission denied') &&
|
|
607
|
-
!/grep:.*: Is a directory/i.test(stderrStr)) {
|
|
608
|
-
stderrChunks.push(chunk);
|
|
609
|
-
}
|
|
610
|
-
});
|
|
611
|
-
child.on('error', err => {
|
|
612
|
-
reject(new Error(`Failed to start ${grepCommand}: ${err.message}`));
|
|
613
|
-
});
|
|
614
|
-
child.on('close', code => {
|
|
615
|
-
const stdoutData = Buffer.concat(stdoutChunks).toString('utf8');
|
|
616
|
-
const stderrData = Buffer.concat(stderrChunks).toString('utf8').trim();
|
|
617
|
-
if (code === 0) {
|
|
618
|
-
const results = parseGrepOutput(stdoutData, this.basePath);
|
|
619
|
-
resolve(results.slice(0, maxResults));
|
|
620
|
-
}
|
|
621
|
-
else if (code === 1) {
|
|
622
|
-
// No matches found
|
|
623
|
-
resolve([]);
|
|
624
|
-
}
|
|
625
|
-
else if (stderrData) {
|
|
626
|
-
reject(new Error(`${grepCommand} exited with code ${code}: ${stderrData}`));
|
|
627
|
-
}
|
|
628
|
-
else {
|
|
629
|
-
// Exit code > 1 but no stderr, likely just suppressed errors
|
|
630
|
-
resolve([]);
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
/**
|
|
636
|
-
* Convert a glob pattern to a RegExp that matches full paths
|
|
637
|
-
* Supports: *, **, ?, {a,b}, [abc]
|
|
638
|
-
*/
|
|
639
|
-
globPatternToRegex(globPattern) {
|
|
640
|
-
// Normalize path separators
|
|
641
|
-
const normalizedGlob = globPattern.replace(/\\/g, '/');
|
|
642
|
-
// First, temporarily replace glob special patterns with placeholders
|
|
643
|
-
// to prevent them from being escaped
|
|
644
|
-
let regexStr = normalizedGlob
|
|
645
|
-
.replace(/\*\*/g, '\x00DOUBLESTAR\x00') // ** -> placeholder
|
|
646
|
-
.replace(/\*/g, '\x00STAR\x00') // * -> placeholder
|
|
647
|
-
.replace(/\?/g, '\x00QUESTION\x00'); // ? -> placeholder
|
|
648
|
-
// Now escape all special regex characters
|
|
649
|
-
regexStr = regexStr.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
650
|
-
// Replace placeholders with actual regex patterns
|
|
651
|
-
regexStr = regexStr
|
|
652
|
-
.replace(/\x00DOUBLESTAR\x00/g, '.*') // ** -> .* (match any path segments)
|
|
653
|
-
.replace(/\x00STAR\x00/g, '[^/]*') // * -> [^/]* (match within single segment)
|
|
654
|
-
.replace(/\x00QUESTION\x00/g, '.'); // ? -> . (match single character)
|
|
655
|
-
return new RegExp(regexStr, 'i');
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Strategy 3: Pure JavaScript fallback search
|
|
659
|
-
*/
|
|
660
|
-
async jsTextSearch(pattern, fileGlob, isRegex = false, maxResults = 100) {
|
|
661
|
-
const results = [];
|
|
662
|
-
// Load exclusion patterns
|
|
663
|
-
await this.loadExclusionPatterns();
|
|
664
|
-
// Compile search pattern
|
|
665
|
-
let searchRegex;
|
|
666
|
-
try {
|
|
667
|
-
if (isRegex) {
|
|
668
|
-
searchRegex = new RegExp(pattern, 'gi');
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
// Escape special regex characters for literal search
|
|
672
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
673
|
-
searchRegex = new RegExp(escaped, 'gi');
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
catch (error) {
|
|
677
|
-
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
678
|
-
}
|
|
679
|
-
// Parse glob pattern if provided using improved glob parser
|
|
680
|
-
const globRegex = fileGlob ? this.globPatternToRegex(fileGlob) : null;
|
|
681
|
-
// Binary file extensions (using Set for O(1) lookup)
|
|
682
|
-
const binaryExts = new Set([
|
|
683
|
-
'.jpg',
|
|
684
|
-
'.jpeg',
|
|
685
|
-
'.png',
|
|
686
|
-
'.gif',
|
|
687
|
-
'.bmp',
|
|
688
|
-
'.ico',
|
|
689
|
-
'.svg',
|
|
690
|
-
'.pdf',
|
|
691
|
-
'.zip',
|
|
692
|
-
'.tar',
|
|
693
|
-
'.gz',
|
|
694
|
-
'.rar',
|
|
695
|
-
'.7z',
|
|
696
|
-
'.exe',
|
|
697
|
-
'.dll',
|
|
698
|
-
'.so',
|
|
699
|
-
'.dylib',
|
|
700
|
-
'.mp3',
|
|
701
|
-
'.mp4',
|
|
702
|
-
'.avi',
|
|
703
|
-
'.mov',
|
|
704
|
-
'.woff',
|
|
705
|
-
'.woff2',
|
|
706
|
-
'.ttf',
|
|
707
|
-
'.eot',
|
|
708
|
-
'.class',
|
|
709
|
-
'.jar',
|
|
710
|
-
'.war',
|
|
711
|
-
'.o',
|
|
712
|
-
'.a',
|
|
713
|
-
'.lib',
|
|
714
|
-
]);
|
|
715
|
-
// Search recursively
|
|
716
|
-
const searchInDirectory = async (dirPath) => {
|
|
717
|
-
if (results.length >= maxResults)
|
|
718
|
-
return;
|
|
719
|
-
try {
|
|
720
|
-
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
721
|
-
for (const entry of entries) {
|
|
722
|
-
if (results.length >= maxResults)
|
|
723
|
-
break;
|
|
724
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
725
|
-
if (entry.isDirectory()) {
|
|
726
|
-
// Use configurable exclusion check
|
|
727
|
-
if (shouldExcludeDirectory(entry.name, fullPath, this.basePath, this.customExcludes, this.regexCache)) {
|
|
728
|
-
continue;
|
|
729
|
-
}
|
|
730
|
-
await searchInDirectory(fullPath);
|
|
731
|
-
}
|
|
732
|
-
else if (entry.isFile()) {
|
|
733
|
-
// Filter by glob if specified
|
|
734
|
-
if (globRegex) {
|
|
735
|
-
// Use relative path from basePath for glob matching
|
|
736
|
-
const relativePath = path
|
|
737
|
-
.relative(this.basePath, fullPath)
|
|
738
|
-
.replace(/\\/g, '/');
|
|
739
|
-
if (!globRegex.test(relativePath)) {
|
|
740
|
-
continue;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
// Skip binary files (using Set for fast lookup)
|
|
744
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
745
|
-
if (binaryExts.has(ext)) {
|
|
746
|
-
continue;
|
|
747
|
-
}
|
|
748
|
-
try {
|
|
749
|
-
const content = await fs.readFile(fullPath, 'utf-8');
|
|
750
|
-
const lines = content.split('\n');
|
|
751
|
-
for (let i = 0; i < lines.length; i++) {
|
|
752
|
-
if (results.length >= maxResults)
|
|
753
|
-
break;
|
|
754
|
-
const line = lines[i];
|
|
755
|
-
if (!line)
|
|
756
|
-
continue;
|
|
757
|
-
// Reset regex for each line
|
|
758
|
-
searchRegex.lastIndex = 0;
|
|
759
|
-
const match = searchRegex.exec(line);
|
|
760
|
-
if (match) {
|
|
761
|
-
results.push({
|
|
762
|
-
filePath: path.relative(this.basePath, fullPath),
|
|
763
|
-
line: i + 1,
|
|
764
|
-
column: match.index + 1,
|
|
765
|
-
content: line.trim(),
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
catch (error) {
|
|
771
|
-
// Skip files that cannot be read (binary, permissions, etc.)
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
catch (error) {
|
|
777
|
-
// Skip directories that cannot be accessed
|
|
778
|
-
}
|
|
779
|
-
};
|
|
780
|
-
await searchInDirectory(this.basePath);
|
|
781
|
-
return results;
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* Fast text search with multi-layer strategy
|
|
785
|
-
* Strategy 1: git grep (fastest, uses git index)
|
|
786
|
-
* Strategy 2: system grep/ripgrep (fast, system-optimized)
|
|
787
|
-
* Strategy 3: JavaScript fallback (slower, but always works)
|
|
788
|
-
* Searches for text patterns across files with glob filtering
|
|
789
|
-
*/
|
|
790
|
-
async textSearch(pattern, fileGlob, isRegex = false, maxResults = 100) {
|
|
791
|
-
// Strategy 1: Try git grep first
|
|
792
|
-
if (await this.isGitRepository()) {
|
|
793
|
-
try {
|
|
794
|
-
const gitAvailable = await isCommandAvailable('git');
|
|
795
|
-
if (gitAvailable) {
|
|
796
|
-
const results = await this.gitGrepSearch(pattern, fileGlob, maxResults, isRegex);
|
|
797
|
-
if (results.length > 0) {
|
|
798
|
-
return await this.sortResultsByRecency(results);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
catch (error) {
|
|
803
|
-
// Fall through to next strategy
|
|
804
|
-
//console.debug('git grep failed, falling back to system grep');
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
// Strategy 2: Try system grep/ripgrep
|
|
808
|
-
try {
|
|
809
|
-
const grepAvailable = (await isCommandAvailable('rg')) || (await isCommandAvailable('grep'));
|
|
810
|
-
if (grepAvailable) {
|
|
811
|
-
const results = await this.systemGrepSearch(pattern, fileGlob, maxResults);
|
|
812
|
-
return await this.sortResultsByRecency(results);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
catch (error) {
|
|
816
|
-
// Fall through to JavaScript fallback
|
|
817
|
-
//console.debug('system grep failed, falling back to JavaScript search');
|
|
818
|
-
}
|
|
819
|
-
// Strategy 3: JavaScript fallback (always works)
|
|
820
|
-
const results = await this.jsTextSearch(pattern, fileGlob, isRegex, maxResults);
|
|
821
|
-
return await this.sortResultsByRecency(results);
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
|
-
* Sort search results by file modification time (recent files first)
|
|
825
|
-
* Files modified within last 24 hours are prioritized
|
|
826
|
-
* Uses parallel stat calls for better performance
|
|
827
|
-
*/
|
|
828
|
-
async sortResultsByRecency(results) {
|
|
829
|
-
if (results.length === 0)
|
|
830
|
-
return results;
|
|
831
|
-
const now = Date.now();
|
|
832
|
-
const recentThreshold = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
|
|
833
|
-
// Get unique file paths
|
|
834
|
-
const uniqueFiles = Array.from(new Set(results.map(r => r.filePath)));
|
|
835
|
-
// Fetch file modification times in parallel using Promise.allSettled
|
|
836
|
-
const statResults = await Promise.allSettled(uniqueFiles.map(async (filePath) => {
|
|
837
|
-
const fullPath = path.resolve(this.basePath, filePath);
|
|
838
|
-
const stats = await fs.stat(fullPath);
|
|
839
|
-
return { filePath, mtimeMs: stats.mtimeMs };
|
|
840
|
-
}));
|
|
841
|
-
// Build map of file modification times
|
|
842
|
-
const fileModTimes = new Map();
|
|
843
|
-
statResults.forEach((result, index) => {
|
|
844
|
-
if (result.status === 'fulfilled') {
|
|
845
|
-
fileModTimes.set(result.value.filePath, result.value.mtimeMs);
|
|
846
|
-
}
|
|
847
|
-
else {
|
|
848
|
-
// If we can't get stats, treat as old file
|
|
849
|
-
fileModTimes.set(uniqueFiles[index], 0);
|
|
850
|
-
}
|
|
851
|
-
});
|
|
852
|
-
// Sort results: recent files first, then by original order
|
|
853
|
-
return results.sort((a, b) => {
|
|
854
|
-
const aMtime = fileModTimes.get(a.filePath) || 0;
|
|
855
|
-
const bMtime = fileModTimes.get(b.filePath) || 0;
|
|
856
|
-
const aIsRecent = now - aMtime < recentThreshold;
|
|
857
|
-
const bIsRecent = now - bMtime < recentThreshold;
|
|
858
|
-
// Recent files come first
|
|
859
|
-
if (aIsRecent && !bIsRecent)
|
|
860
|
-
return -1;
|
|
861
|
-
if (!aIsRecent && bIsRecent)
|
|
862
|
-
return 1;
|
|
863
|
-
// Both recent or both old: sort by modification time (newer first)
|
|
864
|
-
if (aIsRecent && bIsRecent)
|
|
865
|
-
return bMtime - aMtime;
|
|
866
|
-
// Both old: maintain original order (preserve relevance from grep)
|
|
867
|
-
return 0;
|
|
868
|
-
});
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* Get code outline for a file (all symbols in the file)
|
|
872
|
-
*/
|
|
873
|
-
async getFileOutline(filePath) {
|
|
874
|
-
const fullPath = path.resolve(this.basePath, filePath);
|
|
875
|
-
try {
|
|
876
|
-
const content = await fs.readFile(fullPath, 'utf-8');
|
|
877
|
-
return await parseFileSymbols(fullPath, content, this.basePath);
|
|
878
|
-
}
|
|
879
|
-
catch (error) {
|
|
880
|
-
throw new Error(`Failed to get outline for ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
/**
|
|
884
|
-
* Search with language-specific context (cross-reference search)
|
|
885
|
-
*/
|
|
886
|
-
async semanticSearch(query, searchType = 'all', language, symbolType, maxResults = 50) {
|
|
887
|
-
const startTime = Date.now();
|
|
888
|
-
// Get symbol search results
|
|
889
|
-
const symbolResults = await this.searchSymbols(query, symbolType, language, maxResults);
|
|
890
|
-
// Get reference results if needed
|
|
891
|
-
let references = [];
|
|
892
|
-
if (searchType === 'usage' || searchType === 'all') {
|
|
893
|
-
// Find references for the top matching symbols
|
|
894
|
-
const topSymbols = symbolResults.symbols.slice(0, 5);
|
|
895
|
-
for (const symbol of topSymbols) {
|
|
896
|
-
const symbolRefs = await this.findReferences(symbol.name, maxResults);
|
|
897
|
-
references.push(...symbolRefs);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
// Filter results based on search type
|
|
901
|
-
let filteredSymbols = symbolResults.symbols;
|
|
902
|
-
if (searchType === 'definition') {
|
|
903
|
-
filteredSymbols = symbolResults.symbols.filter(s => s.type === 'function' || s.type === 'class' || s.type === 'interface');
|
|
904
|
-
}
|
|
905
|
-
else if (searchType === 'usage') {
|
|
906
|
-
filteredSymbols = [];
|
|
907
|
-
}
|
|
908
|
-
else if (searchType === 'implementation') {
|
|
909
|
-
filteredSymbols = symbolResults.symbols.filter(s => s.type === 'function' || s.type === 'method' || s.type === 'class');
|
|
910
|
-
}
|
|
911
|
-
const searchTime = Date.now() - startTime;
|
|
912
|
-
return {
|
|
913
|
-
query,
|
|
914
|
-
symbols: filteredSymbols,
|
|
915
|
-
references,
|
|
916
|
-
totalResults: filteredSymbols.length + references.length,
|
|
917
|
-
searchTime,
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
// Export a default instance
|
|
922
|
-
export const aceCodeSearchService = new ACECodeSearchService();
|
|
923
|
-
// MCP Tool definitions for integration
|
|
924
|
-
export const mcpTools = [
|
|
925
|
-
{
|
|
926
|
-
name: 'ace-find_definition',
|
|
927
|
-
description: 'ACE Code Search: Find the definition of a symbol (Go to Definition). Locates where a function, class, or variable is defined in the codebase. Returns precise location with full signature and context.',
|
|
928
|
-
inputSchema: {
|
|
929
|
-
type: 'object',
|
|
930
|
-
properties: {
|
|
931
|
-
symbolName: {
|
|
932
|
-
type: 'string',
|
|
933
|
-
description: 'Name of the symbol to find definition for',
|
|
934
|
-
},
|
|
935
|
-
contextFile: {
|
|
936
|
-
type: 'string',
|
|
937
|
-
description: 'Current file path for context-aware search (optional, searches current file first)',
|
|
938
|
-
},
|
|
939
|
-
},
|
|
940
|
-
required: ['symbolName'],
|
|
941
|
-
},
|
|
942
|
-
},
|
|
943
|
-
{
|
|
944
|
-
name: 'ace-find_references',
|
|
945
|
-
description: 'ACE Code Search: Find all references to a symbol (Find All References). Shows where a function, class, or variable is used throughout the codebase. Categorizes references as definition, usage, import, or type reference.',
|
|
946
|
-
inputSchema: {
|
|
947
|
-
type: 'object',
|
|
948
|
-
properties: {
|
|
949
|
-
symbolName: {
|
|
950
|
-
type: 'string',
|
|
951
|
-
description: 'Name of the symbol to find references for',
|
|
952
|
-
},
|
|
953
|
-
maxResults: {
|
|
954
|
-
type: 'number',
|
|
955
|
-
description: 'Maximum number of references to return (default: 100)',
|
|
956
|
-
default: 100,
|
|
957
|
-
},
|
|
958
|
-
},
|
|
959
|
-
required: ['symbolName'],
|
|
960
|
-
},
|
|
961
|
-
},
|
|
962
|
-
{
|
|
963
|
-
name: 'ace-semantic_search',
|
|
964
|
-
description: 'ACE Code Search: 智能符号搜索与语义分析。支持多种搜索模式:(1) definition - 查找符号定义(函数/类/接口);(2) usage - 查找符号引用位置;(3) implementation - 查找具体实现;(4) all - 综合搜索。支持模糊匹配、按语言和符号类型过滤。💡 提示:如果只需要查看单个文件的符号大纲,使用 ace-file_outline 更快。',
|
|
965
|
-
inputSchema: {
|
|
966
|
-
type: 'object',
|
|
967
|
-
properties: {
|
|
968
|
-
query: {
|
|
969
|
-
type: 'string',
|
|
970
|
-
description: '搜索查询 (符号名称或模式,支持模糊匹配如 "gfc" 匹配 "getFileContent")',
|
|
971
|
-
},
|
|
972
|
-
searchType: {
|
|
973
|
-
type: 'string',
|
|
974
|
-
enum: ['definition', 'usage', 'implementation', 'all'],
|
|
975
|
-
description: '搜索类型:definition (查找声明)、usage (查找使用)、implementation (查找实现)、all (全面搜索)',
|
|
976
|
-
default: 'all',
|
|
977
|
-
},
|
|
978
|
-
symbolType: {
|
|
979
|
-
type: 'string',
|
|
980
|
-
enum: [
|
|
981
|
-
'function',
|
|
982
|
-
'class',
|
|
983
|
-
'method',
|
|
984
|
-
'variable',
|
|
985
|
-
'constant',
|
|
986
|
-
'interface',
|
|
987
|
-
'type',
|
|
988
|
-
'enum',
|
|
989
|
-
'import',
|
|
990
|
-
'export',
|
|
991
|
-
],
|
|
992
|
-
description: '可选:按符号类型筛选 (function, class, variable等)',
|
|
993
|
-
},
|
|
994
|
-
language: {
|
|
995
|
-
type: 'string',
|
|
996
|
-
enum: [
|
|
997
|
-
'typescript',
|
|
998
|
-
'javascript',
|
|
999
|
-
'python',
|
|
1000
|
-
'go',
|
|
1001
|
-
'rust',
|
|
1002
|
-
'java',
|
|
1003
|
-
'csharp',
|
|
1004
|
-
],
|
|
1005
|
-
description: '可选:按编程语言筛选',
|
|
1006
|
-
},
|
|
1007
|
-
maxResults: {
|
|
1008
|
-
type: 'number',
|
|
1009
|
-
description: '最大返回结果数 (默认: 50)',
|
|
1010
|
-
default: 50,
|
|
1011
|
-
},
|
|
1012
|
-
},
|
|
1013
|
-
required: ['query'],
|
|
1014
|
-
},
|
|
1015
|
-
},
|
|
1016
|
-
{
|
|
1017
|
-
name: 'ace-file_outline',
|
|
1018
|
-
description: "ACE Code Search: Get complete code outline for a file. Shows all functions, classes, variables, and other symbols defined in the file with their locations. Similar to VS Code's outline view.",
|
|
1019
|
-
inputSchema: {
|
|
1020
|
-
type: 'object',
|
|
1021
|
-
properties: {
|
|
1022
|
-
filePath: {
|
|
1023
|
-
type: 'string',
|
|
1024
|
-
description: 'Path to the file to get outline for (relative to workspace root)',
|
|
1025
|
-
},
|
|
1026
|
-
},
|
|
1027
|
-
required: ['filePath'],
|
|
1028
|
-
},
|
|
1029
|
-
},
|
|
1030
|
-
{
|
|
1031
|
-
name: 'ace-text_search',
|
|
1032
|
-
description: 'ACE Code Search: Literal text/regex pattern matching (grep-style search). Best for finding exact strings: TODOs, comments, log messages, error strings, string constants. NOT recommended for code understanding or exploring functionality - use semantic search tools for that. Use when you know the exact text pattern you are looking for.',
|
|
1033
|
-
inputSchema: {
|
|
1034
|
-
type: 'object',
|
|
1035
|
-
properties: {
|
|
1036
|
-
pattern: {
|
|
1037
|
-
type: 'string',
|
|
1038
|
-
description: 'Text pattern or regex to search for (e.g., "TODO:", "import.*from", "throw new Error")',
|
|
1039
|
-
},
|
|
1040
|
-
fileGlob: {
|
|
1041
|
-
type: 'string',
|
|
1042
|
-
description: 'Glob pattern to filter files (e.g., "*.ts" for TypeScript only, "**/*.{js,ts}" for JS and TS, "src/**/*.py" for Python in src)',
|
|
1043
|
-
},
|
|
1044
|
-
isRegex: {
|
|
1045
|
-
type: 'boolean',
|
|
1046
|
-
description: 'Whether the pattern is a regular expression (default: false for literal text search)',
|
|
1047
|
-
default: false,
|
|
1048
|
-
},
|
|
1049
|
-
maxResults: {
|
|
1050
|
-
type: 'number',
|
|
1051
|
-
description: 'Maximum number of results to return (default: 100)',
|
|
1052
|
-
default: 100,
|
|
1053
|
-
},
|
|
1054
|
-
},
|
|
1055
|
-
required: ['pattern'],
|
|
1056
|
-
},
|
|
1057
|
-
},
|
|
1058
|
-
];
|