snow-ai 0.2.18 → 0.2.19

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.
@@ -15,12 +15,15 @@ export default function ToolResultPreview({ toolName, result, maxLines = 5 }) {
15
15
  else if (toolName === 'filesystem-list') {
16
16
  return renderListPreview(data, maxLines);
17
17
  }
18
- else if (toolName === 'filesystem-search') {
19
- return renderSearchPreview(data, maxLines);
20
- }
21
18
  else if (toolName === 'filesystem-create' || toolName === 'filesystem-write') {
22
19
  return renderCreatePreview(data);
23
20
  }
21
+ else if (toolName === 'filesystem-edit_search') {
22
+ return renderEditSearchPreview(data);
23
+ }
24
+ else if (toolName.startsWith('ace-')) {
25
+ return renderACEPreview(toolName, data, maxLines);
26
+ }
24
27
  else {
25
28
  // Generic preview for unknown tools
26
29
  return renderGenericPreview(data, maxLines);
@@ -66,29 +69,133 @@ function renderListPreview(data, maxLines) {
66
69
  files.length,
67
70
  " items)"))));
68
71
  }
69
- function renderSearchPreview(data, maxLines) {
70
- if (!data.matches || data.matches.length === 0) {
71
- return (React.createElement(Box, { marginLeft: 2 },
72
+ function renderACEPreview(toolName, data, maxLines) {
73
+ // Handle ace-text-search results
74
+ if (toolName === 'ace-text-search' || toolName === 'ace_text_search') {
75
+ if (!data || data.length === 0) {
76
+ return (React.createElement(Box, { marginLeft: 2 },
77
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 No matches found")));
78
+ }
79
+ const results = Array.isArray(data) ? data : [];
80
+ const previewMatches = results.slice(0, maxLines);
81
+ const hasMore = results.length > maxLines;
82
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
83
+ previewMatches.map((match, idx) => (React.createElement(Text, { key: idx, color: "gray", dimColor: true },
84
+ idx === previewMatches.length - 1 && !hasMore ? '└─ ' : '├─ ',
85
+ match.filePath,
86
+ ":",
87
+ match.line,
88
+ " - ",
89
+ match.content.slice(0, 60),
90
+ match.content.length > 60 ? '...' : ''))),
91
+ hasMore && (React.createElement(Text, { color: "gray", dimColor: true },
92
+ "\u2514\u2500 ... (",
93
+ results.length - maxLines,
94
+ " more matches)"))));
95
+ }
96
+ // Handle ace-search-symbols results
97
+ if (toolName === 'ace-search-symbols' || toolName === 'ace_search_symbols') {
98
+ const symbols = data.symbols || [];
99
+ if (symbols.length === 0) {
100
+ return (React.createElement(Box, { marginLeft: 2 },
101
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 No symbols found")));
102
+ }
103
+ const previewSymbols = symbols.slice(0, maxLines);
104
+ const hasMore = symbols.length > maxLines;
105
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
106
+ previewSymbols.map((symbol, idx) => (React.createElement(Text, { key: idx, color: "gray", dimColor: true },
107
+ idx === previewSymbols.length - 1 && !hasMore ? '└─ ' : '├─ ',
108
+ symbol.type,
109
+ " ",
110
+ symbol.name,
111
+ " - ",
112
+ symbol.filePath,
113
+ ":",
114
+ symbol.line))),
115
+ hasMore && (React.createElement(Text, { color: "gray", dimColor: true },
116
+ "\u2514\u2500 ... (",
117
+ data.totalResults - maxLines,
118
+ " more symbols)"))));
119
+ }
120
+ // Handle ace-find-references results
121
+ if (toolName === 'ace-find-references' || toolName === 'ace_find_references') {
122
+ const references = Array.isArray(data) ? data : [];
123
+ if (references.length === 0) {
124
+ return (React.createElement(Box, { marginLeft: 2 },
125
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 No references found")));
126
+ }
127
+ const previewRefs = references.slice(0, maxLines);
128
+ const hasMore = references.length > maxLines;
129
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
130
+ previewRefs.map((ref, idx) => (React.createElement(Text, { key: idx, color: "gray", dimColor: true },
131
+ idx === previewRefs.length - 1 && !hasMore ? '└─ ' : '├─ ',
132
+ ref.referenceType,
133
+ " - ",
134
+ ref.filePath,
135
+ ":",
136
+ ref.line))),
137
+ hasMore && (React.createElement(Text, { color: "gray", dimColor: true },
138
+ "\u2514\u2500 ... (",
139
+ references.length - maxLines,
140
+ " more references)"))));
141
+ }
142
+ // Handle ace-find-definition result
143
+ if (toolName === 'ace-find-definition' || toolName === 'ace_find_definition') {
144
+ if (!data) {
145
+ return (React.createElement(Box, { marginLeft: 2 },
146
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 Definition not found")));
147
+ }
148
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
72
149
  React.createElement(Text, { color: "gray", dimColor: true },
73
- "\u2514\u2500 No matches found (searched ",
74
- data.searchedFiles,
75
- " files)")));
150
+ "\u2514\u2500 ",
151
+ data.type,
152
+ " ",
153
+ data.name,
154
+ " - ",
155
+ data.filePath,
156
+ ":",
157
+ data.line)));
76
158
  }
77
- const previewMatches = data.matches.slice(0, maxLines);
78
- const hasMore = data.matches.length > maxLines;
79
- return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
80
- previewMatches.map((match, idx) => (React.createElement(Text, { key: idx, color: "gray", dimColor: true },
81
- idx === previewMatches.length - 1 && !hasMore ? '└─ ' : '├─ ',
82
- match.filePath,
83
- ":",
84
- match.lineNumber,
85
- " - ",
86
- match.lineContent.slice(0, 60),
87
- match.lineContent.length > 60 ? '...' : ''))),
88
- hasMore && (React.createElement(Text, { color: "gray", dimColor: true },
89
- "\u2514\u2500 ... (",
90
- data.totalMatches - maxLines,
91
- " more matches)"))));
159
+ // Handle ace-file-outline result
160
+ if (toolName === 'ace-file-outline' || toolName === 'ace_file_outline') {
161
+ const symbols = Array.isArray(data) ? data : [];
162
+ if (symbols.length === 0) {
163
+ return (React.createElement(Box, { marginLeft: 2 },
164
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 No symbols in file")));
165
+ }
166
+ const previewSymbols = symbols.slice(0, maxLines);
167
+ const hasMore = symbols.length > maxLines;
168
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
169
+ previewSymbols.map((symbol, idx) => (React.createElement(Text, { key: idx, color: "gray", dimColor: true },
170
+ idx === previewSymbols.length - 1 && !hasMore ? '└─ ' : '├─ ',
171
+ symbol.type,
172
+ " ",
173
+ symbol.name,
174
+ " (line ",
175
+ symbol.line,
176
+ ")"))),
177
+ hasMore && (React.createElement(Text, { color: "gray", dimColor: true },
178
+ "\u2514\u2500 ... (",
179
+ symbols.length - maxLines,
180
+ " more symbols)"))));
181
+ }
182
+ // Handle ace-semantic-search result
183
+ if (toolName === 'ace-semantic-search' || toolName === 'ace_semantic_search') {
184
+ const totalResults = (data.symbols?.length || 0) + (data.references?.length || 0);
185
+ if (totalResults === 0) {
186
+ return (React.createElement(Box, { marginLeft: 2 },
187
+ React.createElement(Text, { color: "gray", dimColor: true }, "\u2514\u2500 No results found")));
188
+ }
189
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
190
+ React.createElement(Text, { color: "gray", dimColor: true },
191
+ "\u251C\u2500 Symbols: ",
192
+ data.symbols?.length || 0),
193
+ React.createElement(Text, { color: "gray", dimColor: true },
194
+ "\u2514\u2500 References: ",
195
+ data.references?.length || 0)));
196
+ }
197
+ // Generic ACE tool preview
198
+ return renderGenericPreview(data, maxLines);
92
199
  }
