snow-ai 0.2.11 → 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.
Files changed (41) hide show
  1. package/dist/api/anthropic.d.ts +2 -0
  2. package/dist/api/anthropic.js +64 -18
  3. package/dist/api/chat.d.ts +3 -0
  4. package/dist/api/chat.js +5 -4
  5. package/dist/api/gemini.d.ts +3 -0
  6. package/dist/api/gemini.js +168 -101
  7. package/dist/api/responses.d.ts +3 -0
  8. package/dist/api/responses.js +5 -4
  9. package/dist/api/systemPrompt.d.ts +1 -1
  10. package/dist/api/systemPrompt.js +149 -40
  11. package/dist/hooks/useConversation.d.ts +1 -1
  12. package/dist/hooks/useConversation.js +5 -3
  13. package/dist/hooks/useGlobalNavigation.js +2 -0
  14. package/dist/hooks/useToolConfirmation.d.ts +2 -1
  15. package/dist/hooks/useToolConfirmation.js +2 -1
  16. package/dist/mcp/filesystem.d.ts +16 -1
  17. package/dist/mcp/filesystem.js +193 -89
  18. package/dist/mcp/multiLanguageASTParser.d.ts +67 -0
  19. package/dist/mcp/multiLanguageASTParser.js +360 -0
  20. package/dist/mcp/todo.d.ts +1 -1
  21. package/dist/mcp/todo.js +21 -26
  22. package/dist/ui/components/ChatInput.d.ts +4 -1
  23. package/dist/ui/components/ChatInput.js +105 -39
  24. package/dist/ui/components/DiffViewer.d.ts +1 -2
  25. package/dist/ui/components/DiffViewer.js +65 -65
  26. package/dist/ui/components/MCPInfoPanel.js +1 -2
  27. package/dist/ui/components/TodoTree.js +1 -1
  28. package/dist/ui/components/ToolConfirmation.d.ts +11 -1
  29. package/dist/ui/components/ToolConfirmation.js +86 -6
  30. package/dist/ui/pages/ChatScreen.js +223 -108
  31. package/dist/ui/pages/SystemPromptConfigScreen.js +25 -12
  32. package/dist/utils/apiConfig.d.ts +6 -1
  33. package/dist/utils/apiConfig.js +24 -0
  34. package/dist/utils/commands/ide.js +18 -1
  35. package/dist/utils/mcpToolsManager.d.ts +1 -1
  36. package/dist/utils/mcpToolsManager.js +45 -36
  37. package/dist/utils/textBuffer.d.ts +5 -0
  38. package/dist/utils/textBuffer.js +23 -2
  39. package/dist/utils/vscodeConnection.js +10 -1
  40. package/package.json +14 -2
  41. package/readme.md +36 -6
@@ -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 === analysis.bracketBalance.curly.close;
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 === analysis.bracketBalance.round.close;
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 === analysis.bracketBalance.square.close;
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('{') || lastLine.endsWith('(') || lastLine.endsWith('[');
127
- const startsWithCloseBrace = firstLine.startsWith('}') || firstLine.startsWith(')') || firstLine.startsWith(']');
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.map((line, idx) => {
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
- }).join('\n');
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.map((line, idx) => {
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
- }).join('\n');
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.map((line, idx) => {
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
- }).join('\n');
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
- // Analyze code structure of the edited content
455
- const structureAnalysis = this.analyzeCodeStructure(modifiedLines.join('\n'), filePath, newContentLines);
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 ? `\n 📍 Context auto-extended to show complete code block (lines ${contextStart}-${newContextEnd})` : ''),
517
+ (smartBoundaries.extended
518
+ ? `\n 📍 Context auto-extended to show complete code block (lines ${contextStart}-${finalContextEnd})`
519
+ : ''),
471
520
  oldContent,
472
- newContent: newContextContent,
521
+ newContent: finalContextContent,
473
522
  replacedLines: replacedContent,
474
523
  contextStartLine: contextStart,
475
- contextEndLine: newContextEnd,
476
- totalLines: newTotalLines,
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 - structureAnalysis.bracketBalance.curly.close;
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 - structureAnalysis.bracketBalance.round.close;
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 - structureAnalysis.bracketBalance.square.close;
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 && structureAnalysis.codeBlockBoundary.suggestion) {
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
- // Convert query to regex for flexible matching
612
+ // Prepare search regex based on mode
560
613
  const flags = caseSensitive ? 'g' : 'gi';
561
- const searchRegex = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), flags);
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' || dirName === '.git' || dirName === 'dist' || dirName === 'build' || dirName.startsWith('.')) {
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
- const lines = content.split('\n');
591
- lines.forEach((line, index) => {
592
- if (matches.length >= maxResults) {
593
- return;
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
- // Reset regex for each line
596
- searchRegex.lastIndex = 0;
597
- const match = searchRegex.exec(line);
598
- if (match) {
599
- matches.push({
600
- filePath: path.relative(this.basePath, fullPath),
601
- lineNumber: index + 1,
602
- lineContent: line.trim(),
603
- column: match.index + 1
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: 'Whether to create parent directories if they don\'t exist',
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: '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.',
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;