snow-ai 0.3.28 → 0.3.29
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/hooks/useConversation.js +17 -10
- package/dist/mcp/filesystem.d.ts +11 -0
- package/dist/mcp/filesystem.js +95 -2
- package/dist/mcp/utils/aceCodeSearch/language.utils.js +354 -35
- package/dist/ui/pages/SubAgentConfigScreen.js +27 -4
- package/dist/utils/sessionConverter.js +23 -20
- package/dist/utils/subAgentConfig.d.ts +3 -1
- package/dist/utils/subAgentConfig.js +3 -1
- package/dist/utils/subAgentExecutor.js +6 -1
- package/dist/utils/vscodeConnection.d.ts +7 -0
- package/dist/utils/vscodeConnection.js +79 -2
- package/package.json +1 -1
|
@@ -515,8 +515,10 @@ export async function handleConversationWithTools(options) {
|
|
|
515
515
|
if (subAgentMessage.message.type === 'tool_calls') {
|
|
516
516
|
const toolCalls = subAgentMessage.message.tool_calls;
|
|
517
517
|
if (toolCalls && toolCalls.length > 0) {
|
|
518
|
-
// Add tool call messages for each tool
|
|
519
|
-
const toolMessages = toolCalls
|
|
518
|
+
// Add tool call messages for each tool (only for time-consuming tools)
|
|
519
|
+
const toolMessages = toolCalls
|
|
520
|
+
.filter((toolCall) => isToolNeedTwoStepDisplay(toolCall.function.name))
|
|
521
|
+
.map((toolCall) => {
|
|
520
522
|
const toolDisplay = formatToolCallMessage(toolCall);
|
|
521
523
|
let toolArgs;
|
|
522
524
|
try {
|
|
@@ -543,16 +545,21 @@ export async function handleConversationWithTools(options) {
|
|
|
543
545
|
},
|
|
544
546
|
subAgentInternal: true, // Mark as internal sub-agent message
|
|
545
547
|
};
|
|
546
|
-
// Save to session as 'assistant' role for API compatibility
|
|
547
|
-
const sessionMsg = {
|
|
548
|
-
role: 'assistant',
|
|
549
|
-
content: `⚇⚡ ${toolDisplay.toolName}`,
|
|
550
|
-
subAgentInternal: true,
|
|
551
|
-
tool_calls: [toolCall],
|
|
552
|
-
};
|
|
553
|
-
saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool call:', err));
|
|
554
548
|
return uiMsg;
|
|
555
549
|
});
|
|
550
|
+
// Save all tool calls to session (regardless of display type)
|
|
551
|
+
const sessionMsg = {
|
|
552
|
+
role: 'assistant',
|
|
553
|
+
content: toolCalls
|
|
554
|
+
.map((tc) => {
|
|
555
|
+
const display = formatToolCallMessage(tc);
|
|
556
|
+
return `⚇⚡ ${display.toolName}`;
|
|
557
|
+
})
|
|
558
|
+
.join(', '),
|
|
559
|
+
subAgentInternal: true,
|
|
560
|
+
tool_calls: toolCalls,
|
|
561
|
+
};
|
|
562
|
+
saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool call:', err));
|
|
556
563
|
return [...prev, ...toolMessages];
|
|
557
564
|
}
|
|
558
565
|
}
|
package/dist/mcp/filesystem.d.ts
CHANGED
|
@@ -10,8 +10,19 @@ export declare class FilesystemMCPService {
|
|
|
10
10
|
*/
|
|
11
11
|
private readonly prettierSupportedExtensions;
|
|
12
12
|
constructor(basePath?: string);
|
|
13
|
+
/**
|
|
14
|
+
* Extract relevant symbol information for a specific line range
|
|
15
|
+
* This provides context that helps AI make more accurate modifications
|
|
16
|
+
* @param symbols - All symbols in the file
|
|
17
|
+
* @param startLine - Start line of the range
|
|
18
|
+
* @param endLine - End line of the range
|
|
19
|
+
* @param _totalLines - Total lines in the file (reserved for future use)
|
|
20
|
+
* @returns Formatted string with relevant symbol information
|
|
21
|
+
*/
|
|
22
|
+
private extractRelevantSymbols;
|
|
13
23
|
/**
|
|
14
24
|
* Get the content of a file with optional line range
|
|
25
|
+
* Enhanced with symbol information for better AI context
|
|
15
26
|
* @param filePath - Path to the file (relative to base path or absolute) or array of file paths or array of file config objects
|
|
16
27
|
* @param startLine - Starting line number (1-indexed, inclusive, optional - defaults to 1). Used for single file or as default for array of strings
|
|
17
28
|
* @param endLine - Ending line number (1-indexed, inclusive, optional - defaults to file end). Used for single file or as default for array of strings
|
package/dist/mcp/filesystem.js
CHANGED
|
@@ -11,6 +11,8 @@ import { calculateSimilarity, normalizeForDisplay, } from './utils/filesystem/si
|
|
|
11
11
|
import { analyzeCodeStructure, findSmartContextBoundaries, } from './utils/filesystem/code-analysis.utils.js';
|
|
12
12
|
import { findClosestMatches, generateDiffMessage, } from './utils/filesystem/match-finder.utils.js';
|
|
13
13
|
import { parseEditBySearchParams, parseEditByLineParams, executeBatchOperation, } from './utils/filesystem/batch-operations.utils.js';
|
|
14
|
+
// ACE Code Search utilities for symbol parsing
|
|
15
|
+
import { parseFileSymbols } from './utils/aceCodeSearch/symbol.utils.js';
|
|
14
16
|
const { resolve, dirname, isAbsolute } = path;
|
|
15
17
|
const execAsync = promisify(exec);
|
|
16
18
|
/**
|
|
@@ -52,8 +54,76 @@ export class FilesystemMCPService {
|
|
|
52
54
|
});
|
|
53
55
|
this.basePath = resolve(basePath);
|
|
54
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Extract relevant symbol information for a specific line range
|
|
59
|
+
* This provides context that helps AI make more accurate modifications
|
|
60
|
+
* @param symbols - All symbols in the file
|
|
61
|
+
* @param startLine - Start line of the range
|
|
62
|
+
* @param endLine - End line of the range
|
|
63
|
+
* @param _totalLines - Total lines in the file (reserved for future use)
|
|
64
|
+
* @returns Formatted string with relevant symbol information
|
|
65
|
+
*/
|
|
66
|
+
extractRelevantSymbols(symbols, startLine, endLine, _totalLines) {
|
|
67
|
+
if (symbols.length === 0) {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
// Categorize symbols
|
|
71
|
+
const imports = symbols.filter(s => s.type === 'import');
|
|
72
|
+
const exports = symbols.filter(s => s.type === 'export');
|
|
73
|
+
// Symbols within the requested range
|
|
74
|
+
const symbolsInRange = symbols.filter(s => s.line >= startLine && s.line <= endLine);
|
|
75
|
+
// Symbols defined before the range that might be referenced
|
|
76
|
+
const symbolsBeforeRange = symbols.filter(s => s.line < startLine);
|
|
77
|
+
// Build context information
|
|
78
|
+
const parts = [];
|
|
79
|
+
// Always include imports (crucial for understanding dependencies)
|
|
80
|
+
if (imports.length > 0) {
|
|
81
|
+
const importList = imports
|
|
82
|
+
.slice(0, 10) // Limit to avoid excessive tokens
|
|
83
|
+
.map(s => ` • ${s.name} (line ${s.line})`)
|
|
84
|
+
.join('\n');
|
|
85
|
+
parts.push(`📦 Imports:\n${importList}`);
|
|
86
|
+
}
|
|
87
|
+
// Symbols defined in the current range
|
|
88
|
+
if (symbolsInRange.length > 0) {
|
|
89
|
+
const rangeSymbols = symbolsInRange
|
|
90
|
+
.slice(0, 15)
|
|
91
|
+
.map(s => ` • ${s.type}: ${s.name} (line ${s.line})${s.signature ? ` - ${s.signature.slice(0, 60)}` : ''}`)
|
|
92
|
+
.join('\n');
|
|
93
|
+
parts.push(`🎯 Symbols in this range:\n${rangeSymbols}`);
|
|
94
|
+
}
|
|
95
|
+
// Key definitions before this range (that might be referenced)
|
|
96
|
+
if (symbolsBeforeRange.length > 0 && startLine > 1) {
|
|
97
|
+
const relevantBefore = symbolsBeforeRange
|
|
98
|
+
.filter(s => s.type === 'function' || s.type === 'class')
|
|
99
|
+
.slice(-5) // Last 5 before the range
|
|
100
|
+
.map(s => ` • ${s.type}: ${s.name} (line ${s.line})`)
|
|
101
|
+
.join('\n');
|
|
102
|
+
if (relevantBefore) {
|
|
103
|
+
parts.push(`⬆️ Key definitions above:\n${relevantBefore}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Exports (important for understanding module interface)
|
|
107
|
+
if (exports.length > 0) {
|
|
108
|
+
const exportList = exports
|
|
109
|
+
.slice(0, 10)
|
|
110
|
+
.map(s => ` • ${s.name} (line ${s.line})`)
|
|
111
|
+
.join('\n');
|
|
112
|
+
parts.push(`📤 Exports:\n${exportList}`);
|
|
113
|
+
}
|
|
114
|
+
if (parts.length === 0) {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
return ('\n\n' +
|
|
118
|
+
'='.repeat(60) +
|
|
119
|
+
'\n📚 SYMBOL INDEX & DEFINITIONS:\n' +
|
|
120
|
+
'='.repeat(60) +
|
|
121
|
+
'\n' +
|
|
122
|
+
parts.join('\n\n'));
|
|
123
|
+
}
|
|
55
124
|
/**
|
|
56
125
|
* Get the content of a file with optional line range
|
|
126
|
+
* Enhanced with symbol information for better AI context
|
|
57
127
|
* @param filePath - Path to the file (relative to base path or absolute) or array of file paths or array of file config objects
|
|
58
128
|
* @param startLine - Starting line number (1-indexed, inclusive, optional - defaults to 1). Used for single file or as default for array of strings
|
|
59
129
|
* @param endLine - Ending line number (1-indexed, inclusive, optional - defaults to file end). Used for single file or as default for array of strings
|
|
@@ -127,7 +197,18 @@ export class FilesystemMCPService {
|
|
|
127
197
|
const lineNum = start + index;
|
|
128
198
|
return `${lineNum}→${line}`;
|
|
129
199
|
});
|
|
130
|
-
|
|
200
|
+
let fileContent = `📄 ${file} (lines ${start}-${end}/${totalLines})\n${numberedLines.join('\n')}`;
|
|
201
|
+
// Parse and append symbol information
|
|
202
|
+
try {
|
|
203
|
+
const symbols = await parseFileSymbols(fullPath, content, this.basePath);
|
|
204
|
+
const symbolInfo = this.extractRelevantSymbols(symbols, start, end, totalLines);
|
|
205
|
+
if (symbolInfo) {
|
|
206
|
+
fileContent += symbolInfo;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Silently fail symbol parsing
|
|
211
|
+
}
|
|
131
212
|
allContents.push(fileContent);
|
|
132
213
|
filesData.push({
|
|
133
214
|
path: file,
|
|
@@ -197,7 +278,19 @@ export class FilesystemMCPService {
|
|
|
197
278
|
const lineNum = start + index;
|
|
198
279
|
return `${lineNum}→${line}`;
|
|
199
280
|
});
|
|
200
|
-
|
|
281
|
+
let partialContent = numberedLines.join('\n');
|
|
282
|
+
// Parse and append symbol information to provide better context for AI
|
|
283
|
+
try {
|
|
284
|
+
const symbols = await parseFileSymbols(fullPath, content, this.basePath);
|
|
285
|
+
const symbolInfo = this.extractRelevantSymbols(symbols, start, end, totalLines);
|
|
286
|
+
if (symbolInfo) {
|
|
287
|
+
partialContent += symbolInfo;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
// Silently fail symbol parsing - don't block file reading
|
|
292
|
+
// This is optional context enhancement, not critical
|
|
293
|
+
}
|
|
201
294
|
return {
|
|
202
295
|
content: partialContent,
|
|
203
296
|
startLine: start,
|
|
@@ -6,80 +6,399 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const LANGUAGE_CONFIG = {
|
|
8
8
|
typescript: {
|
|
9
|
-
extensions: ['.ts', '.tsx'],
|
|
9
|
+
extensions: ['.ts', '.tsx', '.mts', '.cts'],
|
|
10
10
|
parser: 'typescript',
|
|
11
11
|
symbolPatterns: {
|
|
12
|
-
function: /(?:export\s+)?(?:async\s+)?function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s
|
|
13
|
-
class: /(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/,
|
|
14
|
-
variable: /(?:export\s+)?(?:const|let|var)\s+(\w+)\s
|
|
15
|
-
import: /import\s+(?:{[^}]+}|\w+)\s+from\s+['"]([^'"]+)['"]/,
|
|
16
|
-
export: /export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type|enum)\s+(\w+)/,
|
|
12
|
+
function: /(?:export\s+)?(?:async\s+)?(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>)|(?:@\w+\s+)*(?:public|private|protected|static)?\s*(?:async)?\s*(\w+)\s*[<(]/,
|
|
13
|
+
class: /(?:export\s+)?(?:abstract\s+)?(?:class|interface)\s+(\w+)|(?:export\s+)?type\s+(\w+)\s*=|(?:export\s+)?enum\s+(\w+)|(?:export\s+)?namespace\s+(\w+)/,
|
|
14
|
+
variable: /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*(?::|=)|(?:@\w+\s+)*(?:public|private|protected|readonly|static)?\s+(\w+)\s*[?:]/,
|
|
15
|
+
import: /import\s+(?:type\s+)?(?:{[^}]+}|\w+|\*\s+as\s+\w+)\s+from\s+['"]([^'"]+)['"]/,
|
|
16
|
+
export: /export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type|enum|namespace|abstract\s+class)\s+(\w+)/,
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
javascript: {
|
|
20
|
-
extensions: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
20
|
+
extensions: ['.js', '.jsx', '.mjs', '.cjs', '.es', '.es6'],
|
|
21
21
|
parser: 'javascript',
|
|
22
22
|
symbolPatterns: {
|
|
23
|
-
function: /(?:export\s+)?(?:async\s+)?function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)
|
|
23
|
+
function: /(?:export\s+)?(?:async\s+)?(?:function\s*\*?\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:function\s*\*?\s*)?(?:\([^)]*\)\s*=>|\([^)]*\)\s*\{))|(\w+)\s*\([^)]*\)\s*\{/,
|
|
24
24
|
class: /(?:export\s+)?class\s+(\w+)/,
|
|
25
25
|
variable: /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=/,
|
|
26
|
-
import: /import\s+(?:{[^}]+}|\w+)\s+from\s+['"]([^'"]+)['"]/,
|
|
26
|
+
import: /import\s+(?:{[^}]+}|\w+|\*\s+as\s+\w+)\s+from\s+['"]([^'"]+)['"]/,
|
|
27
27
|
export: /export\s+(?:default\s+)?(?:class|function|const|let|var)\s+(\w+)/,
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
30
|
python: {
|
|
31
|
-
extensions: ['.py', '.pyx', '.pyi'],
|
|
31
|
+
extensions: ['.py', '.pyx', '.pyi', '.pyw', '.pyz'],
|
|
32
32
|
parser: 'python',
|
|
33
33
|
symbolPatterns: {
|
|
34
|
-
function: /def\s+(\w+)\s*\(/,
|
|
35
|
-
class: /class\s+(\w+)\s*[(:]/,
|
|
36
|
-
variable:
|
|
37
|
-
import: /(?:from\s+[\w.]
|
|
38
|
-
export: /^(\w+)\s
|
|
34
|
+
function: /(?:@\w+\s+)*(?:async\s+)?def\s+(\w+)\s*\(/,
|
|
35
|
+
class: /(?:@\w+\s+)*class\s+(\w+)\s*[(:]/,
|
|
36
|
+
variable: /^(?:[\t ]*)([\w_][\w\d_]*)\s*(?::.*)?=\s*(?![=\s])|^([\w_][\w\d_]*)\s*:\s*(?!.*=)/m,
|
|
37
|
+
import: /(?:from\s+([\w.]+)\s+import\s+[\w, *]+|import\s+([\w.]+(?:\s+as\s+\w+)?))/,
|
|
38
|
+
export: /^(?:__all__\s*=|def\s+(\w+)|class\s+(\w+))/, // Python exports via __all__ or top-level
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
41
|
go: {
|
|
42
42
|
extensions: ['.go'],
|
|
43
43
|
parser: 'go',
|
|
44
44
|
symbolPatterns: {
|
|
45
|
-
function: /func\s+(?:\([^)]+\)\s+)?(\w+)\s
|
|
46
|
-
class: /type\s+(\w+)\s+struct/,
|
|
47
|
-
variable: /(?:var|const)\s+(\w+)\s
|
|
48
|
-
import: /import\s+(?:"([^"]+)"|[
|
|
49
|
-
export: /^(?:func|type|var|const)\s+([A-Z]\w+)/, // Go exports start with capital letter
|
|
45
|
+
function: /func\s+(?:\([^)]+\)\s+)?(\w+)\s*[<(]/,
|
|
46
|
+
class: /type\s+(\w+)\s+(?:struct|interface)/,
|
|
47
|
+
variable: /(?:var|const)\s+(\w+)\s+[\w\[\]*{]|(?:var|const)\s+\(\s*(\w+)/,
|
|
48
|
+
import: /import\s+(?:"([^"]+)"|_\s+"([^"]+)"|\w+\s+"([^"]+)")/,
|
|
49
|
+
export: /^(?:func|type|var|const)\s+([A-Z]\w+)|^type\s+([A-Z]\w+)\s+(?:struct|interface)/, // Go exports start with capital letter
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
52
|
rust: {
|
|
53
53
|
extensions: ['.rs'],
|
|
54
54
|
parser: 'rust',
|
|
55
55
|
symbolPatterns: {
|
|
56
|
-
function: /(?:pub\s+)?(?:async\s+)?fn\s+(\w+)\s*[<(]/,
|
|
57
|
-
class: /(?:pub
|
|
58
|
-
variable: /(?:pub
|
|
59
|
-
import: /use\s+([^;]+);/,
|
|
60
|
-
export: /pub
|
|
56
|
+
function: /(?:pub(?:\s*\([^)]+\))?\s+)?(?:unsafe\s+)?(?:async\s+)?(?:const\s+)?(?:extern\s+(?:"[^"]+"\s+)?)?fn\s+(\w+)\s*[<(]/,
|
|
57
|
+
class: /(?:pub(?:\s*\([^)]+\))?\s+)?(?:struct|enum|trait|union|type)\s+(\w+)|impl(?:\s+<[^>]+>)?\s+(?:\w+::)*(\w+)/,
|
|
58
|
+
variable: /(?:pub(?:\s*\([^)]+\))?\s+)?(?:static|const|mut)?\s*(?:let\s+(?:mut\s+)?)?(\w+)\s*[:=]/,
|
|
59
|
+
import: /use\s+([^;]+);|extern\s+crate\s+(\w+);/,
|
|
60
|
+
export: /pub(?:\s*\([^)]+\))?\s+(?:fn|struct|enum|trait|const|static|type|mod|use)\s+(\w+)/,
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
63
|
java: {
|
|
64
64
|
extensions: ['.java'],
|
|
65
65
|
parser: 'java',
|
|
66
66
|
symbolPatterns: {
|
|
67
|
-
function: /(?:public|private|protected|static|\s)+[\w<>\[\]]+\s+(\w+)\s*\([^)]*\)\s
|
|
68
|
-
class: /(?:public|private|protected)?\s*(?:abstract|final)?\s*class\s+(\w+)/,
|
|
69
|
-
variable: /(?:public|private|protected|static|final|\s)+[\w<>\[\]]+\s+(\w+)\s*[=;]/,
|
|
70
|
-
import: /import\s+([\w
|
|
71
|
-
export: /public\s+(?:class|interface|enum)\s+(\w+)/,
|
|
67
|
+
function: /(?:@\w+\s+)*(?:public|private|protected|static|final|synchronized|native|abstract|\s)+[\w<>\[\]]+\s+(\w+)\s*\([^)]*\)\s*(?:throws\s+[\w,\s]+)?\s*[{;]/,
|
|
68
|
+
class: /(?:@\w+\s+)*(?:public|private|protected)?\s*(?:abstract|final|static)?\s*(?:class|interface|enum|record|@interface)\s+(\w+)/,
|
|
69
|
+
variable: /(?:@\w+\s+)*(?:public|private|protected|static|final|transient|volatile|\s)+[\w<>\[\]]+\s+(\w+)\s*[=;]/,
|
|
70
|
+
import: /import\s+(?:static\s+)?([\w.*]+);/,
|
|
71
|
+
export: /public\s+(?:class|interface|enum|record|@interface)\s+(\w+)/,
|
|
72
72
|
},
|
|
73
73
|
},
|
|
74
74
|
csharp: {
|
|
75
75
|
extensions: ['.cs'],
|
|
76
76
|
parser: 'csharp',
|
|
77
77
|
symbolPatterns: {
|
|
78
|
-
function: /(?:public|private|protected|internal|static|\s)+[\w<>\[\]]+\s+(\w+)\s
|
|
79
|
-
class: /(?:public|private|protected|internal)?\s*(?:abstract|sealed|static)?\s*class\s+(\w+)/,
|
|
80
|
-
variable: /(?:public|private|protected|internal|static|readonly|\s)+[\w<>\[\]]+\s+(\w+)\s*[=;]/,
|
|
81
|
-
import: /using\s+([\w.]+);/,
|
|
82
|
-
export: /public\s+(?:class|interface|enum|struct)\s+(\w+)/,
|
|
78
|
+
function: /(?:\[[\w\s,()]+\]\s+)*(?:public|private|protected|internal|static|virtual|override|abstract|async|\s)+[\w<>\[\]?]+\s+(\w+)\s*[<(]/,
|
|
79
|
+
class: /(?:\[[\w\s,()]+\]\s+)*(?:public|private|protected|internal)?\s*(?:abstract|sealed|static|partial)?\s*(?:class|interface|struct|record|enum)\s+(\w+)/,
|
|
80
|
+
variable: /(?:\[[\w\s,()]+\]\s+)*(?:public|private|protected|internal|static|readonly|const|volatile|\s)+[\w<>\[\]?]+\s+(\w+)\s*[{=;]|(?:public|private|protected|internal)?\s*[\w<>\[\]?]+\s+(\w+)\s*\{\s*get/,
|
|
81
|
+
import: /using\s+(?:static\s+)?([\w.]+);/,
|
|
82
|
+
export: /public\s+(?:class|interface|enum|struct|record|delegate)\s+(\w+)/,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
c: {
|
|
86
|
+
extensions: ['.c', '.h'],
|
|
87
|
+
parser: 'c',
|
|
88
|
+
symbolPatterns: {
|
|
89
|
+
function: /(?:static|extern|inline)?\s*[\w\s\*]+\s+(\w+)\s*\([^)]*\)\s*\{/,
|
|
90
|
+
class: /(?:struct|union|enum)\s+(\w+)\s*\{/,
|
|
91
|
+
variable: /(?:extern|static|const)?\s*[\w\s\*]+\s+(\w+)\s*[=;]/,
|
|
92
|
+
import: /#include\s+[<"]([^>"]+)[>"]/,
|
|
93
|
+
export: /^[\w\s\*]+\s+(\w+)\s*\([^)]*\)\s*;/, // Function declarations
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
cpp: {
|
|
97
|
+
extensions: ['.cpp', '.cc', '.cxx', '.hpp', '.hh', '.hxx', '.h++', '.c++'],
|
|
98
|
+
parser: 'cpp',
|
|
99
|
+
symbolPatterns: {
|
|
100
|
+
function: /(?:static|extern|inline|virtual|explicit|constexpr)?\s*[\w\s\*&:<>,]+\s+(\w+)\s*\([^)]*\)\s*(?:const)?\s*(?:override)?\s*\{/,
|
|
101
|
+
class: /(?:class|struct|union|enum\s+class|enum\s+struct)\s+(\w+)(?:\s*:\s*(?:public|private|protected)\s+[\w,\s<>]+)?\s*\{/,
|
|
102
|
+
variable: /(?:extern|static|const|constexpr|inline)?\s*[\w\s\*&:<>,]+\s+(\w+)\s*[=;]/,
|
|
103
|
+
import: /#include\s+[<"]([^>"]+)[>"]/,
|
|
104
|
+
export: /^[\w\s\*&:<>,]+\s+(\w+)\s*\([^)]*\)\s*;/,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
php: {
|
|
108
|
+
extensions: ['.php', '.phtml', '.php3', '.php4', '.php5', '.phps'],
|
|
109
|
+
parser: 'php',
|
|
110
|
+
symbolPatterns: {
|
|
111
|
+
function: /(?:public|private|protected|static)?\s*function\s+(\w+)\s*\(/,
|
|
112
|
+
class: /(?:abstract|final)?\s*class\s+(\w+)(?:\s+extends\s+\w+)?(?:\s+implements\s+[\w,\s]+)?\s*\{/,
|
|
113
|
+
variable: /(?:public|private|protected|static)?\s*\$(\w+)\s*[=;]/,
|
|
114
|
+
import: /(?:require|require_once|include|include_once)\s*[('"]([^'"]+)['"]/,
|
|
115
|
+
export: /^(?:public\s+)?(?:function|class|interface|trait)\s+(\w+)/,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
ruby: {
|
|
119
|
+
extensions: ['.rb', '.rake', '.gemspec', '.ru', '.rbw'],
|
|
120
|
+
parser: 'ruby',
|
|
121
|
+
symbolPatterns: {
|
|
122
|
+
function: /def\s+(?:self\.)?(\w+)/,
|
|
123
|
+
class: /class\s+(\w+)(?:\s+<\s+[\w:]+)?/,
|
|
124
|
+
variable: /(?:@|@@|\$)?(\w+)\s*=(?!=)/,
|
|
125
|
+
import: /require(?:_relative)?\s+['"]([^'"]+)['"]/,
|
|
126
|
+
export: /module_function\s+:(\w+)|^def\s+(\w+)/, // Ruby's module exports
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
swift: {
|
|
130
|
+
extensions: ['.swift'],
|
|
131
|
+
parser: 'swift',
|
|
132
|
+
symbolPatterns: {
|
|
133
|
+
function: /(?:public|private|internal|fileprivate|open)?\s*(?:static|class)?\s*func\s+(\w+)\s*[<(]/,
|
|
134
|
+
class: /(?:public|private|internal|fileprivate|open)?\s*(?:final)?\s*(?:class|struct|enum|protocol|actor)\s+(\w+)/,
|
|
135
|
+
variable: /(?:public|private|internal|fileprivate|open)?\s*(?:static|class)?\s*(?:let|var)\s+(\w+)\s*[:=]/,
|
|
136
|
+
import: /import\s+(?:class|struct|enum|protocol)?\s*([\w.]+)/,
|
|
137
|
+
export: /public\s+(?:func|class|struct|enum|protocol|var|let)\s+(\w+)/,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
kotlin: {
|
|
141
|
+
extensions: ['.kt', '.kts'],
|
|
142
|
+
parser: 'kotlin',
|
|
143
|
+
symbolPatterns: {
|
|
144
|
+
function: /(?:public|private|protected|internal)?\s*(?:suspend|inline|infix|operator)?\s*fun\s+(\w+)\s*[<(]/,
|
|
145
|
+
class: /(?:public|private|protected|internal)?\s*(?:abstract|open|final|sealed|data|inline|value)?\s*(?:class|interface|object|enum\s+class)\s+(\w+)/,
|
|
146
|
+
variable: /(?:public|private|protected|internal)?\s*(?:const)?\s*(?:val|var)\s+(\w+)\s*[:=]/,
|
|
147
|
+
import: /import\s+([\w.]+)/,
|
|
148
|
+
export: /^(?:public\s+)?(?:fun|class|interface|object|val|var)\s+(\w+)/,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
dart: {
|
|
152
|
+
extensions: ['.dart'],
|
|
153
|
+
parser: 'dart',
|
|
154
|
+
symbolPatterns: {
|
|
155
|
+
function: /(?:static|abstract|external)?\s*[\w<>?,\s]+\s+(\w+)\s*\([^)]*\)\s*(?:async|sync\*)?\s*\{/,
|
|
156
|
+
class: /(?:abstract)?\s*class\s+(\w+)(?:\s+extends\s+[\w<>]+)?(?:\s+with\s+[\w,\s<>]+)?(?:\s+implements\s+[\w,\s<>]+)?\s*\{/,
|
|
157
|
+
variable: /(?:static|final|const|late)?\s*(?:var|[\w<>?,\s]+)\s+(\w+)\s*[=;]/,
|
|
158
|
+
import: /import\s+['"]([^'"]+)['"]/,
|
|
159
|
+
export: /^(?:class|abstract\s+class|enum|mixin)\s+(\w+)/,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
shell: {
|
|
163
|
+
extensions: ['.sh', '.bash', '.zsh', '.ksh', '.fish'],
|
|
164
|
+
parser: 'shell',
|
|
165
|
+
symbolPatterns: {
|
|
166
|
+
function: /(?:function\s+)?(\w+)\s*\(\s*\)\s*\{/,
|
|
167
|
+
class: /^$/, // Shell doesn't have classes
|
|
168
|
+
variable: /(?:export\s+)?(\w+)=/,
|
|
169
|
+
import: /(?:source|\.)\s+([^\s;]+)/,
|
|
170
|
+
export: /export\s+(?:function\s+)?(\w+)/,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
scala: {
|
|
174
|
+
extensions: ['.scala', '.sc'],
|
|
175
|
+
parser: 'scala',
|
|
176
|
+
symbolPatterns: {
|
|
177
|
+
function: /def\s+(\w+)\s*[:\[(]/,
|
|
178
|
+
class: /(?:sealed|abstract|final|implicit)?\s*(?:class|trait|object|case\s+class|case\s+object)\s+(\w+)/,
|
|
179
|
+
variable: /(?:val|var|lazy\s+val)\s+(\w+)\s*[:=]/,
|
|
180
|
+
import: /import\s+([\w.{},\s=>]+)/,
|
|
181
|
+
export: /^(?:object|class|trait)\s+(\w+)/,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
r: {
|
|
185
|
+
extensions: ['.r', '.R', '.rmd', '.Rmd'],
|
|
186
|
+
parser: 'r',
|
|
187
|
+
symbolPatterns: {
|
|
188
|
+
function: /(\w+)\s*<-\s*function\s*\(|^(\w+)\s*=\s*function\s*\(/,
|
|
189
|
+
class: /setClass\s*\(\s*['"](\w+)['"]/,
|
|
190
|
+
variable: /(\w+)\s*(?:<-|=)\s*(?!function)/,
|
|
191
|
+
import: /(?:library|require)\s*\(\s*['"]?(\w+)['"]?\s*\)/,
|
|
192
|
+
export: /^(\w+)\s*<-\s*function/, // R exports at top level
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
lua: {
|
|
196
|
+
extensions: ['.lua'],
|
|
197
|
+
parser: 'lua',
|
|
198
|
+
symbolPatterns: {
|
|
199
|
+
function: /(?:local\s+)?function\s+(?:[\w.]+[.:])?(\w+)\s*\(/,
|
|
200
|
+
class: /(\w+)\s*=\s*\{\s*\}|(\w+)\s*=\s*class\s*\(/,
|
|
201
|
+
variable: /(?:local\s+)?(\w+)\s*=/,
|
|
202
|
+
import: /require\s*\(?['"]([^'"]+)['"]\)?/,
|
|
203
|
+
export: /return\s+(\w+)|module\s*\(\s*['"]([^'"]+)['"]/,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
perl: {
|
|
207
|
+
extensions: ['.pl', '.pm', '.t', '.pod'],
|
|
208
|
+
parser: 'perl',
|
|
209
|
+
symbolPatterns: {
|
|
210
|
+
function: /sub\s+(\w+)\s*\{/,
|
|
211
|
+
class: /package\s+([\w:]+)\s*;/,
|
|
212
|
+
variable: /(?:my|our|local)\s*[\$@%](\w+)\s*=/,
|
|
213
|
+
import: /(?:use|require)\s+([\w:]+)/,
|
|
214
|
+
export: /^sub\s+(\w+)|our\s+[\$@%](\w+)/,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
objectivec: {
|
|
218
|
+
extensions: ['.m', '.mm', '.h'],
|
|
219
|
+
parser: 'objectivec',
|
|
220
|
+
symbolPatterns: {
|
|
221
|
+
function: /[-+]\s*\([^)]+\)\s*(\w+)(?::|;|\s*\{)/,
|
|
222
|
+
class: /@(?:interface|implementation|protocol)\s+(\w+)/,
|
|
223
|
+
variable: /@property\s+[^;]+\s+(\w+);|^[\w\s\*]+\s+(\w+)\s*[=;]/,
|
|
224
|
+
import: /#import\s+[<"]([^>"]+)[>"]/,
|
|
225
|
+
export: /@interface\s+(\w+)|@protocol\s+(\w+)/,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
haskell: {
|
|
229
|
+
extensions: ['.hs', '.lhs'],
|
|
230
|
+
parser: 'haskell',
|
|
231
|
+
symbolPatterns: {
|
|
232
|
+
function: /^(\w+)\s*::/,
|
|
233
|
+
class: /(?:class|instance)\s+(\w+)/,
|
|
234
|
+
variable: /^(\w+)\s*=/,
|
|
235
|
+
import: /import\s+(?:qualified\s+)?([\w.]+)/,
|
|
236
|
+
export: /module\s+[\w.]+\s*\(([^)]+)\)/,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
elixir: {
|
|
240
|
+
extensions: ['.ex', '.exs'],
|
|
241
|
+
parser: 'elixir',
|
|
242
|
+
symbolPatterns: {
|
|
243
|
+
function: /def(?:p|macro|macrop)?\s+(\w+)(?:\(|,|\s+do)/,
|
|
244
|
+
class: /defmodule\s+([\w.]+)\s+do/,
|
|
245
|
+
variable: /@(\w+)\s+|(\w+)\s*=\s*(?!fn)/,
|
|
246
|
+
import: /(?:import|alias|require|use)\s+([\w.]+)/,
|
|
247
|
+
export: /^def\s+(\w+)/,
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
clojure: {
|
|
251
|
+
extensions: ['.clj', '.cljs', '.cljc', '.edn'],
|
|
252
|
+
parser: 'clojure',
|
|
253
|
+
symbolPatterns: {
|
|
254
|
+
function: /\(defn-?\s+(\w+)/,
|
|
255
|
+
class: /\(defrecord\s+(\w+)|\(deftype\s+(\w+)|\(defprotocol\s+(\w+)/,
|
|
256
|
+
variable: /\(def\s+(\w+)/,
|
|
257
|
+
import: /\(:require\s+\[([^\]]+)\]/,
|
|
258
|
+
export: /\(defn-?\s+(\w+)/,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
fsharp: {
|
|
262
|
+
extensions: ['.fs', '.fsx', '.fsi'],
|
|
263
|
+
parser: 'fsharp',
|
|
264
|
+
symbolPatterns: {
|
|
265
|
+
function: /let\s+(?:rec\s+)?(\w+)(?:\s+\w+)*\s*=/,
|
|
266
|
+
class: /type\s+(\w+)\s*(?:=|<|\()/,
|
|
267
|
+
variable: /let\s+(?:mutable\s+)?(\w+)\s*=/,
|
|
268
|
+
import: /open\s+([\w.]+)/,
|
|
269
|
+
export: /^(?:let|type)\s+(\w+)/,
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
vbnet: {
|
|
273
|
+
extensions: ['.vb', '.vbs'],
|
|
274
|
+
parser: 'vbnet',
|
|
275
|
+
symbolPatterns: {
|
|
276
|
+
function: /(?:Public|Private|Protected|Friend)?\s*(?:Shared)?\s*(?:Function|Sub)\s+(\w+)/i,
|
|
277
|
+
class: /(?:Public|Private|Protected|Friend)?\s*(?:MustInherit|NotInheritable)?\s*Class\s+(\w+)/i,
|
|
278
|
+
variable: /(?:Public|Private|Protected|Friend|Dim|Const)?\s*(\w+)\s+As\s+/i,
|
|
279
|
+
import: /Imports\s+([\w.]+)/i,
|
|
280
|
+
export: /Public\s+(?:Class|Module|Function|Sub)\s+(\w+)/i,
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
matlab: {
|
|
284
|
+
extensions: ['.m', '.mlx'],
|
|
285
|
+
parser: 'matlab',
|
|
286
|
+
symbolPatterns: {
|
|
287
|
+
function: /function\s+(?:\[[^\]]+\]\s*=\s*|[\w,\s]+\s*=\s*)?(\w+)\s*\(/,
|
|
288
|
+
class: /classdef\s+(\w+)/,
|
|
289
|
+
variable: /(\w+)\s*=\s*(?!function)/,
|
|
290
|
+
import: /import\s+([\w.*]+)/,
|
|
291
|
+
export: /^function\s+(?:\[[^\]]+\]\s*=\s*)?(\w+)/,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
sql: {
|
|
295
|
+
extensions: ['.sql', '.ddl', '.dml'],
|
|
296
|
+
parser: 'sql',
|
|
297
|
+
symbolPatterns: {
|
|
298
|
+
function: /CREATE\s+(?:OR\s+REPLACE\s+)?(?:FUNCTION|PROCEDURE)\s+(\w+)/i,
|
|
299
|
+
class: /CREATE\s+(?:TABLE|VIEW)\s+(\w+)/i,
|
|
300
|
+
variable: /DECLARE\s+@?(\w+)/i,
|
|
301
|
+
import: /^$/, // SQL doesn't have imports
|
|
302
|
+
export: /^CREATE\s+(?:FUNCTION|PROCEDURE|VIEW)\s+(\w+)/i,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
html: {
|
|
306
|
+
extensions: ['.html', '.htm', '.xhtml'],
|
|
307
|
+
parser: 'html',
|
|
308
|
+
symbolPatterns: {
|
|
309
|
+
function: /<script[^>]*>[\s\S]*?function\s+(\w+)/,
|
|
310
|
+
class: /class\s*=\s*["']([^"']+)["']/,
|
|
311
|
+
variable: /id\s*=\s*["']([^"']+)["']/,
|
|
312
|
+
import: /<(?:link|script)[^>]+(?:href|src)\s*=\s*["']([^"']+)["']/,
|
|
313
|
+
export: /<(?:div|section|article|header|footer)[^>]+id\s*=\s*["']([^"']+)["']/,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
css: {
|
|
317
|
+
extensions: ['.css', '.scss', '.sass', '.less', '.styl'],
|
|
318
|
+
parser: 'css',
|
|
319
|
+
symbolPatterns: {
|
|
320
|
+
function: /@mixin\s+(\w+)|@function\s+(\w+)/,
|
|
321
|
+
class: /\.(\w+(?:-\w+)*)\s*\{/,
|
|
322
|
+
variable: /--(\w+(?:-\w+)*):|@(\w+):|(\$\w+):/,
|
|
323
|
+
import: /@import\s+(?:url\()?['"]([^'"]+)['"]/,
|
|
324
|
+
export: /@mixin\s+(\w+)|@function\s+(\w+)/,
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
vue: {
|
|
328
|
+
extensions: ['.vue'],
|
|
329
|
+
parser: 'vue',
|
|
330
|
+
symbolPatterns: {
|
|
331
|
+
function: /<script[^>]*>[\s\S]*?(?:export\s+default\s*\{[\s\S]*?)?(?:function|const|let|var)\s+(\w+)|methods\s*:\s*\{[\s\S]*?(\w+)\s*\(/,
|
|
332
|
+
class: /<template[^>]*>[\s\S]*?<(\w+)/,
|
|
333
|
+
variable: /<script[^>]*>[\s\S]*?(?:data\s*\(\s*\)\s*\{[\s\S]*?return\s*\{[\s\S]*?(\w+)|(?:const|let|var)\s+(\w+)\s*=)/,
|
|
334
|
+
import: /<script[^>]*>[\s\S]*?import\s+(?:{[^}]+}|\w+)\s+from\s+['"]([^'"]+)['"]/,
|
|
335
|
+
export: /<script[^>]*>[\s\S]*?export\s+default/,
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
svelte: {
|
|
339
|
+
extensions: ['.svelte'],
|
|
340
|
+
parser: 'svelte',
|
|
341
|
+
symbolPatterns: {
|
|
342
|
+
function: /<script[^>]*>[\s\S]*?(?:function|const|let|var)\s+(\w+)\s*[=(]/,
|
|
343
|
+
class: /<[\w-]+/,
|
|
344
|
+
variable: /<script[^>]*>[\s\S]*?(?:let|const|var)\s+(\w+)\s*=/,
|
|
345
|
+
import: /<script[^>]*>[\s\S]*?import\s+(?:{[^}]+}|\w+)\s+from\s+['"]([^'"]+)['"]/,
|
|
346
|
+
export: /<script[^>]*>[\s\S]*?export\s+(?:let|const|function)\s+(\w+)/,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
xml: {
|
|
350
|
+
extensions: ['.xml', '.xsd', '.xsl', '.xslt', '.svg'],
|
|
351
|
+
parser: 'xml',
|
|
352
|
+
symbolPatterns: {
|
|
353
|
+
function: /<xsl:template[^>]+name\s*=\s*["']([^"']+)["']/,
|
|
354
|
+
class: /<(?:xsd:)?(?:complexType|simpleType)[^>]+name\s*=\s*["']([^"']+)["']/,
|
|
355
|
+
variable: /<(?:xsd:)?element[^>]+name\s*=\s*["']([^"']+)["']/,
|
|
356
|
+
import: /<(?:xsd:)?import[^>]+schemaLocation\s*=\s*["']([^"']+)["']/,
|
|
357
|
+
export: /<(?:xsd:)?element[^>]+name\s*=\s*["']([^"']+)["']/,
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
yaml: {
|
|
361
|
+
extensions: ['.yaml', '.yml'],
|
|
362
|
+
parser: 'yaml',
|
|
363
|
+
symbolPatterns: {
|
|
364
|
+
function: /^(\w+):\s*\|/m,
|
|
365
|
+
class: /^(\w+):$/m,
|
|
366
|
+
variable: /^(\w+):\s*[^|>]/m,
|
|
367
|
+
import: /^$/, // YAML doesn't have imports
|
|
368
|
+
export: /^(\w+):$/m,
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
json: {
|
|
372
|
+
extensions: ['.json', '.jsonc', '.json5'],
|
|
373
|
+
parser: 'json',
|
|
374
|
+
symbolPatterns: {
|
|
375
|
+
function: /^$/,
|
|
376
|
+
class: /^$/,
|
|
377
|
+
variable: /"(\w+)"\s*:/,
|
|
378
|
+
import: /^$/,
|
|
379
|
+
export: /^$/,
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
toml: {
|
|
383
|
+
extensions: ['.toml'],
|
|
384
|
+
parser: 'toml',
|
|
385
|
+
symbolPatterns: {
|
|
386
|
+
function: /^$/,
|
|
387
|
+
class: /^\[(\w+(?:\.\w+)*)\]/,
|
|
388
|
+
variable: /^(\w+)\s*=/,
|
|
389
|
+
import: /^$/,
|
|
390
|
+
export: /^\[(\w+(?:\.\w+)*)\]/,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
markdown: {
|
|
394
|
+
extensions: ['.md', '.markdown', '.mdown', '.mkd'],
|
|
395
|
+
parser: 'markdown',
|
|
396
|
+
symbolPatterns: {
|
|
397
|
+
function: /```[\w]*\n[\s\S]*?function\s+(\w+)/,
|
|
398
|
+
class: /^#{1,6}\s+(.+)$/m,
|
|
399
|
+
variable: /\[([^\]]+)\]:/,
|
|
400
|
+
import: /\[([^\]]+)\]\(([^)]+)\)/,
|
|
401
|
+
export: /^#{1,6}\s+(.+)$/m,
|
|
83
402
|
},
|
|
84
403
|
},
|
|
85
404
|
};
|
|
@@ -88,6 +88,7 @@ const toolCategories = [
|
|
|
88
88
|
export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = false, agentId, }) {
|
|
89
89
|
const [agentName, setAgentName] = useState('');
|
|
90
90
|
const [description, setDescription] = useState('');
|
|
91
|
+
const [role, setRole] = useState('');
|
|
91
92
|
const [selectedTools, setSelectedTools] = useState(new Set());
|
|
92
93
|
const [currentField, setCurrentField] = useState('name');
|
|
93
94
|
const [selectedCategoryIndex, setSelectedCategoryIndex] = useState(0);
|
|
@@ -105,6 +106,7 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
105
106
|
if (agent) {
|
|
106
107
|
setAgentName(agent.name);
|
|
107
108
|
setDescription(agent.description);
|
|
109
|
+
setRole(agent.role || '');
|
|
108
110
|
setSelectedTools(new Set(agent.tools));
|
|
109
111
|
}
|
|
110
112
|
}
|
|
@@ -207,12 +209,13 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
207
209
|
updateSubAgent(agentId, {
|
|
208
210
|
name: agentName,
|
|
209
211
|
description: description,
|
|
212
|
+
role: role || undefined,
|
|
210
213
|
tools: Array.from(selectedTools),
|
|
211
214
|
});
|
|
212
215
|
}
|
|
213
216
|
else {
|
|
214
217
|
// Create new agent
|
|
215
|
-
createSubAgent(agentName, description, Array.from(selectedTools));
|
|
218
|
+
createSubAgent(agentName, description, Array.from(selectedTools), role || undefined);
|
|
216
219
|
}
|
|
217
220
|
setShowSuccess(true);
|
|
218
221
|
setTimeout(() => {
|
|
@@ -223,7 +226,15 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
223
226
|
catch (error) {
|
|
224
227
|
setSaveError(error instanceof Error ? error.message : 'Failed to save sub-agent');
|
|
225
228
|
}
|
|
226
|
-
}, [
|
|
229
|
+
}, [
|
|
230
|
+
agentName,
|
|
231
|
+
description,
|
|
232
|
+
role,
|
|
233
|
+
selectedTools,
|
|
234
|
+
onSave,
|
|
235
|
+
isEditMode,
|
|
236
|
+
agentId,
|
|
237
|
+
]);
|
|
227
238
|
useInput((rawInput, key) => {
|
|
228
239
|
const input = stripFocusArtifacts(rawInput);
|
|
229
240
|
// Ignore focus events completely
|
|
@@ -247,6 +258,10 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
247
258
|
setCurrentField('name');
|
|
248
259
|
return;
|
|
249
260
|
}
|
|
261
|
+
else if (currentField === 'role') {
|
|
262
|
+
setCurrentField('description');
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
250
265
|
else if (currentField === 'tools') {
|
|
251
266
|
// Navigate within tools
|
|
252
267
|
if (selectedToolIndex > 0) {
|
|
@@ -258,8 +273,8 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
258
273
|
setSelectedToolIndex(prevCategory ? prevCategory.tools.length - 1 : 0);
|
|
259
274
|
}
|
|
260
275
|
else {
|
|
261
|
-
// At top of tools, go to
|
|
262
|
-
setCurrentField('
|
|
276
|
+
// At top of tools, go to role
|
|
277
|
+
setCurrentField('role');
|
|
263
278
|
}
|
|
264
279
|
return;
|
|
265
280
|
}
|
|
@@ -270,6 +285,10 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
270
285
|
return;
|
|
271
286
|
}
|
|
272
287
|
else if (currentField === 'description') {
|
|
288
|
+
setCurrentField('role');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
else if (currentField === 'role') {
|
|
273
292
|
setCurrentField('tools');
|
|
274
293
|
setSelectedCategoryIndex(0);
|
|
275
294
|
setSelectedToolIndex(0);
|
|
@@ -387,6 +406,10 @@ export default function SubAgentConfigScreen({ onBack, onSave, inlineMode = fals
|
|
|
387
406
|
React.createElement(Text, { bold: true, color: currentField === 'description' ? 'green' : 'white' }, "Description:"),
|
|
388
407
|
React.createElement(Box, { marginLeft: 2 },
|
|
389
408
|
React.createElement(TextInput, { value: description, onChange: value => setDescription(stripFocusArtifacts(value)), placeholder: "Enter agent description...", focus: currentField === 'description' }))),
|
|
409
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
410
|
+
React.createElement(Text, { bold: true, color: currentField === 'role' ? 'green' : 'white' }, "Role (Optional):"),
|
|
411
|
+
React.createElement(Box, { marginLeft: 2 },
|
|
412
|
+
React.createElement(TextInput, { value: role, onChange: value => setRole(stripFocusArtifacts(value)), placeholder: "Specify agent role to guide output and focus...", focus: currentField === 'role' }))),
|
|
390
413
|
renderToolSelection(),
|
|
391
414
|
React.createElement(Box, { marginTop: 1 },
|
|
392
415
|
React.createElement(Text, { color: "gray", dimColor: true }, "\u2191\u2193: Navigate | \u2190\u2192: Switch category | Space: Toggle | A: Toggle all | Enter: Save | Esc: Back")))));
|
|
@@ -18,27 +18,30 @@ export function convertSessionMessagesToUI(sessionMessages) {
|
|
|
18
18
|
// Handle sub-agent internal tool call messages
|
|
19
19
|
if (msg.subAgentInternal && msg.role === 'assistant' && msg.tool_calls) {
|
|
20
20
|
for (const toolCall of msg.tool_calls) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
toolArgs
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
// 只有耗时工具才创建"进行中"消息
|
|
22
|
+
if (isToolNeedTwoStepDisplay(toolCall.function.name)) {
|
|
23
|
+
const toolDisplay = formatToolCallMessage(toolCall);
|
|
24
|
+
let toolArgs;
|
|
25
|
+
try {
|
|
26
|
+
toolArgs = JSON.parse(toolCall.function.arguments);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
toolArgs = {};
|
|
30
|
+
}
|
|
31
|
+
uiMessages.push({
|
|
32
|
+
role: 'subagent',
|
|
33
|
+
content: `\x1b[38;2;184;122;206m⚇⚡ ${toolDisplay.toolName}\x1b[0m`,
|
|
34
|
+
streaming: false,
|
|
35
|
+
toolCall: {
|
|
36
|
+
name: toolCall.function.name,
|
|
37
|
+
arguments: toolArgs,
|
|
38
|
+
},
|
|
39
|
+
toolDisplay,
|
|
40
|
+
toolCallId: toolCall.id,
|
|
41
|
+
toolPending: false,
|
|
42
|
+
subAgentInternal: true,
|
|
43
|
+
});
|
|
28
44
|
}
|
|
29
|
-
uiMessages.push({
|
|
30
|
-
role: 'subagent',
|
|
31
|
-
content: `\x1b[38;2;184;122;206m⚇⚡ ${toolDisplay.toolName}\x1b[0m`,
|
|
32
|
-
streaming: false,
|
|
33
|
-
toolCall: {
|
|
34
|
-
name: toolCall.function.name,
|
|
35
|
-
arguments: toolArgs,
|
|
36
|
-
},
|
|
37
|
-
toolDisplay,
|
|
38
|
-
toolCallId: toolCall.id,
|
|
39
|
-
toolPending: false,
|
|
40
|
-
subAgentInternal: true,
|
|
41
|
-
});
|
|
42
45
|
processedToolCalls.add(toolCall.id);
|
|
43
46
|
}
|
|
44
47
|
continue;
|
|
@@ -2,6 +2,7 @@ export interface SubAgent {
|
|
|
2
2
|
id: string;
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
+
role?: string;
|
|
5
6
|
tools: string[];
|
|
6
7
|
createdAt: string;
|
|
7
8
|
updatedAt: string;
|
|
@@ -20,13 +21,14 @@ export declare function getSubAgent(id: string): SubAgent | null;
|
|
|
20
21
|
/**
|
|
21
22
|
* Create a new sub-agent
|
|
22
23
|
*/
|
|
23
|
-
export declare function createSubAgent(name: string, description: string, tools: string[]): SubAgent;
|
|
24
|
+
export declare function createSubAgent(name: string, description: string, tools: string[], role?: string): SubAgent;
|
|
24
25
|
/**
|
|
25
26
|
* Update an existing sub-agent
|
|
26
27
|
*/
|
|
27
28
|
export declare function updateSubAgent(id: string, updates: {
|
|
28
29
|
name?: string;
|
|
29
30
|
description?: string;
|
|
31
|
+
role?: string;
|
|
30
32
|
tools?: string[];
|
|
31
33
|
}): SubAgent | null;
|
|
32
34
|
/**
|
|
@@ -53,13 +53,14 @@ function saveSubAgents(agents) {
|
|
|
53
53
|
/**
|
|
54
54
|
* Create a new sub-agent
|
|
55
55
|
*/
|
|
56
|
-
export function createSubAgent(name, description, tools) {
|
|
56
|
+
export function createSubAgent(name, description, tools, role) {
|
|
57
57
|
const agents = getSubAgents();
|
|
58
58
|
const now = new Date().toISOString();
|
|
59
59
|
const newAgent = {
|
|
60
60
|
id: generateId(),
|
|
61
61
|
name,
|
|
62
62
|
description,
|
|
63
|
+
role,
|
|
63
64
|
tools,
|
|
64
65
|
createdAt: now,
|
|
65
66
|
updatedAt: now,
|
|
@@ -85,6 +86,7 @@ export function updateSubAgent(id, updates) {
|
|
|
85
86
|
id: existingAgent.id,
|
|
86
87
|
name: updates.name ?? existingAgent.name,
|
|
87
88
|
description: updates.description ?? existingAgent.description,
|
|
89
|
+
role: updates.role ?? existingAgent.role,
|
|
88
90
|
tools: updates.tools ?? existingAgent.tools,
|
|
89
91
|
createdAt: existingAgent.createdAt,
|
|
90
92
|
updatedAt: new Date().toISOString(),
|
|
@@ -50,10 +50,15 @@ export async function executeSubAgent(agentId, prompt, onMessage, abortSignal, r
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
// Build conversation history for sub-agent
|
|
53
|
+
// Append role to prompt if configured
|
|
54
|
+
let finalPrompt = prompt;
|
|
55
|
+
if (agent.role) {
|
|
56
|
+
finalPrompt = `${prompt}\n\n${agent.role}`;
|
|
57
|
+
}
|
|
53
58
|
const messages = [
|
|
54
59
|
{
|
|
55
60
|
role: 'user',
|
|
56
|
-
content:
|
|
61
|
+
content: finalPrompt,
|
|
57
62
|
},
|
|
58
63
|
];
|
|
59
64
|
// Stream sub-agent execution
|
|
@@ -30,7 +30,14 @@ declare class VSCodeConnectionManager {
|
|
|
30
30
|
private editorContext;
|
|
31
31
|
private listeners;
|
|
32
32
|
private currentWorkingDirectory;
|
|
33
|
+
private connectingPromise;
|
|
34
|
+
private connectionTimeout;
|
|
35
|
+
private readonly CONNECTION_TIMEOUT;
|
|
33
36
|
start(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Clean up connection state and resources
|
|
39
|
+
*/
|
|
40
|
+
private cleanupConnection;
|
|
34
41
|
/**
|
|
35
42
|
* Normalize path for cross-platform compatibility
|
|
36
43
|
* - Converts Windows backslashes to forward slashes
|
|
@@ -89,15 +89,44 @@ class VSCodeConnectionManager {
|
|
|
89
89
|
writable: true,
|
|
90
90
|
value: process.cwd()
|
|
91
91
|
});
|
|
92
|
+
// Connection state management
|
|
93
|
+
Object.defineProperty(this, "connectingPromise", {
|
|
94
|
+
enumerable: true,
|
|
95
|
+
configurable: true,
|
|
96
|
+
writable: true,
|
|
97
|
+
value: null
|
|
98
|
+
});
|
|
99
|
+
Object.defineProperty(this, "connectionTimeout", {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
configurable: true,
|
|
102
|
+
writable: true,
|
|
103
|
+
value: null
|
|
104
|
+
});
|
|
105
|
+
Object.defineProperty(this, "CONNECTION_TIMEOUT", {
|
|
106
|
+
enumerable: true,
|
|
107
|
+
configurable: true,
|
|
108
|
+
writable: true,
|
|
109
|
+
value: 10000
|
|
110
|
+
}); // 10 seconds timeout for initial connection
|
|
92
111
|
}
|
|
93
112
|
async start() {
|
|
94
113
|
// If already connected, just return success
|
|
95
114
|
if (this.client?.readyState === WebSocket.OPEN) {
|
|
96
115
|
return Promise.resolve();
|
|
97
116
|
}
|
|
117
|
+
// If already connecting, return the existing promise to avoid duplicate connections
|
|
118
|
+
if (this.connectingPromise) {
|
|
119
|
+
return this.connectingPromise;
|
|
120
|
+
}
|
|
98
121
|
// Try to find the correct port for this workspace
|
|
99
122
|
const targetPort = this.findPortForWorkspace();
|
|
100
|
-
|
|
123
|
+
// Create a new connection promise and store it
|
|
124
|
+
this.connectingPromise = new Promise((resolve, reject) => {
|
|
125
|
+
// Set connection timeout
|
|
126
|
+
this.connectionTimeout = setTimeout(() => {
|
|
127
|
+
this.cleanupConnection();
|
|
128
|
+
reject(new Error('Connection timeout after 10 seconds'));
|
|
129
|
+
}, this.CONNECTION_TIMEOUT);
|
|
101
130
|
const tryConnect = (port) => {
|
|
102
131
|
// Check both VSCode and JetBrains port ranges
|
|
103
132
|
if (port > this.VSCODE_MAX_PORT && port < this.JETBRAINS_BASE_PORT) {
|
|
@@ -106,6 +135,7 @@ class VSCodeConnectionManager {
|
|
|
106
135
|
return;
|
|
107
136
|
}
|
|
108
137
|
if (port > this.JETBRAINS_MAX_PORT) {
|
|
138
|
+
this.cleanupConnection();
|
|
109
139
|
reject(new Error(`Failed to connect: no IDE server found on ports ${this.VSCODE_BASE_PORT}-${this.VSCODE_MAX_PORT} or ${this.JETBRAINS_BASE_PORT}-${this.JETBRAINS_MAX_PORT}`));
|
|
110
140
|
return;
|
|
111
141
|
}
|
|
@@ -115,6 +145,12 @@ class VSCodeConnectionManager {
|
|
|
115
145
|
// Reset reconnect attempts on successful connection
|
|
116
146
|
this.reconnectAttempts = 0;
|
|
117
147
|
this.port = port;
|
|
148
|
+
// Clear connection state
|
|
149
|
+
if (this.connectionTimeout) {
|
|
150
|
+
clearTimeout(this.connectionTimeout);
|
|
151
|
+
this.connectionTimeout = null;
|
|
152
|
+
}
|
|
153
|
+
this.connectingPromise = null;
|
|
118
154
|
resolve();
|
|
119
155
|
});
|
|
120
156
|
this.client.on('message', message => {
|
|
@@ -148,6 +184,34 @@ class VSCodeConnectionManager {
|
|
|
148
184
|
};
|
|
149
185
|
tryConnect(targetPort);
|
|
150
186
|
});
|
|
187
|
+
// Return the promise and clean up state when it completes or fails
|
|
188
|
+
return this.connectingPromise.finally(() => {
|
|
189
|
+
this.connectingPromise = null;
|
|
190
|
+
if (this.connectionTimeout) {
|
|
191
|
+
clearTimeout(this.connectionTimeout);
|
|
192
|
+
this.connectionTimeout = null;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Clean up connection state and resources
|
|
198
|
+
*/
|
|
199
|
+
cleanupConnection() {
|
|
200
|
+
this.connectingPromise = null;
|
|
201
|
+
if (this.connectionTimeout) {
|
|
202
|
+
clearTimeout(this.connectionTimeout);
|
|
203
|
+
this.connectionTimeout = null;
|
|
204
|
+
}
|
|
205
|
+
if (this.client) {
|
|
206
|
+
try {
|
|
207
|
+
this.client.removeAllListeners();
|
|
208
|
+
this.client.close();
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
// Ignore errors during cleanup
|
|
212
|
+
}
|
|
213
|
+
this.client = null;
|
|
214
|
+
}
|
|
151
215
|
}
|
|
152
216
|
/**
|
|
153
217
|
* Normalize path for cross-platform compatibility
|
|
@@ -228,8 +292,21 @@ class VSCodeConnectionManager {
|
|
|
228
292
|
clearTimeout(this.reconnectTimer);
|
|
229
293
|
this.reconnectTimer = null;
|
|
230
294
|
}
|
|
295
|
+
// Clear connection timeout
|
|
296
|
+
if (this.connectionTimeout) {
|
|
297
|
+
clearTimeout(this.connectionTimeout);
|
|
298
|
+
this.connectionTimeout = null;
|
|
299
|
+
}
|
|
300
|
+
// Clear connecting promise
|
|
301
|
+
this.connectingPromise = null;
|
|
231
302
|
if (this.client) {
|
|
232
|
-
|
|
303
|
+
try {
|
|
304
|
+
this.client.removeAllListeners();
|
|
305
|
+
this.client.close();
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
// Ignore errors during cleanup
|
|
309
|
+
}
|
|
233
310
|
this.client = null;
|
|
234
311
|
}
|
|
235
312
|
this.reconnectAttempts = 0;
|