@mrxkun/mcfast-mcp 3.5.11 → 3.5.12

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +78 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrxkun/mcfast-mcp",
3
- "version": "3.5.11",
3
+ "version": "3.5.12",
4
4
  "description": "Ultra-fast code editing with WASM acceleration, fuzzy patching, multi-layer caching, and 8 unified tools. Optimized for AI code assistants with 80-98% latency reduction.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -595,11 +595,31 @@ async function reportAudit(data) {
595
595
  /**
596
596
  * Handle regex-based filesystem search using grep
597
597
  * Uses native grep for high-performance regex searching
598
+ * Includes validation for unsupported regex patterns
598
599
  */
599
600
  async function handleRegexSearch({ query, path: searchPath, caseSensitive = false }) {
600
601
  const start = Date.now();
601
602
 
602
603
  try {
604
+ // Validate regex pattern before executing
605
+ const validation = validateRegex(query);
606
+ if (!validation.valid) {
607
+ const latency = Date.now() - start;
608
+ return {
609
+ content: [{
610
+ type: "text",
611
+ text: `⚠️ Invalid regex pattern: ${validation.error}\n\nQuery: "${query}"\n\n💡 Tips:\n• Basic regex works: \\bword\\b, [a-z]+, (foo|bar)\n• Avoid lookbehind (?<=...) and lookahead (?=...) - not supported by grep\n• Use word boundaries \\b instead of lookarounds\n• Escape special chars: \\\\, \\+, \\*`
612
+ }],
613
+ isError: true
614
+ };
615
+ }
616
+
617
+ // Warn about potentially unsupported patterns
618
+ if (validation.warnings.length > 0) {
619
+ console.error(`${colors.yellow}[Regex Warning]${colors.reset} Pattern may not work as expected:`);
620
+ validation.warnings.forEach(w => console.error(` - ${w}`));
621
+ }
622
+
603
623
  const flags = [
604
624
  "-r", // Recursive
605
625
  "-n", // Line number
@@ -618,10 +638,18 @@ async function handleRegexSearch({ query, path: searchPath, caseSensitive = fals
618
638
  "--exclude-dir=.cache"
619
639
  ].join(" ");
620
640
 
621
- const command = `grep ${flags} ${excludes} -E "${query.replace(/"/g, '\\"')}" "${searchPath}"`;
641
+ // Escape quotes in query to prevent command injection
642
+ const escapedQuery = query.replace(/"/g, '\\"');
643
+ const command = `grep ${flags} ${excludes} -E "${escapedQuery}" "${searchPath}" 2>&1`;
622
644
 
623
645
  try {
624
- const { stdout } = await execAsync(command, { maxBuffer: 10 * 1024 * 1024 }); // 10MB buffer
646
+ const { stdout, stderr } = await execAsync(command, { maxBuffer: 10 * 1024 * 1024 }); // 10MB buffer
647
+
648
+ // Check for grep regex errors in stderr
649
+ if (stderr && stderr.includes('Invalid regular expression')) {
650
+ throw new Error(`grep: ${stderr}`);
651
+ }
652
+
625
653
  const results = stdout.trim().split('\n').filter(Boolean);
626
654
 
627
655
  const latency = Date.now() - start;
@@ -686,6 +714,12 @@ async function handleRegexSearch({ query, path: searchPath, caseSensitive = fals
686
714
  content: [{ type: "text", text: `🔍 Regex Search: No matches found for "${query}" in ${searchPath} (${latency}ms)` }]
687
715
  };
688
716
  }
717
+
718
+ // Handle regex syntax errors
719
+ if (execErr.stderr && execErr.stderr.includes('Invalid')) {
720
+ throw new Error(`Invalid regex syntax: ${execErr.stderr}. Try simplifying your pattern.`);
721
+ }
722
+
689
723
  throw execErr;
690
724
  }
691
725
  } catch (error) {
@@ -700,12 +734,52 @@ async function handleRegexSearch({ query, path: searchPath, caseSensitive = fals
700
734
  });
701
735
 
702
736
  return {
703
- content: [{ type: "text", text: `❌ Regex search error: ${error.message}` }],
737
+ content: [{ type: "text", text: `❌ Regex search error: ${error.message}\n\n💡 Try using a simpler pattern without lookarounds (?<= or ?=)` }],
704
738
  isError: true
705
739
  };
706
740
  }
707
741
  }
708
742
 
743
+ /**
744
+ * Validate regex pattern for compatibility with grep
745
+ * Returns { valid: boolean, error?: string, warnings: string[] }
746
+ */
747
+ function validateRegex(pattern) {
748
+ const warnings = [];
749
+
750
+ try {
751
+ // Test if it's valid JavaScript regex
752
+ new RegExp(pattern);
753
+ } catch (e) {
754
+ return { valid: false, error: e.message, warnings };
755
+ }
756
+
757
+ // Check for patterns not supported by basic grep (PCRE-only features)
758
+ const unsupportedPatterns = [
759
+ { regex: /\(\?<[=!]/, name: 'Lookbehind (?<=...) or negative lookbehind (?<!...)' },
760
+ { regex: /\(\?=[^=]/, name: 'Lookahead (?=...)' },
761
+ { regex: /\(\?![^=]/, name: 'Negative lookahead (?!...)' },
762
+ { regex: /\(\?<\w+>/, name: 'Named capture groups (?<name>...)' },
763
+ { regex: /\\p\{/, name: 'Unicode properties (\\p{...})' },
764
+ { regex: /\(\?#/, name: 'Comments (?#...)' },
765
+ { regex: /\(\?\d*\)/, name: 'Subroutine calls (?1)' },
766
+ { regex: /\(\?&\w+\)/, name: 'Subroutine calls (?&name)' }
767
+ ];
768
+
769
+ for (const { regex, name } of unsupportedPatterns) {
770
+ if (regex.test(pattern)) {
771
+ warnings.push(`Pattern uses ${name} which may not be supported by grep. Results may be incomplete.`);
772
+ }
773
+ }
774
+
775
+ // Check for potentially dangerous patterns (catastrophic backtracking)
776
+ if (/\(\w+\+\+?\)|\(\w+\*\*?\)\*\*?/.test(pattern)) {
777
+ warnings.push('Pattern may cause performance issues (nested quantifiers)');
778
+ }
779
+
780
+ return { valid: true, warnings };
781
+ }
782
+
709
783
  // ============================================
710
784
 
711
785
  /**
@@ -2188,7 +2262,7 @@ async function handleReferencesInDirectory({ path: dirPath, symbol }) {
2188
2262
  output += ` ${colors.dim}${m.line}:${colors.reset} ${m.content}\n`;
2189
2263
  });
2190
2264
  if (matches.length > 5) {
2191
- output += ` ${colors.dim}... and ${matches.length - 5} more${colors.reset}\n`;
2265
+ output += ` ${colors.dim}... and ${matches.length - 5} more matches${colors.reset}\n`;
2192
2266
  }
2193
2267
  output += "\n";
2194
2268
  });