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
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
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
|
-
//
|
|
1741
|
+
// STRONG BLOCK instruction - tell LLM to NOT guess
|
|
1666
1742
|
textContent += `
|
|
1667
|
-
|
|
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
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: ${
|
|
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 +=
|
|
1783
|
+
usedContext += targetContent.length;
|
|
1697
1784
|
debugLog("Added TARGET COMPONENT with line numbers", {
|
|
1698
|
-
path:
|
|
1699
|
-
lines:
|
|
1700
|
-
size:
|
|
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
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
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
|
-
//
|
|
1710
|
+
// STRONG BLOCK instruction - tell LLM to NOT guess
|
|
1635
1711
|
textContent += `
|
|
1636
|
-
|
|
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
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: ${
|
|
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 +=
|
|
1752
|
+
usedContext += targetContent.length;
|
|
1666
1753
|
debugLog("Added TARGET COMPONENT with line numbers", {
|
|
1667
|
-
path:
|
|
1668
|
-
lines:
|
|
1669
|
-
size:
|
|
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.
|
|
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",
|