93
200
  function renderCreatePreview(data) {
94
201
  // Simple success message for create/write operations
@@ -97,6 +204,21 @@ function renderCreatePreview(data) {
97
204
  "\u2514\u2500 ",
98
205
  data.message || data)));
99
206
  }
207
+ function renderEditSearchPreview(data) {
208
+ // For edit_search, show only key metadata, exclude searchContent and replaceContent
209
+ return (React.createElement(Box, { flexDirection: "column", marginLeft: 2 },
210
+ data.message && (React.createElement(Text, { color: "gray", dimColor: true },
211
+ "\u251C\u2500 ",
212
+ data.message)),
213
+ data.matchLocation && (React.createElement(Text, { color: "gray", dimColor: true },
214
+ "\u251C\u2500 Match: lines ",
215
+ data.matchLocation.startLine,
216
+ "-",
217
+ data.matchLocation.endLine)),
218
+ data.totalLines && (React.createElement(Text, { color: "gray", dimColor: true },
219
+ "\u2514\u2500 Total lines: ",
220
+ data.totalLines))));
221
+ }
100
222
  function renderGenericPreview(data, maxLines) {
101
223
  // For unknown tool types, show first few properties
102
224
  const entries = Object.entries(data).slice(0, maxLines);
@@ -493,7 +493,12 @@ export default function ChatScreen({}) {
493
493
  message.toolCall.name === 'filesystem-edit' &&
494
494
  message.toolCall.arguments.oldContent &&
495
495
  message.toolCall.arguments.newContent && (React.createElement(Box, { marginTop: 1 },
496
- React.createElement(DiffViewer, { oldContent: message.toolCall.arguments.oldContent, newContent: message.toolCall.arguments.newContent, filename: message.toolCall.arguments.filename, completeOldContent: message.toolCall.arguments.completeOldContent, completeNewContent: message.toolCall.arguments.completeNewContent }))),
496
+ React.createElement(DiffViewer, { oldContent: message.toolCall.arguments.oldContent, newContent: message.toolCall.arguments.newContent, filename: message.toolCall.arguments.filename, completeOldContent: message.toolCall.arguments.completeOldContent, completeNewContent: message.toolCall.arguments.completeNewContent, startLineNumber: message.toolCall.arguments.contextStartLine }))),
497
+ message.toolCall &&
498
+ message.toolCall.name === 'filesystem-edit_search' &&
499
+ message.toolCall.arguments.oldContent &&
500
+ message.toolCall.arguments.newContent && (React.createElement(Box, { marginTop: 1 },
501
+ React.createElement(DiffViewer, { oldContent: message.toolCall.arguments.oldContent, newContent: message.toolCall.arguments.newContent, filename: message.toolCall.arguments.filename, completeOldContent: message.toolCall.arguments.completeOldContent, completeNewContent: message.toolCall.arguments.completeNewContent, startLineNumber: message.toolCall.arguments.contextStartLine }))),
497
502
  message.toolCall &&
