sonance-brand-mcp 1.3.105 → 1.3.106

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.
@@ -624,6 +624,52 @@ function findElementLineInFile(
624
624
  }
625
625
  }
626
626
 
627
+ // ========== FALLBACK MATCHING (for non-Sonance codebases) ==========
628
+ // These are lower confidence but help find elements in ANY codebase
629
+
630
+ // FALLBACK 1: Flexible text search (just find the text anywhere)
631
+ if (focusedElement.textContent && focusedElement.textContent.trim().length > 5) {
632
+ const searchText = focusedElement.textContent.trim().substring(0, 30);
633
+ for (let i = 0; i < lines.length; i++) {
634
+ if (lines[i].includes(searchText)) {
635
+ debugLog("Fallback: Flexible text match found", { searchText, lineNumber: i + 1 });
636
+ return {
637
+ lineNumber: i + 1,
638
+ snippet: lines.slice(Math.max(0, i - 3), i + 5).join('\n'),
639
+ confidence: 'low',
640
+ matchedBy: `flexible text match "${searchText}${focusedElement.textContent.length > 30 ? '...' : ''}"`
641
+ };
642
+ }
643
+ }
644
+ }
645
+
646
+ // FALLBACK 2: Class fingerprint (use first few classes as unique identifier)
647
+ if (focusedElement.className && focusedElement.className.trim().length > 15) {
648
+ // Take first 2-3 distinctive classes as a fingerprint
649
+ const classes = focusedElement.className.trim().split(/\s+/);
650
+ // Filter out very common single-word utilities
651
+ const distinctiveClasses = classes.filter(c =>
652
+ c.length > 4 && !['flex', 'grid', 'block', 'hidden', 'relative', 'absolute'].includes(c)
653
+ ).slice(0, 3);
654
+
655
+ if (distinctiveClasses.length >= 2) {
656
+ const fingerprint = distinctiveClasses.join(' ');
657
+ for (let i = 0; i < lines.length; i++) {
658
+ // Check if line contains ALL fingerprint classes
659
+ const lineHasAll = distinctiveClasses.every(cls => lines[i].includes(cls));
660
+ if (lineHasAll) {
661
+ debugLog("Fallback: Class fingerprint match found", { fingerprint, lineNumber: i + 1 });
662
+ return {
663
+ lineNumber: i + 1,
664
+ snippet: lines.slice(Math.max(0, i - 3), i + 5).join('\n'),
665
+ confidence: 'low',
666
+ matchedBy: `class fingerprint "${fingerprint}"`
667
+ };
668
+ }
669
+ }
670
+ }
671
+ }
672
+
627
673
  return null;
628
674
  }
629
675
 
@@ -655,9 +701,14 @@ function scoreFilesForTextContent(
655
701
  filesCount: importedFiles.length
656
702
  });
657
703
 
658
- // Only search component files (where JSX text lives)
704
+ // Search component files and page files (where JSX text lives)
705
+ // Expanded to work with ANY codebase structure
659
706
  const componentFiles = importedFiles.filter(f =>
660
- f.path.includes('components/') || f.path.includes('/ui/')
707
+ f.path.includes('components/') ||
708
+ f.path.includes('/ui/') ||
709
+ f.path.includes('/_components/') ||
710
+ f.path.endsWith('.tsx') ||
711
+ f.path.endsWith('.jsx')
661
712
  );
662
713
 
663
714
  const results: { path: string; content: string; score: number; matchedTexts: string[]; firstMatchLine: number }[] = [];
@@ -753,9 +804,25 @@ function findElementInImportedFiles(
753
804
  const routeName = pageDir.split('/').pop() || ''; // e.g., "typography"
754
805
 
755
806
  for (const file of importedFiles) {
756
- // Focus on component files (where UI elements live)
807
+ // Focus on component/page files (where UI elements live)
757
808
  // Skip types, stores, utils, hooks - they don't contain JSX elements
758
- if (!file.path.includes('components/') && !file.path.includes('/ui/') && !file.path.includes('/_components/')) continue;
809
+ // Expanded to work with ANY codebase structure
810
+ const isComponentFile =
811
+ file.path.includes('components/') ||
812
+ file.path.includes('/ui/') ||
813
+ file.path.includes('/_components/') ||
814
+ file.path.endsWith('.tsx') ||
815
+ file.path.endsWith('.jsx');
816
+
817
+ // Skip non-component files but allow page files
818
+ if (!isComponentFile) continue;
819
+
820
+ // Skip known non-UI files
821
+ if (file.path.includes('/types') ||
822
+ file.path.includes('/hooks/') ||
823
+ file.path.includes('/utils/') ||
824
+ file.path.includes('/lib/') ||
825
+ file.path.includes('.d.ts')) continue;
759
826
 
760
827
  const result = findElementLineInFile(file.content, focusedElement);
761
828
  if (result && result.confidence !== 'low') {
@@ -2580,7 +2647,7 @@ ${linesWithNumbers}
2580
2647
  path: actualTargetFile.path,
2581
2648
  lines: targetContent.split('\n').length,
2582
2649
  size: targetContent.length,
2583
- wasRedirected: actualTargetFile.path !== recommendedFileContent.path
2650
+ wasRedirected: actualTargetFile.path !== recommendedFileContent?.path
2584
2651
  });
2585
2652
  } else if (pageContext.pageContent) {
2586
2653
  // Fallback: use page file if no recommended file
@@ -620,6 +620,52 @@ function findElementLineInFile(
620
620
  }
621
621
  }
