sonance-brand-mcp 1.3.55 → 1.3.57

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.
@@ -686,8 +686,9 @@ export async function POST(request: Request) {
686
686
  const messageContent: Anthropic.MessageCreateParams["messages"][0]["content"] = [];
687
687
 
688
688
  // Add screenshot if provided
689
+ let base64Data = "";
689
690
  if (screenshot) {
690
- const base64Data = screenshot.split(",")[1] || screenshot;
691
+ base64Data = screenshot.split(",")[1] || screenshot;
691
692
  messageContent.push({
692
693
  type: "image",
693
694
  source: {
@@ -698,6 +699,141 @@ export async function POST(request: Request) {
698
699
  });
699
700
  }
700
701
 
702
+ // ========== PHASE A: VISUAL PROBLEM ANALYSIS ==========
703
+ // Before generating patches, analyze WHAT is visually wrong
704
+ // This forces the LLM to articulate the problem before trying to fix it
705
+ interface VisualAnalysis {
706
+ element: string;
707
+ elementText: string; // The visible text content of the element (for finding in code)
708
+ problem: string;
709
+ changeType: "styling" | "structural" | "content" | "other";
710
+ currentClasses?: string; // For styling changes: what classes appear to be applied
711
+ classChange?: {
712
+ remove?: string[]; // Classes to remove
713
+ add?: string[]; // Classes to add
714
+ };
715
+ structuralChange?: string; // For structural changes: what to add/remove/wrap
716
+ solution: string;
717
+ confidence: "high" | "medium" | "low";
718
+ }
719
+
720
+ let visualAnalysis: VisualAnalysis | null = null;
721
+
722
+ if (screenshot && userPrompt) {
723
+ debugLog("Phase A: Starting visual problem analysis");
724
+
725
+ const analysisClient = new Anthropic({ apiKey });
726
+
727
+ try {
728
+ const analysisResponse = await analysisClient.messages.create({
729
+ model: "claude-sonnet-4-20250514",
730
+ max_tokens: 1024,
731
+ messages: [
732
+ {
733
+ role: "user",
734
+ content: [
735
+ {
736
+ type: "image",
737
+ source: {
738
+ type: "base64",
739
+ media_type: "image/png",
740
+ data: base64Data,
741
+ },
742
+ },
743
+ {
744
+ type: "text",
745
+ text: `You are analyzing a UI screenshot to understand what the user wants changed.
746
+
747
+ User request: "${userPrompt}"
748
+
749
+ ${focusedElements && focusedElements.length > 0 ? `User clicked on these elements:
750
+ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at position (${el.coordinates.x}, ${el.coordinates.y})`).join("\n")}
751
+ ` : ""}
752
+
753
+ Analyze the request and determine:
754
+ 1. ELEMENT: Which specific UI element needs to change? (describe location and appearance)
755
+ 2. ELEMENT TEXT: What text is visible inside this element? (EXACT text for finding in code)
756
+ 3. PROBLEM: What is wrong or what does the user want changed?
757
+ 4. CHANGE TYPE: Is this a "styling" (colors/size/spacing), "structural" (add/remove elements), "content" (text changes), or "other" change?
758
+ 5. For STYLING changes: What Tailwind classes should be REMOVED and what should be ADDED?
759
+ 6. For STRUCTURAL changes: What element should be added, removed, or wrapped?
760
+ 7. SOLUTION: Describe the complete fix
761
+ 8. CONFIDENCE: high/medium/low
762
+
763
+ IMPORTANT for styling fixes:
764
+ - If text is invisible on a background, typically REMOVE the problematic text color class and ADD a contrasting one
765
+ - Common fix for invisible text on colored backgrounds: REMOVE "text-accent-foreground" or similar, ADD "text-white" or "text-black"
766
+ - Be SPECIFIC about exact class names
767
+
768
+ Return ONLY valid JSON:
769
+ {
770
+ "element": "description of the UI element",
771
+ "elementText": "exact visible text in the element",
772
+ "problem": "what is wrong",
773
+ "changeType": "styling",
774
+ "currentClasses": "classes that appear to be applied (best guess)",
775
+ "classChange": {
776
+ "remove": ["text-accent-foreground"],
777
+ "add": ["text-white"]
778
+ },
779
+ "solution": "complete description of the fix",
780
+ "confidence": "high"
781
+ }
782
+
783
+ For structural changes:
784
+ {
785
+ "element": "description of the UI element",
786
+ "elementText": "text near the element",
787
+ "problem": "what is wrong",
788
+ "changeType": "structural",
789
+ "structuralChange": "Add a new button after the existing one",
790
+ "solution": "complete description of the fix",
791
+ "confidence": "high"
792
+ }`,
793
+ },
794
+ ],
795
+ },
796
+ ],
797
+ });
798
+
799
+ const analysisText = analysisResponse.content.find((block) => block.type === "text");
800
+ if (analysisText && analysisText.type === "text") {
801
+ // Parse the JSON response
802
+ let jsonText = analysisText.text.trim();
803
+ // Extract JSON if wrapped in code blocks
804
+ const jsonMatch = jsonText.match(/```(?:json)?\s*([\s\S]*?)\s*```/) || jsonText.match(/(\{[\s\S]*\})/);
805
+ if (jsonMatch) {
806
+ jsonText = jsonMatch[1];
807
+ }
808
+
809
+ try {
810
+ visualAnalysis = JSON.parse(jsonText) as VisualAnalysis;
811
+ debugLog("Phase A: Visual problem analysis complete", visualAnalysis);
812
+
813
+ // If low confidence, return early with clarification request
814
+ if (visualAnalysis.confidence === "low") {
815
+ debugLog("Phase A: Low confidence - requesting clarification");
816
+ return NextResponse.json({
817
+ success: false,
818
+ needsClarification: true,
819
+ analysis: visualAnalysis,
820
+ message: `I can see "${visualAnalysis.element}" but I'm not certain about the problem: "${visualAnalysis.problem}". Can you be more specific about what needs to change?`,
821
+ });
822
+ }
823
+ } catch (parseError) {
824
+ debugLog("Phase A: Failed to parse analysis response", {
825
+ error: String(parseError),
826
+ response: jsonText.substring(0, 500)
827
+ });
828
+ // Continue without analysis if parsing fails
829
+ }
830
+ }
831
+ } catch (analysisError) {
832
+ debugLog("Phase A: Analysis call failed", { error: String(analysisError) });
833
+ // Continue without analysis - fall back to existing behavior
834
+ }
835
+ }
836
+
701
837
  // ========== SMART CONTEXT BUDGETING ==========
702
838
  // Claude can handle 200k tokens (~800k chars), so we can safely include large files
703
839
  // Priority: Recommended file (NEVER truncate) > Page file (limited) > Other components (dynamic)
@@ -734,6 +870,58 @@ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at (${el.coordinates.x}
734
870
  `;
735
871
  }
736
872
 
873
+ // ========== VISUAL PROBLEM ANALYSIS (from Phase A) ==========
874
+ if (visualAnalysis) {
875
+ let analysisInstructions = `═══════════════════════════════════════════════════════════════════════════════
876
+ 🔍 VISUAL PROBLEM ANALYSIS (I analyzed the screenshot first)
877
+ ═══════════════════════════════════════════════════════════════════════════════
878
+
879
+ **Element:** ${visualAnalysis.element}
880
+ **Element Text:** "${visualAnalysis.elementText || "unknown"}"
881
+ **Problem:** ${visualAnalysis.problem}
882
+ **Change Type:** ${visualAnalysis.changeType}
883
+ **Solution:** ${visualAnalysis.solution}
884
+ **Confidence:** ${visualAnalysis.confidence}
885
+
886
+ `;
887
+
888
+ // Add specific instructions based on change type
889
+ if (visualAnalysis.changeType === "styling" && visualAnalysis.classChange) {
890
+ analysisInstructions += `🎯 EXACT CLASS CHANGES REQUIRED:
891
+ `;
892
+ if (visualAnalysis.classChange.remove && visualAnalysis.classChange.remove.length > 0) {
893
+ analysisInstructions += ` REMOVE these classes: ${visualAnalysis.classChange.remove.join(", ")}
894
+ `;
895
+ }
896
+ if (visualAnalysis.classChange.add && visualAnalysis.classChange.add.length > 0) {
897
+ analysisInstructions += ` ADD these classes: ${visualAnalysis.classChange.add.join(", ")}
898
+ `;
899
+ }
900
+ analysisInstructions += `
901
+ ⚠️ YOUR PATCH MUST:
902
+ 1. Find the element containing "${visualAnalysis.elementText || visualAnalysis.element}"
903
+ 2. In its className, ${visualAnalysis.classChange.remove?.length ? `REMOVE: ${visualAnalysis.classChange.remove.join(", ")}` : ""}
904
+ 3. ${visualAnalysis.classChange.add?.length ? `ADD: ${visualAnalysis.classChange.add.join(", ")}` : ""}
905
+ 4. Do NOT add unrelated changes like font-weight or hover states unless specifically requested
906
+
907
+ `;
908
+ } else if (visualAnalysis.changeType === "structural" && visualAnalysis.structuralChange) {
909
+ analysisInstructions += `🔧 STRUCTURAL CHANGE REQUIRED:
910
+ ${visualAnalysis.structuralChange}
911
+
912
+ ⚠️ YOUR PATCH MUST implement this structural change exactly as described.
913
+
914
+ `;
915
+ } else {
916
+ analysisInstructions += `⚠️ IMPORTANT: Your patches MUST implement the fix described above.
917
+ Find the code that renders "${visualAnalysis.element}" and apply: ${visualAnalysis.solution}
918
+
919
+ `;
920
+ }
921
+
922
+ textContent += analysisInstructions;
923
+ }
924
+
737
925
  // ========== TARGET COMPONENT (RECOMMENDED FILE) - SHOWN FIRST ==========
738
926
  if (recommendedFileContent) {
739
927
  // Never truncate the recommended file - AI needs full context to avoid hallucination
@@ -695,8 +695,9 @@ export async function POST(request: Request) {
695
695
  const messageContent: Anthropic.MessageCreateParams["messages"][0]["content"] = [];
696
696
 
697
697
  // Add screenshot if provided
698
+ let base64Data = "";
698
699
  if (screenshot) {
699
- const base64Data = screenshot.split(",")[1] || screenshot;
700
+ base64Data = screenshot.split(",")[1] || screenshot;
700
701
  messageContent.push({
701
702
  type: "image",
702
703
  source: {
@@ -707,6 +708,141 @@ export async function POST(request: Request) {
707
708
  });
708
709
  }
709
710
 
711
+ // ========== PHASE A: VISUAL PROBLEM ANALYSIS ==========
712
+ // Before generating patches, analyze WHAT is visually wrong
713
+ // This forces the LLM to articulate the problem before trying to fix it
714
+ interface VisualAnalysis {
715
+ element: string;
716
+ elementText: string; // The visible text content of the element (for finding in code)
717
+ problem: string;
718
+ changeType: "styling" | "structural" | "content" | "other";
719
+ currentClasses?: string; // For styling changes: what classes appear to be applied
720
+ classChange?: {
721
+ remove?: string[]; // Classes to remove
722
+ add?: string[]; // Classes to add
723
+ };
724
+ structuralChange?: string; // For structural changes: what to add/remove/wrap
725
+ solution: string;
726
+ confidence: "high" | "medium" | "low";
727
+ }
728
+
729
+ let visualAnalysis: VisualAnalysis | null = null;
730
+
731
+ if (screenshot && userPrompt) {
732
+ debugLog("Phase A: Starting visual problem analysis");
733
+
734
+ const analysisClient = new Anthropic({ apiKey });
735
+
736
+ try {
737
+ const analysisResponse = await analysisClient.messages.create({
738
+ model: "claude-sonnet-4-20250514",
739
+ max_tokens: 1024,
740
+ messages: [
741
+ {
742
+ role: "user",
743
+ content: [
744
+ {
745
+ type: "image",
746
+ source: {
747
+ type: "base64",
748
+ media_type: "image/png",
749
+ data: base64Data,
750
+ },
751
+ },
752
+ {
753
+ type: "text",
754
+ text: `You are analyzing a UI screenshot to understand what the user wants changed.
755
+
756
+ User request: "${userPrompt}"
757
+
758
+ ${focusedElements && focusedElements.length > 0 ? `User clicked on these elements:
759
+ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at position (${el.coordinates.x}, ${el.coordinates.y})`).join("\n")}
760
+ ` : ""}
761
+
762
+ Analyze the request and determine:
763
+ 1. ELEMENT: Which specific UI element needs to change? (describe location and appearance)
764
+ 2. ELEMENT TEXT: What text is visible inside this element? (EXACT text for finding in code)
765
+ 3. PROBLEM: What is wrong or what does the user want changed?
766
+ 4. CHANGE TYPE: Is this a "styling" (colors/size/spacing), "structural" (add/remove elements), "content" (text changes), or "other" change?
767
+ 5. For STYLING changes: What Tailwind classes should be REMOVED and what should be ADDED?
768
+ 6. For STRUCTURAL changes: What element should be added, removed, or wrapped?
769
+ 7. SOLUTION: Describe the complete fix
770
+ 8. CONFIDENCE: high/medium/low
771
+
772
+ IMPORTANT for styling fixes:
773
+ - If text is invisible on a background, typically REMOVE the problematic text color class and ADD a contrasting one
774
+ - Common fix for invisible text on colored backgrounds: REMOVE "text-accent-foreground" or similar, ADD "text-white" or "text-black"
775
+ - Be SPECIFIC about exact class names
776
+
777
+ Return ONLY valid JSON:
778
+ {
779
+ "element": "description of the UI element",
780
+ "elementText": "exact visible text in the element",
781
+ "problem": "what is wrong",
782
+ "changeType": "styling",
783
+ "currentClasses": "classes that appear to be applied (best guess)",
784
+ "classChange": {
785
+ "remove": ["text-accent-foreground"],
786
+ "add": ["text-white"]
787
+ },
788
+ "solution": "complete description of the fix",
789
+ "confidence": "high"
790
+ }
791
+
792
+ For structural changes:
793
+ {
794
+ "element": "description of the UI element",
795
+ "elementText": "text near the element",
796
+ "problem": "what is wrong",
797
+ "changeType": "structural",
798
+ "structuralChange": "Add a new button after the existing one",
799
+ "solution": "complete description of the fix",
800
+ "confidence": "high"
801
+ }`,
802
+ },
803
+ ],
804
+ },
805
+ ],
806
+ });
807
+
808
+ const analysisText = analysisResponse.content.find((block) => block.type === "text");
809
+ if (analysisText && analysisText.type === "text") {
810
+ // Parse the JSON response
811
+ let jsonText = analysisText.text.trim();
812
+ // Extract JSON if wrapped in code blocks
813
+ const jsonMatch = jsonText.match(/```(?:json)?\s*([\s\S]*?)\s*```/) || jsonText.match(/(\{[\s\S]*\})/);
814
+ if (jsonMatch) {
815
+ jsonText = jsonMatch[1];
816
+ }
817
+
818
+ try {
819
+ visualAnalysis = JSON.parse(jsonText) as VisualAnalysis;
820
+ debugLog("Phase A: Visual problem analysis complete", visualAnalysis);
821
+
822
+ // If low confidence, return early with clarification request
823
+ if (visualAnalysis.confidence === "low") {
824
+ debugLog("Phase A: Low confidence - requesting clarification");
825
+ return NextResponse.json({
826
+ success: false,
827
+ needsClarification: true,
828
+ analysis: visualAnalysis,
829
+ message: `I can see "${visualAnalysis.element}" but I'm not certain about the problem: "${visualAnalysis.problem}". Can you be more specific about what needs to change?`,
830
+ });
831
+ }
832
+ } catch (parseError) {
833
+ debugLog("Phase A: Failed to parse analysis response", {
834
+ error: String(parseError),
835
+ response: jsonText.substring(0, 500)
836
+ });
837
+ // Continue without analysis if parsing fails
838
+ }
839
+ }
840
+ } catch (analysisError) {
841
+ debugLog("Phase A: Analysis call failed", { error: String(analysisError) });
842
+ // Continue without analysis - fall back to existing behavior
843
+ }
844
+ }
845
+
710
846
  // ========== SMART CONTEXT BUDGETING ==========
