snow-ai 0.3.28 โ 0.3.30
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/api/systemPrompt.js +11 -0
- package/dist/hooks/useCommandHandler.js +3 -8
- package/dist/hooks/useConversation.js +17 -10
- package/dist/hooks/useVSCodeState.js +4 -3
- package/dist/mcp/filesystem.d.ts +17 -0
- package/dist/mcp/filesystem.js +138 -2
- package/dist/mcp/notebook.d.ts +10 -0
- package/dist/mcp/notebook.js +161 -0
- package/dist/mcp/utils/aceCodeSearch/language.utils.js +354 -35
- package/dist/ui/pages/SubAgentConfigScreen.js +27 -4
- package/dist/utils/commands/ide.js +2 -0
- package/dist/utils/mcpToolsManager.js +31 -0
- package/dist/utils/notebookManager.d.ts +52 -0
- package/dist/utils/notebookManager.js +181 -0
- 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 +108 -11
- package/package.json +1 -1
package/dist/api/systemPrompt.js
CHANGED
|
@@ -72,6 +72,7 @@ const SYSTEM_PROMPT_TEMPLATE = `You are Snow AI CLI, an intelligent command-line
|
|
|
72
72
|
2. **ACTION FIRST**: Write code immediately when task is clear - stop overthinking
|
|
73
73
|
3. **Smart Context**: Read what's needed for correctness, skip excessive exploration
|
|
74
74
|
4. **Quality Verification**: run build/test after changes
|
|
75
|
+
5. **NO Documentation Files**: โ NEVER create summary .md files after tasks - use \`notebook-add\` for important notes instead
|
|
75
76
|
|
|
76
77
|
## ๐ Execution Strategy - BALANCE ACTION & ANALYSIS
|
|
77
78
|
|
|
@@ -166,6 +167,16 @@ const SYSTEM_PROMPT_TEMPLATE = `You are Snow AI CLI, an intelligent command-line
|
|
|
166
167
|
- Requires IDE plugin installed and running
|
|
167
168
|
- Use AFTER code changes to verify quality
|
|
168
169
|
|
|
170
|
+
**Notebook (Code Memory):**
|
|
171
|
+
- \`notebook-add\` - Record fragile code that new features might break during iteration
|
|
172
|
+
- ๐ฏ Core purpose: Prevent new functionality from breaking old functionality
|
|
173
|
+
- ๐ Record: Bugs that recurred, fragile dependencies, critical constraints
|
|
174
|
+
- โ ๏ธ Examples: "validateInput() must run first - broke twice", "null return required by X"
|
|
175
|
+
- ๐ **IMPORTANT**: Use notebook for documentation, NOT separate .md files
|
|
176
|
+
- \`notebook-query\` - Manual search (rarely needed, auto-shown when reading files)
|
|
177
|
+
- ๐ Auto-attached: Last 10 notebooks appear when reading ANY file
|
|
178
|
+
- ๐ก Use before: Adding features that might affect existing behavior
|
|
179
|
+
|
|
169
180
|
**Web Search:**
|
|
170
181
|
- \`websearch-search\` - Search web for latest docs/solutions
|
|
171
182
|
- \`websearch-fetch\` - Read web page content (always provide userQuery)
|
|
@@ -154,14 +154,9 @@ export function useCommandHandler(options) {
|
|
|
154
154
|
// Handle /ide command
|
|
155
155
|
if (commandName === 'ide') {
|
|
156
156
|
if (result.success) {
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
options.setVscodeConnectionStatus('connected');
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
options.setVscodeConnectionStatus('connecting');
|
|
164
|
-
}
|
|
157
|
+
// Connection successful, set status to connected immediately
|
|
158
|
+
// The่ฝฎ่ฏข mechanism will also update the status, but we do it here for immediate feedback
|
|
159
|
+
options.setVscodeConnectionStatus('connected');
|
|
165
160
|
// Don't add command message to keep UI clean
|
|
166
161
|
}
|
|
167
162
|
else {
|
|
@@ -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
|
}
|
|
@@ -23,7 +23,7 @@ export function useVSCodeState() {
|
|
|
23
23
|
lastStatusRef.current = 'disconnected';
|
|
24
24
|
setVscodeConnectionStatus('disconnected');
|
|
25
25
|
}
|
|
26
|
-
}, 1000);
|
|
26
|
+
}, 1000); // Check every second
|
|
27
27
|
const unsubscribe = vscodeConnection.onContextUpdate(context => {
|
|
28
28
|
// Only update state if context has actually changed
|
|
29
29
|
const hasChanged = context.activeFile !== lastEditorContextRef.current.activeFile ||
|
|
@@ -51,7 +51,7 @@ export function useVSCodeState() {
|
|
|
51
51
|
if (vscodeConnectionStatus !== 'connecting') {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
-
// Set timeout for connecting state (
|
|
54
|
+
// Set timeout for connecting state (15 seconds to allow for port scanning and connection)
|
|
55
55
|
const connectingTimeout = setTimeout(() => {
|
|
56
56
|
const isConnected = vscodeConnection.isConnected();
|
|
57
57
|
const isClientRunning = vscodeConnection.isClientRunning();
|
|
@@ -65,8 +65,9 @@ export function useVSCodeState() {
|
|
|
65
65
|
// Client not running - go back to disconnected
|
|
66
66
|
setVscodeConnectionStatus('disconnected');
|
|
67
67
|
}
|
|
68
|
+
lastStatusRef.current = isClientRunning ? 'error' : 'disconnected';
|
|
68
69
|
}
|
|
69
|
-
},
|
|
70
|
+
}, 15000); // 15 seconds: 10s for connection timeout + 5s buffer
|
|
70
71
|
return () => {
|
|
71
72
|
clearTimeout(connectingTimeout);
|
|
72
73
|
};
|
package/dist/mcp/filesystem.d.ts
CHANGED
|
@@ -10,8 +10,25 @@ 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;
|
|
23
|
+
/**
|
|
24
|
+
* Get notebook entries for a file
|
|
25
|
+
* @param filePath - Path to the file
|
|
26
|
+
* @returns Formatted notebook entries string, or empty if none found
|
|
27
|
+
*/
|
|
28
|
+
private getNotebookEntries;
|
|
13
29
|
/**
|
|
14
30
|
* Get the content of a file with optional line range
|
|
31
|
+
* Enhanced with symbol information for better AI context
|
|
15
32
|
* @param filePath - Path to the file (relative to base path or absolute) or array of file paths or array of file config objects
|
|
16
33
|
* @param startLine - Starting line number (1-indexed, inclusive, optional - defaults to 1). Used for single file or as default for array of strings
|
|
17
34
|
* @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,10 @@ 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';
|
|
16
|
+
// Notebook utilities for automatic note retrieval
|
|
17
|
+
import { queryNotebook } from '../utils/notebookManager.js';
|
|
14
18
|
const { resolve, dirname, isAbsolute } = path;
|
|
15
19
|
const execAsync = promisify(exec);
|
|
16
20
|
/**
|
|
@@ -52,8 +56,107 @@ export class FilesystemMCPService {
|
|
|
52
56
|
});
|
|
53
57
|
this.basePath = resolve(basePath);
|
|
54
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract relevant symbol information for a specific line range
|
|
61
|
+
* This provides context that helps AI make more accurate modifications
|
|
62
|
+
* @param symbols - All symbols in the file
|
|
63
|
+
* @param startLine - Start line of the range
|
|
64
|
+
* @param endLine - End line of the range
|
|
65
|
+
* @param _totalLines - Total lines in the file (reserved for future use)
|
|
66
|
+
* @returns Formatted string with relevant symbol information
|
|
67
|
+
*/
|
|
68
|
+
extractRelevantSymbols(symbols, startLine, endLine, _totalLines) {
|
|
69
|
+
if (symbols.length === 0) {
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
// Categorize symbols
|
|
73
|
+
const imports = symbols.filter(s => s.type === 'import');
|
|
74
|
+
const exports = symbols.filter(s => s.type === 'export');
|
|
75
|
+
// Symbols within the requested range
|
|
76
|
+
const symbolsInRange = symbols.filter(s => s.line >= startLine && s.line <= endLine);
|
|
77
|
+
// Symbols defined before the range that might be referenced
|
|
78
|
+
const symbolsBeforeRange = symbols.filter(s => s.line < startLine);
|
|
79
|
+
// Build context information
|
|
80
|
+
const parts = [];
|
|
81
|
+
// Always include imports (crucial for understanding dependencies)
|
|
82
|
+
if (imports.length > 0) {
|
|
83
|
+
const importList = imports
|
|
84
|
+
.slice(0, 10) // Limit to avoid excessive tokens
|
|
85
|
+
.map(s => ` โข ${s.name} (line ${s.line})`)
|
|
86
|
+
.join('\n');
|
|
87
|
+
parts.push(`๐ฆ Imports:\n${importList}`);
|
|
88
|
+
}
|
|
89
|
+
// Symbols defined in the current range
|
|
90
|
+
if (symbolsInRange.length > 0) {
|
|
91
|
+
const rangeSymbols = symbolsInRange
|
|
92
|
+
.slice(0, 15)
|
|
93
|
+
.map(s => ` โข ${s.type}: ${s.name} (line ${s.line})${s.signature ? ` - ${s.signature.slice(0, 60)}` : ''}`)
|
|
94
|
+
.join('\n');
|
|
95
|
+
parts.push(`๐ฏ Symbols in this range:\n${rangeSymbols}`);
|
|
96
|
+
}
|
|
97
|
+
// Key definitions before this range (that might be referenced)
|
|
98
|
+
if (symbolsBeforeRange.length > 0 && startLine > 1) {
|
|
99
|
+
const relevantBefore = symbolsBeforeRange
|
|
100
|
+
.filter(s => s.type === 'function' || s.type === 'class')
|
|
101
|
+
.slice(-5) // Last 5 before the range
|
|
102
|
+
.map(s => ` โข ${s.type}: ${s.name} (line ${s.line})`)
|
|
103
|
+
.join('\n');
|
|
104
|
+
if (relevantBefore) {
|
|
105
|
+
parts.push(`โฌ๏ธ Key definitions above:\n${relevantBefore}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Exports (important for understanding module interface)
|
|
109
|
+
if (exports.length > 0) {
|
|
110
|
+
const exportList = exports
|
|
111
|
+
.slice(0, 10)
|
|
112
|
+
.map(s => ` โข ${s.name} (line ${s.line})`)
|
|
113
|
+
.join('\n');
|
|
114
|
+
parts.push(`๐ค Exports:\n${exportList}`);
|
|
115
|
+
}
|
|
116
|
+
if (parts.length === 0) {
|
|
117
|
+
return '';
|
|
118
|
+
}
|
|
119
|
+
return ('\n\n' +
|
|
120
|
+
'='.repeat(60) +
|
|
121
|
+
'\n๐ SYMBOL INDEX & DEFINITIONS:\n' +
|
|
122
|
+
'='.repeat(60) +
|
|
123
|
+
'\n' +
|
|
124
|
+
parts.join('\n\n'));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get notebook entries for a file
|
|
128
|
+
* @param filePath - Path to the file
|
|
129
|
+
* @returns Formatted notebook entries string, or empty if none found
|
|
130
|
+
*/
|
|
131
|
+
getNotebookEntries(filePath) {
|
|
132
|
+
try {
|
|
133
|
+
const entries = queryNotebook(filePath, 10);
|
|
134
|
+
if (entries.length === 0) {
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
const notesText = entries
|
|
138
|
+
.map((entry, index) => {
|
|
139
|
+
// createdAt ๅทฒ็ปๆฏๆฌๅฐๆถ้ดๆ ผๅผ: "YYYY-MM-DDTHH:mm:ss.SSS"
|
|
140
|
+
// ๆๅๆฅๆๅๆถ้ด้จๅ: "YYYY-MM-DD HH:mm"
|
|
141
|
+
const dateStr = entry.createdAt.substring(0, 16).replace('T', ' ');
|
|
142
|
+
return ` ${index + 1}. [${dateStr}] ${entry.note}`;
|
|
143
|
+
})
|
|
144
|
+
.join('\n');
|
|
145
|
+
return ('\n\n' +
|
|
146
|
+
'='.repeat(60) +
|
|
147
|
+
'\n๐ CODE NOTEBOOKS (Latest 10):\n' +
|
|
148
|
+
'='.repeat(60) +
|
|
149
|
+
'\n' +
|
|
150
|
+
notesText);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Silently fail notebook retrieval - don't block file reading
|
|
154
|
+
return '';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
55
157
|
/**
|
|
56
158
|
* Get the content of a file with optional line range
|
|
159
|
+
* Enhanced with symbol information for better AI context
|
|
57
160
|
* @param filePath - Path to the file (relative to base path or absolute) or array of file paths or array of file config objects
|
|
58
161
|
* @param startLine - Starting line number (1-indexed, inclusive, optional - defaults to 1). Used for single file or as default for array of strings
|
|
59
162
|
* @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 +230,23 @@ export class FilesystemMCPService {
|
|
|
127
230
|
const lineNum = start + index;
|
|
128
231
|
return `${lineNum}โ${line}`;
|
|
129
232
|
});
|
|
130
|
-
|
|
233
|
+
let fileContent = `๐ ${file} (lines ${start}-${end}/${totalLines})\n${numberedLines.join('\n')}`;
|
|
234
|
+
// Parse and append symbol information
|
|
235
|
+
try {
|
|
236
|
+
const symbols = await parseFileSymbols(fullPath, content, this.basePath);
|
|
237
|
+
const symbolInfo = this.extractRelevantSymbols(symbols, start, end, totalLines);
|
|
238
|
+
if (symbolInfo) {
|
|
239
|
+
fileContent += symbolInfo;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Silently fail symbol parsing
|
|
244
|
+
}
|
|
245
|
+
// Append notebook entries
|
|
246
|
+
const notebookInfo = this.getNotebookEntries(file);
|
|
247
|
+
if (notebookInfo) {
|
|
248
|
+
fileContent += notebookInfo;
|
|
249
|
+
}
|
|
131
250
|
allContents.push(fileContent);
|
|
132
251
|
filesData.push({
|
|
133
252
|
path: file,
|
|
@@ -197,7 +316,24 @@ export class FilesystemMCPService {
|
|
|
197
316
|
const lineNum = start + index;
|
|
198
317
|
return `${lineNum}โ${line}`;
|
|
199
318
|
});
|
|
200
|
-
|
|
319
|
+
let partialContent = numberedLines.join('\n');
|
|
320
|
+
// Parse and append symbol information to provide better context for AI
|
|
321
|
+
try {
|
|
322
|
+
const symbols = await parseFileSymbols(fullPath, content, this.basePath);
|
|
323
|
+
const symbolInfo = this.extractRelevantSymbols(symbols, start, end, totalLines);
|
|
324
|
+
if (symbolInfo) {
|
|
325
|
+
partialContent += symbolInfo;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
// Silently fail symbol parsing - don't block file reading
|
|
330
|
+
// This is optional context enhancement, not critical
|
|
331
|
+
}
|
|
332
|
+
// Append notebook entries
|
|
333
|
+
const notebookInfo = this.getNotebookEntries(filePath);
|
|
334
|
+
if (notebookInfo) {
|
|
335
|
+
partialContent += notebookInfo;
|
|
336
|
+
}
|
|
201
337
|
return {
|
|
202
338
|
content: partialContent,
|
|
203
339
|
startLine: start,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Tool, type CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Notebook MCP ๅทฅๅ
ทๅฎไน
|
|
4
|
+
* ็จไบไปฃ็ ๅคๅฟๅฝ็ฎก็๏ผๅธฎๅฉAI่ฎฐๅฝ้่ฆ็ไปฃ็ ๆณจๆไบ้กน
|
|
5
|
+
*/
|
|
6
|
+
export declare const mcpTools: Tool[];
|
|
7
|
+
/**
|
|
8
|
+
* ๆง่ก Notebook ๅทฅๅ
ท
|
|
9
|
+
*/
|
|
10
|
+
export declare function executeNotebookTool(toolName: string, args: any): Promise<CallToolResult>;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { addNotebook, queryNotebook } from '../utils/notebookManager.js';
|
|
2
|
+
/**
|
|
3
|
+
* Notebook MCP ๅทฅๅ
ทๅฎไน
|
|
4
|
+
* ็จไบไปฃ็ ๅคๅฟๅฝ็ฎก็๏ผๅธฎๅฉAI่ฎฐๅฝ้่ฆ็ไปฃ็ ๆณจๆไบ้กน
|
|
5
|
+
*/
|
|
6
|
+
export const mcpTools = [
|
|
7
|
+
{
|
|
8
|
+
name: 'notebook-add',
|
|
9
|
+
description: `๐ Record code parts that are fragile and easily broken during iteration.
|
|
10
|
+
|
|
11
|
+
**Core Purpose:** Prevent new features from breaking existing functionality.
|
|
12
|
+
|
|
13
|
+
**When to record:**
|
|
14
|
+
- After fixing bugs that could easily reoccur
|
|
15
|
+
- Fragile code that new features might break
|
|
16
|
+
- Non-obvious dependencies between components
|
|
17
|
+
- Workarounds that shouldn't be "optimized away"
|
|
18
|
+
|
|
19
|
+
**Examples:**
|
|
20
|
+
- "โ ๏ธ validateInput() MUST be called first - new features broke this twice"
|
|
21
|
+
- "Component X depends on null return - DO NOT change to empty array"
|
|
22
|
+
- "setTimeout workaround for race condition - don't remove"
|
|
23
|
+
- "Parser expects exact format - adding fields breaks backward compat"`,
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
filePath: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'File path (relative or absolute). Example: "src/utils/parser.ts"',
|
|
30
|
+
},
|
|
31
|
+
note: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Brief, specific note. Focus on risks/constraints, NOT what code does.',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ['filePath', 'note'],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'notebook-query',
|
|
41
|
+
description: `๐ Search notebook entries by file path pattern.
|
|
42
|
+
|
|
43
|
+
**Auto-triggered:** When reading files, last 10 notebooks are automatically shown.
|
|
44
|
+
**Manual use:** Query specific patterns or see more entries.`,
|
|
45
|
+
inputSchema: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
filePathPattern: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'Fuzzy search pattern (e.g., "parser"). Empty = all entries.',
|
|
51
|
+
default: '',
|
|
52
|
+
},
|
|
53
|
+
topN: {
|
|
54
|
+
type: 'number',
|
|
55
|
+
description: 'Max results to return (default: 10, max: 50)',
|
|
56
|
+
default: 10,
|
|
57
|
+
minimum: 1,
|
|
58
|
+
maximum: 50,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
/**
|
|
65
|
+
* ๆง่ก Notebook ๅทฅๅ
ท
|
|
66
|
+
*/
|
|
67
|
+
export async function executeNotebookTool(toolName, args) {
|
|
68
|
+
try {
|
|
69
|
+
switch (toolName) {
|
|
70
|
+
case 'notebook-add': {
|
|
71
|
+
const { filePath, note } = args;
|
|
72
|
+
if (!filePath || !note) {
|
|
73
|
+
return {
|
|
74
|
+
content: [
|
|
75
|
+
{
|
|
76
|
+
type: 'text',
|
|
77
|
+
text: 'Error: Both filePath and note are required',
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
isError: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const entry = addNotebook(filePath, note);
|
|
84
|
+
return {
|
|
85
|
+
content: [
|
|
86
|
+
{
|
|
87
|
+
type: 'text',
|
|
88
|
+
text: JSON.stringify({
|
|
89
|
+
success: true,
|
|
90
|
+
message: `Notebook entry added for: ${entry.filePath}`,
|
|
91
|
+
entry: {
|
|
92
|
+
id: entry.id,
|
|
93
|
+
filePath: entry.filePath,
|
|
94
|
+
note: entry.note,
|
|
95
|
+
createdAt: entry.createdAt,
|
|
96
|
+
},
|
|
97
|
+
}, null, 2),
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
case 'notebook-query': {
|
|
103
|
+
const { filePathPattern = '', topN = 10 } = args;
|
|
104
|
+
const results = queryNotebook(filePathPattern, topN);
|
|
105
|
+
if (results.length === 0) {
|
|
106
|
+
return {
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: 'text',
|
|
110
|
+
text: JSON.stringify({
|
|
111
|
+
message: 'No notebook entries found',
|
|
112
|
+
pattern: filePathPattern || '(all)',
|
|
113
|
+
totalResults: 0,
|
|
114
|
+
}, null, 2),
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: 'text',
|
|
123
|
+
text: JSON.stringify({
|
|
124
|
+
message: `Found ${results.length} notebook entries`,
|
|
125
|
+
pattern: filePathPattern || '(all)',
|
|
126
|
+
totalResults: results.length,
|
|
127
|
+
entries: results.map(entry => ({
|
|
128
|
+
id: entry.id,
|
|
129
|
+
filePath: entry.filePath,
|
|
130
|
+
note: entry.note,
|
|
131
|
+
createdAt: entry.createdAt,
|
|
132
|
+
})),
|
|
133
|
+
}, null, 2),
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
return {
|
|
140
|
+
content: [
|
|
141
|
+
{
|
|
142
|
+
type: 'text',
|
|
143
|
+
text: `Unknown notebook tool: ${toolName}`,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
isError: true,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
return {
|
|
152
|
+
content: [
|
|
153
|
+
{
|
|
154
|
+
type: 'text',
|
|
155
|
+
text: `Error executing notebook tool: ${error instanceof Error ? error.message : String(error)}`,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
isError: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|