498
503
  message.toolCall.name === 'terminal-execute' &&
499
504
  message.toolCall.arguments.command && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
@@ -5,6 +5,7 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
5
5
  import { getMCPConfig } from './apiConfig.js';
6
6
  import { mcpTools as filesystemTools } from '../mcp/filesystem.js';
7
7
  import { mcpTools as terminalTools } from '../mcp/bash.js';
8
+ import { mcpTools as aceCodeSearchTools } from '../mcp/aceCodeSearch.js';
8
9
  import { TodoService } from '../mcp/todo.js';
9
10
  import { sessionManager } from './sessionManager.js';
10
11
  import { logger } from './logger.js';
@@ -130,6 +131,28 @@ async function refreshToolsCache() {
130
131
  },
131
132
  });
132
133
  }
134
+ // Add built-in ACE Code Search tools (always available)
135
+ const aceServiceTools = aceCodeSearchTools.map(tool => ({
136
+ name: tool.name.replace('ace_', ''),
137
+ description: tool.description,
138
+ inputSchema: tool.inputSchema,
139
+ }));
140
+ servicesInfo.push({
141
+ serviceName: 'ace',
142
+ tools: aceServiceTools,
143
+ isBuiltIn: true,
144
+ connected: true,
145
+ });
146
+ for (const tool of aceCodeSearchTools) {
147
+ allTools.push({
148
+ type: 'function',
149
+ function: {
150
+ name: `ace-${tool.name.replace('ace_', '')}`,
151
+ description: tool.description,
152
+ parameters: tool.inputSchema,
153
+ },
154
+ });
155
+ }
133
156
  // Add user-configured MCP server tools (probe for availability but don't maintain connections)
