sonance-brand-mcp 1.3.64 → 1.3.66

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.
@@ -3,7 +3,8 @@ import * as fs from "fs";
3
3
  import * as path from "path";
4
4
  import Anthropic from "@anthropic-ai/sdk";
5
5
  import { randomUUID } from "crypto";
6
- import { discoverTheme } from "./theme-discovery";
6
+ import { discoverTheme, formatThemeForPrompt } from "./theme-discovery";
7
+ import * as babelParser from "@babel/parser";
7
8
 
8
9
  /**
9
10
  * Sonance DevTools API - Apply-First Vision Mode
@@ -86,6 +87,39 @@ function debugLog(message: string, data?: unknown) {
86
87
  }
87
88
  }
88
89
 
90
+ /**
91
+ * AST-based syntax validation using Babel parser
92
+ * This catches actual syntax errors, not just tag counting
93
+ */
94
+ function validateSyntaxWithAST(content: string, filePath: string): { valid: boolean; error?: string } {
95
+ // Only validate JSX/TSX files
96
+ if (!filePath.endsWith('.tsx') && !filePath.endsWith('.jsx') && !filePath.endsWith('.ts') && !filePath.endsWith('.js')) {
97
+ return { valid: true };
98
+ }
99
+
100
+ try {
101
+ const isTypeScript = filePath.endsWith('.tsx') || filePath.endsWith('.ts');
102
+ const hasJsx = filePath.endsWith('.tsx') || filePath.endsWith('.jsx');
103
+
104
+ const plugins: babelParser.ParserPlugin[] = [];
105
+ if (isTypeScript) plugins.push('typescript');
106
+ if (hasJsx) plugins.push('jsx');
107
+
108
+ babelParser.parse(content, {
109
+ sourceType: 'module',
110
+ plugins,
111
+ });
112
+ return { valid: true };
113
+ } catch (e: unknown) {
114
+ const error = e as { message?: string; loc?: { line: number; column: number } };
115
+ const location = error.loc ? ` at line ${error.loc.line}, column ${error.loc.column}` : '';
116
+ return {
117
+ valid: false,
118
+ error: `Syntax error in ${filePath}${location}: ${error.message || 'Unknown parse error'}`
119
+ };
120
+ }
121
+ }
122
+
89
123
  /**
90
124
  * Result of LLM screenshot analysis for smart file discovery
91
125
  */
@@ -901,14 +935,18 @@ ${linesWithNumbers}
901
935
  // NOTE: We intentionally skip SUPPORTING COMPONENTS to reduce noise
902
936
  // The LLM only needs the TARGET file to make accurate edits
903
937
 
904
- // ========== THEME DISCOVERY (REFERENCE ONLY) ==========
905
- // Dynamically discover theme tokens (minimal - just for logging)
938
+ // ========== THEME DISCOVERY ==========
939
+ // Discover theme tokens and contrast analysis to help LLM make informed color decisions
906
940
  const discoveredTheme = await discoverTheme(projectRoot);
907
941
 
908
942
  if (discoveredTheme.discoveredFiles.length > 0) {
909
943
  debugLog("Theme discovery complete", {
910
944
  filesFound: discoveredTheme.discoveredFiles,
911
945
  });
946
+
947
+ // Add theme context to the prompt so LLM knows actual color values and safe combinations
948
+ const themeContext = formatThemeForPrompt(discoveredTheme);
949
+ textContent += `\n${themeContext}\n`;
912
950
  }
913
951
 
914
952
  // ========== SIMPLIFIED INSTRUCTIONS ==========
