sonance-brand-mcp 1.3.50 → 1.3.53

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,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 { randomUUID } from "crypto";
6
+ import { discoverTheme, formatThemeForPrompt } from "./theme-discovery";
6
7
 
7
8
  /**
8
9
  * Sonance DevTools API - Apply-First Vision Mode
@@ -471,10 +472,47 @@ Return search/replace patches (NOT full files). The system applies your patches
471
472
  - CRITICAL: NEVER invent or guess code. Your "search" string MUST be copied EXACTLY from the provided file content. If you cannot find the exact code to modify, return an empty modifications array.
472
473
  - If the file content appears truncated, only modify code that is visible in the provided content.
473
474
 
474
- **SONANCE BRAND COLORS:**
475
- - Charcoal: #333F48 (primary text)
476
- - IPORT Orange: #FC4C02
477
- - Blaze Blue: #00A3E1
475
+ ═══════════════════════════════════════════════════════════════════════════════
476
+ SONANCE BRAND COLOR SYSTEM
477
+ ═══════════════════════════════════════════════════════════════════════════════
478
+
479
+ **Core Colors:**
480
+ - Primary (Charcoal): #333F48 - main text, dark backgrounds
481
+ - Accent (Cyan "The Beam"): #00D3C8 - highlights, interactive elements, CTAs
482
+ - Success: Green tones - positive states, confirmations
483
+ - Warning: Amber/Orange tones - caution states, alerts
484
+ - Destructive: Red tones - errors, delete actions
485
+
486
+ **CRITICAL CONTRAST RULES:**
487
+ When using colored backgrounds, ALWAYS use high-contrast text:
488
+
489
+ | Background | Use This Text | NEVER Use |
490
+ |-------------------|------------------------------------|-----------------------|
491
+ | bg-accent | text-white | text-accent-foreground |
492
+ | bg-primary | text-primary-foreground, text-white | text-primary |
493
+ | bg-success | text-white | text-success |
494
+ | bg-warning | text-white | text-warning |
495
+ | bg-destructive | text-white | text-destructive |
496
+
497
+ **SEMANTIC TOKEN PATTERNS:**
498
+ - text-{color} = the color itself (use on NEUTRAL backgrounds like white/gray)
499
+ - text-{color}-foreground = INTENDED for text on {color} backgrounds, but MAY have contrast issues
500
+ - bg-{color} = background in that color
501
+ - WHEN IN DOUBT: Use text-white on any colored background for guaranteed contrast
502
+
503
+ **BUTTON PATTERNS (Sonance Standard):**
504
+ - Primary CTA: bg-primary text-primary-foreground
505
+ - Accent/Highlight: bg-accent text-white (NOT text-accent-foreground)
506
+ - Success: bg-success text-white
507
+ - Warning: bg-warning text-white
508
+ - Destructive: bg-destructive text-white
509
+ - Outlined: border-border bg-transparent text-foreground
510
+
511
+ **COMMON MISTAKES TO AVOID:**
512
+ - WRONG: bg-accent text-accent-foreground (accent-foreground is often dark = invisible text)
513
+ - RIGHT: bg-accent text-white (white text on cyan = visible)
514
+ - WRONG: bg-primary text-primary (same color = invisible)
515
+ - RIGHT: bg-primary text-primary-foreground OR text-white
478
516
 
479
517
  **RESPONSE FORMAT:**
480
518
  CRITICAL: Return ONLY the JSON object below. Do NOT include any text, explanation, or thinking before or after the JSON. No preamble. No "Looking at the screenshot..." No markdown code blocks. Just raw JSON:
@@ -666,7 +704,7 @@ export async function POST(request: Request) {
666
704
  const TOTAL_CONTEXT_BUDGET = 500000; // 500k chars total budget
667
705
  const MAX_RECOMMENDED_FILE = Infinity; // NEVER truncate the target file - AI needs full context
668
706
  const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
669
- const MAX_GLOBALS_CSS = 1500;
707
+ const MAX_GLOBALS_CSS = 15000; // Increased to capture full theme definitions
670
708
  const MAX_FILES = 25;
671
709
 
672
710
  let usedContext = 0;
@@ -760,6 +798,25 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
760
798
  }
761
799
  }
762
800
 
801
+ // ========== THEME DISCOVERY ==========
802
+ // Dynamically discover theme tokens from the target codebase
803
+ const discoveredTheme = await discoverTheme(projectRoot);
804
+ const themeContext = formatThemeForPrompt(discoveredTheme);
805
+
806
+ if (discoveredTheme.discoveredFiles.length > 0) {
807
+ textContent += `
808
+ ═══════════════════════════════════════════════════════════════════════════════
809
+ ${themeContext}
810
+ ═══════════════════════════════════════════════════════════════════════════════
811
+
812
+ `;
813
+ debugLog("Theme discovery complete", {
814
+ filesFound: discoveredTheme.discoveredFiles,
815
+ cssVariableCount: Object.keys(discoveredTheme.cssVariables).length,
816
+ tailwindColorCount: Object.keys(discoveredTheme.tailwindColors).length,
817
+ });
818
+ }
819
+
763
820
  // ========== GLOBALS CSS ==========
764
821
  const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
765
822
  textContent += `
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Theme Discovery Module for Sonance DevTools
3
+ *
4
+ * Dynamically discovers and parses theme/design system files from target codebases.
5
+ * Extracts CSS variables, Tailwind colors, and available semantic tokens to provide
6
+ * context to the LLM for intelligent styling decisions.
7
+ */
8
+
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+
12
+ /**
13
+ * Discovered theme information from a codebase
14
+ */
15
+ export interface DiscoveredTheme {
16
+ // Raw CSS variable definitions found (e.g., { "--accent": "#00D3C8" })
17
+ cssVariables: Record<string, string>;
18
+
19
+ // Tailwind custom colors from config (if found)
20
+ tailwindColors: Record<string, string>;
21
+
22
+ // Available semantic class tokens discovered
23
+ availableTokens: string[];
24
+
25
+ // Full raw theme CSS content for additional context
26
+ rawThemeCSS: string;
27
+
28
+ // Files that were discovered and parsed
29
+ discoveredFiles: string[];
30
+ }
31
+
32
+ /**
33
+ * Patterns for finding theme-related files
34
+ */
35
+ const THEME_FILE_PATTERNS = [
36
+ // CSS files
37
+ "src/app/globals.css",
38
+ "app/globals.css",
39
+ "src/styles/globals.css",
40
+ "styles/globals.css",
41
+ "src/styles/global.css",
42
+ "styles/global.css",
43
+ "src/styles/variables.css",
44
+ "styles/variables.css",
45
+ "src/styles/theme.css",
46
+ "styles/theme.css",
47
+ "src/styles/brand-overrides.css",
48
+ "styles/brand-overrides.css",
49
+ // Tailwind config files
50
+ "tailwind.config.js",
51
+ "tailwind.config.ts",
52
+ "tailwind.config.mjs",
53
+ "tailwind.config.cjs",
54
+ ];
55
+
56
+ /**
57
+ * Find all theme-related files in a project
58
+ */
59
+ export function findThemeFiles(projectRoot: string): string[] {
60
+ const foundFiles: string[] = [];
61
+
62
+ for (const pattern of THEME_FILE_PATTERNS) {
63
+ const fullPath = path.join(projectRoot, pattern);
64
+ if (fs.existsSync(fullPath)) {
65
+ foundFiles.push(pattern);
66
+ }
67
+ }
68
+
69
+ // Also search for any additional CSS files in common locations
70
+ const additionalDirs = ["src/styles", "styles", "src/app", "app"];
71
+ for (const dir of additionalDirs) {
72
+ const dirPath = path.join(projectRoot, dir);
73
+ if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
74
+ try {
75
+ const files = fs.readdirSync(dirPath);
76
+ for (const file of files) {
77
+ if (file.endsWith(".css") && !foundFiles.includes(path.join(dir, file))) {
78
+ foundFiles.push(path.join(dir, file));
79
+ }
80
+ }
81
+ } catch {
82
+ // Ignore read errors
83
+ }
84
+ }
85
+ }
86
+
87
+ return foundFiles;
88
+ }
89
+
90
+ /**
91
+ * Parse CSS content and extract variable definitions
92
+ * Extracts patterns like: --primary: #333F48; or --accent: hsl(180 100% 50%);
93
+ */
94
+ export function parseCSSVariables(cssContent: string): Record<string, string> {
95
+ const variables: Record<string, string> = {};
96
+
97
+ // Match CSS custom property definitions
98
+ // Handles: --name: value; with various value formats
99
+ const regex = /--([a-zA-Z0-9-]+)\s*:\s*([^;]+);/g;
100
+
101
+ let match;
102
+ while ((match = regex.exec(cssContent)) !== null) {
103
+ const name = match[1].trim();
104
+ const value = match[2].trim();
105
+ variables[`--${name}`] = value;
106
+ }
107
+
108
+ return variables;
109
+ }
110
+
111
+ /**
112
+ * Parse Tailwind config file and extract custom color definitions
113
+ * Handles both JS and TS config formats
114
+ */
115
+ export function parseTailwindConfig(configPath: string, projectRoot: string): Record<string, string> {
116
+ const colors: Record<string, string> = {};
117
+
118
+ const fullPath = path.join(projectRoot, configPath);
119
+ if (!fs.existsSync(fullPath)) {
120
+ return colors;
121
+ }
122
+
123
+ try {
124
+ const content = fs.readFileSync(fullPath, "utf-8");
125
+
126
+ // Extract color definitions from the config
127
+ // This is a simplified parser that handles common patterns
128
+
129
+ // Pattern 1: colors: { name: "value" } or colors: { name: { DEFAULT: "value" } }
130
+ const colorBlockMatch = content.match(/colors\s*:\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
131
+ if (colorBlockMatch) {
132
+ const colorBlock = colorBlockMatch[1];
133
+
134
+ // Simple key-value pairs: name: "value" or name: 'value'
135
+ const simpleColorRegex = /(\w+[-\w]*)\s*:\s*["']([^"']+)["']/g;
136
+ let match;
137
+ while ((match = simpleColorRegex.exec(colorBlock)) !== null) {
138
+ colors[match[1]] = match[2];
139
+ }
140
+
141
+ // Nested objects with DEFAULT: name: { DEFAULT: "value" }
142
+ const nestedColorRegex = /(\w+[-\w]*)\s*:\s*\{\s*DEFAULT\s*:\s*["']([^"']+)["']/g;
143
+ while ((match = nestedColorRegex.exec(colorBlock)) !== null) {
144
+ colors[match[1]] = match[2];
145
+ }
146
+ }
147
+
148
+ // Pattern 2: extend: { colors: { ... } }
149
+ const extendColorMatch = content.match(/extend\s*:\s*\{[^}]*colors\s*:\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
150
+ if (extendColorMatch) {
151
+ const extendBlock = extendColorMatch[1];
152
+
153
+ const simpleColorRegex = /(\w+[-\w]*)\s*:\s*["']([^"']+)["']/g;
154
+ let match;
155
+ while ((match = simpleColorRegex.exec(extendBlock)) !== null) {
156
+ colors[match[1]] = match[2];
157
+ }
158
+
159
+ // CSS variable references: name: "var(--name)" or "hsl(var(--name))"
160
+ const varRefRegex = /(\w+[-\w]*)\s*:\s*["'](?:hsl\()?var\(--([^)]+)\)(?:\))?["']/g;
161
+ while ((match = varRefRegex.exec(extendBlock)) !== null) {
162
+ colors[match[1]] = `var(--${match[2]})`;
163
+ }
164
+ }
165
+
166
+ } catch (error) {
167
+ // Silently fail - config parsing is best-effort
168
+ console.warn(`[Theme Discovery] Failed to parse Tailwind config: ${error}`);
169
+ }
170
+
171
+ return colors;
172
+ }
173
+
174
+ /**
175
+ * Extract available semantic tokens from CSS content
176
+ * Finds class-like patterns that suggest available Tailwind/utility classes
177
+ */
178
+ export function extractAvailableTokens(cssVariables: Record<string, string>): string[] {
179
+ const tokens: string[] = [];
180
+
181
+ // Generate bg-* and text-* tokens from CSS variable names
182
+ for (const varName of Object.keys(cssVariables)) {
183
+ // Remove -- prefix
184
+ const name = varName.replace(/^--/, "");
185
+
186
+ // Skip numeric or utility variables
187
+ if (/^\d/.test(name) || name.includes("radius") || name.includes("shadow")) {
188
+ continue;
189
+ }
190
+
191
+ // Common color-related variable patterns
192
+ if (
193
+ name.includes("background") ||
194
+ name.includes("foreground") ||
195
+ name.includes("primary") ||
196
+ name.includes("secondary") ||
197
+ name.includes("accent") ||
198
+ name.includes("muted") ||
199
+ name.includes("destructive") ||
200
+ name.includes("success") ||
201
+ name.includes("warning") ||
202
+ name.includes("border") ||
203
+ name.includes("ring") ||
204
+ name.includes("card") ||
205
+ name.includes("popover")
206
+ ) {
207
+ // Add as potential class token
208
+ tokens.push(name);
209
+ }
210
+ }
211
+
212
+ // Always include common utility tokens
213
+ const commonTokens = [
214
+ "text-white",
215
+ "text-black",
216
+ "bg-white",
217
+ "bg-black",
218
+ "bg-transparent",
219
+ ];
220
+
221
+ return [...new Set([...tokens, ...commonTokens])];
222
+ }
223
+
224
+ /**
225
+ * Analyze CSS variables to identify contrast relationships
226
+ * Returns warnings about potentially problematic color combinations
227
+ */
228
+ export function analyzeContrastRelationships(
229
+ cssVariables: Record<string, string>
230
+ ): { warnings: string[]; goodPairs: { bg: string; text: string }[] } {
231
+ const warnings: string[] = [];
232
+ const goodPairs: { bg: string; text: string }[] = [];
233
+
234
+ // Common pattern: --{name} is background, --{name}-foreground is text
235
+ // Check if foreground values look like they might have contrast issues
236
+
237
+ for (const [varName, value] of Object.entries(cssVariables)) {
238
+ // Skip if this is a foreground variable
239
+ if (varName.includes("foreground")) continue;
240
+
241
+ const baseName = varName.replace(/^--/, "");
242
+ const foregroundVar = `--${baseName}-foreground`;
243
+
244
+ if (cssVariables[foregroundVar]) {
245
+ const bgValue = value;
246
+ const fgValue = cssVariables[foregroundVar];
247
+
248
+ // Simple heuristic: if both values reference same color or are similar
249
+ // This is a basic check - real contrast would need color parsing
250
+ if (bgValue === fgValue) {
251
+ warnings.push(
252
+ `WARNING: ${varName} and ${foregroundVar} have identical values - text will be invisible`
253
+ );
254
+ }
255
+
256
+ // Add as a known pair (user should verify contrast)
257
+ goodPairs.push({
258
+ bg: `bg-${baseName}`,
259
+ text: `text-${baseName}-foreground`,
260
+ });
261
+ }
262
+ }
263
+
264
+ return { warnings, goodPairs };
265
+ }
266
+
267
+ /**
268
+ * Main entry point: Discover theme from a project
269
+ */
270
+ export async function discoverTheme(projectRoot: string): Promise<DiscoveredTheme> {
271
+ const result: DiscoveredTheme = {
272
+ cssVariables: {},
273
+ tailwindColors: {},
274
+ availableTokens: [],
275
+ rawThemeCSS: "",
276
+ discoveredFiles: [],
277
+ };
278
+
279
+ // Find theme files
280
+ const themeFiles = findThemeFiles(projectRoot);
281
+ result.discoveredFiles = themeFiles;
282
+
283
+ // Parse each file
284
+ for (const file of themeFiles) {
285
+ const fullPath = path.join(projectRoot, file);
286
+
287
+ try {
288
+ if (file.endsWith(".css")) {
289
+ const content = fs.readFileSync(fullPath, "utf-8");
290
+
291
+ // Accumulate raw CSS
292
+ result.rawThemeCSS += `\n/* === ${file} === */\n${content}\n`;
293
+
294
+ // Parse variables
295
+ const variables = parseCSSVariables(content);
296
+ Object.assign(result.cssVariables, variables);
297
+
298
+ } else if (file.includes("tailwind.config")) {
299
+ // Parse Tailwind config
300
+ const colors = parseTailwindConfig(file, projectRoot);
301
+ Object.assign(result.tailwindColors, colors);
302
+ }
303
+ } catch (error) {
304
+ console.warn(`[Theme Discovery] Error reading ${file}:`, error);
305
+ }
306
+ }
307
+
308
+ // Extract available tokens
309
+ result.availableTokens = extractAvailableTokens(result.cssVariables);
310
+
311
+ return result;
312
+ }
313
+
314
+ /**
315
+ * Format discovered theme as context for the LLM prompt
316
+ */
317
+ export function formatThemeForPrompt(theme: DiscoveredTheme): string {
318
+ const lines: string[] = [];
319
+
320
+ lines.push("**TARGET CODEBASE THEME (discovered):**");
321
+ lines.push("");
322
+
323
+ // CSS Variables
324
+ if (Object.keys(theme.cssVariables).length > 0) {
325
+ lines.push("CSS Variables:");
326
+
327
+ // Group by category
328
+ const categories: Record<string, string[]> = {
329
+ colors: [],
330
+ other: [],
331
+ };
332
+
333
+ for (const [name, value] of Object.entries(theme.cssVariables)) {
334
+ const entry = ` ${name}: ${value}`;
335
+ if (
336
+ name.includes("background") ||
337
+ name.includes("foreground") ||
338
+ name.includes("primary") ||
339
+ name.includes("secondary") ||
340
+ name.includes("accent") ||
341
+ name.includes("muted") ||
342
+ name.includes("destructive") ||
343
+ name.includes("success") ||
344
+ name.includes("warning") ||
345
+ name.includes("border") ||
346
+ name.includes("card")
347
+ ) {
348
+ categories.colors.push(entry);
349
+ } else {
350
+ categories.other.push(entry);
351
+ }
352
+ }
353
+
354
+ // Only include color-related variables (most relevant for styling)
355
+ lines.push(...categories.colors.slice(0, 30)); // Limit to avoid prompt bloat
356
+
357
+ if (categories.colors.length > 30) {
358
+ lines.push(` ... and ${categories.colors.length - 30} more color variables`);
359
+ }
360
+ lines.push("");
361
+ }
362
+
363
+ // Tailwind Colors
364
+ if (Object.keys(theme.tailwindColors).length > 0) {
365
+ lines.push("Tailwind Custom Colors:");
366
+ for (const [name, value] of Object.entries(theme.tailwindColors)) {
367
+ lines.push(` ${name}: ${value}`);
368
+ }
369
+ lines.push("");
370
+ }
371
+
372
+ // Available Tokens
373
+ if (theme.availableTokens.length > 0) {
374
+ lines.push(`Available Semantic Tokens: ${theme.availableTokens.slice(0, 20).join(", ")}`);
375
+ if (theme.availableTokens.length > 20) {
376
+ lines.push(` ... and ${theme.availableTokens.length - 20} more`);
377
+ }
378
+ lines.push("");
379
+ }
380
+
381
+ // Discovered Files
382
+ if (theme.discoveredFiles.length > 0) {
383
+ lines.push(`Theme Files Found: ${theme.discoveredFiles.join(", ")}`);
384
+ }
385
+
386
+ return lines.join("\n");
387
+ }
388
+
@@ -2,6 +2,7 @@ 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, formatThemeForPrompt } from "../sonance-vision-apply/theme-discovery";
5
6
 
6
7
  /**
7
8
  * Sonance DevTools API - Vision Mode Editor
@@ -469,10 +470,47 @@ Return search/replace patches (NOT full files). The system applies your patches
469
470
  - CRITICAL: NEVER invent or guess code. Your "search" string MUST be copied EXACTLY from the provided file content. If you cannot find the exact code to modify, return an empty modifications array.
470
471
  - If the file content appears truncated, only modify code that is visible in the provided content.
471
472
 
472
- **SONANCE BRAND COLORS:**
473
- - Charcoal: #333F48 (primary text)
474
- - IPORT Orange: #FC4C02
475
- - Blaze Blue: #00A3E1
473
+ ═══════════════════════════════════════════════════════════════════════════════
474
+ SONANCE BRAND COLOR SYSTEM
475
+ ═══════════════════════════════════════════════════════════════════════════════
476
+
477
+ **Core Colors:**
478
+ - Primary (Charcoal): #333F48 - main text, dark backgrounds
479
+ - Accent (Cyan "The Beam"): #00D3C8 - highlights, interactive elements, CTAs
480
+ - Success: Green tones - positive states, confirmations
481
+ - Warning: Amber/Orange tones - caution states, alerts
482
+ - Destructive: Red tones - errors, delete actions
483
+
484
+ **CRITICAL CONTRAST RULES:**
485
+ When using colored backgrounds, ALWAYS use high-contrast text:
486
+
487
+ | Background | Use This Text | NEVER Use |
488
+ |-------------------|------------------------------------|-----------------------|
489
+ | bg-accent | text-white | text-accent-foreground |
490
+ | bg-primary | text-primary-foreground, text-white | text-primary |
491
+ | bg-success | text-white | text-success |
492
+ | bg-warning | text-white | text-warning |
493
+ | bg-destructive | text-white | text-destructive |
494
+
495
+ **SEMANTIC TOKEN PATTERNS:**
496
+ - text-{color} = the color itself (use on NEUTRAL backgrounds like white/gray)
497
+ - text-{color}-foreground = INTENDED for text on {color} backgrounds, but MAY have contrast issues
498
+ - bg-{color} = background in that color
499
+ - WHEN IN DOUBT: Use text-white on any colored background for guaranteed contrast
500
+
501
+ **BUTTON PATTERNS (Sonance Standard):**
502
+ - Primary CTA: bg-primary text-primary-foreground
503
+ - Accent/Highlight: bg-accent text-white (NOT text-accent-foreground)
504
+ - Success: bg-success text-white
505
+ - Warning: bg-warning text-white
506
+ - Destructive: bg-destructive text-white
507
+ - Outlined: border-border bg-transparent text-foreground
508
+
509
+ **COMMON MISTAKES TO AVOID:**
510
+ - WRONG: bg-accent text-accent-foreground (accent-foreground is often dark = invisible text)
511
+ - RIGHT: bg-accent text-white (white text on cyan = visible)
512
+ - WRONG: bg-primary text-primary (same color = invisible)
513
+ - RIGHT: bg-primary text-primary-foreground OR text-white
476
514
 
477
515
  **RESPONSE FORMAT:**
478
516
  CRITICAL: Return ONLY the JSON object below. Do NOT include any text, explanation, or thinking before or after the JSON. No preamble. No "Looking at the screenshot..." No markdown code blocks. Just raw JSON:
@@ -675,7 +713,7 @@ export async function POST(request: Request) {
675
713
  const TOTAL_CONTEXT_BUDGET = 500000; // 500k chars total budget
676
714
  const MAX_RECOMMENDED_FILE = Infinity; // NEVER truncate the target file - AI needs full context
677
715
  const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
678
- const MAX_GLOBALS_CSS = 1500;
716
+ const MAX_GLOBALS_CSS = 15000; // Increased to capture full theme definitions
679
717
  const MAX_FILES = 25;
680
718
 
681
719
  let usedContext = 0;
@@ -769,6 +807,25 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
769
807
  }
770
808
  }
771
809
 
810
+ // ========== THEME DISCOVERY ==========
811
+ // Dynamically discover theme tokens from the target codebase
812
+ const discoveredTheme = await discoverTheme(projectRoot);
813
+ const themeContext = formatThemeForPrompt(discoveredTheme);
814
+
815
+ if (discoveredTheme.discoveredFiles.length > 0) {
816
+ textContent += `
817
+ ═══════════════════════════════════════════════════════════════════════════════
818
+ ${themeContext}
819
+ ═══════════════════════════════════════════════════════════════════════════════
820
+
821
+ `;
822
+ debugLog("Theme discovery complete", {
823
+ filesFound: discoveredTheme.discoveredFiles,
824
+ cssVariableCount: Object.keys(discoveredTheme.cssVariables).length,
825
+ tailwindColorCount: Object.keys(discoveredTheme.tailwindColors).length,
826
+ });
827
+ }
828
+
772
829
  // ========== GLOBALS CSS ==========
773
830
  const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
774
831
  textContent += `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.50",
3
+ "version": "1.3.53",
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",