sonance-brand-mcp 1.3.41 → 1.3.42

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.
@@ -589,6 +589,27 @@ export async function POST(request: Request) {
589
589
  });
590
590
  }
591
591
 
592
+ // ========== SMART CONTEXT BUDGETING ==========
593
+ // Total budget: 100k chars (~25k tokens, safe for Claude)
594
+ // Priority: Recommended file (full) > Page file (limited) > Other components (dynamic)
595
+ const TOTAL_CONTEXT_BUDGET = 100000;
596
+ const MAX_RECOMMENDED_FILE = 50000; // 50k chars max for recommended file
597
+ const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
598
+ const MAX_GLOBALS_CSS = 1500;
599
+ const MAX_FILES = 25;
600
+
601
+ let usedContext = 0;
602
+
603
+ // Extract recommended file from component sources (to show first, avoid duplication)
604
+ let recommendedFileContent: { path: string; content: string } | null = null;
605
+ if (recommendedFile) {
606
+ const idx = pageContext.componentSources.findIndex(c => c.path === recommendedFile.path);
607
+ if (idx !== -1) {
608
+ recommendedFileContent = pageContext.componentSources[idx];
609
+ pageContext.componentSources.splice(idx, 1); // Remove to avoid duplication
610
+ }
611
+ }
612
+
592
613
  // Build text content
593
614
  let textContent = `VISION MODE EDIT REQUEST
594
615
 
@@ -604,41 +625,63 @@ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at (${el.coordinates.x}
604
625
  `;
605
626
  }
606
627
 
607
- textContent += `PAGE CONTEXT:
628
+ // ========== TARGET COMPONENT (RECOMMENDED FILE) - SHOWN FIRST ==========
629
+ if (recommendedFileContent) {
630
+ const content = recommendedFileContent.content.length > MAX_RECOMMENDED_FILE
631
+ ? recommendedFileContent.content.substring(0, MAX_RECOMMENDED_FILE) + "\n// ... (file truncated at 50k chars, showing component definition and main logic)"
632
+ : recommendedFileContent.content;
633
+
634
+ textContent += `═══════════════════════════════════════════════════════════════════════════════
635
+ ⚡ TARGET COMPONENT - YOU MUST EDIT THIS FILE
636
+ ═══════════════════════════════════════════════════════════════════════════════
637
+
638
+ This is the component that renders the UI you see in the screenshot.
639
+ File: ${recommendedFileContent.path}
640
+
641
+ \`\`\`tsx
642
+ ${content}
643
+ \`\`\`
644
+
645
+ `;
646
+ usedContext += content.length;
647
+ debugLog("Added TARGET COMPONENT to context", {
648
+ path: recommendedFileContent.path,
649
+ originalSize: recommendedFileContent.content.length,
650
+ includedSize: content.length
651
+ });
652
+ }
653
+
654
+ // ========== PAGE CONTEXT (wrapper - de-emphasized) ==========
655
+ const pageContentTruncated = pageContext.pageContent.substring(0, MAX_PAGE_FILE);
656
+ const pageWasTruncated = pageContext.pageContent.length > MAX_PAGE_FILE;
657
+
658
+ textContent += `PAGE CONTEXT (wrapper only${recommendedFileContent ? " - DO NOT edit this, edit the TARGET COMPONENT above" : ""}):
608
659
 
609
660
  Page File: ${pageContext.pageFile || "Not found"}
610
- ${pageContext.pageContent ? `\`\`\`tsx\n${pageContext.pageContent}\n\`\`\`` : ""}
661
+ ${pageContext.pageContent ? `\`\`\`tsx\n${pageContentTruncated}${pageWasTruncated ? "\n// ... (wrapper truncated)" : ""}\n\`\`\`` : ""}
611
662
 
