sonance-brand-mcp 1.3.91 → 1.3.92

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.
@@ -500,6 +500,50 @@ function findElementLineInFile(
500
500
  return null;
501
501
  }
502
502
 
503
+ /**
504
+ * Search imported component files for the focused element
505
+ * Reuses findElementLineInFile() for consistent detection
506
+ *
507
+ * This is called when the element cannot be found in the main target file,
508
+ * allowing us to redirect to the actual file containing the element.
509
+ */
510
+ function findElementInImportedFiles(
511
+ focusedElement: VisionFocusedElement,
512
+ importedFiles: { path: string; content: string }[]
513
+ ): { path: string; lineNumber: number; matchedBy: string; content: string; confidence: string } | null {
514
+ debugLog("Searching imported components for element", {
515
+ elementType: focusedElement.type,
516
+ textContent: focusedElement.textContent?.substring(0, 30),
517
+ filesCount: importedFiles.length
518
+ });
519
+
520
+ for (const file of importedFiles) {
521
+ // Focus on component files (where UI elements live)
522
+ // Skip types, stores, utils, hooks - they don't contain JSX elements
523
+ if (!file.path.includes('components/') && !file.path.includes('/ui/')) continue;
524
+
525
+ const result = findElementLineInFile(file.content, focusedElement);
526
+ if (result && result.confidence !== 'low') {
527
+ debugLog("Found element in imported component", {
528
+ path: file.path,
529
+ lineNumber: result.lineNumber,
530
+ matchedBy: result.matchedBy,
531
+ confidence: result.confidence
532
+ });
533
+ return {
534
+ path: file.path,
535
+ lineNumber: result.lineNumber,
536
+ matchedBy: result.matchedBy,
537
+ content: file.content,
538
+ confidence: result.confidence
539
+ };
540
+ }
541
+ }
542
+
543
+ debugLog("Element not found in any imported component files");
544
+ return null;
545
+ }
546
+
503
547
  /**
504
548
  * PHASE 0: Deterministic Element ID Search (Cursor-style)
505
549
  * Grep entire codebase for the element ID. If found in multiple files,
@@ -1604,6 +1648,8 @@ User Request: "${userPrompt}"
1604
1648
  // Search for focused element in the file using multiple strategies
1605
1649
  // Priority: DOM id > textContent > className patterns
1606
1650
  let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
1651
+ let actualTargetFile = recommendedFileContent; // May change if we redirect
1652
+
1607
1653
  if (focusedElements && focusedElements.length > 0) {
1608
1654
  for (const el of focusedElements) {
1609
1655
  elementLocation = findElementLineInFile(content, el);
@@ -1617,6 +1663,35 @@ User Request: "${userPrompt}"
1617
1663
  break;
1618
1664
  }
1619
1665
  }
1666
+
1667
+ // DYNAMIC IMPORT SEARCH: If not found in main file, search imported components
1668
+ if (!elementLocation) {
1669
+ debugLog("Element not in main file, searching imported components...", {
1670
+ mainFile: recommendedFileContent.path,
1671
+ importedFilesCount: pageContext.componentSources.length
1672
+ });
1673
+
1674
+ const importedMatch = findElementInImportedFiles(
1675
+ focusedElements[0],
1676
+ pageContext.componentSources
1677
+ );
1678
+
1679
+ if (importedMatch) {
1680
+ debugLog("REDIRECT: Element found in imported component", {
1681
+ originalFile: recommendedFileContent.path,
1682
+ redirectTo: importedMatch.path,
1683
+ matchedBy: importedMatch.matchedBy,
1684
+ lineNumber: importedMatch.lineNumber
1685
+ });
1686
+
1687
+ // Switch target file to where element actually is
1688
+ actualTargetFile = {
1689
+ path: importedMatch.path,
1690
+ content: importedMatch.content
1691
+ };
1692
+ elementLocation = findElementLineInFile(importedMatch.content, focusedElements[0]);
1693
+ }
1694
+ }
1620
1695
  }
1621
1696
 
1622
1697
  // Build focused elements section with precise targeting info
@@ -1649,10 +1724,11 @@ ${elementLocation.snippet}
1649
1724
 
1650
1725
  `;
1651
1726
  } else {
1652
- textContent += `\n`;
1653
- debugLog("WARNING: Could not locate focused element in file", {
1654
- file: recommendedFileContent.path,
1655
- hint: "The clicked element may be rendered by a child component (e.g., DialogContent, Button component). Check the component's source file instead.",
1727
+ // Element NOT found in main file OR any imported components
1728
+ // BLOCK the LLM from guessing - require empty modifications
1729
+ debugLog("BLOCK: Could not locate focused element anywhere", {
1730
+ mainFile: recommendedFileContent.path,
1731
+ searchedImports: pageContext.componentSources.length,
1656
1732
  focusedElements: focusedElements.map(el => ({
1657
1733
  name: el.name,
1658
1734
  type: el.type,
@@ -1662,27 +1738,38 @@ ${elementLocation.snippet}
1662
1738
  }))
1663
1739
  });
1664
1740
 
1665
- // Add a hint to the LLM that we couldn't precisely locate the element
1741
+ // STRONG BLOCK instruction - tell LLM to NOT guess
1666
1742
  textContent += `
1667
- ⚠️ WARNING: Could not precisely locate the clicked element in this file.
1743
+ ⛔ STOP: CANNOT LOCATE THE CLICKED ELEMENT
1744
+
1745
+ The user clicked on a specific element, but it could NOT be found in:
1746
+ - ${recommendedFileContent.path} (main target file)
1747
+ - Any of the ${pageContext.componentSources.length} imported component files
1748
+
1668
1749
  The element may be:
1669
- - Rendered by a child component (e.g., DialogContent, Button)
1670
- - Dynamically generated
1671
- - In a different file imported by this component
1750
+ - Deeply nested in a component not in the import tree
1751
+ - Dynamically generated at runtime
1752
+ - Part of a third-party library component
1672
1753
 
1673
- Please proceed with caution and verify you're modifying the correct element.
1754
+ DO NOT GUESS. Return this exact response:
1755
+ {
1756
+ "modifications": [],
1757
+ "explanation": "Could not locate the clicked element in this file or any of its ${pageContext.componentSources.length} imported components. The element may be rendered by a deeply nested or third-party component."
1758
+ }
1674
1759
 
1675
1760
  `;
1676
1761
  }
1677
1762
  }
1678
1763
 
1679
1764
  // Add line numbers to make it easy for LLM to reference exact code
1680
- const linesWithNumbers = content.split('\n').map((line, i) =>
1765
+ // Use actualTargetFile which may have been redirected to an imported component
1766
+ const targetContent = actualTargetFile.content;
1767
+ const linesWithNumbers = targetContent.split('\n').map((line, i) =>
1681
1768
  `${String(i + 1).padStart(4, ' ')}| ${line}`
1682
1769
  ).join('\n');
1683
1770
 
1684
1771
  textContent += `═══════════════════════════════════════════════════════════════════════════════
1685
- FILE TO EDIT: ${recommendedFileContent.path}
1772
+ FILE TO EDIT: ${actualTargetFile.path}
1686
1773
  ═══════════════════════════════════════════════════════════════════════════════
1687
1774
 
1688
1775
  IMPORTANT: Copy code EXACTLY as shown below (including line numbers for reference).
@@ -1693,11 +1780,12 @@ ${linesWithNumbers}
1693
1780
  \`\`\`
1694
1781
 
1695
1782
  `;
1696
- usedContext += content.length;
1783
+ usedContext += targetContent.length;
1697
1784
  debugLog("Added TARGET COMPONENT with line numbers", {
1698
- path: recommendedFileContent.path,
1699
- lines: content.split('\n').length,
1700
- size: content.length
1785
+ path: actualTargetFile.path,
1786
+ lines: targetContent.split('\n').length,
1787
+ size: targetContent.length,
1788
+ wasRedirected: actualTargetFile.path !== recommendedFileContent.path
1701
1789
  });
1702
1790
  } else if (pageContext.pageContent) {
1703
1791
  // Fallback: use page file if no recommended file
@@ -496,6 +496,50 @@ function findElementLineInFile(
496
496
  return null;
497
497
  }
498
498
 
499
+ /**
500
+ * Search imported component files for the focused element
501
+ * Reuses findElementLineInFile() for consistent detection
502
+ *
503
+ * This is called when the element cannot be found in the main target file,
504
+ * allowing us to redirect to the actual file containing the element.
505
+ */
506
+ function findElementInImportedFiles(
507
+ focusedElement: VisionFocusedElement,
508
+ importedFiles: { path: string; content: string }[]
509
+ ): { path: string; lineNumber: number; matchedBy: string; content: string; confidence: string } | null {
510
+ debugLog("Searching imported components for element", {
511
+ elementType: focusedElement.type,
512
+ textContent: focusedElement.textContent?.substring(0, 30),
513
+ filesCount: importedFiles.length
514
+ });
515
+
516
+ for (const file of importedFiles) {
517
+ // Focus on component files (where UI elements live)
518
+ // Skip types, stores, utils, hooks - they don't contain JSX elements
519
+ if (!file.path.includes('components/') && !file.path.includes('/ui/')) continue;
520
+
521
+ const result = findElementLineInFile(file.content, focusedElement);
522
+ if (result && result.confidence !== 'low') {
523
+ debugLog("Found element in imported component", {
524
+ path: file.path,
525
+ lineNumber: result.lineNumber,
526
+ matchedBy: result.matchedBy,
527
+ confidence: result.confidence
528
+ });
529
+ return {
530
+ path: file.path,
531
+ lineNumber: result.lineNumber,
532
+ matchedBy: result.matchedBy,
533
+ content: file.content,
534
+ confidence: result.confidence
535
+ };
536
+ }
537
+ }
538
+
539
+ debugLog("Element not found in any imported component files");
540
+ return null;
541
+ }
542
+
499
543
  /**
500
544
  * PHASE 0: Deterministic Element ID Search (Cursor-style)
501
545
  * Grep entire codebase for the element ID. If found in multiple files,
@@ -1573,6 +1617,8 @@ User Request: "${userPrompt}"
1573
1617
  // Search for focused element in the file using multiple strategies
1574
1618
  // Priority: DOM id > textContent > className patterns
1575
1619
  let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
1620
+ let actualTargetFile = recommendedFileContent; // May change if we redirect
1621
+
1576
1622
  if (focusedElements && focusedElements.length > 0) {
1577
1623
  for (const el of focusedElements) {
1578
1624
  elementLocation = findElementLineInFile(content, el);
@@ -1586,6 +1632,35 @@ User Request: "${userPrompt}"
1586
1632
  break;
1587
1633
  }
1588
1634
  }
1635
+
1636
+ // DYNAMIC IMPORT SEARCH: If not found in main file, search imported components
1637
+ if (!elementLocation) {
1638
+ debugLog("Element not in main file, searching imported components...", {
1639
+ mainFile: recommendedFileContent.path,
1640
+ importedFilesCount: pageContext.componentSources.length
1641
+ });
1642
+
1643
+ const importedMatch = findElementInImportedFiles(
1644
+ focusedElements[0],
1645
+ pageContext.componentSources
1646
+ );
1647
+
1648
+ if (importedMatch) {
1649
+ debugLog("REDIRECT: Element found in imported component", {
1650
+ originalFile: recommendedFileContent.path,
1651
+ redirectTo: importedMatch.path,
1652
+ matchedBy: importedMatch.matchedBy,
1653
+ lineNumber: importedMatch.lineNumber
1654
+ });
1655
+
1656
+ // Switch target file to where element actually is
1657
+ actualTargetFile = {
1658
+ path: importedMatch.path,
1659
+ content: importedMatch.content
1660
+ };
1661
+ elementLocation = findElementLineInFile(importedMatch.content, focusedElements[0]);
1662
+ }
1663
+ }
1589
1664
  }
1590
1665
 
1591
1666
  // Build focused elements section with precise targeting info
@@ -1618,10 +1693,11 @@ ${elementLocation.snippet}
1618
1693
 
1619
1694
  `;
1620
1695
  } else {
1621
- textContent += `\n`;
1622
- debugLog("WARNING: Could not locate focused element in file", {
1623
- file: recommendedFileContent.path,
1624
- hint: "The clicked element may be rendered by a child component (e.g., DialogContent, Button component). Check the component's source file instead.",
1696
+ // Element NOT found in main file OR any imported components
1697
+ // BLOCK the LLM from guessing - require empty modifications
1698
+ debugLog("BLOCK: Could not locate focused element anywhere", {
1699
+ mainFile: recommendedFileContent.path,
1700
+ searchedImports: pageContext.componentSources.length,
1625
1701
  focusedElements: focusedElements.map(el => ({
1626
1702
  name: el.name,
1627
1703
  type: el.type,
@@ -1631,27 +1707,38 @@ ${elementLocation.snippet}
1631
1707
  }))
1632
1708
  });
1633
1709
 
1634
- // Add a hint to the LLM that we couldn't precisely locate the element
1710
+ // STRONG BLOCK instruction - tell LLM to NOT guess
1635
1711
  textContent += `
1636
- ⚠️ WARNING: Could not precisely locate the clicked element in this file.
1712
+ ⛔ STOP: CANNOT LOCATE THE CLICKED ELEMENT
1713
+
1714
+ The user clicked on a specific element, but it could NOT be found in:
1715
+ - ${recommendedFileContent.path} (main target file)
1716
+ - Any of the ${pageContext.componentSources.length} imported component files
1717
+
1637
1718
  The element may be:
1638
- - Rendered by a child component (e.g., DialogContent, Button)
1639
- - Dynamically generated
1640
- - In a different file imported by this component
1719
+ - Deeply nested in a component not in the import tree
1720
+ - Dynamically generated at runtime
1721
+ - Part of a third-party library component
1641
1722
 
1642
- Please proceed with caution and verify you're modifying the correct element.
1723
+ DO NOT GUESS. Return this exact response:
1724
+ {
1725
+ "modifications": [],
1726
+ "explanation": "Could not locate the clicked element in this file or any of its ${pageContext.componentSources.length} imported components. The element may be rendered by a deeply nested or third-party component."
1727
+ }
1643
1728
 
1644
1729
  `;
1645
1730
  }
1646
1731
  }
1647
1732
 
1648
1733
  // Add line numbers to make it easy for LLM to reference exact code
1649
- const linesWithNumbers = content.split('\n').map((line, i) =>
1734
+ // Use actualTargetFile which may have been redirected to an imported component
1735
+ const targetContent = actualTargetFile.content;
1736
+ const linesWithNumbers = targetContent.split('\n').map((line, i) =>
1650
1737
  `${String(i + 1).padStart(4, ' ')}| ${line}`
1651
1738
  ).join('\n');
1652
1739
 
1653
1740
  textContent += `═══════════════════════════════════════════════════════════════════════════════
1654
- FILE TO EDIT: ${recommendedFileContent.path}
1741
+ FILE TO EDIT: ${actualTargetFile.path}
1655
1742
  ═══════════════════════════════════════════════════════════════════════════════
1656
1743
 
1657
1744
  IMPORTANT: Copy code EXACTLY as shown below (including line numbers for reference).
@@ -1662,11 +1749,12 @@ ${linesWithNumbers}
1662
1749
  \`\`\`
1663
1750
 
1664
1751
  `;
1665
- usedContext += content.length;
1752
+ usedContext += targetContent.length;
1666
1753
  debugLog("Added TARGET COMPONENT with line numbers", {
1667
- path: recommendedFileContent.path,
1668
- lines: content.split('\n').length,
1669
- size: content.length
1754
+ path: actualTargetFile.path,
1755
+ lines: targetContent.split('\n').length,
1756
+ size: targetContent.length,
1757
+ wasRedirected: actualTargetFile.path !== recommendedFileContent.path
1670
1758
  });
1671
1759
  } else if (pageContext.pageContent) {
1672
1760
  // Fallback: use page file if no recommended file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.91",
3
+ "version": "1.3.92",
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",