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.
Files changed (92) hide show
  1. package/dist/agents/codebaseIndexAgent.js +1 -0
  2. package/dist/agents/codebaseReviewAgent.d.ts +61 -0
  3. package/dist/agents/codebaseReviewAgent.js +301 -0
  4. package/dist/api/anthropic.js +1 -0
  5. package/dist/api/chat.js +1 -0
  6. package/dist/api/embedding.js +1 -0
  7. package/dist/api/gemini.js +2 -1
  8. package/dist/api/responses.js +1 -0
  9. package/dist/api/systemPrompt.d.ts +1 -5
  10. package/dist/api/systemPrompt.js +198 -141
  11. package/dist/app.js +14 -6
  12. package/dist/cli.js +1 -1
  13. package/dist/hooks/useCommandPanel.js +48 -46
  14. package/dist/hooks/useConversation.d.ts +2 -1
  15. package/dist/hooks/useConversation.js +110 -28
  16. package/dist/hooks/useGlobalExit.js +4 -2
  17. package/dist/hooks/useKeyboardInput.js +132 -26
  18. package/dist/hooks/useStreamingState.d.ts +9 -0
  19. package/dist/hooks/useStreamingState.js +3 -0
  20. package/dist/i18n/I18nContext.d.ts +14 -0
  21. package/dist/i18n/I18nContext.js +24 -0
  22. package/dist/i18n/index.d.ts +3 -0
  23. package/dist/i18n/index.js +2 -0
  24. package/dist/i18n/lang/en.d.ts +2 -0
  25. package/dist/i18n/lang/en.js +481 -0
  26. package/dist/i18n/lang/es.d.ts +2 -0
  27. package/dist/i18n/lang/es.js +481 -0
  28. package/dist/i18n/lang/ja.d.ts +2 -0
  29. package/dist/i18n/lang/ja.js +481 -0
  30. package/dist/i18n/lang/ko.d.ts +2 -0
  31. package/dist/i18n/lang/ko.js +481 -0
  32. package/dist/i18n/lang/zh-TW.d.ts +2 -0
  33. package/dist/i18n/lang/zh-TW.js +481 -0
  34. package/dist/i18n/lang/zh.d.ts +2 -0
  35. package/dist/i18n/lang/zh.js +481 -0
  36. package/dist/i18n/translations.d.ts +2 -0
  37. package/dist/i18n/translations.js +14 -0
  38. package/dist/i18n/types.d.ts +457 -0
  39. package/dist/i18n/types.js +1 -0
  40. package/dist/mcp/aceCodeSearch.d.ts +23 -48
  41. package/dist/mcp/aceCodeSearch.js +154 -144
  42. package/dist/mcp/codebaseSearch.d.ts +1 -1
  43. package/dist/mcp/codebaseSearch.js +159 -30
  44. package/dist/mcp/filesystem.d.ts +3 -80
  45. package/dist/mcp/filesystem.js +7 -92
  46. package/dist/mcp/subagent.d.ts +2 -1
  47. package/dist/mcp/subagent.js +54 -5
  48. package/dist/ui/components/ChatInput.js +39 -50
  49. package/dist/ui/components/CommandPanel.d.ts +1 -1
  50. package/dist/ui/components/CommandPanel.js +20 -13
  51. package/dist/ui/components/HelpPanel.js +47 -21
  52. package/dist/ui/components/Menu.js +6 -2
  53. package/dist/ui/components/MessageList.d.ts +6 -0
  54. package/dist/ui/components/MessageList.js +1 -1
  55. package/dist/ui/components/ToolConfirmation.d.ts +4 -1
  56. package/dist/ui/components/ToolConfirmation.js +28 -2
  57. package/dist/ui/components/ToolResultPreview.d.ts +2 -1
  58. package/dist/ui/components/ToolResultPreview.js +26 -22
  59. package/dist/ui/pages/ChatScreen.js +126 -49
  60. package/dist/ui/pages/CodeBaseConfigScreen.js +54 -30
  61. package/dist/ui/pages/ConfigScreen.js +102 -98
  62. package/dist/ui/pages/CustomHeadersScreen.js +75 -69
  63. package/dist/ui/pages/LanguageSettingsScreen.d.ts +7 -0
  64. package/dist/ui/pages/LanguageSettingsScreen.js +89 -0
  65. package/dist/ui/pages/ProxyConfigScreen.js +27 -23
  66. package/dist/ui/pages/SensitiveCommandConfigScreen.js +32 -25
  67. package/dist/ui/pages/SubAgentConfigScreen.js +88 -75
  68. package/dist/ui/pages/SystemPromptConfigScreen.js +31 -26
  69. package/dist/ui/pages/WelcomeScreen.js +40 -26
  70. package/dist/utils/codebaseConfig.d.ts +1 -5
  71. package/dist/utils/codebaseConfig.js +2 -10
  72. package/dist/utils/codebaseSearchEvents.d.ts +16 -0
  73. package/dist/utils/codebaseSearchEvents.js +13 -0
  74. package/dist/utils/commands/agent.js +2 -2
  75. package/dist/utils/commands/init.js +1 -1
  76. package/dist/utils/configManager.js +26 -5
  77. package/dist/utils/contextCompressor.js +1 -1
  78. package/dist/utils/languageConfig.d.ts +21 -0
  79. package/dist/utils/languageConfig.js +61 -0
  80. package/dist/utils/mcpToolsManager.js +0 -9
  81. package/dist/utils/notebookManager.js +11 -4
  82. package/dist/utils/sessionConverter.js +13 -3
  83. package/dist/utils/subAgentConfig.d.ts +10 -5
  84. package/dist/utils/subAgentConfig.js +112 -19
  85. package/dist/utils/subAgentExecutor.d.ts +9 -1
  86. package/dist/utils/subAgentExecutor.js +122 -9
  87. package/dist/utils/textBuffer.d.ts +1 -0
  88. package/dist/utils/textBuffer.js +69 -4
  89. package/dist/utils/toolExecutor.d.ts +2 -1
  90. package/dist/utils/toolExecutor.js +1 -2
  91. package/dist/utils/usageLogger.js +18 -3
  92. 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