612
663
  `;
664
+ usedContext += pageContentTruncated.length;
613
665
 
666
+ // ========== SUPPORTING COMPONENTS (dynamic budget) ==========
614
667
  if (pageContext.componentSources.length > 0) {
615
- // Smart truncation: prioritize first components (direct imports) and limit total context
616
- const MAX_TOTAL_CONTEXT = 80000; // ~80k chars to stay well under Claude's limit
617
- const MAX_PER_FILE_PRIORITY = 4000; // First 10 files get more space
618
- const MAX_PER_FILE_SECONDARY = 1500; // Remaining files get less
619
- const MAX_FILES = 30; // Limit total number of files
668
+ const remainingBudget = TOTAL_CONTEXT_BUDGET - usedContext - MAX_GLOBALS_CSS - 5000; // Reserve 5k for instructions
669
+ const filesToInclude = pageContext.componentSources.slice(0, MAX_FILES);
670
+ const perFileLimit = Math.max(1000, Math.floor(remainingBudget / Math.max(filesToInclude.length, 1)));
620
671
 
621
- let usedContext = pageContext.pageContent.length + pageContext.globalsCSS.length;
622
- const truncatedComponents = pageContext.componentSources.slice(0, MAX_FILES);
672
+ textContent += `SUPPORTING COMPONENTS (${filesToInclude.length} files, ~${Math.round(perFileLimit/1000)}k chars each):\n`;
623
673
 
624
- textContent += `IMPORTED COMPONENTS (${truncatedComponents.length} files, ${pageContext.componentSources.length > MAX_FILES ? `${pageContext.componentSources.length - MAX_FILES} omitted` : 'complete'}):\n`;
625
-
626
- for (let i = 0; i < truncatedComponents.length; i++) {
627
- const comp = truncatedComponents[i];
628
- const isPriority = i < 10; // First 10 files are priority (direct imports)
629
- const maxSize = isPriority ? MAX_PER_FILE_PRIORITY : MAX_PER_FILE_SECONDARY;
630
-
631
- // Stop if we've used too much context
632
- if (usedContext > MAX_TOTAL_CONTEXT) {
633
- textContent += `\n// ... (${truncatedComponents.length - i} more files omitted to stay within context limits)\n`;
674
+ for (const comp of filesToInclude) {
675
+ if (usedContext > TOTAL_CONTEXT_BUDGET - 10000) {
676
+ textContent += `\n// ... (remaining files omitted to stay within context limits)\n`;
634
677
  break;
635
678
  }
636
679
 
637
- const truncatedContent = comp.content.substring(0, maxSize);
638
- const wasTruncated = comp.content.length > maxSize;
680
+ const truncatedContent = comp.content.substring(0, perFileLimit);
681
+ const wasTruncated = comp.content.length > perFileLimit;
639
682
 
640
683
  textContent += `
641
- File: ${comp.path}${isPriority ? '' : ' (nested)'}
684
+ File: ${comp.path}
642
685
  \`\`\`tsx
643
686
  ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
644
687
  \`\`\`
@@ -647,42 +690,28 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
647
690
  }
648
691
  }
649
692
 
693
+ // ========== GLOBALS CSS ==========
694
+ const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
650
695
  textContent += `
651
- GLOBALS.CSS (relevant theme variables):
696
+ GLOBALS.CSS (theme variables):
652
697
  \`\`\`css
653
- ${pageContext.globalsCSS.substring(0, 2000)}${pageContext.globalsCSS.length > 2000 ? "\n/* ... (truncated) */" : ""}
698
+ ${globalsTruncated}${pageContext.globalsCSS.length > MAX_GLOBALS_CSS ? "\n/* ... (truncated) */" : ""}
654
699
  \`\`\`
655
700
 
656
701
  `;
657
702
 
658
- // Add recommendation block just before VALID FILES if we have one
659
- if (recommendedFile) {
660
- textContent += `═══════════════════════════════════════════════════════════════════════════════
661
- ⚡ RECOMMENDED FILE TO EDIT
662
- ═══════════════════════════════════════════════════════════════════════════════
663
-
664
- Based on the screenshot analysis, the component you should edit is:
665
- **${recommendedFile.path}**
666
- Reason: ${recommendedFile.reason}
667
-
668
- The page file (${pageContext.pageFile}) is just a wrapper - the actual UI elements are in the component above.
669
- YOU MUST edit the recommended file unless you have a specific reason not to.
670
-
671
- `;
672
- }
673
-
674
- // Build VALID FILES list with recommended file first and marked
703
+ // ========== VALID FILES LIST ==========
675
704
  const validFilesList: string[] = [];
676
705
  const recommendedPath = recommendedFile?.path;
677
706
 
678
707
  // Add recommended file first with marker
679
708
  if (recommendedPath) {
680
- validFilesList.push(`- ${recommendedPath} (*** RECOMMENDED - EDIT THIS FILE ***)`);
709
+ validFilesList.push(`- ${recommendedPath} (*** TARGET - EDIT THIS FILE ***)`);
681
710
  }
682
711
 
683
712
  // Add page file (if not the recommended file)
684
713
  if (pageContext.pageFile && pageContext.pageFile !== recommendedPath) {
685
- validFilesList.push(`- ${pageContext.pageFile}`);
714
+ validFilesList.push(`- ${pageContext.pageFile} (wrapper only)`);
686
715
  }
687
716
 
688
717
  // Add other component sources (excluding recommended file)
@@ -697,14 +726,14 @@ ${validFilesList.join("\n")}
697
726
 
698
727
  INSTRUCTIONS:
699
728
  1. Look at the screenshot and identify elements mentioned in the user's request
700
- 2. Review the code to understand current implementation
729
+ 2. The TARGET COMPONENT shown first contains the UI - EDIT THAT FILE
701
730
  3. Make SURGICAL EDITS - change only the specific lines needed
702
731
  4. PRESERVE all existing logic, hooks, API calls, and error handling
703
- 5. Return the FULL file content (no truncation, no "// ... existing ..." comments)
732
+ 5. Return patches in the specified format (search/replace)
704
733
  6. Only use file paths from the VALID FILES list above
705
- 7. ALWAYS prioritize the (*** RECOMMENDED ***) file if one is marked - it contains the UI you see in the screenshot
734
+ 7. DO NOT edit the page wrapper unless the TARGET COMPONENT is unavailable
706
735
 
707
- CRITICAL: Your modified file should have approximately the same number of lines as the original. If the original has 200 lines and your output has 50 lines, you have made a mistake.`;
736
+ CRITICAL: Edit the TARGET COMPONENT (marked with ***), not the page wrapper.`;
708
737
 
709
738
  messageContent.push({
710
739
  type: "text",
@@ -599,6 +599,27 @@ export async function POST(request: Request) {
599
599
  });
600
600
  }
601
601
 
602
+ // ========== SMART CONTEXT BUDGETING ==========
603
+ // Total budget: 100k chars (~25k tokens, safe for Claude)
604
+ // Priority: Recommended file (full) > Page file (limited) > Other components (dynamic)
605
+ const TOTAL_CONTEXT_BUDGET = 100000;
606
+ const MAX_RECOMMENDED_FILE = 50000; // 50k chars max for recommended file
607
+ const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
608
+ const MAX_GLOBALS_CSS = 1500;
609
+ const MAX_FILES = 25;
610
+
611
+ let usedContext = 0;
612
+
613
+ // Extract recommended file from component sources (to show first, avoid duplication)
614
+ let recommendedFileContent: { path: string; content: string } | null = null;
615
+ if (recommendedFile) {
616
+ const idx = pageContext.componentSources.findIndex(c => c.path === recommendedFile.path);
617
+ if (idx !== -1) {
618
+ recommendedFileContent = pageContext.componentSources[idx];
619
+ pageContext.componentSources.splice(idx, 1); // Remove to avoid duplication
620
+ }
621
+ }
622
+
602
623
  // Build text content
603
624
  let textContent = `VISION MODE EDIT REQUEST
604
625
 
@@ -614,41 +635,63 @@ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at (${el.coordinates.x}
614
635
  `;
615
636
  }
