snow-ai 0.3.35 → 0.3.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/codebaseIndexAgent.js +1 -0
- package/dist/agents/codebaseReviewAgent.d.ts +61 -0
- package/dist/agents/codebaseReviewAgent.js +301 -0
- package/dist/api/anthropic.js +1 -0
- package/dist/api/chat.js +1 -0
- package/dist/api/embedding.js +1 -0
- package/dist/api/gemini.js +2 -1
- package/dist/api/responses.js +1 -0
- package/dist/api/systemPrompt.d.ts +1 -5
- package/dist/api/systemPrompt.js +198 -141
- package/dist/app.js +14 -6
- package/dist/cli.js +1 -1
- package/dist/hooks/useCommandPanel.js +48 -46
- package/dist/hooks/useConversation.d.ts +2 -1
- package/dist/hooks/useConversation.js +110 -28
- package/dist/hooks/useGlobalExit.js +4 -2
- package/dist/hooks/useKeyboardInput.js +132 -26
- package/dist/hooks/useStreamingState.d.ts +9 -0
- package/dist/hooks/useStreamingState.js +3 -0
- package/dist/i18n/I18nContext.d.ts +14 -0
- package/dist/i18n/I18nContext.js +24 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/lang/en.d.ts +2 -0
- package/dist/i18n/lang/en.js +481 -0
- package/dist/i18n/lang/es.d.ts +2 -0
- package/dist/i18n/lang/es.js +481 -0
- package/dist/i18n/lang/ja.d.ts +2 -0
- package/dist/i18n/lang/ja.js +481 -0
- package/dist/i18n/lang/ko.d.ts +2 -0
- package/dist/i18n/lang/ko.js +481 -0
- package/dist/i18n/lang/zh-TW.d.ts +2 -0
- package/dist/i18n/lang/zh-TW.js +481 -0
- package/dist/i18n/lang/zh.d.ts +2 -0
- package/dist/i18n/lang/zh.js +481 -0
- package/dist/i18n/translations.d.ts +2 -0
- package/dist/i18n/translations.js +14 -0
- package/dist/i18n/types.d.ts +457 -0
- package/dist/i18n/types.js +1 -0
- package/dist/mcp/aceCodeSearch.d.ts +23 -48
- package/dist/mcp/aceCodeSearch.js +154 -144
- package/dist/mcp/codebaseSearch.d.ts +1 -1
- package/dist/mcp/codebaseSearch.js +159 -30
- package/dist/mcp/filesystem.d.ts +3 -80
- package/dist/mcp/filesystem.js +7 -92
- package/dist/mcp/subagent.d.ts +2 -1
- package/dist/mcp/subagent.js +54 -5
- package/dist/ui/components/ChatInput.js +39 -50
- package/dist/ui/components/CommandPanel.d.ts +1 -1
- package/dist/ui/components/CommandPanel.js +20 -13
- package/dist/ui/components/HelpPanel.js +47 -21
- package/dist/ui/components/Menu.js +6 -2
- package/dist/ui/components/MessageList.d.ts +6 -0
- package/dist/ui/components/MessageList.js +1 -1
- package/dist/ui/components/ToolConfirmation.d.ts +4 -1
- package/dist/ui/components/ToolConfirmation.js +28 -2
- package/dist/ui/components/ToolResultPreview.d.ts +2 -1
- package/dist/ui/components/ToolResultPreview.js +26 -22
- package/dist/ui/pages/ChatScreen.js +126 -49
- package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
- package/dist/ui/pages/ConfigScreen.js +102 -98
- package/dist/ui/pages/CustomHeadersScreen.js +75 -69
- package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
- package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
- package/dist/ui/pages/ProxyConfigScreen.js +27 -23
- package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
- package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
- package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
- package/dist/ui/pages/WelcomeScreen.js +40 -26
- package/dist/utils/codebaseConfig.d.ts +1 -5
- package/dist/utils/codebaseConfig.js +2 -10
- package/dist/utils/codebaseSearchEvents.d.ts +16 -0
- package/dist/utils/codebaseSearchEvents.js +13 -0
- package/dist/utils/commands/agent.js +2 -2
- package/dist/utils/commands/init.js +1 -1
- package/dist/utils/configManager.js +26 -5
- package/dist/utils/contextCompressor.js +1 -1
- package/dist/utils/languageConfig.d.ts +21 -0
- package/dist/utils/languageConfig.js +61 -0
- package/dist/utils/mcpToolsManager.js +0 -9
- package/dist/utils/notebookManager.js +11 -4
- package/dist/utils/sessionConverter.js +13 -3
- package/dist/utils/subAgentConfig.d.ts +10 -5
- package/dist/utils/subAgentConfig.js +112 -19
- package/dist/utils/subAgentExecutor.d.ts +9 -1
- package/dist/utils/subAgentExecutor.js +122 -9
- package/dist/utils/textBuffer.d.ts +1 -0
- package/dist/utils/textBuffer.js +69 -4
- package/dist/utils/toolExecutor.d.ts +2 -1
- package/dist/utils/toolExecutor.js +1 -2
- package/dist/utils/usageLogger.js +18 -3
- package/package.json +1 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { CodebaseDatabase } from '../utils/codebaseDatabase.js';
|
|
2
2
|
import { createEmbedding } from '../api/embedding.js';
|
|
3
3
|
import { logger } from '../utils/logger.js';
|
|
4
|
+
import { codebaseReviewAgent } from '../agents/codebaseReviewAgent.js';
|
|
5
|
+
import { codebaseSearchEvents } from '../utils/codebaseSearchEvents.js';
|
|
6
|
+
import { loadCodebaseConfig } from '../utils/codebaseConfig.js';
|
|
4
7
|
import path from 'node:path';
|
|
5
8
|
import fs from 'node:fs';
|
|
6
9
|
/**
|
|
@@ -61,9 +64,12 @@ class CodebaseSearchService {
|
|
|
61
64
|
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
64
|
-
* Search codebase using semantic similarity
|
|
67
|
+
* Search codebase using semantic similarity with retry logic
|
|
65
68
|
*/
|
|
66
69
|
async search(query, topN = 10) {
|
|
70
|
+
// Load codebase config
|
|
71
|
+
const config = loadCodebaseConfig();
|
|
72
|
+
const enableAgentReview = config.enableAgentReview;
|
|
67
73
|
// Check if codebase index is available
|
|
68
74
|
const { available, reason } = this.isCodebaseIndexAvailable();
|
|
69
75
|
if (!available) {
|
|
@@ -73,38 +79,160 @@ class CodebaseSearchService {
|
|
|
73
79
|
totalResults: 0,
|
|
74
80
|
};
|
|
75
81
|
}
|
|
82
|
+
const MAX_SEARCH_RETRIES = 3;
|
|
83
|
+
const MIN_RESULTS_THRESHOLD = Math.ceil(topN * 0.5); // 50% of topN
|
|
76
84
|
try {
|
|
77
85
|
const projectRoot = process.cwd();
|
|
78
86
|
const db = new CodebaseDatabase(projectRoot);
|
|
79
87
|
db.initialize();
|
|
80
88
|
const totalChunks = db.getTotalChunks();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
let lastResults = null;
|
|
90
|
+
let searchAttempt = 0;
|
|
91
|
+
let currentTopN = topN;
|
|
92
|
+
// Retry loop: if results are too few, increase search range and retry
|
|
93
|
+
while (searchAttempt < MAX_SEARCH_RETRIES) {
|
|
94
|
+
searchAttempt++;
|
|
95
|
+
// Emit search event (only if agent review is enabled)
|
|
96
|
+
if (enableAgentReview) {
|
|
97
|
+
codebaseSearchEvents.emitSearchEvent({
|
|
98
|
+
type: searchAttempt === 1 ? 'search-start' : 'search-retry',
|
|
99
|
+
attempt: searchAttempt,
|
|
100
|
+
maxAttempts: MAX_SEARCH_RETRIES,
|
|
101
|
+
currentTopN,
|
|
102
|
+
message: `Searching top ${currentTopN} code chunks...`,
|
|
103
|
+
query,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// Generate embedding for query
|
|
107
|
+
logger.info(`Search attempt ${searchAttempt}/${MAX_SEARCH_RETRIES}: Searching top ${currentTopN} chunks for query: "${query}"`);
|
|
108
|
+
const queryEmbedding = await createEmbedding(query);
|
|
109
|
+
// Search similar chunks
|
|
110
|
+
const results = db.searchSimilar(queryEmbedding, currentTopN);
|
|
111
|
+
// Format results with similarity scores and full content
|
|
112
|
+
const formattedResults = results.map((chunk, index) => {
|
|
113
|
+
const score = this.cosineSimilarity(queryEmbedding, chunk.embedding);
|
|
114
|
+
const scorePercent = (score * 100).toFixed(2);
|
|
115
|
+
return {
|
|
116
|
+
rank: index + 1,
|
|
117
|
+
filePath: chunk.filePath,
|
|
118
|
+
startLine: chunk.startLine,
|
|
119
|
+
endLine: chunk.endLine,
|
|
120
|
+
content: chunk.content,
|
|
121
|
+
similarityScore: scorePercent,
|
|
122
|
+
location: `${chunk.filePath}:${chunk.startLine}-${chunk.endLine}`,
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
// Use review agent to filter irrelevant results (if enabled)
|
|
126
|
+
let finalResults;
|
|
127
|
+
let reviewFailed = false;
|
|
128
|
+
let removedCount = 0;
|
|
129
|
+
let suggestions = [];
|
|
130
|
+
if (enableAgentReview) {
|
|
131
|
+
// Emit reviewing event
|
|
132
|
+
codebaseSearchEvents.emitSearchEvent({
|
|
133
|
+
type: 'search-retry',
|
|
134
|
+
attempt: searchAttempt,
|
|
135
|
+
maxAttempts: MAX_SEARCH_RETRIES,
|
|
136
|
+
currentTopN,
|
|
137
|
+
message: `Reviewing ${formattedResults.length} results with AI...`,
|
|
138
|
+
query,
|
|
139
|
+
});
|
|
140
|
+
logger.info(`Reviewing ${formattedResults.length} search results (attempt ${searchAttempt})`);
|
|
141
|
+
const reviewResult = await codebaseReviewAgent.reviewResults(query, formattedResults);
|
|
142
|
+
finalResults = reviewResult.filteredResults;
|
|
143
|
+
reviewFailed = reviewResult.reviewFailed || false;
|
|
144
|
+
removedCount = reviewResult.removedCount;
|
|
145
|
+
suggestions = reviewResult.suggestions || [];
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// Skip agent review, use all formatted results
|
|
149
|
+
finalResults = formattedResults;
|
|
150
|
+
reviewFailed = false;
|
|
151
|
+
removedCount = 0;
|
|
152
|
+
suggestions = [];
|
|
153
|
+
// When agent review is disabled, we don't need to retry
|
|
154
|
+
// Just return results immediately
|
|
155
|
+
logger.info(`Agent review disabled, returning all ${finalResults.length} search results`);
|
|
156
|
+
}
|
|
157
|
+
// Store current results as last results
|
|
158
|
+
lastResults = {
|
|
159
|
+
query,
|
|
160
|
+
totalChunks,
|
|
161
|
+
originalResultsCount: formattedResults.length,
|
|
162
|
+
resultsCount: finalResults.length,
|
|
163
|
+
removedCount,
|
|
164
|
+
reviewFailed,
|
|
165
|
+
results: finalResults,
|
|
166
|
+
suggestions,
|
|
167
|
+
searchAttempts: searchAttempt,
|
|
99
168
|
};
|
|
169
|
+
// If agent review is disabled, return immediately (no need to retry)
|
|
170
|
+
if (!enableAgentReview) {
|
|
171
|
+
// Emit search complete event
|
|
172
|
+
codebaseSearchEvents.emitSearchEvent({
|
|
173
|
+
type: 'search-complete',
|
|
174
|
+
attempt: searchAttempt,
|
|
175
|
+
maxAttempts: MAX_SEARCH_RETRIES,
|
|
176
|
+
currentTopN,
|
|
177
|
+
message: `Found ${finalResults.length} results`,
|
|
178
|
+
query,
|
|
179
|
+
});
|
|
180
|
+
db.close();
|
|
181
|
+
return lastResults;
|
|
182
|
+
}
|
|
183
|
+
// If review failed, return immediately (no point retrying)
|
|
184
|
+
if (reviewFailed) {
|
|
185
|
+
logger.info('Review failed, returning all results without retry');
|
|
186
|
+
// Emit search complete event
|
|
187
|
+
codebaseSearchEvents.emitSearchEvent({
|
|
188
|
+
type: 'search-complete',
|
|
189
|
+
attempt: searchAttempt,
|
|
190
|
+
maxAttempts: MAX_SEARCH_RETRIES,
|
|
191
|
+
currentTopN,
|
|
192
|
+
message: 'Review failed, returning all results',
|
|
193
|
+
query,
|
|
194
|
+
});
|
|
195
|
+
db.close();
|
|
196
|
+
return lastResults;
|
|
197
|
+
}
|
|
198
|
+
// Check if we have enough results
|
|
199
|
+
if (finalResults.length >= MIN_RESULTS_THRESHOLD) {
|
|
200
|
+
logger.info(`Found ${finalResults.length} results (>= ${MIN_RESULTS_THRESHOLD} threshold), search complete`);
|
|
201
|
+
// Emit search complete event
|
|
202
|
+
codebaseSearchEvents.emitSearchEvent({
|
|
203
|
+
type: 'search-complete',
|
|
204
|
+
attempt: searchAttempt,
|
|
205
|
+
maxAttempts: MAX_SEARCH_RETRIES,
|
|
206
|
+
currentTopN,
|
|
207
|
+
message: `Found ${finalResults.length} relevant results`,
|
|
208
|
+
query,
|
|
209
|
+
});
|
|
210
|
+
db.close();
|
|
211
|
+
return lastResults;
|
|
212
|
+
}
|
|
213
|
+
// Too few results, need to retry with more candidates
|
|
214
|
+
if (searchAttempt < MAX_SEARCH_RETRIES) {
|
|
215
|
+
const removedPercentage = ((removedCount / formattedResults.length) *
|
|
216
|
+
100).toFixed(1);
|
|
217
|
+
logger.warn(`Only ${finalResults.length} results after filtering (${removedPercentage}% removed, threshold: ${MIN_RESULTS_THRESHOLD}). Retrying with more candidates...`);
|
|
218
|
+
// Increase search range for next attempt (double it)
|
|
219
|
+
currentTopN = Math.min(currentTopN * 2, totalChunks);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
// Last attempt exhausted
|
|
223
|
+
logger.warn(`Search attempt ${searchAttempt} complete. Only ${finalResults.length} results found (threshold: ${MIN_RESULTS_THRESHOLD}). Returning last results.`);
|
|
224
|
+
}
|
|
225
|
+
// Emit search complete event before closing
|
|
226
|
+
codebaseSearchEvents.emitSearchEvent({
|
|
227
|
+
type: 'search-complete',
|
|
228
|
+
attempt: searchAttempt,
|
|
229
|
+
maxAttempts: MAX_SEARCH_RETRIES,
|
|
230
|
+
currentTopN,
|
|
231
|
+
message: `Completed with ${lastResults?.resultsCount || 0} results`,
|
|
232
|
+
query,
|
|
100
233
|
});
|
|
101
234
|
db.close();
|
|
102
|
-
return
|
|
103
|
-
query,
|
|
104
|
-
totalChunks,
|
|
105
|
-
resultsCount: formattedResults.length,
|
|
106
|
-
results: formattedResults,
|
|
107
|
-
};
|
|
235
|
+
return lastResults;
|
|
108
236
|
}
|
|
109
237
|
catch (error) {
|
|
110
238
|
logger.error('Codebase search failed:', error);
|
|
@@ -120,11 +248,12 @@ export const codebaseSearchService = new CodebaseSearchService();
|
|
|
120
248
|
export const mcpTools = [
|
|
121
249
|
{
|
|
122
250
|
name: 'codebase-search',
|
|
123
|
-
description: '
|
|
124
|
-
'
|
|
125
|
-
'
|
|
126
|
-
'
|
|
127
|
-
'
|
|
251
|
+
description: '**Important:When you need to search for code, this is the highest priority tool. You need to use this Codebase tool first.**' +
|
|
252
|
+
'* Semantic search across the codebase using embeddings. ' +
|
|
253
|
+
'* Finds code snippets similar to your query based on meaning, not just keywords. ' +
|
|
254
|
+
'* Returns full code content with similarity scores and file locations. ' +
|
|
255
|
+
'* NOTE: Only available when codebase indexing is enabled and the index has been built. ' +
|
|
256
|
+
'* If the index is not available, the tool will return an error message with instructions.',
|
|
128
257
|
inputSchema: {
|
|
129
258
|
type: 'object',
|
|
130
259
|
properties: {
|
package/dist/mcp/filesystem.d.ts
CHANGED
|
@@ -64,19 +64,13 @@ export declare class FilesystemMCPService {
|
|
|
64
64
|
*/
|
|
65
65
|
createFile(filePath: string, content: string, createDirectories?: boolean): Promise<string>;
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
68
|
-
* @param filePaths - Single file path or array of file paths to delete
|
|
69
|
-
* @returns Success message with details
|
|
70
|
-
* @throws Error if file deletion fails
|
|
71
|
-
*/
|
|
72
|
-
deleteFile(filePaths: string | string[]): Promise<string>;
|
|
73
|
-
/**
|
|
74
|
-
* List files in a directory
|
|
67
|
+
* List files in a directory (internal use for read tool)
|
|
75
68
|
* @param dirPath - Directory path relative to base path or absolute path
|
|
76
69
|
* @returns Array of file names
|
|
77
70
|
* @throws Error if directory cannot be read
|
|
71
|
+
* @private
|
|
78
72
|
*/
|
|
79
|
-
listFiles
|
|
73
|
+
private listFiles;
|
|
80
74
|
/**
|
|
81
75
|
* Check if a file or directory exists
|
|
82
76
|
* @param filePath - Path to check
|
|
@@ -199,8 +193,6 @@ export declare const mcpTools: ({
|
|
|
199
193
|
};
|
|
200
194
|
content?: undefined;
|
|
201
195
|
createDirectories?: undefined;
|
|
202
|
-
filePaths?: undefined;
|
|
203
|
-
dirPath?: undefined;
|
|
204
196
|
searchContent?: undefined;
|
|
205
197
|
replaceContent?: undefined;
|
|
206
198
|
occurrence?: undefined;
|
|
@@ -231,8 +223,6 @@ export declare const mcpTools: ({
|
|
|
231
223
|
};
|
|
232
224
|
startLine?: undefined;
|
|
233
225
|
endLine?: undefined;
|
|
234
|
-
filePaths?: undefined;
|
|
235
|
-
dirPath?: undefined;
|
|
236
226
|
searchContent?: undefined;
|
|
237
227
|
replaceContent?: undefined;
|
|
238
228
|
occurrence?: undefined;
|
|
@@ -241,69 +231,6 @@ export declare const mcpTools: ({
|
|
|
241
231
|
};
|
|
242
232
|
required: string[];
|
|
243
233
|
};
|
|
244
|
-
} | {
|
|
245
|
-
name: string;
|
|
246
|
-
description: string;
|
|
247
|
-
inputSchema: {
|
|
248
|
-
type: string;
|
|
249
|
-
properties: {
|
|
250
|
-
filePath: {
|
|
251
|
-
type: string;
|
|
252
|
-
description: string;
|
|
253
|
-
oneOf?: undefined;
|
|
254
|
-
};
|
|
255
|
-
filePaths: {
|
|
256
|
-
oneOf: ({
|
|
257
|
-
type: string;
|
|
258
|
-
description: string;
|
|
259
|
-
items?: undefined;
|
|
260
|
-
} | {
|
|
261
|
-
type: string;
|
|
262
|
-
items: {
|
|
263
|
-
type: string;
|
|
264
|
-
};
|
|
265
|
-
description: string;
|
|
266
|
-
})[];
|
|
267
|
-
description: string;
|
|
268
|
-
};
|
|
269
|
-
startLine?: undefined;
|
|
270
|
-
endLine?: undefined;
|
|
271
|
-
content?: undefined;
|
|
272
|
-
createDirectories?: undefined;
|
|
273
|
-
dirPath?: undefined;
|
|
274
|
-
searchContent?: undefined;
|
|
275
|
-
replaceContent?: undefined;
|
|
276
|
-
occurrence?: undefined;
|
|
277
|
-
contextLines?: undefined;
|
|
278
|
-
newContent?: undefined;
|
|
279
|
-
};
|
|
280
|
-
required?: undefined;
|
|
281
|
-
};
|
|
282
|
-
} | {
|
|
283
|
-
name: string;
|
|
284
|
-
description: string;
|
|
285
|
-
inputSchema: {
|
|
286
|
-
type: string;
|
|
287
|
-
properties: {
|
|
288
|
-
dirPath: {
|
|
289
|
-
type: string;
|
|
290
|
-
description: string;
|
|
291
|
-
default: string;
|
|
292
|
-
};
|
|
293
|
-
filePath?: undefined;
|
|
294
|
-
startLine?: undefined;
|
|
295
|
-
endLine?: undefined;
|
|
296
|
-
content?: undefined;
|
|
297
|
-
createDirectories?: undefined;
|
|
298
|
-
filePaths?: undefined;
|
|
299
|
-
searchContent?: undefined;
|
|
300
|
-
replaceContent?: undefined;
|
|
301
|
-
occurrence?: undefined;
|
|
302
|
-
contextLines?: undefined;
|
|
303
|
-
newContent?: undefined;
|
|
304
|
-
};
|
|
305
|
-
required?: undefined;
|
|
306
|
-
};
|
|
307
234
|
} | {
|
|
308
235
|
name: string;
|
|
309
236
|
description: string;
|
|
@@ -374,8 +301,6 @@ export declare const mcpTools: ({
|
|
|
374
301
|
endLine?: undefined;
|
|
375
302
|
content?: undefined;
|
|
376
303
|
createDirectories?: undefined;
|
|
377
|
-
filePaths?: undefined;
|
|
378
|
-
dirPath?: undefined;
|
|
379
304
|
newContent?: undefined;
|
|
380
305
|
};
|
|
381
306
|
required: string[];
|
|
@@ -447,8 +372,6 @@ export declare const mcpTools: ({
|
|
|
447
372
|
};
|
|
448
373
|
content?: undefined;
|
|
449
374
|
createDirectories?: undefined;
|
|
450
|
-
filePaths?: undefined;
|
|
451
|
-
dirPath?: undefined;
|
|
452
375
|
searchContent?: undefined;
|
|
453
376
|
replaceContent?: undefined;
|
|
454
377
|
occurrence?: undefined;
|
package/dist/mcp/filesystem.js
CHANGED
|
@@ -382,52 +382,11 @@ export class FilesystemMCPService {
|
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
384
|
/**
|
|
385
|
-
*
|
|
386
|
-
* @param filePaths - Single file path or array of file paths to delete
|
|
387
|
-
* @returns Success message with details
|
|
388
|
-
* @throws Error if file deletion fails
|
|
389
|
-
*/
|
|
390
|
-
async deleteFile(filePaths) {
|
|
391
|
-
try {
|
|
392
|
-
const paths = Array.isArray(filePaths) ? filePaths : [filePaths];
|
|
393
|
-
const results = [];
|
|
394
|
-
const errors = [];
|
|
395
|
-
for (const filePath of paths) {
|
|
396
|
-
try {
|
|
397
|
-
const fullPath = this.resolvePath(filePath);
|
|
398
|
-
await this.validatePath(fullPath);
|
|
399
|
-
const stats = await fs.stat(fullPath);
|
|
400
|
-
if (!stats.isFile()) {
|
|
401
|
-
throw new Error(`Path is not a file: ${filePath}`);
|
|
402
|
-
}
|
|
403
|
-
// Backup file before deletion
|
|
404
|
-
await incrementalSnapshotManager.backupFile(fullPath);
|
|
405
|
-
await fs.unlink(fullPath);
|
|
406
|
-
results.push(`✅ ${filePath}`);
|
|
407
|
-
}
|
|
408
|
-
catch (error) {
|
|
409
|
-
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
410
|
-
errors.push(`❌ ${filePath}: ${errorMsg}`);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
const summary = [];
|
|
414
|
-
if (results.length > 0) {
|
|
415
|
-
summary.push(`Successfully deleted ${results.length} file(s):\n${results.join('\n')}`);
|
|
416
|
-
}
|
|
417
|
-
if (errors.length > 0) {
|
|
418
|
-
summary.push(`Failed to delete ${errors.length} file(s):\n${errors.join('\n')}`);
|
|
419
|
-
}
|
|
420
|
-
return summary.join('\n\n');
|
|
421
|
-
}
|
|
422
|
-
catch (error) {
|
|
423
|
-
throw new Error(`Failed to delete files: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* List files in a directory
|
|
385
|
+
* List files in a directory (internal use for read tool)
|
|
428
386
|
* @param dirPath - Directory path relative to base path or absolute path
|
|
429
387
|
* @returns Array of file names
|
|
430
388
|
* @throws Error if directory cannot be read
|
|
389
|
+
* @private
|
|
431
390
|
*/
|
|
432
391
|
async listFiles(dirPath = '.') {
|
|
433
392
|
try {
|
|
@@ -1113,7 +1072,7 @@ export const filesystemService = new FilesystemMCPService();
|
|
|
1113
1072
|
export const mcpTools = [
|
|
1114
1073
|
{
|
|
1115
1074
|
name: 'filesystem-read',
|
|
1116
|
-
description: 'Read file content with line numbers. **SUPPORTS MULTIPLE FILES WITH FLEXIBLE LINE RANGES**: Pass either (1) a single file path (string), (2) array of file paths (strings) with unified startLine/endLine, or (3) array of file config objects with per-file line ranges. ⚠️ **IMPORTANT WORKFLOW**: (1) ALWAYS use ACE search tools FIRST (ace-text_search/ace-search_symbols/ace-file_outline) to locate the relevant code, (2) ONLY use filesystem-read when you know the approximate location and need precise line numbers for editing. **ANTI-PATTERN**: Reading files line-by-line from the top wastes tokens - use search instead! **USAGE**: Call without parameters to read entire file(s), or specify startLine/endLine for partial reads. Returns content with line numbers (format: "123→code") for precise editing. **EXAMPLES**: (A) Unified: filePath=["a.ts", "b.ts"], startLine=1, endLine=
|
|
1075
|
+
description: 'Read file content with line numbers. **Read only when the actual file or folder path is found or provided by the user, do not make random guesses.** **SUPPORTS MULTIPLE FILES WITH FLEXIBLE LINE RANGES**: Pass either (1) a single file path (string), (2) array of file paths (strings) with unified startLine/endLine, or (3) array of file config objects with per-file line ranges. **INTEGRATED DIRECTORY LISTING**: When filePath is a directory, automatically lists its contents instead of throwing error. ⚠️ **IMPORTANT WORKFLOW**: (1) ALWAYS use ACE search tools FIRST (ace-text_search/ace-search_symbols/ace-file_outline) to locate the relevant code, (2) ONLY use filesystem-read when you know the approximate location and need precise line numbers for editing. **ANTI-PATTERN**: Reading files line-by-line from the top wastes tokens - use search instead! **USAGE**: Call without parameters to read entire file(s), or specify startLine/endLine for partial reads. Returns content with line numbers (format: "123→code") for precise editing. **EXAMPLES**: (A) Unified: filePath=["a.ts", "b.ts"], startLine=1, endLine=500 reads lines 1-500 from both. (B) Per-file: filePath=[{path:"a.ts", startLine:1, endLine:300}, {path:"b.ts", startLine:100, endLine:550}] reads different ranges from each file. (C) Directory: filePath="./src" returns list of files in src/.',
|
|
1117
1076
|
inputSchema: {
|
|
1118
1077
|
type: 'object',
|
|
1119
1078
|
properties: {
|
|
@@ -1121,7 +1080,7 @@ export const mcpTools = [
|
|
|
1121
1080
|
oneOf: [
|
|
1122
1081
|
{
|
|
1123
1082
|
type: 'string',
|
|
1124
|
-
description: 'Path to a single file to read',
|
|
1083
|
+
description: 'Path to a single file to read or directory to list',
|
|
1125
1084
|
},
|
|
1126
1085
|
{
|
|
1127
1086
|
type: 'array',
|
|
@@ -1153,7 +1112,7 @@ export const mcpTools = [
|
|
|
1153
1112
|
description: 'Array of file config objects with per-file line ranges. Each file can have its own startLine/endLine.',
|
|
1154
1113
|
},
|
|
1155
1114
|
],
|
|
1156
|
-
description: 'Path to the file(s) to read: string, array of strings, or array of {path, startLine?, endLine?} objects',
|
|
1115
|
+
description: 'Path to the file(s) to read or directory to list: string, array of strings, or array of {path, startLine?, endLine?} objects',
|
|
1157
1116
|
},
|
|
1158
1117
|
startLine: {
|
|
1159
1118
|
type: 'number',
|
|
@@ -1190,53 +1149,9 @@ export const mcpTools = [
|
|
|
1190
1149
|
required: ['filePath', 'content'],
|
|
1191
1150
|
},
|
|
1192
1151
|
},
|
|
1193
|
-
{
|
|
1194
|
-
name: 'filesystem-delete',
|
|
1195
|
-
description: 'Delete one or multiple files. Supports both single file and batch deletion.',
|
|
1196
|
-
inputSchema: {
|
|
1197
|
-
type: 'object',
|
|
1198
|
-
properties: {
|
|
1199
|
-
filePath: {
|
|
1200
|
-
type: 'string',
|
|
1201
|
-
description: 'Path to a single file to delete (deprecated: use filePaths for single or multiple files)',
|
|
1202
|
-
},
|
|
1203
|
-
filePaths: {
|
|
1204
|
-
oneOf: [
|
|
1205
|
-
{
|
|
1206
|
-
type: 'string',
|
|
1207
|
-
description: 'Path to a single file to delete',
|
|
1208
|
-
},
|
|
1209
|
-
{
|
|
1210
|
-
type: 'array',
|
|
1211
|
-
items: {
|
|
1212
|
-
type: 'string',
|
|
1213
|
-
},
|
|
1214
|
-
description: 'Array of file paths to delete',
|
|
1215
|
-
},
|
|
1216
|
-
],
|
|
1217
|
-
description: 'Single file path or array of file paths to delete',
|
|
1218
|
-
},
|
|
1219
|
-
},
|
|
1220
|
-
// Make both optional, but at least one is required (validated in code)
|
|
1221
|
-
},
|
|
1222
|
-
},
|
|
1223
|
-
{
|
|
1224
|
-
name: 'filesystem-list',
|
|
1225
|
-
description: 'List files in a directory',
|
|
1226
|
-
inputSchema: {
|
|
1227
|
-
type: 'object',
|
|
1228
|
-
properties: {
|
|
1229
|
-
dirPath: {
|
|
1230
|
-
type: 'string',
|
|
1231
|
-
description: 'Directory path to list files from',
|
|
1232
|
-
default: '.',
|
|
1233
|
-
},
|
|
1234
|
-
},
|
|
1235
|
-
},
|
|
1236
|
-
},
|
|
1237
1152
|
{
|
|
1238
1153
|
name: 'filesystem-edit_search',
|
|
1239
|
-
description: '
|
|
1154
|
+
description: 'RECOMMENDED for most edits: Search-and-replace with SMART FUZZY MATCHING. SUPPORTS BATCH EDITING: Pass (1) single file with search/replace, (2) array of file paths with unified search/replace, or (3) array of {path, searchContent, replaceContent, occurrence?} for per-file edits. CRITICAL WORKFLOW FOR CODE SAFETY: (1) Use ace-text_search/ace-search_symbols to locate code, (2) MUST use filesystem-read to identify COMPLETE code boundaries (entire function body with all braces, complete markup tags with opening/closing pairs, full code blocks), (3) Copy the COMPLETE code block (without line numbers), (4) Verify boundaries are intact (matching braces/brackets/tags), (5) Use THIS tool. WHY: No line tracking, auto-handles spacing/tabs, finds best match. COMMON ERRORS TO AVOID: Modifying only part of a function (missing closing brace), incomplete markup tags (HTML/Vue/JSX), partial code blocks. Always include complete syntactic units. BATCH EXAMPLE: filePath=[{path:"a.ts", searchContent:"old1", replaceContent:"new1"}, {path:"b.ts", searchContent:"old2", replaceContent:"new2"}]',
|
|
1240
1155
|
inputSchema: {
|
|
1241
1156
|
type: 'object',
|
|
1242
1157
|
properties: {
|
|
@@ -1306,7 +1221,7 @@ export const mcpTools = [
|
|
|
1306
1221
|
},
|
|
1307
1222
|
{
|
|
1308
1223
|
name: 'filesystem-edit',
|
|
1309
|
-
description: 'Line-based editing for precise control.
|
|
1224
|
+
description: 'Line-based editing for precise control. SUPPORTS BATCH EDITING: Pass (1) single file with line range, (2) array of file paths with unified line range, or (3) array of {path, startLine, endLine, newContent} for per-file edits. WHEN TO USE: (1) Adding new code sections, (2) Deleting specific line ranges, (3) When search-replace not suitable. CRITICAL WORKFLOW FOR CODE SAFETY: (1) Use ace-text_search/ace-file_outline to locate area, (2) MUST use filesystem-read to identify COMPLETE code boundaries - for functions: include opening line to closing brace; for markup tags (HTML/Vue/JSX): include opening tag to closing tag; for code blocks: include all braces/brackets, (3) Verify line range covers the ENTIRE syntactic unit (check indentation levels, matching pairs), (4) Use THIS tool with exact startLine/endLine. RECOMMENDATION: For modifying existing code, use filesystem-edit_search - safer and no line tracking needed. COMMON ERRORS TO AVOID: Line range stops mid-function (missing closing brace), partial markup tags, incomplete code blocks. Always verify boundaries with filesystem-read first. BATCH EXAMPLE: filePath=[{path:"a.ts", startLine:10, endLine:20, newContent:"..."}, {path:"b.ts", startLine:50, endLine:60, newContent:"..."}]',
|
|
1310
1225
|
inputSchema: {
|
|
1311
1226
|
type: 'object',
|
|
1312
1227
|
properties: {
|
package/dist/mcp/subagent.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { SubAgentMessage } from '../utils/subAgentExecutor.js';
|
|
2
2
|
import type { ToolCall } from '../utils/toolExecutor.js';
|
|
3
|
+
import type { ConfirmationResult } from '../ui/components/ToolConfirmation.js';
|
|
3
4
|
export interface SubAgentToolExecutionOptions {
|
|
4
5
|
agentId: string;
|
|
5
6
|
prompt: string;
|
|
6
7
|
onMessage?: (message: SubAgentMessage) => void;
|
|
7
8
|
abortSignal?: AbortSignal;
|
|
8
|
-
requestToolConfirmation?: (toolCall: ToolCall, batchToolNames?: string, allTools?: ToolCall[]) => Promise<
|
|
9
|
+
requestToolConfirmation?: (toolCall: ToolCall, batchToolNames?: string, allTools?: ToolCall[]) => Promise<ConfirmationResult>;
|
|
9
10
|
isToolAutoApproved?: (toolName: string) => boolean;
|
|
10
11
|
yoloMode?: boolean;
|
|
11
12
|
addToAlwaysApproved?: (toolName: string) => void;
|
package/dist/mcp/subagent.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { executeSubAgent } from '../utils/subAgentExecutor.js';
|
|
2
|
-
import {
|
|
2
|
+
import { getUserSubAgents } from '../utils/subAgentConfig.js';
|
|
3
3
|
/**
|
|
4
4
|
* Sub-Agent MCP Service
|
|
5
5
|
* Provides tools for executing sub-agents with their own specialized system prompts and tool access
|
|
@@ -32,14 +32,62 @@ export class SubAgentService {
|
|
|
32
32
|
return {
|
|
33
33
|
success: true,
|
|
34
34
|
result: result.result,
|
|
35
|
+
usage: result.usage,
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
39
|
* Get all available sub-agents as MCP tools
|
|
39
40
|
*/
|
|
40
41
|
getTools() {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
// Get only user-configured agents (built-in agents are hardcoded below)
|
|
43
|
+
const userAgents = getUserSubAgents();
|
|
44
|
+
// Built-in agents (hardcoded, always available)
|
|
45
|
+
const tools = [
|
|
46
|
+
{
|
|
47
|
+
name: 'agent_explore',
|
|
48
|
+
description: 'Explore Agent: Specialized for quickly exploring and understanding codebases. Excels at searching code, finding definitions, analyzing code structure and dependencies. Read-only operations, will not modify files or execute commands.',
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
prompt: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include: (1) Full task description with business requirements, (2) Known file locations and code paths, (3) Relevant code snippets or patterns already discovered, (4) Any constraints or important context. Example: "Explore authentication implementation. Main flow uses OAuth in src/auth/oauth.ts, need to find all related error handling. User mentioned JWT tokens are validated in middleware."',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ['prompt'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'agent_plan',
|
|
62
|
+
description: 'Plan Agent: Specialized for planning complex tasks. Analyzes requirements, explores code, identifies relevant files, and creates detailed implementation plans. Read-only operations, outputs structured implementation proposals.',
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
prompt: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include: (1) Full requirement details and business objectives, (2) Current architecture/file structure understanding, (3) Known dependencies and constraints, (4) Files/modules already identified that need changes, (5) User preferences or specific implementation approaches mentioned. Example: "Plan caching implementation. Current API uses Express in src/server.ts, data layer in src/models/. Need Redis caching, user wants minimal changes to existing controllers in src/controllers/."',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ['prompt'],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'agent_general',
|
|
76
|
+
description: 'General Purpose Agent: General-purpose multi-step task execution agent. Has full tool access for searching, modifying files, and executing commands. Best for complex tasks requiring actual operations.',
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object',
|
|
79
|
+
properties: {
|
|
80
|
+
prompt: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include: (1) Full task description with step-by-step requirements, (2) Exact file paths and locations to modify, (3) Code patterns/snippets to follow or replicate, (4) Dependencies between files/changes, (5) Testing/verification requirements, (6) Any business logic or constraints discovered in main session. Example: "Update error handling across API. Files: src/api/users.ts, src/api/posts.ts, src/api/comments.ts. Replace old pattern try-catch with new ErrorHandler class from src/utils/errorHandler.ts. Must preserve existing error codes. Run npm test after changes."',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ['prompt'],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
// Add user-configured agents (avoid duplicates with built-in)
|
|
90
|
+
tools.push(...userAgents.map(agent => ({
|
|
43
91
|
name: agent.id,
|
|
44
92
|
description: `${agent.name}: ${agent.description}`,
|
|
45
93
|
inputSchema: {
|
|
@@ -47,12 +95,13 @@ export class SubAgentService {
|
|
|
47
95
|
properties: {
|
|
48
96
|
prompt: {
|
|
49
97
|
type: 'string',
|
|
50
|
-
description: '
|
|
98
|
+
description: 'CRITICAL: Provide COMPLETE context from main session. Sub-agent has NO access to main conversation history. Include all relevant: (1) Task requirements and objectives, (2) Known file locations and code structure, (3) Business logic and constraints, (4) Code patterns or examples, (5) Dependencies and relationships. Be specific and comprehensive - sub-agent cannot ask for clarification from main session.',
|
|
51
99
|
},
|
|
52
100
|
},
|
|
53
101
|
required: ['prompt'],
|
|
54
102
|
},
|
|
55
|
-
}));
|
|
103
|
+
})));
|
|
104
|
+
return tools;
|
|
56
105
|
}
|
|
57
106
|
}
|
|
58
107
|
// Export a default instance
|