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
|
|
905
|
-
//
|
|
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
|
-
//
|
|
1240
|
-
// This
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
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
|
-
//
|
|
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.
|
|
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",
|