616
637
 
617
- textContent += `PAGE CONTEXT:
638
+ // ========== TARGET COMPONENT (RECOMMENDED FILE) - SHOWN FIRST ==========
639
+ if (recommendedFileContent) {
640
+ const content = recommendedFileContent.content.length > MAX_RECOMMENDED_FILE
641
+ ? recommendedFileContent.content.substring(0, MAX_RECOMMENDED_FILE) + "\n// ... (file truncated at 50k chars, showing component definition and main logic)"
642
+ : recommendedFileContent.content;
643
+
644
+ textContent += `═══════════════════════════════════════════════════════════════════════════════
645
+ ⚡ TARGET COMPONENT - YOU MUST EDIT THIS FILE
646
+ ═══════════════════════════════════════════════════════════════════════════════
647
+
648
+ This is the component that renders the UI you see in the screenshot.
649
+ File: ${recommendedFileContent.path}
650
+
651
+ \`\`\`tsx
652
+ ${content}
653
+ \`\`\`
654
+
655
+ `;
656
+ usedContext += content.length;
657
+ debugLog("Added TARGET COMPONENT to context", {
658
+ path: recommendedFileContent.path,
659
+ originalSize: recommendedFileContent.content.length,
660
+ includedSize: content.length
661
+ });
662
+ }
663
+
664
+ // ========== PAGE CONTEXT (wrapper - de-emphasized) ==========
665
+ const pageContentTruncated = pageContext.pageContent.substring(0, MAX_PAGE_FILE);
666
+ const pageWasTruncated = pageContext.pageContent.length > MAX_PAGE_FILE;
667
+
668
+ textContent += `PAGE CONTEXT (wrapper only${recommendedFileContent ? " - DO NOT edit this, edit the TARGET COMPONENT above" : ""}):
618
669
 
619
670
  Page File: ${pageContext.pageFile || "Not found"}
620
- ${pageContext.pageContent ? `\`\`\`tsx\n${pageContext.pageContent}\n\`\`\`` : ""}
671
+ ${pageContext.pageContent ? `\`\`\`tsx\n${pageContentTruncated}${pageWasTruncated ? "\n// ... (wrapper truncated)" : ""}\n\`\`\`` : ""}
621
672
 
