sonance-brand-mcp 1.3.64 → 1.3.65

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.
@@ -4,6 +4,7 @@ import * as path from "path";
4
4
  import Anthropic from "@anthropic-ai/sdk";
5
5
  import { randomUUID } from "crypto";
6
6
  import { discoverTheme } 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
  */
@@ -1236,32 +1270,17 @@ This is better than generating patches with made-up code.`,
1236
1270
  console.log(`[Apply-First] All ${mod.patches.length} patches applied successfully to ${mod.filePath}`);
1237
1271
  }
1238
1272
 
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
- }
1273
+ // AST VALIDATION: Use Babel parser to catch actual syntax errors
1274
+ // This is more accurate than tag counting and catches real issues
1275
+ const syntaxValidation = validateSyntaxWithAST(modifiedContent, mod.filePath);
1276
+ if (!syntaxValidation.valid) {
1277
+ debugLog("SYNTAX ERROR: AST validation failed", {
1278
+ filePath: mod.filePath,
1279
+ error: syntaxValidation.error,
1280
+ });
1281
+ console.warn(`[Apply-First] ⚠️ SYNTAX ERROR: ${syntaxValidation.error}`);
1282
+ patchErrors.push(`${mod.filePath}: ${syntaxValidation.error}`);
1283
+ continue; // Skip this file, trigger retry
1265
1284
  }
1266
1285
  } else {
1267
1286
  // No patches - skip
@@ -3,6 +3,7 @@ import * as fs from "fs";
3
3
  import * as path from "path";
4
4
  import Anthropic from "@anthropic-ai/sdk";
5
5
  import { discoverTheme } 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
  */
@@ -1209,6 +1243,18 @@ This is better than generating patches with made-up code.`,
1209
1243
  modifiedContent = patchResult.modifiedContent;
1210
1244
  console.log(`[Vision Mode] All ${mod.patches.length} patches applied successfully to ${mod.filePath}`);
1211
1245
  }
1246
+
1247
+ // AST VALIDATION: Use Babel parser to catch actual syntax errors
1248
+ const syntaxValidation = validateSyntaxWithAST(modifiedContent, mod.filePath);
1249
+ if (!syntaxValidation.valid) {
1250
+ debugLog("SYNTAX ERROR: AST validation failed", {
1251
+ filePath: mod.filePath,
1252
+ error: syntaxValidation.error,
1253
+ });
1254
+ console.warn(`[Vision Mode] ⚠️ SYNTAX ERROR: ${syntaxValidation.error}`);
1255
+ patchErrors.push(`${mod.filePath}: ${syntaxValidation.error}`);
1256
+ continue; // Skip this file, trigger retry
1257
+ }
1212
1258
  } else {
1213
1259
  // No patches - skip
1214
1260
  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.65",
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",