711
847
  // Claude can handle 200k tokens (~800k chars), so we can safely include large files
712
848
  // Priority: Recommended file (NEVER truncate) > Page file (limited) > Other components (dynamic)
@@ -743,6 +879,58 @@ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at (${el.coordinates.x}
743
879
  `;
744
880
  }
745
881
 
882
+ // ========== VISUAL PROBLEM ANALYSIS (from Phase A) ==========
883
+ if (visualAnalysis) {
884
+ let analysisInstructions = `═══════════════════════════════════════════════════════════════════════════════
885
+ 🔍 VISUAL PROBLEM ANALYSIS (I analyzed the screenshot first)
886
+ ═══════════════════════════════════════════════════════════════════════════════
887
+
888
+ **Element:** ${visualAnalysis.element}
889
+ **Element Text:** "${visualAnalysis.elementText || "unknown"}"
890
+ **Problem:** ${visualAnalysis.problem}
891
+ **Change Type:** ${visualAnalysis.changeType}
892
+ **Solution:** ${visualAnalysis.solution}
893
+ **Confidence:** ${visualAnalysis.confidence}
894
+
895
+ `;
896
+
897
+ // Add specific instructions based on change type
898
+ if (visualAnalysis.changeType === "styling" && visualAnalysis.classChange) {
899
+ analysisInstructions += `🎯 EXACT CLASS CHANGES REQUIRED:
900
+ `;
901
+ if (visualAnalysis.classChange.remove && visualAnalysis.classChange.remove.length > 0) {
902
+ analysisInstructions += ` REMOVE these classes: ${visualAnalysis.classChange.remove.join(", ")}
903
+ `;
904
+ }
905
+ if (visualAnalysis.classChange.add && visualAnalysis.classChange.add.length > 0) {
906
+ analysisInstructions += ` ADD these classes: ${visualAnalysis.classChange.add.join(", ")}
907
+ `;
908
+ }
909
+ analysisInstructions += `
910
+ ⚠️ YOUR PATCH MUST:
911
+ 1. Find the element containing "${visualAnalysis.elementText || visualAnalysis.element}"
912
+ 2. In its className, ${visualAnalysis.classChange.remove?.length ? `REMOVE: ${visualAnalysis.classChange.remove.join(", ")}` : ""}
913
+ 3. ${visualAnalysis.classChange.add?.length ? `ADD: ${visualAnalysis.classChange.add.join(", ")}` : ""}
914
+ 4. Do NOT add unrelated changes like font-weight or hover states unless specifically requested
915
+
916
+ `;
917
+ } else if (visualAnalysis.changeType === "structural" && visualAnalysis.structuralChange) {
918
+ analysisInstructions += `🔧 STRUCTURAL CHANGE REQUIRED:
919
+ ${visualAnalysis.structuralChange}
920
+
921
+ ⚠️ YOUR PATCH MUST implement this structural change exactly as described.
922
+
923
+ `;
924
+ } else {
925
+ analysisInstructions += `⚠️ IMPORTANT: Your patches MUST implement the fix described above.
926
+ Find the code that renders "${visualAnalysis.element}" and apply: ${visualAnalysis.solution}
927
+
928
+ `;
929
+ }
930
+
931
+ textContent += analysisInstructions;
932
+ }
933
+
746
934
  // ========== TARGET COMPONENT (RECOMMENDED FILE) - SHOWN FIRST ==========
747
935
  if (recommendedFileContent) {
748
936
  // Never truncate the recommended file - AI needs full context to avoid hallucination
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.55",
3
+ "version": "1.3.57",
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",