- // Generate embedding for query
82
- logger.info(`Generating embedding for query: "${query}"`);
83
- const queryEmbedding = await createEmbedding(query);
84
- // Search similar chunks
85
- logger.info(`Searching top ${topN} similar chunks from ${totalChunks} total chunks`);
86
- const results = db.searchSimilar(queryEmbedding, topN);
87
- // Format results with similarity scores and full content (no truncation)
88
- const formattedResults = results.map((chunk, index) => {
89
- const score = this.cosineSimilarity(queryEmbedding, chunk.embedding);
90
- const scorePercent = (score * 100).toFixed(2);
91
- return {
92
- rank: index + 1,
93
- filePath: chunk.filePath,
94
- startLine: chunk.startLine,
95
- endLine: chunk.endLine,
96
- content: chunk.content, // Full content, no truncation
97
- similarityScore: scorePercent,
98
- location: `${chunk.filePath}:${chunk.startLine}-${chunk.endLine}`,
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: '🔍 Semantic search across the codebase using embeddings. ' +
124
- 'Finds code snippets similar to your query based on meaning, not just keywords. ' +
125
- 'Returns full code content with similarity scores and file locations. ' +
126
- 'NOTE: Only available when codebase indexing is enabled and the index has been built. ' +
127
- 'If the index is not available, the tool will return an error message with instructions.',
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: {
@@ -64,19 +64,13 @@ export declare class FilesystemMCPService {
64
64
  */
65
65
  createFile(filePath: string, content: string, createDirectories?: boolean): Promise<string>;
66
66
  /**
67
- * Delete one or multiple files
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(dirPath?: string): Promise<string[]>;
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;
@@ -382,52 +382,11 @@ export class FilesystemMCPService {
382
382
  }
383
383
  }
384
384
  /**
385
- * Delete one or multiple files
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=50 reads lines 1-50 from both. (B) Per-file: filePath=[{path:"a.ts", startLine:1, endLine:30}, {path:"b.ts", startLine:100, endLine:150}] reads different ranges from each file.',
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: '**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. **WORKFLOW**: (1) Use ace-text_search/ace-search_symbols to locate code, (2) Use filesystem-read to view content, (3) Copy code blocks (without line numbers), (4) Use THIS tool. **WHY**: No line tracking, auto-handles spacing/tabs, finds best match. **BATCH EXAMPLE**: filePath=[{path:"a.ts", searchContent:"old1", replaceContent:"new1"}, {path:"b.ts", searchContent:"old2", replaceContent:"new2"}] **It is very important to use the filesystem-read tool to determine the code boundaries of the area that need to be modified first to avoid syntactic errors**',
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. **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. **WORKFLOW**: (1) Use ace-text_search/ace-file_outline to locate area, (2) Use filesystem-read to get line numbers, (3) Use THIS tool. **RECOMMENDATION**: For modifying existing code, use filesystem-edit_search - safer. **BATCH EXAMPLE**: filePath=[{path:"a.ts", startLine:10, endLine:20, newContent:"..."}, {path:"b.ts", startLine:50, endLine:60, newContent:"..."}] **It is very important to use the filesystem-read tool to determine the code boundaries of the area that need to be modified first to avoid syntactic errors**',
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: {
@@ -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<string>;
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;
@@ -1,5 +1,5 @@
1
1
  import { executeSubAgent } from '../utils/subAgentExecutor.js';
2
- import { getSubAgents } from '../utils/subAgentConfig.js';
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
- const subAgents = getSubAgents();
42
- return subAgents.map(agent => ({
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: 'The task prompt to send to the sub-agent',
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