134
157
  try {
135
158
  const mcpConfig = getMCPConfig();
@@ -413,6 +436,10 @@ export async function executeMCPTool(toolName, args) {
413
436
  serviceName = 'terminal';
414
437
  actualToolName = toolName.substring('terminal-'.length);
415
438
  }
439
+ else if (toolName.startsWith('ace-')) {
440
+ serviceName = 'ace';
441
+ actualToolName = toolName.substring('ace-'.length);
442
+ }
416
443
  else {
417
444
  // Check configured MCP services
418
445
  try {
@@ -460,15 +487,8 @@ export async function executeMCPTool(toolName, args) {
460
487
  return await filesystemService.getFileInfo(args.filePath);
461
488
  case 'edit':
462
489
  return await filesystemService.editFile(args.filePath, args.startLine, args.endLine, args.newContent, args.contextLines);
463
- case 'search': {
464
- // 兼容性处理:如果 searchMode 不存在或无效,默认使用 'text'
465
- const validSearchModes = ['text', 'regex'];
466
- let searchMode = 'text';
467
- if (args.searchMode && validSearchModes.includes(args.searchMode)) {
468
- searchMode = args.searchMode;
469
- }
470
- return await filesystemService.searchCode(args.query, args.dirPath, args.fileExtensions, args.caseSensitive, args.maxResults, searchMode);
471
- }
490
+ case 'edit_search':
491
+ return await filesystemService.editFileBySearch(args.filePath, args.searchContent, args.replaceContent, args.occurrence, args.contextLines);
472
492
  default:
473
493
  throw new Error(`Unknown filesystem tool: ${actualToolName}`);
474
494
  }
@@ -483,6 +503,31 @@ export async function executeMCPTool(toolName, args) {
483
503
  throw new Error(`Unknown terminal tool: ${actualToolName}`);
484
504
  }
485
505
  }
506
+ else if (serviceName === 'ace') {
507
+ // Handle built-in ACE Code Search tools (no connection needed)
508
+ const { aceCodeSearchService } = await import('../mcp/aceCodeSearch.js');
509
+ switch (actualToolName) {
510
+ case 'search_symbols':
511
+ return await aceCodeSearchService.searchSymbols(args.query, args.symbolType, args.language, args.maxResults);
512
+ case 'find_definition':
513
+ return await aceCodeSearchService.findDefinition(args.symbolName, args.contextFile);
514
+ case 'find_references':
515
+ return await aceCodeSearchService.findReferences(args.symbolName, args.maxResults);
516
+ case 'semantic_search':
517
+ return await aceCodeSearchService.semanticSearch(args.query, args.searchType, args.language, args.maxResults);
518
+ case 'file_outline':
519
+ return await aceCodeSearchService.getFileOutline(args.filePath);
520
+ case 'text_search':
521
+ return await aceCodeSearchService.textSearch(args.pattern, args.fileGlob, args.isRegex, args.maxResults);
522
+ case 'index_stats':
523
+ return aceCodeSearchService.getIndexStats();
524
+ case 'clear_cache':
525
+ aceCodeSearchService.clearCache();
526
+ return { message: 'ACE Code Search cache cleared successfully' };
527
+ default:
528
+ throw new Error(`Unknown ACE tool: ${actualToolName}`);
529
+ }
530
+ }
486
531
  else {
487
532
  // Handle user-configured MCP service tools - connect only when needed
488
533
  const mcpConfig = getMCPConfig();
@@ -41,9 +41,9 @@ export function convertSessionMessagesToUI(sessionMessages) {
41
41
  // Get the tool result for this tool call
42
42
  const toolResult = toolResultsMap.get(toolCall.id);
43
43
  const isError = toolResult?.startsWith('Error:') || false;
44
- // For filesystem-edit, try to extract diff data from result
44
+ // For filesystem-edit and filesystem-edit_search, try to extract diff data from result
45
45
  let editDiffData;
46
- if (toolCall.function.name === 'filesystem-edit' &&
46
+ if ((toolCall.function.name === 'filesystem-edit' || toolCall.function.name === 'filesystem-edit_search') &&
47
47
  toolResult &&
48
48
  !isError) {
49
49
  try {
@@ -53,10 +53,16 @@ export function convertSessionMessagesToUI(sessionMessages) {
53
53
  oldContent: resultData.oldContent,
54
54
  newContent: resultData.newContent,
55
55
  filename: toolArgs.filePath,
56
+ completeOldContent: resultData.completeOldContent,
57
+ completeNewContent: resultData.completeNewContent,
58
+ contextStartLine: resultData.contextStartLine
56
59
  };
57
60
  // Merge diff data into toolArgs for DiffViewer
58
61
  toolArgs.oldContent = resultData.oldContent;
59
62
  toolArgs.newContent = resultData.newContent;
63
+ toolArgs.completeOldContent = resultData.completeOldContent;
64
+ toolArgs.completeNewContent = resultData.completeNewContent;
65
+ toolArgs.contextStartLine = resultData.contextStartLine;
60
66
  }
61
67
  }
62
68
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {