sonance-brand-mcp 1.3.88 → 1.3.89

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.
@@ -229,6 +229,109 @@ function findElementIdInFile(
229
229
  return null;
230
230
  }
231
231
 
232
+ /**
233
+ * PHASE 0: Deterministic Element ID Search (Cursor-style)
234
+ * Grep entire codebase for the element ID. If found in multiple files,
235
+ * use the current route to disambiguate (prioritize page file for route).
236
+ *
237
+ * This is the most reliable signal - like Cursor knowing which file you're in.
238
+ * Element IDs should be unique, so if we find one, that's THE file.
239
+ */
240
+ function findFilesByElementId(
241
+ projectRoot: string,
242
+ elementId: string,
243
+ currentRoute: string,
244
+ discoverPageFileFn: (route: string, projectRoot: string) => string | null
245
+ ): { path: string; lineNumber: number; isRouteMatch: boolean }[] {
246
+ const pattern = new RegExp(`id=["'\`]${elementId}["'\`]`);
247
+ const matches: { path: string; lineNumber: number; isRouteMatch: boolean }[] = [];
248
+
249
+ // Determine expected page file from route
250
+ const routePageFile = discoverPageFileFn(currentRoute, projectRoot);
251
+
252
+ // Search all common project directories that exist
253
+ // This supports: src/app, app/, pages/, components/, lib/ structures
254
+ const commonDirs = ['src', 'app', 'pages', 'components', 'lib'];
255
+ const searchDirs = commonDirs
256
+ .map(dir => path.join(projectRoot, dir))
257
+ .filter(dir => {
258
+ try {
259
+ return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
260
+ } catch {
261
+ return false;
262
+ }
263
+ });
264
+
265
+ // If no standard dirs found, search project root (excluding node_modules)
266
+ if (searchDirs.length === 0) {
267
+ searchDirs.push(projectRoot);
268
+ }
269
+
270
+ debugLog("PHASE 0: Searching for element ID", {
271
+ elementId,
272
+ currentRoute,
273
+ routePageFile,
274
+ searchDirs: searchDirs.map(d => d.replace(projectRoot + '/', ''))
275
+ });
276
+
277
+ function searchDirRecursive(dir: string): void {
278
+ try {
279
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
280
+ for (const entry of entries) {
281
+ const fullPath = path.join(dir, entry.name);
282
+
283
+ // Skip node_modules, hidden directories, and build outputs
284
+ if (entry.isDirectory()) {
285
+ if (
286
+ entry.name.includes('node_modules') ||
287
+ entry.name.startsWith('.') ||
288
+ entry.name === 'dist' ||
289
+ entry.name === 'build' ||
290
+ entry.name === '.next'
291
+ ) {
292
+ continue;
293
+ }
294
+ searchDirRecursive(fullPath);
295
+ } else if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name)) {
296
+ try {
297
+ const content = fs.readFileSync(fullPath, 'utf-8');
298
+ const lines = content.split('\n');
299
+ for (let i = 0; i < lines.length; i++) {
300
+ if (pattern.test(lines[i])) {
301
+ const relativePath = fullPath.replace(projectRoot + '/', '');
302
+ const isRouteMatch = relativePath === routePageFile;
303
+ matches.push({
304
+ path: relativePath,
305
+ lineNumber: i + 1,
306
+ isRouteMatch
307
+ });
308
+ debugLog("PHASE 0: Found ID match", {
309
+ file: relativePath,
310
+ line: i + 1,
311
+ isRouteMatch
312
+ });
313
+ break; // One match per file is enough
314
+ }
315
+ }
316
+ } catch {
317
+ // Skip files that can't be read
318
+ }
319
+ }
320
+ }
321
+ } catch {
322
+ // Skip directories that can't be read
323
+ }
324
+ }
325
+
326
+ // Search all directories
327
+ for (const searchDir of searchDirs) {
328
+ searchDirRecursive(searchDir);
329
+ }
330
+
331
+ // Sort: route matches first
332
+ return matches.sort((a, b) => (b.isRouteMatch ? 1 : 0) - (a.isRouteMatch ? 1 : 0));
333
+ }
334
+
232
335
  /**
233
336
  * Result of LLM screenshot analysis for smart file discovery
234
337
  */
@@ -927,12 +1030,73 @@ export async function POST(request: Request) {
927
1030
  // Generate a unique session ID
928
1031
  const newSessionId = randomUUID().slice(0, 8);
929
1032
 
930
- // PHASE 1+2: LLM-driven smart file discovery
931
- // The LLM analyzes the screenshot and deduces component names, code patterns, and visible text
1033
+ // Initialize file discovery variables
932
1034
  let smartSearchFiles: { path: string; content: string }[] = [];
933
1035
  let recommendedFile: { path: string; reason: string } | null = null;
934
-
935
- if (screenshot) {
1036
+ let deterministicMatch: { path: string; lineNumber: number } | null = null;
1037
+
1038
+ // ========================================================================
1039
+ // PHASE 0: Deterministic Element ID Search (Cursor-style explicit selection)
1040
+ // If we have an element ID, find it directly - no scoring, no heuristics
1041
+ // This is the most reliable signal, like Cursor knowing which file you're in
1042
+ // ========================================================================
1043
+ if (focusedElements?.some(el => el.elementId)) {
1044
+ const elementWithId = focusedElements.find(el => el.elementId)!;
1045
+ debugLog("PHASE 0: Element has ID, starting deterministic search", {
1046
+ elementId: elementWithId.elementId,
1047
+ route: pageRoute
1048
+ });
1049
+
1050
+ const matches = findFilesByElementId(
1051
+ projectRoot,
1052
+ elementWithId.elementId!,
1053
+ pageRoute || "/",
1054
+ discoverPageFile
1055
+ );
1056
+
1057
+ if (matches.length === 1) {
1058
+ // Single match - 100% confidence
1059
+ deterministicMatch = matches[0];
1060
+ recommendedFile = {
1061
+ path: matches[0].path,
1062
+ reason: `Deterministic ID match: id="${elementWithId.elementId}" (unique in codebase)`
1063
+ };
1064
+ debugLog("PHASE 0 SUCCESS: Single ID match - skipping smart search", deterministicMatch);
1065
+ } else if (matches.length > 1) {
1066
+ // Multiple matches - use route to disambiguate
1067
+ const routeMatch = matches.find(m => m.isRouteMatch);
1068
+ if (routeMatch) {
1069
+ deterministicMatch = routeMatch;
1070
+ recommendedFile = {
1071
+ path: routeMatch.path,
1072
+ reason: `Deterministic ID match: id="${elementWithId.elementId}" (route "${pageRoute}" disambiguated from ${matches.length} files)`
1073
+ };
1074
+ debugLog("PHASE 0 SUCCESS: ID found in multiple files, using route match", {
1075
+ match: deterministicMatch,
1076
+ otherFiles: matches.filter(m => !m.isRouteMatch).map(m => m.path)
1077
+ });
1078
+ } else {
1079
+ debugLog("PHASE 0 FALLBACK: ID found in multiple files, no route match", {
1080
+ elementId: elementWithId.elementId,
1081
+ matchCount: matches.length,
1082
+ files: matches.map(m => m.path),
1083
+ route: pageRoute
1084
+ });
1085
+ // Fall through to smart search
1086
+ }
1087
+ } else {
1088
+ debugLog("PHASE 0 FALLBACK: Element ID not found in codebase", {
1089
+ elementId: elementWithId.elementId
1090
+ });
1091
+ // Fall through to smart search
1092
+ }
1093
+ }
1094
+
1095
+ // ========================================================================
1096
+ // PHASE 1+2: LLM-driven smart file discovery (only if Phase 0 didn't match)
1097
+ // The LLM analyzes the screenshot and deduces component names, code patterns, and visible text
1098
+ // ========================================================================
1099
+ if (!deterministicMatch && screenshot) {
936
1100
  debugLog("Starting Phase 1: LLM screenshot analysis");
937
1101
  const analysis = await analyzeScreenshotForSearch(screenshot, userPrompt, apiKey);
938
1102
 
@@ -1024,8 +1188,12 @@ export async function POST(request: Request) {
1024
1188
  return bestMatch;
1025
1189
  };
1026
1190
 
1191
+ // ====================================================================
1192
+ // FALLBACK PRIORITY LOGIC (only reached when Phase 0 didn't find a match)
1193
+ // This handles cases: no element ID, dynamic IDs, or ambiguous ID matches
1194
+ // ====================================================================
1027
1195
  if (phase2aConfirmed) {
1028
- // PRIORITY 1: Phase 2a match confirmed by element search - highest confidence
1196
+ // FALLBACK PRIORITY 1: Phase 2a match confirmed by element search
1029
1197
  const confirmedPath = phase2aMatches.find(p =>
1030
1198
  focusedElementHints.some(h => h.path === p && h.score > 0)
1031
1199
  );
@@ -1033,33 +1201,47 @@ export async function POST(request: Request) {
1033
1201
  path: confirmedPath!,
1034
1202
  reason: `Phase 2a component-name match confirmed by element search`
1035
1203
  };
1036
- debugLog("PRIORITY 1: Phase 2a confirmed by element search", recommendedFile);
1204
+ debugLog("FALLBACK PRIORITY 1: Phase 2a confirmed by element search", recommendedFile);
1037
1205
  } else if (focusedTopScore > smartSearchTopScore * 0.5 && focusedTopScore >= 400) {
1038
- // PRIORITY 2: Focused element has strong absolute score AND relative to smart search
1206
+ // FALLBACK PRIORITY 2: Focused element has strong score
1039
1207
  recommendedFile = {
1040
1208
  path: focusedTopPath!,
1041
1209
  reason: `Focused element match (score: ${focusedTopScore}, exceeds threshold)`
1042
1210
  };
1043
- debugLog("PRIORITY 2: Strong focused element match", recommendedFile);
1211
+ debugLog("FALLBACK PRIORITY 2: Strong focused element match", recommendedFile);
1044
1212
  } else if (phase2aMatches.length > 0) {
1045
- // PRIORITY 2.5: Phase 2a match WITHOUT focused element - still strong signal
1213
+ // FALLBACK PRIORITY 3: Phase 2a match WITHOUT focused element
1046
1214
  // Use prompt-aware selection to pick the best match
1047
1215
  const bestMatch = findBestPhase2aMatch()!;
1048
1216
  recommendedFile = {
1049
1217
  path: bestMatch,
1050
1218
  reason: `Phase 2a component-name match (prompt-aware selection from ${phase2aMatches.length} candidates)`
1051
1219
  };
1052
- debugLog("PRIORITY 2.5: Phase 2a match (no focused element)", {
1220
+ debugLog("FALLBACK PRIORITY 3: Phase 2a match (no focused element)", {
1053
1221
  selectedPath: bestMatch,
1054
1222
  allCandidates: phase2aMatches
1055
1223
  });
1056
- } else if (smartSearchTopPath) {
1057
- // PRIORITY 3: Smart search top result - trusted baseline
1058
- recommendedFile = {
1059
- path: smartSearchTopPath,
1060
- reason: `Smart search top result (score: ${smartSearchTopScore})`
1061
- };
1062
- debugLog("PRIORITY 3: Using smart search top result", recommendedFile);
1224
+ } else {
1225
+ // FALLBACK PRIORITY 4: Use the page file from the current route
1226
+ const routePageFile = discoverPageFile(pageRoute || "/", projectRoot);
1227
+
1228
+ if (routePageFile && fs.existsSync(path.join(projectRoot, routePageFile))) {
1229
+ recommendedFile = {
1230
+ path: routePageFile,
1231
+ reason: `Page file from route "${pageRoute || "/"}"`
1232
+ };
1233
+ debugLog("FALLBACK PRIORITY 4: Using page file from route", {
1234
+ route: pageRoute || "/",
1235
+ pageFile: routePageFile
1236
+ });
1237
+ } else if (smartSearchTopPath) {
1238
+ // FALLBACK PRIORITY 5: Smart search top result - last resort
1239
+ recommendedFile = {
1240
+ path: smartSearchTopPath,
1241
+ reason: `Smart search top result (score: ${smartSearchTopScore})`
1242
+ };
1243
+ debugLog("FALLBACK PRIORITY 5: Using smart search top result", recommendedFile);
1244
+ }
1063
1245
  }
1064
1246
 
1065
1247
  debugLog("Phase 1+2 complete", {
@@ -225,6 +225,109 @@ function findElementIdInFile(
225
225
  return null;
226
226
  }
227
227
 
228
+ /**
229
+ * PHASE 0: Deterministic Element ID Search (Cursor-style)
230
+ * Grep entire codebase for the element ID. If found in multiple files,
231
+ * use the current route to disambiguate (prioritize page file for route).
232
+ *
233
+ * This is the most reliable signal - like Cursor knowing which file you're in.
234
+ * Element IDs should be unique, so if we find one, that's THE file.
235
+ */
236
+ function findFilesByElementId(
237
+ projectRoot: string,
238
+ elementId: string,
239
+ currentRoute: string,
240
+ discoverPageFileFn: (route: string, projectRoot: string) => string | null
241
+ ): { path: string; lineNumber: number; isRouteMatch: boolean }[] {
242
+ const pattern = new RegExp(`id=["'\`]${elementId}["'\`]`);
243
+ const matches: { path: string; lineNumber: number; isRouteMatch: boolean }[] = [];
244
+
245
+ // Determine expected page file from route
246
+ const routePageFile = discoverPageFileFn(currentRoute, projectRoot);
247
+
248
+ // Search all common project directories that exist
249
+ // This supports: src/app, app/, pages/, components/, lib/ structures
250
+ const commonDirs = ['src', 'app', 'pages', 'components', 'lib'];
251
+ const searchDirs = commonDirs
252
+ .map(dir => path.join(projectRoot, dir))
253
+ .filter(dir => {
254
+ try {
255
+ return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
256
+ } catch {
257
+ return false;
258
+ }
259
+ });
260
+
261
+ // If no standard dirs found, search project root (excluding node_modules)
262
+ if (searchDirs.length === 0) {
263
+ searchDirs.push(projectRoot);
264
+ }
265
+
266
+ debugLog("PHASE 0: Searching for element ID", {
267
+ elementId,
268
+ currentRoute,
269
+ routePageFile,
270
+ searchDirs: searchDirs.map(d => d.replace(projectRoot + '/', ''))
271
+ });
272
+
273
+ function searchDirRecursive(dir: string): void {
274
+ try {
275
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
276
+ for (const entry of entries) {
277
+ const fullPath = path.join(dir, entry.name);
278
+
279
+ // Skip node_modules, hidden directories, and build outputs
280
+ if (entry.isDirectory()) {
281
+ if (
282
+ entry.name.includes('node_modules') ||
283
+ entry.name.startsWith('.') ||
284
+ entry.name === 'dist' ||
285
+ entry.name === 'build' ||
286
+ entry.name === '.next'
287
+ ) {
288
+ continue;
289
+ }
290
+ searchDirRecursive(fullPath);
291
+ } else if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name)) {
292
+ try {
293
+ const content = fs.readFileSync(fullPath, 'utf-8');
294
+ const lines = content.split('\n');
295
+ for (let i = 0; i < lines.length; i++) {
296
+ if (pattern.test(lines[i])) {
297
+ const relativePath = fullPath.replace(projectRoot + '/', '');
298
+ const isRouteMatch = relativePath === routePageFile;
299
+ matches.push({
300
+ path: relativePath,
301
+ lineNumber: i + 1,
302
+ isRouteMatch
303
+ });
304
+ debugLog("PHASE 0: Found ID match", {
305
+ file: relativePath,
306
+ line: i + 1,
307
+ isRouteMatch
308
+ });
309
+ break; // One match per file is enough
310
+ }
311
+ }
312
+ } catch {
313
+ // Skip files that can't be read
314
+ }
315
+ }
316
+ }
317
+ } catch {
318
+ // Skip directories that can't be read
319
+ }
320
+ }
321
+
322
+ // Search all directories
323
+ for (const searchDir of searchDirs) {
324
+ searchDirRecursive(searchDir);
325
+ }
326
+
327
+ // Sort: route matches first
328
+ return matches.sort((a, b) => (b.isRouteMatch ? 1 : 0) - (a.isRouteMatch ? 1 : 0));
329
+ }
330
+
228
331
  /**
229
332
  * Result of LLM screenshot analysis for smart file discovery
230
333
  */
@@ -896,12 +999,73 @@ export async function POST(request: Request) {
896
999
  );
897
1000
  }
898
1001
 
899
- // PHASE 1+2: LLM-driven smart file discovery
900
- // The LLM analyzes the screenshot and deduces component names, code patterns, and visible text
1002
+ // Initialize file discovery variables
901
1003
  let smartSearchFiles: { path: string; content: string }[] = [];
902
1004
  let recommendedFile: { path: string; reason: string } | null = null;
903
-
904
- if (screenshot) {
1005
+ let deterministicMatch: { path: string; lineNumber: number } | null = null;
1006
+
1007
+ // ========================================================================
1008
+ // PHASE 0: Deterministic Element ID Search (Cursor-style explicit selection)
1009
+ // If we have an element ID, find it directly - no scoring, no heuristics
1010
+ // This is the most reliable signal, like Cursor knowing which file you're in
1011
+ // ========================================================================
1012
+ if (focusedElements?.some(el => el.elementId)) {
1013
+ const elementWithId = focusedElements.find(el => el.elementId)!;
1014
+ debugLog("PHASE 0: Element has ID, starting deterministic search", {
1015
+ elementId: elementWithId.elementId,
1016
+ route: pageRoute
1017
+ });
1018
+
1019
+ const matches = findFilesByElementId(
1020
+ projectRoot,
1021
+ elementWithId.elementId!,
1022
+ pageRoute || "/",
1023
+ discoverPageFile
1024
+ );
1025
+
1026
+ if (matches.length === 1) {
1027
+ // Single match - 100% confidence
1028
+ deterministicMatch = matches[0];
1029
+ recommendedFile = {
1030
+ path: matches[0].path,
1031
+ reason: `Deterministic ID match: id="${elementWithId.elementId}" (unique in codebase)`
1032
+ };
1033
+ debugLog("PHASE 0 SUCCESS: Single ID match - skipping smart search", deterministicMatch);
1034
+ } else if (matches.length > 1) {
1035
+ // Multiple matches - use route to disambiguate
1036
+ const routeMatch = matches.find(m => m.isRouteMatch);
1037
+ if (routeMatch) {
1038
+ deterministicMatch = routeMatch;
1039
+ recommendedFile = {
1040
+ path: routeMatch.path,
1041
+ reason: `Deterministic ID match: id="${elementWithId.elementId}" (route "${pageRoute}" disambiguated from ${matches.length} files)`
1042
+ };
1043
+ debugLog("PHASE 0 SUCCESS: ID found in multiple files, using route match", {
1044
+ match: deterministicMatch,
1045
+ otherFiles: matches.filter(m => !m.isRouteMatch).map(m => m.path)
1046
+ });
1047
+ } else {
1048
+ debugLog("PHASE 0 FALLBACK: ID found in multiple files, no route match", {
1049
+ elementId: elementWithId.elementId,
1050
+ matchCount: matches.length,
1051
+ files: matches.map(m => m.path),
1052
+ route: pageRoute
1053
+ });
1054
+ // Fall through to smart search
1055
+ }
1056
+ } else {
1057
+ debugLog("PHASE 0 FALLBACK: Element ID not found in codebase", {
1058
+ elementId: elementWithId.elementId
1059
+ });
1060
+ // Fall through to smart search
1061
+ }
1062
+ }
1063
+
1064
+ // ========================================================================
1065
+ // PHASE 1+2: LLM-driven smart file discovery (only if Phase 0 didn't match)
1066
+ // The LLM analyzes the screenshot and deduces component names, code patterns, and visible text
1067
+ // ========================================================================
1068
+ if (!deterministicMatch && screenshot) {
905
1069
  debugLog("Starting Phase 1: LLM screenshot analysis");
906
1070
  const analysis = await analyzeScreenshotForSearch(screenshot, userPrompt, apiKey);
907
1071
 
@@ -993,8 +1157,12 @@ export async function POST(request: Request) {
993
1157
  return bestMatch;
994
1158
  };
995
1159
 
1160
+ // ====================================================================
1161
+ // FALLBACK PRIORITY LOGIC (only reached when Phase 0 didn't find a match)
1162
+ // This handles cases: no element ID, dynamic IDs, or ambiguous ID matches
1163
+ // ====================================================================
996
1164
  if (phase2aConfirmed) {
997
- // PRIORITY 1: Phase 2a match confirmed by element search - highest confidence
1165
+ // FALLBACK PRIORITY 1: Phase 2a match confirmed by element search
998
1166
  const confirmedPath = phase2aMatches.find(p =>
999
1167
  focusedElementHints.some(h => h.path === p && h.score > 0)
1000
1168
  );
@@ -1002,33 +1170,47 @@ export async function POST(request: Request) {
1002
1170
  path: confirmedPath!,
1003
1171
  reason: `Phase 2a component-name match confirmed by element search`
1004
1172
  };
1005
- debugLog("PRIORITY 1: Phase 2a confirmed by element search", recommendedFile);
1173
+ debugLog("FALLBACK PRIORITY 1: Phase 2a confirmed by element search", recommendedFile);
1006
1174
  } else if (focusedTopScore > smartSearchTopScore * 0.5 && focusedTopScore >= 400) {
1007
- // PRIORITY 2: Focused element has strong absolute score AND relative to smart search
1175
+ // FALLBACK PRIORITY 2: Focused element has strong score
1008
1176
  recommendedFile = {
1009
1177
  path: focusedTopPath!,
1010
1178
  reason: `Focused element match (score: ${focusedTopScore}, exceeds threshold)`
1011
1179
  };
1012
- debugLog("PRIORITY 2: Strong focused element match", recommendedFile);
1180
+ debugLog("FALLBACK PRIORITY 2: Strong focused element match", recommendedFile);
1013
1181
  } else if (phase2aMatches.length > 0) {
1014
- // PRIORITY 2.5: Phase 2a match WITHOUT focused element - still strong signal
1182
+ // FALLBACK PRIORITY 3: Phase 2a match WITHOUT focused element
1015
1183
  // Use prompt-aware selection to pick the best match
1016
1184
  const bestMatch = findBestPhase2aMatch()!;
1017
1185
  recommendedFile = {
1018
1186
  path: bestMatch,
1019
1187
  reason: `Phase 2a component-name match (prompt-aware selection from ${phase2aMatches.length} candidates)`
1020
1188
  };
1021
- debugLog("PRIORITY 2.5: Phase 2a match (no focused element)", {
1189
+ debugLog("FALLBACK PRIORITY 3: Phase 2a match (no focused element)", {
1022
1190
  selectedPath: bestMatch,
1023
1191
  allCandidates: phase2aMatches
1024
1192
  });
1025
- } else if (smartSearchTopPath) {
1026
- // PRIORITY 3: Smart search top result - trusted baseline
1027
- recommendedFile = {
1028
- path: smartSearchTopPath,
1029
- reason: `Smart search top result (score: ${smartSearchTopScore})`
1030
- };
1031
- debugLog("PRIORITY 3: Using smart search top result", recommendedFile);
1193
+ } else {
1194
+ // FALLBACK PRIORITY 4: Use the page file from the current route
1195
+ const routePageFile = discoverPageFile(pageRoute || "/", projectRoot);
1196
+
1197
+ if (routePageFile && fs.existsSync(path.join(projectRoot, routePageFile))) {
1198
+ recommendedFile = {
1199
+ path: routePageFile,
1200
+ reason: `Page file from route "${pageRoute || "/"}"`
1201
+ };
1202
+ debugLog("FALLBACK PRIORITY 4: Using page file from route", {
1203
+ route: pageRoute || "/",
1204
+ pageFile: routePageFile
1205
+ });
1206
+ } else if (smartSearchTopPath) {
1207
+ // FALLBACK PRIORITY 5: Smart search top result - last resort
1208
+ recommendedFile = {
1209
+ path: smartSearchTopPath,
1210
+ reason: `Smart search top result (score: ${smartSearchTopScore})`
1211
+ };
1212
+ debugLog("FALLBACK PRIORITY 5: Using smart search top result", recommendedFile);
1213
+ }
1032
1214
  }
1033
1215
 
1034
1216
  debugLog("Phase 1+2 complete", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.88",
3
+ "version": "1.3.89",
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",