@@ -1236,32 +1274,17 @@ This is better than generating patches with made-up code.`,
1236
1274
  console.log(`[Apply-First] All ${mod.patches.length} patches applied successfully to ${mod.filePath}`);
1237
1275
  }
1238
1276
 
1239
- // SYNTAX VALIDATION: Check for common JSX/HTML tag mismatches
1240
- // This catches cases where the LLM changes opening tags but not closing tags
1241
- if (mod.filePath.endsWith('.tsx') || mod.filePath.endsWith('.jsx')) {
1242
- const openDivs = (modifiedContent.match(/<div[\s>]/g) || []).length;
1243
- const closeDivs = (modifiedContent.match(/<\/div>/g) || []).length;
1244
- const openSpans = (modifiedContent.match(/<span[\s>]/g) || []).length;
1245
- const closeSpans = (modifiedContent.match(/<\/span>/g) || []).length;
1246
-
1247
- if (openDivs !== closeDivs || openSpans !== closeSpans) {
1248
- debugLog("SYNTAX WARNING: Tag mismatch detected", {
1249
- filePath: mod.filePath,
1250
- divs: { open: openDivs, close: closeDivs },
1251
- spans: { open: openSpans, close: closeSpans },
1252
- });
1253
- console.warn(`[Apply-First] ⚠️ SYNTAX WARNING: Tag mismatch in ${mod.filePath}`);
1254
- console.warn(`[Apply-First] divs: ${openDivs} open, ${closeDivs} close`);
1255
- console.warn(`[Apply-First] spans: ${openSpans} open, ${closeSpans} close`);
1256
-
1257
- // If there's a significant mismatch, reject the change
1258
- const divDiff = Math.abs(openDivs - closeDivs);
1259
- const spanDiff = Math.abs(openSpans - closeSpans);
1260
- if (divDiff > 0 || spanDiff > 0) {
1261
- patchErrors.push(`${mod.filePath}: LLM introduced syntax error - tag mismatch detected (${divDiff} div, ${spanDiff} span). Change rejected.`);
1262
- continue;
1263
- }
1264
- }
1277
+ // AST VALIDATION: Use Babel parser to catch actual syntax errors
1278
+ // This is more accurate than tag counting and catches real issues
1279
+ const syntaxValidation = validateSyntaxWithAST(modifiedContent, mod.filePath);
1280
+ if (!syntaxValidation.valid) {
1281
+ debugLog("SYNTAX ERROR: AST validation failed", {
1282
+ filePath: mod.filePath,
1283
+ error: syntaxValidation.error,
1284
+ });
1285
+ console.warn(`[Apply-First] ⚠️ SYNTAX ERROR: ${syntaxValidation.error}`);
1286
+ patchErrors.push(`${mod.filePath}: ${syntaxValidation.error}`);
1287
+ continue; // Skip this file, trigger retry
1265
1288
  }
1266
1289
  } else {
1267
1290
  // No patches - skip
@@ -2,7 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
4
  import Anthropic from "@anthropic-ai/sdk";
5
- import { discoverTheme } from "../sonance-vision-apply/theme-discovery";
5
+ import { discoverTheme, formatThemeForPrompt } from "../sonance-vision-apply/theme-discovery";
6
+ import * as babelParser from "@babel/parser";
6
7
 
7
8
  /**
8
9
  * Sonance DevTools API - Vision Mode Editor
@@ -82,6 +83,39 @@ function debugLog(message: string, data?: unknown) {
82
83
  }
83
84
  }
84
85
 
86
+ /**
87
+ * AST-based syntax validation using Babel parser
88
+ * This catches actual syntax errors, not just tag counting
89
+ */
90
+ function validateSyntaxWithAST(content: string, filePath: string): { valid: boolean; error?: string } {
91
+ // Only validate JSX/TSX files
92
+ if (!filePath.endsWith('.tsx') && !filePath.endsWith('.jsx') && !filePath.endsWith('.ts') && !filePath.endsWith('.js')) {
93
+ return { valid: true };
94
+ }
95
+
96
+ try {
97
+ const isTypeScript = filePath.endsWith('.tsx') || filePath.endsWith('.ts');
98
+ const hasJsx = filePath.endsWith('.tsx') || filePath.endsWith('.jsx');
99
+
100
+ const plugins: babelParser.ParserPlugin[] = [];
101
+ if (isTypeScript) plugins.push('typescript');
102
+ if (hasJsx) plugins.push('jsx');
103
+
104
+ babelParser.parse(content, {
105
+ sourceType: 'module',
106
+ plugins,
107
+ });
108
+ return { valid: true };
109
+ } catch (e: unknown) {
110
+ const error = e as { message?: string; loc?: { line: number; column: number } };
111
+ const location = error.loc ? ` at line ${error.loc.line}, column ${error.loc.column}` : '';
112
+ return {
113
+ valid: false,
114
+ error: `Syntax error in ${filePath}${location}: ${error.message || 'Unknown parse error'}`
115
+ };
116
+ }
117
+ }
118
+
85
119
  /**
86
120
  * Result of LLM screenshot analysis for smart file discovery
87
121
  */
@@ -870,13 +904,18 @@ ${linesWithNumbers}
870
904
  // NOTE: We intentionally skip SUPPORTING COMPONENTS to reduce noise
871
905
  // The LLM only needs the TARGET file to make accurate edits
872
906
 
873
- // Dynamically discover theme tokens (minimal - just for logging)
907
+ // ========== THEME DISCOVERY ==========
908
+ // Discover theme tokens and contrast analysis to help LLM make informed color decisions
874
909
  const discoveredTheme = await discoverTheme(projectRoot);
875
910
 
876
911
  if (discoveredTheme.discoveredFiles.length > 0) {
877
912
  debugLog("Theme discovery complete", {
878
913
  filesFound: discoveredTheme.discoveredFiles,
879
914
  });
915
+
916
+ // Add theme context to the prompt so LLM knows actual color values and safe combinations
917
+ const themeContext = formatThemeForPrompt(discoveredTheme);
918
+ textContent += `\n${themeContext}\n`;
880
919
  }
881
920
 
882
921
  // ========== SIMPLIFIED INSTRUCTIONS ==========
@@ -1209,6 +1248,18 @@ This is better than generating patches with made-up code.`,
1209
1248
  modifiedContent = patchResult.modifiedContent;
1210
1249
  console.log(`[Vision Mode] All ${mod.patches.length} patches applied successfully to ${mod.filePath}`);
1211
1250
  }
1251
+
1252
+ // AST VALIDATION: Use Babel parser to catch actual syntax errors
1253
+ const syntaxValidation = validateSyntaxWithAST(modifiedContent, mod.filePath);
1254
+ if (!syntaxValidation.valid) {
1255
+ debugLog("SYNTAX ERROR: AST validation failed", {
1256
+ filePath: mod.filePath,
1257
+ error: syntaxValidation.error,
1258
+ });
1259
+ console.warn(`[Vision Mode] ⚠️ SYNTAX ERROR: ${syntaxValidation.error}`);
1260
+ patchErrors.push(`${mod.filePath}: ${syntaxValidation.error}`);
1261
+ continue; // Skip this file, trigger retry
1262
+ }
1212
1263
  } else {
1213
1264
  // No patches - skip
1214
1265
  console.warn(`[Vision Mode] No patches for ${mod.filePath}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.64",
3
+ "version": "1.3.66",
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",