622
673
  `;
674
+ usedContext += pageContentTruncated.length;
623
675
 
676
+ // ========== SUPPORTING COMPONENTS (dynamic budget) ==========
624
677
  if (pageContext.componentSources.length > 0) {
625
- // Smart truncation: prioritize first components (direct imports) and limit total context
626
- const MAX_TOTAL_CONTEXT = 80000; // ~80k chars to stay well under Claude's limit
627
- const MAX_PER_FILE_PRIORITY = 4000; // First 10 files get more space
628
- const MAX_PER_FILE_SECONDARY = 1500; // Remaining files get less
629
- const MAX_FILES = 30; // Limit total number of files
678
+ const remainingBudget = TOTAL_CONTEXT_BUDGET - usedContext - MAX_GLOBALS_CSS - 5000; // Reserve 5k for instructions
679
+ const filesToInclude = pageContext.componentSources.slice(0, MAX_FILES);
680
+ const perFileLimit = Math.max(1000, Math.floor(remainingBudget / Math.max(filesToInclude.length, 1)));
630
681
 
631
- let usedContext = pageContext.pageContent.length + pageContext.globalsCSS.length;
632
- const truncatedComponents = pageContext.componentSources.slice(0, MAX_FILES);
682
+ textContent += `SUPPORTING COMPONENTS (${filesToInclude.length} files, ~${Math.round(perFileLimit/1000)}k chars each):\n`;
633
683
 
634
- textContent += `IMPORTED COMPONENTS (${truncatedComponents.length} files, ${pageContext.componentSources.length > MAX_FILES ? `${pageContext.componentSources.length - MAX_FILES} omitted` : 'complete'}):\n`;
635
-
636
- for (let i = 0; i < truncatedComponents.length; i++) {
637
- const comp = truncatedComponents[i];
638
- const isPriority = i < 10; // First 10 files are priority (direct imports)
639
- const maxSize = isPriority ? MAX_PER_FILE_PRIORITY : MAX_PER_FILE_SECONDARY;
640
-
641
- // Stop if we've used too much context
642
- if (usedContext > MAX_TOTAL_CONTEXT) {
643
- textContent += `\n// ... (${truncatedComponents.length - i} more files omitted to stay within context limits)\n`;
684
+ for (const comp of filesToInclude) {
685
+ if (usedContext > TOTAL_CONTEXT_BUDGET - 10000) {
686
+ textContent += `\n// ... (remaining files omitted to stay within context limits)\n`;
644
687
  break;
645
688
  }
646
689
 
647
- const truncatedContent = comp.content.substring(0, maxSize);
648
- const wasTruncated = comp.content.length > maxSize;
690
+ const truncatedContent = comp.content.substring(0, perFileLimit);
691
+ const wasTruncated = comp.content.length > perFileLimit;
649
692
 
650
693
  textContent += `
651
- File: ${comp.path}${isPriority ? '' : ' (nested)'}
694
+ File: ${comp.path}
652
695
  \`\`\`tsx
653
696
  ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
654
697
  \`\`\`
@@ -657,42 +700,28 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
657
700
  }
658
701
  }
659
702
 
703
+ // ========== GLOBALS CSS ==========
704
+ const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
660
705
  textContent += `
661
- GLOBALS.CSS (relevant theme variables):
706
+ GLOBALS.CSS (theme variables):
662
707
  \`\`\`css
663
- ${pageContext.globalsCSS.substring(0, 2000)}${pageContext.globalsCSS.length > 2000 ? "\n/* ... (truncated) */" : ""}
708
+ ${globalsTruncated}${pageContext.globalsCSS.length > MAX_GLOBALS_CSS ? "\n/* ... (truncated) */" : ""}
664
709
  \`\`\`
665
710
 
666
711
  `;
667
712
 
668
- // Add recommendation block just before VALID FILES if we have one
669
- if (recommendedFile) {
670
- textContent += `═══════════════════════════════════════════════════════════════════════════════
671
- ⚡ RECOMMENDED FILE TO EDIT
672
- ═══════════════════════════════════════════════════════════════════════════════
673
-
674
- Based on the screenshot analysis, the component you should edit is:
675
- **${recommendedFile.path}**
676
- Reason: ${recommendedFile.reason}
677
-
678
- The page file (${pageContext.pageFile}) is just a wrapper - the actual UI elements are in the component above.
679
- YOU MUST edit the recommended file unless you have a specific reason not to.
680
-
681
- `;
682
- }
683
-
684
- // Build VALID FILES list with recommended file first and marked
713
+ // ========== VALID FILES LIST ==========
685
714
  const validFilesList: string[] = [];
686
715
  const recommendedPath = recommendedFile?.path;
687
716
 
688
717
  // Add recommended file first with marker
689
718
  if (recommendedPath) {
690
- validFilesList.push(`- ${recommendedPath} (*** RECOMMENDED - EDIT THIS FILE ***)`);
719
+ validFilesList.push(`- ${recommendedPath} (*** TARGET - EDIT THIS FILE ***)`);
691
720
  }
692
721
 
693
722
  // Add page file (if not the recommended file)
694
723
  if (pageContext.pageFile && pageContext.pageFile !== recommendedPath) {
695
- validFilesList.push(`- ${pageContext.pageFile}`);
724
+ validFilesList.push(`- ${pageContext.pageFile} (wrapper only)`);
696
725
  }
697
726
 
698
727
  // Add other component sources (excluding recommended file)
@@ -707,14 +736,14 @@ ${validFilesList.join("\n")}
707
736
 
708
737
  INSTRUCTIONS:
709
738
  1. Look at the screenshot and identify elements mentioned in the user's request
710
- 2. Review the provided code to understand current implementation
739
+ 2. The TARGET COMPONENT shown first contains the UI - EDIT THAT FILE
711
740
  3. Choose which of the VALID FILES above need modifications
712
741
  4. Generate complete modified code for each file
713
742
  5. Provide previewCSS for immediate visual feedback
714
743
  6. Return as JSON in the specified format
715
- 7. ALWAYS prioritize the (*** RECOMMENDED ***) file if one is marked - it contains the UI you see in the screenshot
744
+ 7. DO NOT edit the page wrapper unless the TARGET COMPONENT is unavailable
716
745
 
717
- CRITICAL: Only use file paths from the VALID FILES list above. Do NOT create new files.`;
746
+ CRITICAL: Edit the TARGET COMPONENT (marked with ***), not the page wrapper.`;
718
747
 
719
748
  messageContent.push({
720
749
  type: "text",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.41",
3
+ "version": "1.3.42",
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",