sonance-brand-mcp 1.3.106 → 1.3.107

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.
@@ -825,14 +825,21 @@ function findElementInImportedFiles(
825
825
  file.path.includes('.d.ts')) continue;
826
826
 
827
827
  const result = findElementLineInFile(file.content, focusedElement);
828
- if (result && result.confidence !== 'low') {
828
+ // Accept ALL matches including low confidence - let the scoring decide
829
+ // Low confidence matches from fallback logic (text/class fingerprint) are still useful
830
+ if (result) {
829
831
  let score = 0;
830
832
 
831
833
  // Text content match is highest priority (+100)
832
- if (result.matchedBy.includes('textContent') || result.matchedBy.includes('word match')) {
834
+ if (result.matchedBy.includes('textContent') || result.matchedBy.includes('word match') || result.matchedBy.includes('flexible text')) {
833
835
  score += 100;
834
836
  }
835
837
 
838
+ // Class fingerprint match gets medium score (+60)
839
+ if (result.matchedBy.includes('class fingerprint')) {
840
+ score += 60;
841
+ }
842
+
836
843
  // Directory proximity - same directory tree as page file (+50)
837
844
  // e.g., for page src/app/typography/page.tsx, prefer src/app/typography/_components/
838
845
  if (pageDir && file.path.includes(pageDir)) {
@@ -845,8 +852,8 @@ function findElementInImportedFiles(
845
852
  score += 30;
846
853
  }
847
854
 
848
- // Confidence bonus
849
- score += result.confidence === 'high' ? 20 : 10;
855
+ // Confidence bonus - low confidence still gets some points
856
+ score += result.confidence === 'high' ? 30 : result.confidence === 'medium' ? 20 : 5;
850
857
 
851
858
  matches.push({
852
859
  path: file.path,
@@ -2299,9 +2306,25 @@ export async function POST(request: Request) {
2299
2306
 
2300
2307
  let usedContext = 0;
2301
2308
 
2309
+ // ========== PHASE 0 PRIORITY: If Phase 0 found a file, use it directly ==========
2310
+ // Phase 0 is deterministic (ID match) so it should ALWAYS take priority
2311
+ let phase0FileContent: { path: string; content: string } | null = null;
2312
+ if (deterministicMatch) {
2313
+ const fullPath = path.join(projectRoot, deterministicMatch.path);
2314
+ if (fs.existsSync(fullPath)) {
2315
+ const content = fs.readFileSync(fullPath, 'utf-8');
2316
+ phase0FileContent = { path: deterministicMatch.path, content };
2317
+ debugLog("PHASE 0 PRIORITY: Using deterministic match as target file", {
2318
+ path: deterministicMatch.path,
2319
+ lineNumber: deterministicMatch.lineNumber,
2320
+ contentLength: content.length
2321
+ });
2322
+ }
2323
+ }
2324
+
2302
2325
  // Extract recommended file from component sources (to show first, avoid duplication)
2303
2326
  let recommendedFileContent: { path: string; content: string } | null = null;
2304
- if (recommendedFile) {
2327
+ if (recommendedFile && !phase0FileContent) {
2305
2328
  // Check componentSources first
2306
2329
  const idx = pageContext.componentSources.findIndex(c => c.path === recommendedFile.path);
2307
2330
  if (idx !== -1) {
@@ -2317,10 +2340,28 @@ export async function POST(request: Request) {
2317
2340
  // ========== FILE REDIRECT LOGIC (runs BEFORE building instructions) ==========
2318
2341
  // This determines the ACTUAL target file by checking for text matches in imported components
2319
2342
  // Must run first so that all subsequent code uses the correct file path
2320
- let actualTargetFile = recommendedFileContent || (pageContext.pageContent ? { path: pageContext.pageFile, content: pageContext.pageContent } : null);
2343
+ // PHASE 0 FILES SKIP REDIRECT - they're already definitively correct
2344
+ let actualTargetFile = phase0FileContent || recommendedFileContent || (pageContext.pageContent ? { path: pageContext.pageFile, content: pageContext.pageContent } : null);
2321
2345
  let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
2322
2346
 
2323
- if (actualTargetFile && focusedElements && focusedElements.length > 0) {
2347
+ // If Phase 0 succeeded, we already know the exact line - use it directly
2348
+ if (phase0FileContent && deterministicMatch) {
2349
+ const lines = phase0FileContent.content.split('\n');
2350
+ const lineNum = deterministicMatch.lineNumber;
2351
+ elementLocation = {
2352
+ lineNumber: lineNum,
2353
+ snippet: lines.slice(Math.max(0, lineNum - 4), lineNum + 5).join('\n'),
2354
+ confidence: 'high',
2355
+ matchedBy: 'Phase 0 deterministic ID match'
2356
+ };
2357
+ debugLog("PHASE 0: Set element location from deterministic match", {
2358
+ lineNumber: lineNum,
2359
+ confidence: 'high'
2360
+ });
2361
+ }
2362
+
2363
+ // SKIP REDIRECT LOGIC if Phase 0 already determined the file (it's already correct)
2364
+ if (!phase0FileContent && actualTargetFile && focusedElements && focusedElements.length > 0) {
2324
2365
  const content = actualTargetFile.content;
2325
2366
 
2326
2367
  // Search for focused element in the file using multiple strategies
@@ -821,14 +821,21 @@ function findElementInImportedFiles(
821
821
  file.path.includes('.d.ts')) continue;
822
822
 
823
823
  const result = findElementLineInFile(file.content, focusedElement);
824
- if (result && result.confidence !== 'low') {
824
+ // Accept ALL matches including low confidence - let the scoring decide
825
+ // Low confidence matches from fallback logic (text/class fingerprint) are still useful
826
+ if (result) {
825
827
  let score = 0;
826
828
 
827
829
  // Text content match is highest priority (+100)
828
- if (result.matchedBy.includes('textContent') || result.matchedBy.includes('word match')) {
830
+ if (result.matchedBy.includes('textContent') || result.matchedBy.includes('word match') || result.matchedBy.includes('flexible text')) {
829
831
  score += 100;
830
832
  }
831
833
 
834
+ // Class fingerprint match gets medium score (+60)
835
+ if (result.matchedBy.includes('class fingerprint')) {
836
+ score += 60;
837
+ }
838
+
832
839
  // Directory proximity - same directory tree as page file (+50)
833
840
  // e.g., for page src/app/typography/page.tsx, prefer src/app/typography/_components/
834
841
  if (pageDir && file.path.includes(pageDir)) {
@@ -841,8 +848,8 @@ function findElementInImportedFiles(
841
848
  score += 30;
842
849
  }
843
850
 
844
- // Confidence bonus
845
- score += result.confidence === 'high' ? 20 : 10;
851
+ // Confidence bonus - low confidence still gets some points
852
+ score += result.confidence === 'high' ? 30 : result.confidence === 'medium' ? 20 : 5;
846
853
 
847
854
  matches.push({
848
855
  path: file.path,
@@ -2268,9 +2275,25 @@ export async function POST(request: Request) {
2268
2275
 
2269
2276
  let usedContext = 0;
2270
2277
 
2278
+ // ========== PHASE 0 PRIORITY: If Phase 0 found a file, use it directly ==========
2279
+ // Phase 0 is deterministic (ID match) so it should ALWAYS take priority
2280
+ let phase0FileContent: { path: string; content: string } | null = null;
2281
+ if (deterministicMatch) {
2282
+ const fullPath = path.join(projectRoot, deterministicMatch.path);
2283
+ if (fs.existsSync(fullPath)) {
2284
+ const content = fs.readFileSync(fullPath, 'utf-8');
2285
+ phase0FileContent = { path: deterministicMatch.path, content };
2286
+ debugLog("PHASE 0 PRIORITY: Using deterministic match as target file", {
2287
+ path: deterministicMatch.path,
2288
+ lineNumber: deterministicMatch.lineNumber,
2289
+ contentLength: content.length
2290
+ });
2291
+ }
2292
+ }
2293
+
2271
2294
  // Extract recommended file from component sources (to show first, avoid duplication)
2272
2295
  let recommendedFileContent: { path: string; content: string } | null = null;
2273
- if (recommendedFile) {
2296
+ if (recommendedFile && !phase0FileContent) {
2274
2297
  // Check componentSources first
2275
2298
  const idx = pageContext.componentSources.findIndex(c => c.path === recommendedFile.path);
2276
2299
  if (idx !== -1) {
@@ -2286,10 +2309,28 @@ export async function POST(request: Request) {
2286
2309
  // ========== FILE REDIRECT LOGIC (runs BEFORE building instructions) ==========
2287
2310
  // This determines the ACTUAL target file by checking for text matches in imported components
2288
2311
  // Must run first so that all subsequent code uses the correct file path
2289
- let actualTargetFile = recommendedFileContent || (pageContext.pageContent ? { path: pageContext.pageFile, content: pageContext.pageContent } : null);
2312
+ // PHASE 0 FILES SKIP REDIRECT - they're already definitively correct
2313
+ let actualTargetFile = phase0FileContent || recommendedFileContent || (pageContext.pageContent ? { path: pageContext.pageFile, content: pageContext.pageContent } : null);
2290
2314
  let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
2291
2315
 
2292
- if (actualTargetFile && focusedElements && focusedElements.length > 0) {
2316
+ // If Phase 0 succeeded, we already know the exact line - use it directly
2317
+ if (phase0FileContent && deterministicMatch) {
2318
+ const lines = phase0FileContent.content.split('\n');
2319
+ const lineNum = deterministicMatch.lineNumber;
2320
+ elementLocation = {
2321
+ lineNumber: lineNum,
2322
+ snippet: lines.slice(Math.max(0, lineNum - 4), lineNum + 5).join('\n'),
2323
+ confidence: 'high',
2324
+ matchedBy: 'Phase 0 deterministic ID match'
2325
+ };
2326
+ debugLog("PHASE 0: Set element location from deterministic match", {
2327
+ lineNumber: lineNum,
2328
+ confidence: 'high'
2329
+ });
2330
+ }
2331
+
2332
+ // SKIP REDIRECT LOGIC if Phase 0 already determined the file (it's already correct)
2333
+ if (!phase0FileContent && actualTargetFile && focusedElements && focusedElements.length > 0) {
2293
2334
  const content = actualTargetFile.content;
2294
2335
 
2295
2336
  // Search for focused element in the file using multiple strategies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.106",
3
+ "version": "1.3.107",
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",