snow-ai 0.2.12 → 0.2.13
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/anthropic.js +15 -4
- package/dist/api/systemPrompt.d.ts +1 -1
- package/dist/api/systemPrompt.js +149 -40
- package/dist/hooks/useConversation.d.ts +1 -1
- package/dist/hooks/useConversation.js +5 -3
- package/dist/hooks/useGlobalNavigation.js +2 -0
- package/dist/hooks/useToolConfirmation.d.ts +2 -1
- package/dist/hooks/useToolConfirmation.js +2 -1
- package/dist/mcp/filesystem.d.ts +16 -1
- package/dist/mcp/filesystem.js +193 -89
- package/dist/mcp/multiLanguageASTParser.d.ts +67 -0
- package/dist/mcp/multiLanguageASTParser.js +360 -0
- package/dist/mcp/todo.d.ts +1 -1
- package/dist/mcp/todo.js +21 -26
- package/dist/ui/components/ChatInput.d.ts +1 -1
- package/dist/ui/components/ChatInput.js +84 -45
- package/dist/ui/components/DiffViewer.d.ts +1 -2
- package/dist/ui/components/DiffViewer.js +65 -65
- package/dist/ui/components/MCPInfoPanel.js +1 -2
- package/dist/ui/components/TodoTree.js +1 -1
- package/dist/ui/components/ToolConfirmation.d.ts +11 -1
- package/dist/ui/components/ToolConfirmation.js +86 -6
- package/dist/ui/pages/ChatScreen.js +223 -111
- package/dist/utils/commands/ide.js +18 -1
- package/dist/utils/mcpToolsManager.d.ts +1 -1
- package/dist/utils/mcpToolsManager.js +45 -36
- package/dist/utils/textBuffer.d.ts +5 -0
- package/dist/utils/textBuffer.js +23 -2
- package/dist/utils/vscodeConnection.js +10 -1
- package/package.json +13 -1
- package/readme.md +12 -2
package/dist/mcp/filesystem.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
3
4
|
import { vscodeConnection } from '../utils/vscodeConnection.js';
|
|
4
5
|
import { incrementalSnapshotManager } from '../utils/incrementalSnapshot.js';
|
|
6
|
+
import { multiLanguageASTParser } from './multiLanguageASTParser.js';
|
|
5
7
|
const { resolve, dirname, isAbsolute } = path;
|
|
6
8
|
/**
|
|
7
9
|
* Filesystem MCP Service
|
|
@@ -26,9 +28,9 @@ export class FilesystemMCPService {
|
|
|
26
28
|
bracketBalance: {
|
|
27
29
|
curly: { open: 0, close: 0, balanced: true },
|
|
28
30
|
round: { open: 0, close: 0, balanced: true },
|
|
29
|
-
square: { open: 0, close: 0, balanced: true }
|
|
31
|
+
square: { open: 0, close: 0, balanced: true },
|
|
30
32
|
},
|
|
31
|
-
indentationWarnings: []
|
|
33
|
+
indentationWarnings: [],
|
|
32
34
|
};
|
|
33
35
|
// Count brackets in the edited content
|
|
34
36
|
const editedContent = editedLines.join('\n');
|
|
@@ -41,15 +43,18 @@ export class FilesystemMCPService {
|
|
|
41
43
|
analysis.bracketBalance.curly.open = (cleanContent.match(/\{/g) || []).length;
|
|
42
44
|
analysis.bracketBalance.curly.close = (cleanContent.match(/\}/g) || []).length;
|
|
43
45
|
analysis.bracketBalance.curly.balanced =
|
|
44
|
-
analysis.bracketBalance.curly.open ===
|
|
46
|
+
analysis.bracketBalance.curly.open ===
|
|
47
|
+
analysis.bracketBalance.curly.close;
|
|
45
48
|
analysis.bracketBalance.round.open = (cleanContent.match(/\(/g) || []).length;
|
|
46
49
|
analysis.bracketBalance.round.close = (cleanContent.match(/\)/g) || []).length;
|
|
47
50
|
analysis.bracketBalance.round.balanced =
|
|
48
|
-
analysis.bracketBalance.round.open ===
|
|
51
|
+
analysis.bracketBalance.round.open ===
|
|
52
|
+
analysis.bracketBalance.round.close;
|
|
49
53
|
analysis.bracketBalance.square.open = (cleanContent.match(/\[/g) || []).length;
|
|
50
54
|
analysis.bracketBalance.square.close = (cleanContent.match(/\]/g) || []).length;
|
|
51
55
|
analysis.bracketBalance.square.balanced =
|
|
52
|
-
analysis.bracketBalance.square.open ===
|
|
56
|
+
analysis.bracketBalance.square.open ===
|
|
57
|
+
analysis.bracketBalance.square.close;
|
|
53
58
|
// HTML/JSX tag analysis (for .html, .jsx, .tsx, .vue files)
|
|
54
59
|
const isMarkupFile = /\.(html|jsx|tsx|vue)$/i.test(filePath);
|
|
55
60
|
if (isMarkupFile) {
|
|
@@ -82,7 +87,7 @@ export class FilesystemMCPService {
|
|
|
82
87
|
analysis.htmlTags = {
|
|
83
88
|
unclosedTags,
|
|
84
89
|
unopenedTags,
|
|
85
|
-
balanced: unclosedTags.length === 0 && unopenedTags.length === 0
|
|
90
|
+
balanced: unclosedTags.length === 0 && unopenedTags.length === 0,
|
|
86
91
|
};
|
|
87
92
|
}
|
|
88
93
|
// Check indentation consistency
|
|
@@ -123,14 +128,18 @@ export class FilesystemMCPService {
|
|
|
123
128
|
// Check if edit is at a code block boundary
|
|
124
129
|
const lastLine = editedLines[editedLines.length - 1]?.trim() || '';
|
|
125
130
|
const firstLine = editedLines[0]?.trim() || '';
|
|
126
|
-
const endsWithOpenBrace = lastLine.endsWith('{') ||
|
|
127
|
-
|
|
131
|
+
const endsWithOpenBrace = lastLine.endsWith('{') ||
|
|
132
|
+
lastLine.endsWith('(') ||
|
|
133
|
+
lastLine.endsWith('[');
|
|
134
|
+
const startsWithCloseBrace = firstLine.startsWith('}') ||
|
|
135
|
+
firstLine.startsWith(')') ||
|
|
136
|
+
firstLine.startsWith(']');
|
|
128
137
|
if (endsWithOpenBrace || startsWithCloseBrace) {
|
|
129
138
|
analysis.codeBlockBoundary = {
|
|
130
139
|
isInCompleteBlock: false,
|
|
131
140
|
suggestion: endsWithOpenBrace
|
|
132
141
|
? 'Edit ends with an opening bracket - ensure the closing bracket is included in a subsequent edit or already exists in the file'
|
|
133
|
-
: 'Edit starts with a closing bracket - ensure the opening bracket exists before this edit'
|
|
142
|
+
: 'Edit starts with a closing bracket - ensure the opening bracket exists before this edit',
|
|
134
143
|
};
|
|
135
144
|
}
|
|
136
145
|
return analysis;
|
|
@@ -214,7 +223,7 @@ export class FilesystemMCPService {
|
|
|
214
223
|
content: `Directory: ${filePath}\n\n${fileList}`,
|
|
215
224
|
startLine: 1,
|
|
216
225
|
endLine: lines.length,
|
|
217
|
-
totalLines: lines.length
|
|
226
|
+
totalLines: lines.length,
|
|
218
227
|
};
|
|
219
228
|
}
|
|
220
229
|
const content = await fs.readFile(fullPath, 'utf-8');
|
|
@@ -248,7 +257,7 @@ export class FilesystemMCPService {
|
|
|
248
257
|
content: partialContent,
|
|
249
258
|
startLine: start,
|
|
250
259
|
endLine: end,
|
|
251
|
-
totalLines
|
|
260
|
+
totalLines,
|
|
252
261
|
};
|
|
253
262
|
}
|
|
254
263
|
catch (error) {
|
|
@@ -417,22 +426,26 @@ export class FilesystemMCPService {
|
|
|
417
426
|
await incrementalSnapshotManager.backupFile(fullPath);
|
|
418
427
|
// Extract the lines that will be replaced (for comparison)
|
|
419
428
|
const replacedLines = lines.slice(startLine - 1, adjustedEndLine);
|
|
420
|
-
const replacedContent = replacedLines
|
|
429
|
+
const replacedContent = replacedLines
|
|
430
|
+
.map((line, idx) => {
|
|
421
431
|
const lineNum = startLine + idx;
|
|
422
432
|
const paddedNum = String(lineNum).padStart(String(adjustedEndLine).length, ' ');
|
|
423
433
|
return `${paddedNum}→${line}`;
|
|
424
|
-
})
|
|
434
|
+
})
|
|
435
|
+
.join('\n');
|
|
425
436
|
// Calculate context range using smart boundary detection
|
|
426
437
|
const smartBoundaries = this.findSmartContextBoundaries(lines, startLine, adjustedEndLine, contextLines);
|
|
427
438
|
const contextStart = smartBoundaries.start;
|
|
428
439
|
const contextEnd = smartBoundaries.end;
|
|
429
440
|
// Extract old content for context (including the lines to be replaced)
|
|
430
441
|
const oldContextLines = lines.slice(contextStart - 1, contextEnd);
|
|
431
|
-
const oldContent = oldContextLines
|
|
442
|
+
const oldContent = oldContextLines
|
|
443
|
+
.map((line, idx) => {
|
|
432
444
|
const lineNum = contextStart + idx;
|
|
433
445
|
const paddedNum = String(lineNum).padStart(String(contextEnd).length, ' ');
|
|
434
446
|
return `${paddedNum}→${line}`;
|
|
435
|
-
})
|
|
447
|
+
})
|
|
448
|
+
.join('\n');
|
|
436
449
|
// Replace the specified lines
|
|
437
450
|
const newContentLines = newContent.split('\n');
|
|
438
451
|
const beforeLines = lines.slice(0, startLine - 1);
|
|
@@ -444,15 +457,49 @@ export class FilesystemMCPService {
|
|
|
444
457
|
const newContextEnd = Math.min(newTotalLines, contextEnd + lineDifference);
|
|
445
458
|
// Extract new content for context with line numbers
|
|
446
459
|
const newContextLines = modifiedLines.slice(contextStart - 1, newContextEnd);
|
|
447
|
-
const newContextContent = newContextLines
|
|
460
|
+
const newContextContent = newContextLines
|
|
461
|
+
.map((line, idx) => {
|
|
448
462
|
const lineNum = contextStart + idx;
|
|
449
463
|
const paddedNum = String(lineNum).padStart(String(newContextEnd).length, ' ');
|
|
450
464
|
return `${paddedNum}→${line}`;
|
|
451
|
-
})
|
|
465
|
+
})
|
|
466
|
+
.join('\n');
|
|
452
467
|
// Write the modified content back to file
|
|
453
468
|
await fs.writeFile(fullPath, modifiedLines.join('\n'), 'utf-8');
|
|
454
|
-
//
|
|
455
|
-
|
|
469
|
+
// Format the file with Prettier after editing to ensure consistent code style
|
|
470
|
+
let finalLines = modifiedLines;
|
|
471
|
+
let finalTotalLines = newTotalLines;
|
|
472
|
+
let finalContextEnd = newContextEnd;
|
|
473
|
+
let finalContextContent = newContextContent;
|
|
474
|
+
try {
|
|
475
|
+
execSync(`npx prettier --write "${fullPath}"`, {
|
|
476
|
+
stdio: 'pipe',
|
|
477
|
+
encoding: 'utf-8',
|
|
478
|
+
});
|
|
479
|
+
// Re-read the file after formatting to get the formatted content
|
|
480
|
+
const formattedContent = await fs.readFile(fullPath, 'utf-8');
|
|
481
|
+
finalLines = formattedContent.split('\n');
|
|
482
|
+
finalTotalLines = finalLines.length;
|
|
483
|
+
// Recalculate the context end line based on formatted content
|
|
484
|
+
finalContextEnd = Math.min(finalTotalLines, contextStart + (newContextEnd - contextStart));
|
|
485
|
+
// Extract formatted content for context with line numbers
|
|
486
|
+
const formattedContextLines = finalLines.slice(contextStart - 1, finalContextEnd);
|
|
487
|
+
finalContextContent = formattedContextLines
|
|
488
|
+
.map((line, idx) => {
|
|
489
|
+
const lineNum = contextStart + idx;
|
|
490
|
+
const paddedNum = String(lineNum).padStart(String(finalContextEnd).length, ' ');
|
|
491
|
+
return `${paddedNum}→${line}`;
|
|
492
|
+
})
|
|
493
|
+
.join('\n');
|
|
494
|
+
}
|
|
495
|
+
catch (formatError) {
|
|
496
|
+
// If formatting fails, continue with the original content
|
|
497
|
+
// This ensures editing is not blocked by formatting issues
|
|
498
|
+
console.warn(`⚠️ Prettier formatting failed for ${filePath}, returning unformatted content:`, formatError instanceof Error ? formatError.message : 'Unknown error');
|
|
499
|
+
}
|
|
500
|
+
// Analyze code structure of the edited content (using formatted content if available)
|
|
501
|
+
const editedContentLines = finalLines.slice(startLine - 1, startLine - 1 + newContentLines.length);
|
|
502
|
+
const structureAnalysis = this.analyzeCodeStructure(finalLines.join('\n'), filePath, editedContentLines);
|
|
456
503
|
// Try to get diagnostics from VS Code after editing
|
|
457
504
|
let diagnostics = [];
|
|
458
505
|
try {
|
|
@@ -467,15 +514,17 @@ export class FilesystemMCPService {
|
|
|
467
514
|
message: `✅ File edited successfully: ${filePath}\n` +
|
|
468
515
|
` Replaced: lines ${startLine}-${adjustedEndLine} (${linesToModify} lines)\n` +
|
|
469
516
|
` Result: ${newContentLines.length} new lines` +
|
|
470
|
-
(smartBoundaries.extended
|
|
517
|
+
(smartBoundaries.extended
|
|
518
|
+
? `\n 📍 Context auto-extended to show complete code block (lines ${contextStart}-${finalContextEnd})`
|
|
519
|
+
: ''),
|
|
471
520
|
oldContent,
|
|
472
|
-
newContent:
|
|
521
|
+
newContent: finalContextContent,
|
|
473
522
|
replacedLines: replacedContent,
|
|
474
523
|
contextStartLine: contextStart,
|
|
475
|
-
contextEndLine:
|
|
476
|
-
totalLines:
|
|
524
|
+
contextEndLine: finalContextEnd,
|
|
525
|
+
totalLines: finalTotalLines,
|
|
477
526
|
linesModified: linesToModify,
|
|
478
|
-
structureAnalysis
|
|
527
|
+
structureAnalysis,
|
|
479
528
|
};
|
|
480
529
|
// Add diagnostics if any were found
|
|
481
530
|
if (diagnostics.length > 0) {
|
|
@@ -501,15 +550,18 @@ export class FilesystemMCPService {
|
|
|
501
550
|
const structureWarnings = [];
|
|
502
551
|
// Check bracket balance
|
|
503
552
|
if (!structureAnalysis.bracketBalance.curly.balanced) {
|
|
504
|
-
const diff = structureAnalysis.bracketBalance.curly.open -
|
|
553
|
+
const diff = structureAnalysis.bracketBalance.curly.open -
|
|
554
|
+
structureAnalysis.bracketBalance.curly.close;
|
|
505
555
|
structureWarnings.push(`Curly brackets: ${diff > 0 ? `${diff} unclosed {` : `${Math.abs(diff)} extra }`}`);
|
|
506
556
|
}
|
|
507
557
|
if (!structureAnalysis.bracketBalance.round.balanced) {
|
|
508
|
-
const diff = structureAnalysis.bracketBalance.round.open -
|
|
558
|
+
const diff = structureAnalysis.bracketBalance.round.open -
|
|
559
|
+
structureAnalysis.bracketBalance.round.close;
|
|
509
560
|
structureWarnings.push(`Round brackets: ${diff > 0 ? `${diff} unclosed (` : `${Math.abs(diff)} extra )`}`);
|
|
510
561
|
}
|
|
511
562
|
if (!structureAnalysis.bracketBalance.square.balanced) {
|
|
512
|
-
const diff = structureAnalysis.bracketBalance.square.open -
|
|
563
|
+
const diff = structureAnalysis.bracketBalance.square.open -
|
|
564
|
+
structureAnalysis.bracketBalance.square.close;
|
|
513
565
|
structureWarnings.push(`Square brackets: ${diff > 0 ? `${diff} unclosed [` : `${Math.abs(diff)} extra ]`}`);
|
|
514
566
|
}
|
|
515
567
|
// Check HTML tags
|
|
@@ -526,7 +578,8 @@ export class FilesystemMCPService {
|
|
|
526
578
|
structureWarnings.push(...structureAnalysis.indentationWarnings.map(w => `Indentation: ${w}`));
|
|
527
579
|
}
|
|
528
580
|
// Add code block boundary warnings
|
|
529
|
-
if (structureAnalysis.codeBlockBoundary &&
|
|
581
|
+
if (structureAnalysis.codeBlockBoundary &&
|
|
582
|
+
structureAnalysis.codeBlockBoundary.suggestion) {
|
|
530
583
|
structureWarnings.push(`Boundary: ${structureAnalysis.codeBlockBoundary.suggestion}`);
|
|
531
584
|
}
|
|
532
585
|
// Format structure warnings
|
|
@@ -552,13 +605,21 @@ export class FilesystemMCPService {
|
|
|
552
605
|
* @param maxResults - Maximum number of results to return (default: 100)
|
|
553
606
|
* @returns Search results with file paths, line numbers, and matched content
|
|
554
607
|
*/
|
|
555
|
-
async searchCode(query, dirPath = '.', fileExtensions = [], caseSensitive = false, maxResults = 100) {
|
|
608
|
+
async searchCode(query, dirPath = '.', fileExtensions = [], caseSensitive = false, maxResults = 100, searchMode = 'text') {
|
|
556
609
|
const matches = [];
|
|
557
610
|
let searchedFiles = 0;
|
|
558
611
|
const fullDirPath = this.resolvePath(dirPath);
|
|
559
|
-
//
|
|
612
|
+
// Prepare search regex based on mode
|
|
560
613
|
const flags = caseSensitive ? 'g' : 'gi';
|
|
561
|
-
|
|
614
|
+
let searchRegex = null;
|
|
615
|
+
if (searchMode === 'text') {
|
|
616
|
+
// Escape special regex characters for literal text search
|
|
617
|
+
searchRegex = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), flags);
|
|
618
|
+
}
|
|
619
|
+
else if (searchMode === 'regex') {
|
|
620
|
+
// Use query as-is for regex search
|
|
621
|
+
searchRegex = new RegExp(query, flags);
|
|
622
|
+
}
|
|
562
623
|
// Recursively search files
|
|
563
624
|
const searchInDirectory = async (currentPath) => {
|
|
564
625
|
try {
|
|
@@ -571,7 +632,11 @@ export class FilesystemMCPService {
|
|
|
571
632
|
// Skip common directories that should be ignored
|
|
572
633
|
if (entry.isDirectory()) {
|
|
573
634
|
const dirName = entry.name;
|
|
574
|
-
if (dirName === 'node_modules' ||
|
|
635
|
+
if (dirName === 'node_modules' ||
|
|
636
|
+
dirName === '.git' ||
|
|
637
|
+
dirName === 'dist' ||
|
|
638
|
+
dirName === 'build' ||
|
|
639
|
+
dirName.startsWith('.')) {
|
|
575
640
|
continue;
|
|
576
641
|
}
|
|
577
642
|
await searchInDirectory(fullPath);
|
|
@@ -587,23 +652,56 @@ export class FilesystemMCPService {
|
|
|
587
652
|
searchedFiles++;
|
|
588
653
|
try {
|
|
589
654
|
const content = await fs.readFile(fullPath, 'utf-8');
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if
|
|
593
|
-
|
|
655
|
+
// AST search mode - supports multiple languages via tree-sitter
|
|
656
|
+
if (searchMode === 'ast') {
|
|
657
|
+
// Check if file is supported for AST parsing
|
|
658
|
+
if (multiLanguageASTParser.isSupported(fullPath)) {
|
|
659
|
+
try {
|
|
660
|
+
const astResults = multiLanguageASTParser.searchAST(content, fullPath, query, caseSensitive);
|
|
661
|
+
for (const result of astResults) {
|
|
662
|
+
if (matches.length >= maxResults) {
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
const lineContent = content.split('\n')[result.startPosition.line - 1] ||
|
|
666
|
+
'';
|
|
667
|
+
matches.push({
|
|
668
|
+
filePath: path.relative(this.basePath, fullPath),
|
|
669
|
+
lineNumber: result.startPosition.line,
|
|
670
|
+
lineContent: lineContent.trim(),
|
|
671
|
+
column: result.startPosition.column,
|
|
672
|
+
matchedText: result.name,
|
|
673
|
+
nodeType: result.type,
|
|
674
|
+
nodeName: result.name,
|
|
675
|
+
language: result.language,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
catch (error) {
|
|
680
|
+
// Skip files with AST parsing errors
|
|
681
|
+
}
|
|
594
682
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
683
|
+
}
|
|
684
|
+
else if (searchRegex) {
|
|
685
|
+
// Text or Regex search mode
|
|
686
|
+
const lines = content.split('\n');
|
|
687
|
+
lines.forEach((line, index) => {
|
|
688
|
+
if (matches.length >= maxResults) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
// Reset regex for each line
|
|
692
|
+
searchRegex.lastIndex = 0;
|
|
693
|
+
const match = searchRegex.exec(line);
|
|
694
|
+
if (match) {
|
|
695
|
+
matches.push({
|
|
696
|
+
filePath: path.relative(this.basePath, fullPath),
|
|
697
|
+
lineNumber: index + 1,
|
|
698
|
+
lineContent: line.trim(),
|
|
699
|
+
column: match.index + 1,
|
|
700
|
+
matchedText: match[0],
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
}
|
|
607
705
|
}
|
|
608
706
|
catch (error) {
|
|
609
707
|
// Skip files that cannot be read (binary files, permission issues, etc.)
|
|
@@ -620,7 +718,7 @@ export class FilesystemMCPService {
|
|
|
620
718
|
query,
|
|
621
719
|
totalMatches: matches.length,
|
|
622
720
|
matches,
|
|
623
|
-
searchedFiles
|
|
721
|
+
searchedFiles,
|
|
624
722
|
};
|
|
625
723
|
}
|
|
626
724
|
/**
|
|
@@ -663,19 +761,19 @@ export const mcpTools = [
|
|
|
663
761
|
properties: {
|
|
664
762
|
filePath: {
|
|
665
763
|
type: 'string',
|
|
666
|
-
description: 'Path to the file to read (or directory to list)'
|
|
764
|
+
description: 'Path to the file to read (or directory to list)',
|
|
667
765
|
},
|
|
668
766
|
startLine: {
|
|
669
767
|
type: 'number',
|
|
670
|
-
description: 'Starting line number (1-indexed, inclusive). Must be >= 1.'
|
|
768
|
+
description: 'Starting line number (1-indexed, inclusive). Must be >= 1.',
|
|
671
769
|
},
|
|
672
770
|
endLine: {
|
|
673
771
|
type: 'number',
|
|
674
|
-
description: 'Ending line number (1-indexed, inclusive). Can exceed file length (will be capped automatically).'
|
|
675
|
-
}
|
|
772
|
+
description: 'Ending line number (1-indexed, inclusive). Can exceed file length (will be capped automatically).',
|
|
773
|
+
},
|
|
676
774
|
},
|
|
677
|
-
required: ['filePath', 'startLine', 'endLine']
|
|
678
|
-
}
|
|
775
|
+
required: ['filePath', 'startLine', 'endLine'],
|
|
776
|
+
},
|
|
679
777
|
},
|
|
680
778
|
{
|
|
681
779
|
name: 'filesystem_create',
|
|
@@ -685,20 +783,20 @@ export const mcpTools = [
|
|
|
685
783
|
properties: {
|
|
686
784
|
filePath: {
|
|
687
785
|
type: 'string',
|
|
688
|
-
description: 'Path where the file should be created'
|
|
786
|
+
description: 'Path where the file should be created',
|
|
689
787
|
},
|
|
690
788
|
content: {
|
|
691
789
|
type: 'string',
|
|
692
|
-
description: 'Content to write to the file'
|
|
790
|
+
description: 'Content to write to the file',
|
|
693
791
|
},
|
|
694
792
|
createDirectories: {
|
|
695
793
|
type: 'boolean',
|
|
696
|
-
description:
|
|
697
|
-
default: true
|
|
698
|
-
}
|
|
794
|
+
description: "Whether to create parent directories if they don't exist",
|
|
795
|
+
default: true,
|
|
796
|
+
},
|
|
699
797
|
},
|
|
700
|
-
required: ['filePath', 'content']
|
|
701
|
-
}
|
|
798
|
+
required: ['filePath', 'content'],
|
|
799
|
+
},
|
|
702
800
|
},
|
|
703
801
|
{
|
|
704
802
|
name: 'filesystem_delete',
|
|
@@ -708,11 +806,11 @@ export const mcpTools = [
|
|
|
708
806
|
properties: {
|
|
709
807
|
filePath: {
|
|
710
808
|
type: 'string',
|
|
711
|
-
description: 'Path to the file to delete'
|
|
712
|
-
}
|
|
809
|
+
description: 'Path to the file to delete',
|
|
810
|
+
},
|
|
713
811
|
},
|
|
714
|
-
required: ['filePath']
|
|
715
|
-
}
|
|
812
|
+
required: ['filePath'],
|
|
813
|
+
},
|
|
716
814
|
},
|
|
717
815
|
{
|
|
718
816
|
name: 'filesystem_list',
|
|
@@ -723,10 +821,10 @@ export const mcpTools = [
|
|
|
723
821
|
dirPath: {
|
|
724
822
|
type: 'string',
|
|
725
823
|
description: 'Directory path to list files from',
|
|
726
|
-
default: '.'
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
}
|
|
824
|
+
default: '.',
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
},
|
|
730
828
|
},
|
|
731
829
|
{
|
|
732
830
|
name: 'filesystem_edit',
|
|
@@ -736,64 +834,70 @@ export const mcpTools = [
|
|
|
736
834
|
properties: {
|
|
737
835
|
filePath: {
|
|
738
836
|
type: 'string',
|
|
739
|
-
description: 'Path to the file to edit (absolute or relative)'
|
|
837
|
+
description: 'Path to the file to edit (absolute or relative)',
|
|
740
838
|
},
|
|
741
839
|
startLine: {
|
|
742
840
|
type: 'number',
|
|
743
|
-
description: '⚠️ CRITICAL: Starting line number (1-indexed, inclusive). MUST match exact line number from filesystem_read output. Double-check this value!'
|
|
841
|
+
description: '⚠️ CRITICAL: Starting line number (1-indexed, inclusive). MUST match exact line number from filesystem_read output. Double-check this value!',
|
|
744
842
|
},
|
|
745
843
|
endLine: {
|
|
746
844
|
type: 'number',
|
|
747
|
-
description: '⚠️ CRITICAL: Ending line number (1-indexed, inclusive). MUST match exact line number from filesystem_read output. 💡 TIP: Keep edits small (≤15 lines recommended) for better accuracy.'
|
|
845
|
+
description: '⚠️ CRITICAL: Ending line number (1-indexed, inclusive). MUST match exact line number from filesystem_read output. 💡 TIP: Keep edits small (≤15 lines recommended) for better accuracy.',
|
|
748
846
|
},
|
|
749
847
|
newContent: {
|
|
750
848
|
type: 'string',
|
|
751
|
-
description: 'New content to replace specified lines. ⚠️ Do NOT include line numbers. ⚠️ Ensure proper indentation and bracket closure. Keep changes MINIMAL and FOCUSED.'
|
|
849
|
+
description: 'New content to replace specified lines. ⚠️ Do NOT include line numbers. ⚠️ Ensure proper indentation and bracket closure. Keep changes MINIMAL and FOCUSED.',
|
|
752
850
|
},
|
|
753
851
|
contextLines: {
|
|
754
852
|
type: 'number',
|
|
755
853
|
description: 'Number of context lines to show before/after edit for verification (default: 8)',
|
|
756
|
-
default: 8
|
|
757
|
-
}
|
|
854
|
+
default: 8,
|
|
855
|
+
},
|
|
758
856
|
},
|
|
759
|
-
required: ['filePath', 'startLine', 'endLine', 'newContent']
|
|
760
|
-
}
|
|
857
|
+
required: ['filePath', 'startLine', 'endLine', 'newContent'],
|
|
858
|
+
},
|
|
761
859
|
},
|
|
762
860
|
{
|
|
763
861
|
name: 'filesystem_search',
|
|
764
|
-
description:
|
|
862
|
+
description: "Search for code keywords across files in a directory. Useful for finding function definitions, variable usages, or any code patterns. Similar to VS Code's global search feature.",
|
|
765
863
|
inputSchema: {
|
|
766
864
|
type: 'object',
|
|
767
865
|
properties: {
|
|
768
866
|
query: {
|
|
769
867
|
type: 'string',
|
|
770
|
-
description: 'The keyword or text to search for (e.g., function name, variable name, or any code pattern)'
|
|
868
|
+
description: 'The keyword or text to search for (e.g., function name, variable name, or any code pattern)',
|
|
771
869
|
},
|
|
772
870
|
dirPath: {
|
|
773
871
|
type: 'string',
|
|
774
872
|
description: 'Directory to search in (relative to base path or absolute). Defaults to current directory.',
|
|
775
|
-
default: '.'
|
|
873
|
+
default: '.',
|
|
776
874
|
},
|
|
777
875
|
fileExtensions: {
|
|
778
876
|
type: 'array',
|
|
779
877
|
items: {
|
|
780
|
-
type: 'string'
|
|
878
|
+
type: 'string',
|
|
781
879
|
},
|
|
782
880
|
description: 'Array of file extensions to search (e.g., [".ts", ".tsx", ".js"]). If empty, searches all text files.',
|
|
783
|
-
default: []
|
|
881
|
+
default: [],
|
|
784
882
|
},
|
|
785
883
|
caseSensitive: {
|
|
786
884
|
type: 'boolean',
|
|
787
885
|
description: 'Whether the search should be case-sensitive',
|
|
788
|
-
default: false
|
|
886
|
+
default: false,
|
|
789
887
|
},
|
|
790
888
|
maxResults: {
|
|
791
889
|
type: 'number',
|
|
792
890
|
description: 'Maximum number of results to return',
|
|
793
|
-
default: 100
|
|
794
|
-
}
|
|
891
|
+
default: 100,
|
|
892
|
+
},
|
|
893
|
+
searchMode: {
|
|
894
|
+
type: 'string',
|
|
895
|
+
enum: ['text', 'regex', 'ast'],
|
|
896
|
+
description: 'Search mode: "text" for literal text search (default), "regex" for regular expression search, "ast" for AST-based semantic search (supports function/class/variable names)',
|
|
897
|
+
default: 'text',
|
|
898
|
+
},
|
|
795
899
|
},
|
|
796
|
-
required: ['query']
|
|
797
|
-
}
|
|
798
|
-
}
|
|
900
|
+
required: ['query'],
|
|
901
|
+
},
|
|
902
|
+
},
|
|
799
903
|
];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported programming languages for AST parsing
|
|
3
|
+
*/
|
|
4
|
+
export type SupportedLanguage = 'javascript' | 'typescript' | 'tsx' | 'python' | 'java' | 'go' | 'cpp' | 'c' | 'csharp' | 'rust' | 'ruby' | 'php';
|
|
5
|
+
/**
|
|
6
|
+
* AST node result with metadata
|
|
7
|
+
*/
|
|
8
|
+
export interface ASTNodeResult {
|
|
9
|
+
name: string;
|
|
10
|
+
type: string;
|
|
11
|
+
startPosition: {
|
|
12
|
+
line: number;
|
|
13
|
+
column: number;
|
|
14
|
+
};
|
|
15
|
+
endPosition: {
|
|
16
|
+
line: number;
|
|
17
|
+
column: number;
|
|
18
|
+
};
|
|
19
|
+
text: string;
|
|
20
|
+
language: SupportedLanguage;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Multi-language AST parser using tree-sitter
|
|
24
|
+
* Supports JavaScript, TypeScript, Python, Java, Go, C/C++, C#, Rust, Ruby, PHP
|
|
25
|
+
*/
|
|
26
|
+
export declare class MultiLanguageASTParser {
|
|
27
|
+
private parsers;
|
|
28
|
+
private languageConfigs;
|
|
29
|
+
constructor();
|
|
30
|
+
/**
|
|
31
|
+
* Initialize language configurations
|
|
32
|
+
*/
|
|
33
|
+
private initializeLanguageConfigs;
|
|
34
|
+
/**
|
|
35
|
+
* Initialize parsers for all supported languages
|
|
36
|
+
*/
|
|
37
|
+
private initializeParsers;
|
|
38
|
+
/**
|
|
39
|
+
* Detect language from file extension
|
|
40
|
+
*/
|
|
41
|
+
detectLanguage(filePath: string): SupportedLanguage | null;
|
|
42
|
+
/**
|
|
43
|
+
* Parse source code and search for nodes matching the query
|
|
44
|
+
*/
|
|
45
|
+
searchAST(sourceCode: string, filePath: string, query: string, caseSensitive?: boolean): ASTNodeResult[];
|
|
46
|
+
/**
|
|
47
|
+
* Extract the name/identifier from an AST node based on language
|
|
48
|
+
*/
|
|
49
|
+
private extractNodeName;
|
|
50
|
+
/**
|
|
51
|
+
* Recursively find the first identifier node
|
|
52
|
+
*/
|
|
53
|
+
private findIdentifier;
|
|
54
|
+
/**
|
|
55
|
+
* Get supported file extensions
|
|
56
|
+
*/
|
|
57
|
+
getSupportedExtensions(): string[];
|
|
58
|
+
/**
|
|
59
|
+
* Check if a file is supported for AST parsing
|
|
60
|
+
*/
|
|
61
|
+
isSupported(filePath: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Get node types for a specific language
|
|
64
|
+
*/
|
|
65
|
+
getNodeTypes(language: SupportedLanguage): string[];
|
|
66
|
+
}
|
|
67
|
+
export declare const multiLanguageASTParser: MultiLanguageASTParser;
|