snow-ai 0.4.15 → 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.
Files changed (351) hide show
  1. package/bundle/cli.mjs +477445 -0
  2. package/bundle/sql-wasm.wasm +0 -0
  3. package/package.json +31 -26
  4. package/dist/agents/codebaseIndexAgent.d.ts +0 -102
  5. package/dist/agents/codebaseIndexAgent.js +0 -641
  6. package/dist/agents/codebaseReviewAgent.d.ts +0 -61
  7. package/dist/agents/codebaseReviewAgent.js +0 -301
  8. package/dist/agents/compactAgent.d.ts +0 -55
  9. package/dist/agents/compactAgent.js +0 -306
  10. package/dist/agents/promptOptimizeAgent.d.ts +0 -54
  11. package/dist/agents/promptOptimizeAgent.js +0 -268
  12. package/dist/agents/reviewAgent.d.ts +0 -50
  13. package/dist/agents/reviewAgent.js +0 -265
  14. package/dist/agents/summaryAgent.d.ts +0 -57
  15. package/dist/agents/summaryAgent.js +0 -260
  16. package/dist/api/anthropic.d.ts +0 -44
  17. package/dist/api/anthropic.js +0 -598
  18. package/dist/api/chat.d.ts +0 -73
  19. package/dist/api/chat.js +0 -386
  20. package/dist/api/embedding.d.ts +0 -34
  21. package/dist/api/embedding.js +0 -80
  22. package/dist/api/gemini.d.ts +0 -31
  23. package/dist/api/gemini.js +0 -445
  24. package/dist/api/models.d.ts +0 -15
  25. package/dist/api/models.js +0 -139
  26. package/dist/api/responses.d.ts +0 -38
  27. package/dist/api/responses.js +0 -515
  28. package/dist/api/systemPrompt.d.ts +0 -4
  29. package/dist/api/systemPrompt.js +0 -408
  30. package/dist/api/types.d.ts +0 -53
  31. package/dist/api/types.js +0 -4
  32. package/dist/app.d.ts +0 -8
  33. package/dist/app.js +0 -112
  34. package/dist/cli.d.ts +0 -2
  35. package/dist/cli.js +0 -199
  36. package/dist/hooks/useAgentPicker.d.ts +0 -14
  37. package/dist/hooks/useAgentPicker.js +0 -119
  38. package/dist/hooks/useClipboard.d.ts +0 -4
  39. package/dist/hooks/useClipboard.js +0 -175
  40. package/dist/hooks/useCommandHandler.d.ts +0 -35
  41. package/dist/hooks/useCommandHandler.js +0 -346
  42. package/dist/hooks/useCommandPanel.d.ts +0 -17
  43. package/dist/hooks/useCommandPanel.js +0 -114
  44. package/dist/hooks/useConversation.d.ts +0 -49
  45. package/dist/hooks/useConversation.js +0 -1052
  46. package/dist/hooks/useFilePicker.d.ts +0 -18
  47. package/dist/hooks/useFilePicker.js +0 -224
  48. package/dist/hooks/useGlobalExit.d.ts +0 -5
  49. package/dist/hooks/useGlobalExit.js +0 -34
  50. package/dist/hooks/useGlobalNavigation.d.ts +0 -6
  51. package/dist/hooks/useGlobalNavigation.js +0 -17
  52. package/dist/hooks/useHistoryNavigation.d.ts +0 -35
  53. package/dist/hooks/useHistoryNavigation.js +0 -133
  54. package/dist/hooks/useInputBuffer.d.ts +0 -6
  55. package/dist/hooks/useInputBuffer.js +0 -45
  56. package/dist/hooks/useKeyboardInput.d.ts +0 -80
  57. package/dist/hooks/useKeyboardInput.js +0 -608
  58. package/dist/hooks/useSessionManagement.d.ts +0 -10
  59. package/dist/hooks/useSessionManagement.js +0 -43
  60. package/dist/hooks/useSessionSave.d.ts +0 -8
  61. package/dist/hooks/useSessionSave.js +0 -63
  62. package/dist/hooks/useSnapshotState.d.ts +0 -26
  63. package/dist/hooks/useSnapshotState.js +0 -28
  64. package/dist/hooks/useStreamingState.d.ts +0 -33
  65. package/dist/hooks/useStreamingState.js +0 -105
  66. package/dist/hooks/useTerminalFocus.d.ts +0 -28
  67. package/dist/hooks/useTerminalFocus.js +0 -87
  68. package/dist/hooks/useTerminalSize.d.ts +0 -4
  69. package/dist/hooks/useTerminalSize.js +0 -20
  70. package/dist/hooks/useTodoPicker.d.ts +0 -16
  71. package/dist/hooks/useTodoPicker.js +0 -94
  72. package/dist/hooks/useToolConfirmation.d.ts +0 -19
  73. package/dist/hooks/useToolConfirmation.js +0 -61
  74. package/dist/hooks/useVSCodeState.d.ts +0 -8
  75. package/dist/hooks/useVSCodeState.js +0 -81
  76. package/dist/i18n/I18nContext.d.ts +0 -14
  77. package/dist/i18n/I18nContext.js +0 -24
  78. package/dist/i18n/index.d.ts +0 -3
  79. package/dist/i18n/index.js +0 -2
  80. package/dist/i18n/lang/en.d.ts +0 -2
  81. package/dist/i18n/lang/en.js +0 -502
  82. package/dist/i18n/lang/es.d.ts +0 -2
  83. package/dist/i18n/lang/es.js +0 -502
  84. package/dist/i18n/lang/ja.d.ts +0 -2
  85. package/dist/i18n/lang/ja.js +0 -502
  86. package/dist/i18n/lang/ko.d.ts +0 -2
  87. package/dist/i18n/lang/ko.js +0 -502
  88. package/dist/i18n/lang/zh-TW.d.ts +0 -2
  89. package/dist/i18n/lang/zh-TW.js +0 -502
  90. package/dist/i18n/lang/zh.d.ts +0 -2
  91. package/dist/i18n/lang/zh.js +0 -502
  92. package/dist/i18n/translations.d.ts +0 -2
  93. package/dist/i18n/translations.js +0 -14
  94. package/dist/i18n/types.d.ts +0 -478
  95. package/dist/i18n/types.js +0 -1
  96. package/dist/mcp/aceCodeSearch.d.ts +0 -247
  97. package/dist/mcp/aceCodeSearch.js +0 -1058
  98. package/dist/mcp/bash.d.ts +0 -50
  99. package/dist/mcp/bash.js +0 -153
  100. package/dist/mcp/codebaseSearch.d.ts +0 -44
  101. package/dist/mcp/codebaseSearch.js +0 -275
  102. package/dist/mcp/filesystem.d.ts +0 -392
  103. package/dist/mcp/filesystem.js +0 -1445
  104. package/dist/mcp/ideDiagnostics.d.ts +0 -36
  105. package/dist/mcp/ideDiagnostics.js +0 -90
  106. package/dist/mcp/notebook.d.ts +0 -10
  107. package/dist/mcp/notebook.js +0 -367
  108. package/dist/mcp/subagent.d.ts +0 -37
  109. package/dist/mcp/subagent.js +0 -113
  110. package/dist/mcp/todo.d.ts +0 -46
  111. package/dist/mcp/todo.js +0 -511
  112. package/dist/mcp/types/aceCodeSearch.types.d.ts +0 -92
  113. package/dist/mcp/types/aceCodeSearch.types.js +0 -4
  114. package/dist/mcp/types/bash.types.d.ts +0 -13
  115. package/dist/mcp/types/bash.types.js +0 -4
  116. package/dist/mcp/types/filesystem.types.d.ts +0 -210
  117. package/dist/mcp/types/filesystem.types.js +0 -27
  118. package/dist/mcp/types/todo.types.d.ts +0 -27
  119. package/dist/mcp/types/todo.types.js +0 -4
  120. package/dist/mcp/types/websearch.types.d.ts +0 -30
  121. package/dist/mcp/types/websearch.types.js +0 -4
  122. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.d.ts +0 -34
  123. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.js +0 -146
  124. package/dist/mcp/utils/aceCodeSearch/language.utils.d.ts +0 -14
  125. package/dist/mcp/utils/aceCodeSearch/language.utils.js +0 -418
  126. package/dist/mcp/utils/aceCodeSearch/search.utils.d.ts +0 -31
  127. package/dist/mcp/utils/aceCodeSearch/search.utils.js +0 -136
  128. package/dist/mcp/utils/aceCodeSearch/symbol.utils.d.ts +0 -20
  129. package/dist/mcp/utils/aceCodeSearch/symbol.utils.js +0 -141
  130. package/dist/mcp/utils/bash/security.utils.d.ts +0 -20
  131. package/dist/mcp/utils/bash/security.utils.js +0 -34
  132. package/dist/mcp/utils/filesystem/batch-operations.utils.d.ts +0 -39
  133. package/dist/mcp/utils/filesystem/batch-operations.utils.js +0 -182
  134. package/dist/mcp/utils/filesystem/code-analysis.utils.d.ts +0 -18
  135. package/dist/mcp/utils/filesystem/code-analysis.utils.js +0 -165
  136. package/dist/mcp/utils/filesystem/match-finder.utils.d.ts +0 -16
  137. package/dist/mcp/utils/filesystem/match-finder.utils.js +0 -85
  138. package/dist/mcp/utils/filesystem/office-parser.utils.d.ts +0 -43
  139. package/dist/mcp/utils/filesystem/office-parser.utils.js +0 -163
  140. package/dist/mcp/utils/filesystem/path-fixer.utils.d.ts +0 -7
  141. package/dist/mcp/utils/filesystem/path-fixer.utils.js +0 -60
  142. package/dist/mcp/utils/filesystem/similarity.utils.d.ts +0 -22
  143. package/dist/mcp/utils/filesystem/similarity.utils.js +0 -75
  144. package/dist/mcp/utils/todo/date.utils.d.ts +0 -9
  145. package/dist/mcp/utils/todo/date.utils.js +0 -14
  146. package/dist/mcp/utils/websearch/browser.utils.d.ts +0 -8
  147. package/dist/mcp/utils/websearch/browser.utils.js +0 -58
  148. package/dist/mcp/utils/websearch/text.utils.d.ts +0 -16
  149. package/dist/mcp/utils/websearch/text.utils.js +0 -39
  150. package/dist/mcp/websearch.d.ts +0 -88
  151. package/dist/mcp/websearch.js +0 -375
  152. package/dist/test/logger-test.d.ts +0 -1
  153. package/dist/test/logger-test.js +0 -7
  154. package/dist/types/index.d.ts +0 -15
  155. package/dist/types/index.js +0 -1
  156. package/dist/ui/components/AgentPickerPanel.d.ts +0 -10
  157. package/dist/ui/components/AgentPickerPanel.js +0 -74
  158. package/dist/ui/components/ChatInput.d.ts +0 -46
  159. package/dist/ui/components/ChatInput.js +0 -379
  160. package/dist/ui/components/CommandPanel.d.ts +0 -15
  161. package/dist/ui/components/CommandPanel.js +0 -80
  162. package/dist/ui/components/DiffViewer.d.ts +0 -11
  163. package/dist/ui/components/DiffViewer.js +0 -178
  164. package/dist/ui/components/FileList.d.ts +0 -15
  165. package/dist/ui/components/FileList.js +0 -360
  166. package/dist/ui/components/FileRollbackConfirmation.d.ts +0 -8
  167. package/dist/ui/components/FileRollbackConfirmation.js +0 -108
  168. package/dist/ui/components/HelpPanel.d.ts +0 -2
  169. package/dist/ui/components/HelpPanel.js +0 -67
  170. package/dist/ui/components/MCPInfoPanel.d.ts +0 -2
  171. package/dist/ui/components/MCPInfoPanel.js +0 -108
  172. package/dist/ui/components/MCPInfoScreen.d.ts +0 -7
  173. package/dist/ui/components/MCPInfoScreen.js +0 -115
  174. package/dist/ui/components/MarkdownRenderer.d.ts +0 -6
  175. package/dist/ui/components/MarkdownRenderer.js +0 -70
  176. package/dist/ui/components/Menu.d.ts +0 -17
  177. package/dist/ui/components/Menu.js +0 -88
  178. package/dist/ui/components/MessageList.d.ts +0 -56
  179. package/dist/ui/components/MessageList.js +0 -97
  180. package/dist/ui/components/PendingMessages.d.ts +0 -13
  181. package/dist/ui/components/PendingMessages.js +0 -29
  182. package/dist/ui/components/PendingToolCalls.d.ts +0 -11
  183. package/dist/ui/components/PendingToolCalls.js +0 -35
  184. package/dist/ui/components/ScrollableSelectInput.d.ts +0 -29
  185. package/dist/ui/components/ScrollableSelectInput.js +0 -157
  186. package/dist/ui/components/SessionListPanel.d.ts +0 -7
  187. package/dist/ui/components/SessionListPanel.js +0 -175
  188. package/dist/ui/components/SessionListScreen.d.ts +0 -7
  189. package/dist/ui/components/SessionListScreen.js +0 -217
  190. package/dist/ui/components/SessionListScreenWrapper.d.ts +0 -7
  191. package/dist/ui/components/SessionListScreenWrapper.js +0 -14
  192. package/dist/ui/components/ShimmerText.d.ts +0 -9
  193. package/dist/ui/components/ShimmerText.js +0 -30
  194. package/dist/ui/components/TodoPickerPanel.d.ts +0 -14
  195. package/dist/ui/components/TodoPickerPanel.js +0 -119
  196. package/dist/ui/components/TodoTree.d.ts +0 -15
  197. package/dist/ui/components/TodoTree.js +0 -60
  198. package/dist/ui/components/ToolConfirmation.d.ts +0 -21
  199. package/dist/ui/components/ToolConfirmation.js +0 -204
  200. package/dist/ui/components/ToolResultPreview.d.ts +0 -13
  201. package/dist/ui/components/ToolResultPreview.js +0 -337
  202. package/dist/ui/components/UsagePanel.d.ts +0 -2
  203. package/dist/ui/components/UsagePanel.js +0 -394
  204. package/dist/ui/contexts/ThemeContext.d.ts +0 -13
  205. package/dist/ui/contexts/ThemeContext.js +0 -28
  206. package/dist/ui/pages/ChatScreen.d.ts +0 -6
  207. package/dist/ui/pages/ChatScreen.js +0 -1495
  208. package/dist/ui/pages/CodeBaseConfigScreen.d.ts +0 -8
  209. package/dist/ui/pages/CodeBaseConfigScreen.js +0 -350
  210. package/dist/ui/pages/ConfigScreen.d.ts +0 -8
  211. package/dist/ui/pages/ConfigScreen.js +0 -1101
  212. package/dist/ui/pages/CustomHeadersScreen.d.ts +0 -6
  213. package/dist/ui/pages/CustomHeadersScreen.js +0 -502
  214. package/dist/ui/pages/HeadlessModeScreen.d.ts +0 -7
  215. package/dist/ui/pages/HeadlessModeScreen.js +0 -381
  216. package/dist/ui/pages/LanguageSettingsScreen.d.ts +0 -7
  217. package/dist/ui/pages/LanguageSettingsScreen.js +0 -91
  218. package/dist/ui/pages/MCPConfigScreen.d.ts +0 -6
  219. package/dist/ui/pages/MCPConfigScreen.js +0 -55
  220. package/dist/ui/pages/ProxyConfigScreen.d.ts +0 -8
  221. package/dist/ui/pages/ProxyConfigScreen.js +0 -149
  222. package/dist/ui/pages/SensitiveCommandConfigScreen.d.ts +0 -7
  223. package/dist/ui/pages/SensitiveCommandConfigScreen.js +0 -271
  224. package/dist/ui/pages/SubAgentConfigScreen.d.ts +0 -9
  225. package/dist/ui/pages/SubAgentConfigScreen.js +0 -435
  226. package/dist/ui/pages/SubAgentListScreen.d.ts +0 -9
  227. package/dist/ui/pages/SubAgentListScreen.js +0 -131
  228. package/dist/ui/pages/SystemPromptConfigScreen.d.ts +0 -6
  229. package/dist/ui/pages/SystemPromptConfigScreen.js +0 -326
  230. package/dist/ui/pages/ThemeSettingsScreen.d.ts +0 -7
  231. package/dist/ui/pages/ThemeSettingsScreen.js +0 -106
  232. package/dist/ui/pages/WelcomeScreen.d.ts +0 -7
  233. package/dist/ui/pages/WelcomeScreen.js +0 -217
  234. package/dist/ui/themes/index.d.ts +0 -23
  235. package/dist/ui/themes/index.js +0 -140
  236. package/dist/utils/apiConfig.d.ts +0 -126
  237. package/dist/utils/apiConfig.js +0 -423
  238. package/dist/utils/autoCompress.d.ts +0 -15
  239. package/dist/utils/autoCompress.js +0 -24
  240. package/dist/utils/chatExporter.d.ts +0 -9
  241. package/dist/utils/chatExporter.js +0 -118
  242. package/dist/utils/checkpointManager.d.ts +0 -74
  243. package/dist/utils/checkpointManager.js +0 -181
  244. package/dist/utils/codebaseConfig.d.ts +0 -16
  245. package/dist/utils/codebaseConfig.js +0 -67
  246. package/dist/utils/codebaseDatabase.d.ts +0 -102
  247. package/dist/utils/codebaseDatabase.js +0 -333
  248. package/dist/utils/codebaseSearchEvents.d.ts +0 -16
  249. package/dist/utils/codebaseSearchEvents.js +0 -13
  250. package/dist/utils/commandExecutor.d.ts +0 -13
  251. package/dist/utils/commandExecutor.js +0 -26
  252. package/dist/utils/commands/agent.d.ts +0 -2
  253. package/dist/utils/commands/agent.js +0 -12
  254. package/dist/utils/commands/clear.d.ts +0 -2
  255. package/dist/utils/commands/clear.js +0 -12
  256. package/dist/utils/commands/compact.d.ts +0 -2
  257. package/dist/utils/commands/compact.js +0 -12
  258. package/dist/utils/commands/export.d.ts +0 -2
  259. package/dist/utils/commands/export.js +0 -12
  260. package/dist/utils/commands/help.d.ts +0 -2
  261. package/dist/utils/commands/help.js +0 -11
  262. package/dist/utils/commands/home.d.ts +0 -2
  263. package/dist/utils/commands/home.js +0 -34
  264. package/dist/utils/commands/ide.d.ts +0 -2
  265. package/dist/utils/commands/ide.js +0 -32
  266. package/dist/utils/commands/init.d.ts +0 -2
  267. package/dist/utils/commands/init.js +0 -93
  268. package/dist/utils/commands/mcp.d.ts +0 -2
  269. package/dist/utils/commands/mcp.js +0 -12
  270. package/dist/utils/commands/resume.d.ts +0 -2
  271. package/dist/utils/commands/resume.js +0 -12
  272. package/dist/utils/commands/review.d.ts +0 -2
  273. package/dist/utils/commands/review.js +0 -81
  274. package/dist/utils/commands/role.d.ts +0 -2
  275. package/dist/utils/commands/role.js +0 -37
  276. package/dist/utils/commands/todoPicker.d.ts +0 -2
  277. package/dist/utils/commands/todoPicker.js +0 -12
  278. package/dist/utils/commands/usage.d.ts +0 -2
  279. package/dist/utils/commands/usage.js +0 -12
  280. package/dist/utils/commands/yolo.d.ts +0 -2
  281. package/dist/utils/commands/yolo.js +0 -12
  282. package/dist/utils/configManager.d.ts +0 -45
  283. package/dist/utils/configManager.js +0 -303
  284. package/dist/utils/contextCompressor.d.ts +0 -16
  285. package/dist/utils/contextCompressor.js +0 -334
  286. package/dist/utils/devMode.d.ts +0 -13
  287. package/dist/utils/devMode.js +0 -54
  288. package/dist/utils/escapeHandler.d.ts +0 -79
  289. package/dist/utils/escapeHandler.js +0 -153
  290. package/dist/utils/fileDialog.d.ts +0 -9
  291. package/dist/utils/fileDialog.js +0 -74
  292. package/dist/utils/fileUtils.d.ts +0 -40
  293. package/dist/utils/fileUtils.js +0 -185
  294. package/dist/utils/historyManager.d.ts +0 -45
  295. package/dist/utils/historyManager.js +0 -159
  296. package/dist/utils/incrementalSnapshot.d.ts +0 -109
  297. package/dist/utils/incrementalSnapshot.js +0 -383
  298. package/dist/utils/index.d.ts +0 -11
  299. package/dist/utils/index.js +0 -18
  300. package/dist/utils/languageConfig.d.ts +0 -21
  301. package/dist/utils/languageConfig.js +0 -61
  302. package/dist/utils/logger.d.ts +0 -37
  303. package/dist/utils/logger.js +0 -122
  304. package/dist/utils/mcpToolsManager.d.ts +0 -52
  305. package/dist/utils/mcpToolsManager.js +0 -878
  306. package/dist/utils/messageFormatter.d.ts +0 -12
  307. package/dist/utils/messageFormatter.js +0 -115
  308. package/dist/utils/notebookManager.d.ts +0 -59
  309. package/dist/utils/notebookManager.js +0 -213
  310. package/dist/utils/patch-highlight.d.ts +0 -5
  311. package/dist/utils/patch-highlight.js +0 -23
  312. package/dist/utils/processManager.d.ts +0 -27
  313. package/dist/utils/processManager.js +0 -75
  314. package/dist/utils/proxyUtils.d.ts +0 -15
  315. package/dist/utils/proxyUtils.js +0 -50
  316. package/dist/utils/resourceMonitor.d.ts +0 -65
  317. package/dist/utils/resourceMonitor.js +0 -175
  318. package/dist/utils/retryUtils.d.ts +0 -49
  319. package/dist/utils/retryUtils.js +0 -303
  320. package/dist/utils/sensitiveCommandManager.d.ts +0 -53
  321. package/dist/utils/sensitiveCommandManager.js +0 -308
  322. package/dist/utils/sessionConverter.d.ts +0 -7
  323. package/dist/utils/sessionConverter.js +0 -306
  324. package/dist/utils/sessionManager.d.ts +0 -53
  325. package/dist/utils/sessionManager.js +0 -371
  326. package/dist/utils/subAgentConfig.d.ts +0 -50
  327. package/dist/utils/subAgentConfig.js +0 -221
  328. package/dist/utils/subAgentExecutor.d.ts +0 -40
  329. package/dist/utils/subAgentExecutor.js +0 -434
  330. package/dist/utils/terminal.d.ts +0 -5
  331. package/dist/utils/terminal.js +0 -13
  332. package/dist/utils/textBuffer.d.ts +0 -99
  333. package/dist/utils/textBuffer.js +0 -547
  334. package/dist/utils/textUtils.d.ts +0 -37
  335. package/dist/utils/textUtils.js +0 -102
  336. package/dist/utils/themeConfig.d.ts +0 -21
  337. package/dist/utils/themeConfig.js +0 -61
  338. package/dist/utils/todoPreprocessor.d.ts +0 -5
  339. package/dist/utils/todoPreprocessor.js +0 -18
  340. package/dist/utils/todoScanner.d.ts +0 -8
  341. package/dist/utils/todoScanner.js +0 -148
  342. package/dist/utils/toolDisplayConfig.d.ts +0 -16
  343. package/dist/utils/toolDisplayConfig.js +0 -47
  344. package/dist/utils/toolExecutor.d.ts +0 -37
  345. package/dist/utils/toolExecutor.js +0 -224
  346. package/dist/utils/usageLogger.d.ts +0 -11
  347. package/dist/utils/usageLogger.js +0 -114
  348. package/dist/utils/vscodeConnection.d.ts +0 -76
  349. package/dist/utils/vscodeConnection.js +0 -430
  350. package/dist/utils/workspaceSnapshot.d.ts +0 -63
  351. 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
- ];