622
622
 
623
+ // ========== FALLBACK MATCHING (for non-Sonance codebases) ==========
624
+ // These are lower confidence but help find elements in ANY codebase
625
+
626
+ // FALLBACK 1: Flexible text search (just find the text anywhere)
627
+ if (focusedElement.textContent && focusedElement.textContent.trim().length > 5) {
628
+ const searchText = focusedElement.textContent.trim().substring(0, 30);
629
+ for (let i = 0; i < lines.length; i++) {
630
+ if (lines[i].includes(searchText)) {
631
+ debugLog("Fallback: Flexible text match found", { searchText, lineNumber: i + 1 });
632
+ return {
633
+ lineNumber: i + 1,
634
+ snippet: lines.slice(Math.max(0, i - 3), i + 5).join('\n'),
635
+ confidence: 'low',
636
+ matchedBy: `flexible text match "${searchText}${focusedElement.textContent.length > 30 ? '...' : ''}"`
637
+ };
638
+ }
639
+ }
640
+ }
641
+
642
+ // FALLBACK 2: Class fingerprint (use first few classes as unique identifier)
643
+ if (focusedElement.className && focusedElement.className.trim().length > 15) {
644
+ // Take first 2-3 distinctive classes as a fingerprint
645
+ const classes = focusedElement.className.trim().split(/\s+/);
646
+ // Filter out very common single-word utilities
647
+ const distinctiveClasses = classes.filter(c =>
648
+ c.length > 4 && !['flex', 'grid', 'block', 'hidden', 'relative', 'absolute'].includes(c)
649
+ ).slice(0, 3);
650
+
651
+ if (distinctiveClasses.length >= 2) {
652
+ const fingerprint = distinctiveClasses.join(' ');
653
+ for (let i = 0; i < lines.length; i++) {
654
+ // Check if line contains ALL fingerprint classes
655
+ const lineHasAll = distinctiveClasses.every(cls => lines[i].includes(cls));
656
+ if (lineHasAll) {
657
+ debugLog("Fallback: Class fingerprint match found", { fingerprint, lineNumber: i + 1 });
658
+ return {
659
+ lineNumber: i + 1,
660
+ snippet: lines.slice(Math.max(0, i - 3), i + 5).join('\n'),
661
+ confidence: 'low',
662
+ matchedBy: `class fingerprint "${fingerprint}"`
663
+ };
664
+ }
665
+ }
666
+ }
667
+ }
668
+
623
669
  return null;
624
670
  }
625
671
 
@@ -651,9 +697,14 @@ function scoreFilesForTextContent(
651
697
  filesCount: importedFiles.length
652
698
  });
653
699
 
654
- // Only search component files (where JSX text lives)
700
+ // Search component files and page files (where JSX text lives)
701
+ // Expanded to work with ANY codebase structure
655
702
  const componentFiles = importedFiles.filter(f =>
656
- f.path.includes('components/') || f.path.includes('/ui/')
703
+ f.path.includes('components/') ||
704
+ f.path.includes('/ui/') ||
705
+ f.path.includes('/_components/') ||
706
+ f.path.endsWith('.tsx') ||
707
+ f.path.endsWith('.jsx')
657
708
  );
658
709
 
659
710
  const results: { path: string; content: string; score: number; matchedTexts: string[]; firstMatchLine: number }[] = [];
@@ -749,9 +800,25 @@ function findElementInImportedFiles(
749
800
  const routeName = pageDir.split('/').pop() || ''; // e.g., "typography"
750
801
 
751
802
  for (const file of importedFiles) {
752
- // Focus on component files (where UI elements live)
803
+ // Focus on component/page files (where UI elements live)
753
804
  // Skip types, stores, utils, hooks - they don't contain JSX elements
754
- if (!file.path.includes('components/') && !file.path.includes('/ui/') && !file.path.includes('/_components/')) continue;
805
+ // Expanded to work with ANY codebase structure
806
+ const isComponentFile =
807
+ file.path.includes('components/') ||
808
+ file.path.includes('/ui/') ||
809
+ file.path.includes('/_components/') ||
810
+ file.path.endsWith('.tsx') ||
811
+ file.path.endsWith('.jsx');
812
+
813
+ // Skip non-component files but allow page files
814
+ if (!isComponentFile) continue;
815
+
816
+ // Skip known non-UI files
817
+ if (file.path.includes('/types') ||
818
+ file.path.includes('/hooks/') ||
819
+ file.path.includes('/utils/') ||
820
+ file.path.includes('/lib/') ||
821
+ file.path.includes('.d.ts')) continue;
755
822
 
756
823
  const result = findElementLineInFile(file.content, focusedElement);
757
824
  if (result && result.confidence !== 'low') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.105",
3
+ "version": "1.3.106",
4
4
  "description": "MCP Server for Sonance Brand Guidelines and Component Library - gives Claude instant access to brand colors, typography, and UI components.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",