sonance-brand-mcp 1.3.100 → 1.3.102
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.
|
@@ -1519,53 +1519,37 @@ function buildPhase2DesignPrompt(problems: DesignProblem[]): string {
|
|
|
1519
1519
|
return ''; // No problems identified, skip design protocol
|
|
1520
1520
|
}
|
|
1521
1521
|
|
|
1522
|
-
|
|
1522
|
+
// Create a simple, readable list of problems (no JSON complexity)
|
|
1523
|
+
const problemsList = problems.map(p => `• ${p.description}`).join('\n');
|
|
1523
1524
|
|
|
1524
1525
|
return `
|
|
1525
1526
|
═══════════════════════════════════════════════════════════════════════════════
|
|
1526
|
-
|
|
1527
|
+
DESIGN ISSUES TO FIX
|
|
1527
1528
|
═══════════════════════════════════════════════════════════════════════════════
|
|
1528
1529
|
|
|
1529
|
-
|
|
1530
|
+
These visual issues were identified:
|
|
1531
|
+
${problemsList}
|
|
1530
1532
|
|
|
1531
|
-
|
|
1533
|
+
Fix these issues by modifying the code below.
|
|
1532
1534
|
|
|
1533
|
-
|
|
1535
|
+
⚠️ CRITICAL - EXACT CODE MATCHING:
|
|
1536
|
+
Your "search" string MUST be copied CHARACTER-FOR-CHARACTER from the file.
|
|
1537
|
+
Use the line numbers on the left to find the exact code.
|
|
1534
1538
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
3. Make the SMALLEST change that fixes each problem
|
|
1539
|
-
4. Do NOT change layout systems (flex→grid) unless absolutely necessary to fix a problem
|
|
1540
|
-
5. Preserve existing structure - adjust properties, don't restructure
|
|
1539
|
+
EXAMPLE:
|
|
1540
|
+
If line 42 shows: 42| <div className="mt-6 flex items-center">
|
|
1541
|
+
Your search must be: <div className="mt-6 flex items-center">
|
|
1541
1542
|
|
|
1542
|
-
|
|
1543
|
+
DO NOT:
|
|
1544
|
+
- Combine multiple lines into one search string
|
|
1545
|
+
- Add or remove classes/attributes
|
|
1546
|
+
- Invent code that "should" exist
|
|
1547
|
+
- Guess at the structure
|
|
1543
1548
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
"patches": [
|
|
1549
|
-
{
|
|
1550
|
-
"problemId": "grouping-1",
|
|
1551
|
-
"search": "<Badge className=\"absolute right-4\">",
|
|
1552
|
-
"replace": "<Badge className=\"ml-2\">",
|
|
1553
|
-
"rationale": "Removes absolute positioning to group badge with label"
|
|
1554
|
-
},
|
|
1555
|
-
{
|
|
1556
|
-
"problemId": "spacing-1",
|
|
1557
|
-
"search": "className=\"mt-8\"",
|
|
1558
|
-
"replace": "className=\"mt-4\"",
|
|
1559
|
-
"rationale": "Reduces excessive vertical gap"
|
|
1560
|
-
}
|
|
1561
|
-
]
|
|
1562
|
-
}]
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
VALIDATION:
|
|
1566
|
-
- Patches without a valid problemId will be REJECTED
|
|
1567
|
-
- Patches that don't trace to an identified problem will be REJECTED
|
|
1568
|
-
- Each patch must have search, replace, problemId, and rationale fields
|
|
1549
|
+
DO:
|
|
1550
|
+
- Copy the exact text from the numbered lines
|
|
1551
|
+
- Make small, targeted changes in the "replace" field
|
|
1552
|
+
- Keep patches focused on one change each
|
|
1569
1553
|
`;
|
|
1570
1554
|
}
|
|
1571
1555
|
|
|
@@ -2087,6 +2071,124 @@ export async function POST(request: Request) {
|
|
|
2087
2071
|
}
|
|
2088
2072
|
}
|
|
2089
2073
|
|
|
2074
|
+
// ========== FILE REDIRECT LOGIC (runs BEFORE building instructions) ==========
|
|
2075
|
+
// This determines the ACTUAL target file by checking for text matches in imported components
|
|
2076
|
+
// Must run first so that all subsequent code uses the correct file path
|
|
2077
|
+
let actualTargetFile = recommendedFileContent || (pageContext.pageContent ? { path: pageContext.pageFile, content: pageContext.pageContent } : null);
|
|
2078
|
+
let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
|
|
2079
|
+
|
|
2080
|
+
if (actualTargetFile && focusedElements && focusedElements.length > 0) {
|
|
2081
|
+
const content = actualTargetFile.content;
|
|
2082
|
+
|
|
2083
|
+
// Search for focused element in the file using multiple strategies
|
|
2084
|
+
// Priority: DOM id > textContent > className patterns
|
|
2085
|
+
for (const el of focusedElements) {
|
|
2086
|
+
elementLocation = findElementLineInFile(content, el);
|
|
2087
|
+
if (elementLocation) {
|
|
2088
|
+
debugLog("Found focused element in file", {
|
|
2089
|
+
matchedBy: elementLocation.matchedBy,
|
|
2090
|
+
lineNumber: elementLocation.lineNumber,
|
|
2091
|
+
confidence: elementLocation.confidence,
|
|
2092
|
+
file: actualTargetFile.path,
|
|
2093
|
+
});
|
|
2094
|
+
break;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
// TEXT SCORING REDIRECT: Score ALL imported files by how many discovered
|
|
2099
|
+
// text strings they contain. The file with the highest score is the TRUE target.
|
|
2100
|
+
if (elementLocation && elementLocation.confidence !== 'high') {
|
|
2101
|
+
const allTextContent = focusedElements
|
|
2102
|
+
.filter(e => e.textContent && e.textContent.length > 5)
|
|
2103
|
+
.map(e => e.textContent!);
|
|
2104
|
+
|
|
2105
|
+
if (allTextContent.length > 0) {
|
|
2106
|
+
debugLog("Medium/low confidence match - scoring imports for all text", {
|
|
2107
|
+
currentFile: actualTargetFile.path,
|
|
2108
|
+
currentConfidence: elementLocation.confidence,
|
|
2109
|
+
textCount: allTextContent.length,
|
|
2110
|
+
texts: allTextContent.slice(0, 5).map(t => t.substring(0, 25))
|
|
2111
|
+
});
|
|
2112
|
+
|
|
2113
|
+
const bestMatch = scoreFilesForTextContent(
|
|
2114
|
+
allTextContent,
|
|
2115
|
+
pageContext.componentSources
|
|
2116
|
+
);
|
|
2117
|
+
|
|
2118
|
+
const currentFileScore = allTextContent.filter(text =>
|
|
2119
|
+
actualTargetFile!.content.includes(text.substring(0, 20))
|
|
2120
|
+
).length;
|
|
2121
|
+
|
|
2122
|
+
debugLog("Text scoring comparison", {
|
|
2123
|
+
currentFile: actualTargetFile.path,
|
|
2124
|
+
currentScore: currentFileScore,
|
|
2125
|
+
bestImport: bestMatch?.path || 'none',
|
|
2126
|
+
bestImportScore: bestMatch?.score || 0
|
|
2127
|
+
});
|
|
2128
|
+
|
|
2129
|
+
if (bestMatch && bestMatch.score > currentFileScore) {
|
|
2130
|
+
debugLog("TEXT REDIRECT: Imported file has more text matches", {
|
|
2131
|
+
originalFile: actualTargetFile.path,
|
|
2132
|
+
originalScore: currentFileScore,
|
|
2133
|
+
redirectTo: bestMatch.path,
|
|
2134
|
+
redirectScore: bestMatch.score,
|
|
2135
|
+
matchedTexts: bestMatch.matchedTexts
|
|
2136
|
+
});
|
|
2137
|
+
|
|
2138
|
+
actualTargetFile = {
|
|
2139
|
+
path: bestMatch.path,
|
|
2140
|
+
content: bestMatch.content
|
|
2141
|
+
};
|
|
2142
|
+
|
|
2143
|
+
const lines = bestMatch.content.split('\n');
|
|
2144
|
+
const snippetStart = Math.max(0, bestMatch.firstMatchLine - 4);
|
|
2145
|
+
const snippetEnd = Math.min(lines.length, bestMatch.firstMatchLine + 5);
|
|
2146
|
+
|
|
2147
|
+
elementLocation = {
|
|
2148
|
+
lineNumber: bestMatch.firstMatchLine,
|
|
2149
|
+
snippet: lines.slice(snippetStart, snippetEnd).join('\n'),
|
|
2150
|
+
confidence: 'high',
|
|
2151
|
+
matchedBy: `text scoring: ${bestMatch.score} matches in imported file`
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// DYNAMIC IMPORT SEARCH: If not found in main file, search imported components
|
|
2158
|
+
if (!elementLocation) {
|
|
2159
|
+
debugLog("Element not in main file, searching imported components...", {
|
|
2160
|
+
mainFile: actualTargetFile.path,
|
|
2161
|
+
importedFilesCount: pageContext.componentSources.length
|
|
2162
|
+
});
|
|
2163
|
+
|
|
2164
|
+
const importedMatch = findElementInImportedFiles(
|
|
2165
|
+
focusedElements[0],
|
|
2166
|
+
pageContext.componentSources
|
|
2167
|
+
);
|
|
2168
|
+
|
|
2169
|
+
if (importedMatch) {
|
|
2170
|
+
debugLog("REDIRECT: Element found in imported component", {
|
|
2171
|
+
originalFile: actualTargetFile.path,
|
|
2172
|
+
redirectTo: importedMatch.path,
|
|
2173
|
+
matchedBy: importedMatch.matchedBy,
|
|
2174
|
+
lineNumber: importedMatch.lineNumber
|
|
2175
|
+
});
|
|
2176
|
+
|
|
2177
|
+
actualTargetFile = {
|
|
2178
|
+
path: importedMatch.path,
|
|
2179
|
+
content: importedMatch.content
|
|
2180
|
+
};
|
|
2181
|
+
elementLocation = findElementLineInFile(importedMatch.content, focusedElements[0]);
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
debugLog("File redirect complete", {
|
|
2187
|
+
originalRecommended: recommendedFileContent?.path || 'none',
|
|
2188
|
+
actualTarget: actualTargetFile?.path || 'none',
|
|
2189
|
+
wasRedirected: actualTargetFile?.path !== recommendedFileContent?.path
|
|
2190
|
+
});
|
|
2191
|
+
|
|
2090
2192
|
// Build text content
|
|
2091
2193
|
let textContent = `VISION MODE EDIT REQUEST
|
|
2092
2194
|
|
|
@@ -2110,7 +2212,8 @@ User Request: "${userPrompt}"
|
|
|
2110
2212
|
|
|
2111
2213
|
// Phase 1: Analyze the screenshot to identify specific problems
|
|
2112
2214
|
// This is a SEPARATE LLM call that only identifies problems
|
|
2113
|
-
|
|
2215
|
+
// Use actualTargetFile since redirect has already happened
|
|
2216
|
+
const fileContentForAnalysis = actualTargetFile?.content || pageContext.pageContent || '';
|
|
2114
2217
|
identifiedProblems = await analyzeDesignProblems(
|
|
2115
2218
|
screenshot,
|
|
2116
2219
|
fileContentForAnalysis,
|
|
@@ -2135,124 +2238,8 @@ User Request: "${userPrompt}"
|
|
|
2135
2238
|
|
|
2136
2239
|
// ========== TARGET COMPONENT ONLY (with line numbers) ==========
|
|
2137
2240
|
// CRITICAL: Only include the TARGET file to avoid overwhelming the LLM with noise
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
// Search for focused element in the file using multiple strategies
|
|
2142
|
-
// Priority: DOM id > textContent > className patterns
|
|
2143
|
-
let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
|
|
2144
|
-
let actualTargetFile = recommendedFileContent; // May change if we redirect
|
|
2145
|
-
|
|
2146
|
-
if (focusedElements && focusedElements.length > 0) {
|
|
2147
|
-
for (const el of focusedElements) {
|
|
2148
|
-
elementLocation = findElementLineInFile(content, el);
|
|
2149
|
-
if (elementLocation) {
|
|
2150
|
-
debugLog("Found focused element in file", {
|
|
2151
|
-
matchedBy: elementLocation.matchedBy,
|
|
2152
|
-
lineNumber: elementLocation.lineNumber,
|
|
2153
|
-
confidence: elementLocation.confidence,
|
|
2154
|
-
file: recommendedFileContent.path,
|
|
2155
|
-
});
|
|
2156
|
-
break;
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
|
|
2160
|
-
// TEXT SCORING REDIRECT: Score ALL imported files by how many discovered
|
|
2161
|
-
// text strings they contain. The file with the highest score is the TRUE target.
|
|
2162
|
-
// This handles parent/child component cases accurately.
|
|
2163
|
-
if (elementLocation && elementLocation.confidence !== 'high') {
|
|
2164
|
-
// Collect ALL text content from focused elements
|
|
2165
|
-
const allTextContent = focusedElements
|
|
2166
|
-
.filter(e => e.textContent && e.textContent.length > 5)
|
|
2167
|
-
.map(e => e.textContent!);
|
|
2168
|
-
|
|
2169
|
-
if (allTextContent.length > 0) {
|
|
2170
|
-
debugLog("Medium/low confidence match - scoring imports for all text", {
|
|
2171
|
-
currentFile: recommendedFileContent.path,
|
|
2172
|
-
currentConfidence: elementLocation.confidence,
|
|
2173
|
-
textCount: allTextContent.length,
|
|
2174
|
-
texts: allTextContent.slice(0, 5).map(t => t.substring(0, 25))
|
|
2175
|
-
});
|
|
2176
|
-
|
|
2177
|
-
// Score all imported files
|
|
2178
|
-
const bestMatch = scoreFilesForTextContent(
|
|
2179
|
-
allTextContent,
|
|
2180
|
-
pageContext.componentSources
|
|
2181
|
-
);
|
|
2182
|
-
|
|
2183
|
-
// Also score the current file for comparison
|
|
2184
|
-
const currentFileScore = allTextContent.filter(text =>
|
|
2185
|
-
recommendedFileContent.content.includes(text.substring(0, 20))
|
|
2186
|
-
).length;
|
|
2187
|
-
|
|
2188
|
-
debugLog("Text scoring comparison", {
|
|
2189
|
-
currentFile: recommendedFileContent.path,
|
|
2190
|
-
currentScore: currentFileScore,
|
|
2191
|
-
bestImport: bestMatch?.path || 'none',
|
|
2192
|
-
bestImportScore: bestMatch?.score || 0
|
|
2193
|
-
});
|
|
2194
|
-
|
|
2195
|
-
// Redirect only if imported file has MORE matches than current file
|
|
2196
|
-
if (bestMatch && bestMatch.score > currentFileScore) {
|
|
2197
|
-
debugLog("TEXT REDIRECT: Imported file has more text matches", {
|
|
2198
|
-
originalFile: recommendedFileContent.path,
|
|
2199
|
-
originalScore: currentFileScore,
|
|
2200
|
-
redirectTo: bestMatch.path,
|
|
2201
|
-
redirectScore: bestMatch.score,
|
|
2202
|
-
matchedTexts: bestMatch.matchedTexts
|
|
2203
|
-
});
|
|
2204
|
-
|
|
2205
|
-
// Switch target file to where most text content lives
|
|
2206
|
-
actualTargetFile = {
|
|
2207
|
-
path: bestMatch.path,
|
|
2208
|
-
content: bestMatch.content
|
|
2209
|
-
};
|
|
2210
|
-
|
|
2211
|
-
// Find a line with matched text for element location
|
|
2212
|
-
const lines = bestMatch.content.split('\n');
|
|
2213
|
-
const snippetStart = Math.max(0, bestMatch.firstMatchLine - 4);
|
|
2214
|
-
const snippetEnd = Math.min(lines.length, bestMatch.firstMatchLine + 5);
|
|
2215
|
-
|
|
2216
|
-
elementLocation = {
|
|
2217
|
-
lineNumber: bestMatch.firstMatchLine,
|
|
2218
|
-
snippet: lines.slice(snippetStart, snippetEnd).join('\n'),
|
|
2219
|
-
confidence: 'high',
|
|
2220
|
-
matchedBy: `text scoring: ${bestMatch.score} matches in imported file`
|
|
2221
|
-
};
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
|
|
2226
|
-
// DYNAMIC IMPORT SEARCH: If not found in main file, search imported components
|
|
2227
|
-
if (!elementLocation) {
|
|
2228
|
-
debugLog("Element not in main file, searching imported components...", {
|
|
2229
|
-
mainFile: recommendedFileContent.path,
|
|
2230
|
-
importedFilesCount: pageContext.componentSources.length
|
|
2231
|
-
});
|
|
2232
|
-
|
|
2233
|
-
const importedMatch = findElementInImportedFiles(
|
|
2234
|
-
focusedElements[0],
|
|
2235
|
-
pageContext.componentSources
|
|
2236
|
-
);
|
|
2237
|
-
|
|
2238
|
-
if (importedMatch) {
|
|
2239
|
-
debugLog("REDIRECT: Element found in imported component", {
|
|
2240
|
-
originalFile: recommendedFileContent.path,
|
|
2241
|
-
redirectTo: importedMatch.path,
|
|
2242
|
-
matchedBy: importedMatch.matchedBy,
|
|
2243
|
-
lineNumber: importedMatch.lineNumber
|
|
2244
|
-
});
|
|
2245
|
-
|
|
2246
|
-
// Switch target file to where element actually is
|
|
2247
|
-
actualTargetFile = {
|
|
2248
|
-
path: importedMatch.path,
|
|
2249
|
-
content: importedMatch.content
|
|
2250
|
-
};
|
|
2251
|
-
elementLocation = findElementLineInFile(importedMatch.content, focusedElements[0]);
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2241
|
+
// Note: Redirect logic already ran above, so actualTargetFile is the correct file
|
|
2242
|
+
if (actualTargetFile) {
|
|
2256
2243
|
// Build focused elements section with precise targeting info
|
|
2257
2244
|
if (focusedElements && focusedElements.length > 0) {
|
|
2258
2245
|
textContent += `FOCUSED ELEMENTS (user clicked on these):\n`;
|
|
@@ -2316,7 +2303,7 @@ ${elementLocation.snippet}
|
|
|
2316
2303
|
// Element NOT found in main file OR any imported components
|
|
2317
2304
|
// BLOCK the LLM from guessing - require empty modifications
|
|
2318
2305
|
debugLog("BLOCK: Could not locate focused element anywhere", {
|
|
2319
|
-
mainFile:
|
|
2306
|
+
mainFile: actualTargetFile.path,
|
|
2320
2307
|
searchedImports: pageContext.componentSources.length,
|
|
2321
2308
|
focusedElements: focusedElements.map(el => ({
|
|
2322
2309
|
name: el.name,
|
|
@@ -2332,7 +2319,7 @@ ${elementLocation.snippet}
|
|
|
2332
2319
|
⛔ STOP: CANNOT LOCATE THE CLICKED ELEMENT
|
|
2333
2320
|
|
|
2334
2321
|
The user clicked on a specific element, but it could NOT be found in:
|
|
2335
|
-
- ${
|
|
2322
|
+
- ${actualTargetFile.path} (main target file)
|
|
2336
2323
|
- Any of the ${pageContext.componentSources.length} imported component files
|
|
2337
2324
|
|
|
2338
2325
|
The element may be:
|
|
@@ -2447,7 +2434,8 @@ ${variantsMatch[0]}
|
|
|
2447
2434
|
}
|
|
2448
2435
|
|
|
2449
2436
|
// ========== SIMPLIFIED INSTRUCTIONS ==========
|
|
2450
|
-
|
|
2437
|
+
// Use actualTargetFile which may have been redirected to an imported component
|
|
2438
|
+
const targetPath = actualTargetFile?.path || pageContext.pageFile || "unknown";
|
|
2451
2439
|
|
|
2452
2440
|
textContent += `═══════════════════════════════════════════════════════════════════════════════
|
|
2453
2441
|
HOW TO MAKE YOUR EDIT
|
|
@@ -2491,10 +2479,14 @@ CRITICAL: Your "search" string MUST exist in the file. If you can't find the exa
|
|
|
2491
2479
|
validFilePaths.add(comp.path);
|
|
2492
2480
|
}
|
|
2493
2481
|
|
|
2494
|
-
// FIX: Add the recommended file
|
|
2482
|
+
// FIX: Add the recommended file and actual target file to validFilePaths
|
|
2483
|
+
// (recommended was spliced out for display, actual may be different due to redirect)
|
|
2495
2484
|
if (recommendedFileContent) {
|
|
2496
2485
|
validFilePaths.add(recommendedFileContent.path);
|
|
2497
2486
|
}
|
|
2487
|
+
if (actualTargetFile && actualTargetFile.path !== recommendedFileContent?.path) {
|
|
2488
|
+
validFilePaths.add(actualTargetFile.path);
|
|
2489
|
+
}
|
|
2498
2490
|
|
|
2499
2491
|
// Retry loop for handling patch failures
|
|
2500
2492
|
const MAX_RETRIES = 1;
|
|
@@ -2630,7 +2622,8 @@ This is better than generating patches with made-up code.`,
|
|
|
2630
2622
|
|
|
2631
2623
|
// CRITICAL: Warn if LLM is trying to modify a file OTHER than the TARGET COMPONENT
|
|
2632
2624
|
// This usually means the LLM is trying to modify a file it doesn't have full visibility into
|
|
2633
|
-
|
|
2625
|
+
// Use actualTargetFile since it may have been redirected from recommendedFileContent
|
|
2626
|
+
const targetComponentPath = actualTargetFile?.path;
|
|
2634
2627
|
if (targetComponentPath && mod.filePath !== targetComponentPath) {
|
|
2635
2628
|
debugLog("WARNING: LLM trying to modify non-target file", {
|
|
2636
2629
|
targetComponent: targetComponentPath,
|
|
@@ -2655,36 +2648,17 @@ This is better than generating patches with made-up code.`,
|
|
|
2655
2648
|
// New patch-based approach
|
|
2656
2649
|
console.log(`[Apply-First] Applying ${mod.patches.length} patches to ${mod.filePath}`);
|
|
2657
2650
|
|
|
2658
|
-
//
|
|
2659
|
-
|
|
2651
|
+
// Use patches directly - simplified approach focuses on exact code matching
|
|
2652
|
+
// rather than problemId validation (which was causing LLM to prioritize
|
|
2653
|
+
// problem-solving over exact code copying)
|
|
2654
|
+
const patchesToApply = mod.patches;
|
|
2655
|
+
|
|
2660
2656
|
if (isDesignRequest && identifiedProblems.length > 0) {
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
if (validationResult.rejected.length > 0) {
|
|
2664
|
-
debugLog("Design patch validation rejected some patches", {
|
|
2665
|
-
filePath: mod.filePath,
|
|
2666
|
-
validCount: validationResult.valid.length,
|
|
2667
|
-
rejectedCount: validationResult.rejected.length,
|
|
2668
|
-
rejectedReasons: validationResult.rejected.map(r => r.reason)
|
|
2669
|
-
});
|
|
2670
|
-
console.warn(`[Apply-First] ${validationResult.rejected.length} patches rejected for missing/invalid problemId`);
|
|
2671
|
-
}
|
|
2672
|
-
|
|
2673
|
-
// Only use validated patches
|
|
2674
|
-
if (validationResult.valid.length === 0) {
|
|
2675
|
-
patchErrors.push(`${mod.filePath}: All patches were rejected - they don't reference identified problems`);
|
|
2676
|
-
continue;
|
|
2677
|
-
}
|
|
2678
|
-
|
|
2679
|
-
// Log which problems are being fixed
|
|
2680
|
-
const problemsBeingFixed = [...new Set(validationResult.valid.map(p => p.problemId))];
|
|
2681
|
-
debugLog("Design patches validated", {
|
|
2657
|
+
debugLog("Design patches received", {
|
|
2682
2658
|
filePath: mod.filePath,
|
|
2683
|
-
|
|
2684
|
-
|
|
2659
|
+
patchCount: patchesToApply.length,
|
|
2660
|
+
problemsIdentified: identifiedProblems.length
|
|
2685
2661
|
});
|
|
2686
|
-
|
|
2687
|
-
patchesToApply = validationResult.valid;
|
|
2688
2662
|
}
|
|
2689
2663
|
|
|
2690
2664
|
// PRE-VALIDATION: Check if all search strings exist in the file BEFORE applying
|
|
@@ -1515,53 +1515,37 @@ function buildPhase2DesignPrompt(problems: DesignProblem[]): string {
|
|
|
1515
1515
|
return ''; // No problems identified, skip design protocol
|
|
1516
1516
|
}
|
|
1517
1517
|
|
|
1518
|
-
|
|
1518
|
+
// Create a simple, readable list of problems (no JSON complexity)
|
|
1519
|
+
const problemsList = problems.map(p => `• ${p.description}`).join('\n');
|
|
1519
1520
|
|
|
1520
1521
|
return `
|
|
1521
1522
|
═══════════════════════════════════════════════════════════════════════════════
|
|
1522
|
-
|
|
1523
|
+
DESIGN ISSUES TO FIX
|
|
1523
1524
|
═══════════════════════════════════════════════════════════════════════════════
|
|
1524
1525
|
|
|
1525
|
-
|
|
1526
|
+
These visual issues were identified:
|
|
1527
|
+
${problemsList}
|
|
1526
1528
|
|
|
1527
|
-
|
|
1529
|
+
Fix these issues by modifying the code below.
|
|
1528
1530
|
|
|
1529
|
-
|
|
1531
|
+
⚠️ CRITICAL - EXACT CODE MATCHING:
|
|
1532
|
+
Your "search" string MUST be copied CHARACTER-FOR-CHARACTER from the file.
|
|
1533
|
+
Use the line numbers on the left to find the exact code.
|
|
1530
1534
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
3. Make the SMALLEST change that fixes each problem
|
|
1535
|
-
4. Do NOT change layout systems (flex→grid) unless absolutely necessary to fix a problem
|
|
1536
|
-
5. Preserve existing structure - adjust properties, don't restructure
|
|
1535
|
+
EXAMPLE:
|
|
1536
|
+
If line 42 shows: 42| <div className="mt-6 flex items-center">
|
|
1537
|
+
Your search must be: <div className="mt-6 flex items-center">
|
|
1537
1538
|
|
|
1538
|
-
|
|
1539
|
+
DO NOT:
|
|
1540
|
+
- Combine multiple lines into one search string
|
|
1541
|
+
- Add or remove classes/attributes
|
|
1542
|
+
- Invent code that "should" exist
|
|
1543
|
+
- Guess at the structure
|
|
1539
1544
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
"patches": [
|
|
1545
|
-
{
|
|
1546
|
-
"problemId": "grouping-1",
|
|
1547
|
-
"search": "<Badge className=\"absolute right-4\">",
|
|
1548
|
-
"replace": "<Badge className=\"ml-2\">",
|
|
1549
|
-
"rationale": "Removes absolute positioning to group badge with label"
|
|
1550
|
-
},
|
|
1551
|
-
{
|
|
1552
|
-
"problemId": "spacing-1",
|
|
1553
|
-
"search": "className=\"mt-8\"",
|
|
1554
|
-
"replace": "className=\"mt-4\"",
|
|
1555
|
-
"rationale": "Reduces excessive vertical gap"
|
|
1556
|
-
}
|
|
1557
|
-
]
|
|
1558
|
-
}]
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
VALIDATION:
|
|
1562
|
-
- Patches without a valid problemId will be REJECTED
|
|
1563
|
-
- Patches that don't trace to an identified problem will be REJECTED
|
|
1564
|
-
- Each patch must have search, replace, problemId, and rationale fields
|
|
1545
|
+
DO:
|
|
1546
|
+
- Copy the exact text from the numbered lines
|
|
1547
|
+
- Make small, targeted changes in the "replace" field
|
|
1548
|
+
- Keep patches focused on one change each
|
|
1565
1549
|
`;
|
|
1566
1550
|
}
|
|
1567
1551
|
|
|
@@ -2056,6 +2040,124 @@ export async function POST(request: Request) {
|
|
|
2056
2040
|
}
|
|
2057
2041
|
}
|
|
2058
2042
|
|
|
2043
|
+
// ========== FILE REDIRECT LOGIC (runs BEFORE building instructions) ==========
|
|
2044
|
+
// This determines the ACTUAL target file by checking for text matches in imported components
|
|
2045
|
+
// Must run first so that all subsequent code uses the correct file path
|
|
2046
|
+
let actualTargetFile = recommendedFileContent || (pageContext.pageContent ? { path: pageContext.pageFile, content: pageContext.pageContent } : null);
|
|
2047
|
+
let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
|
|
2048
|
+
|
|
2049
|
+
if (actualTargetFile && focusedElements && focusedElements.length > 0) {
|
|
2050
|
+
const content = actualTargetFile.content;
|
|
2051
|
+
|
|
2052
|
+
// Search for focused element in the file using multiple strategies
|
|
2053
|
+
// Priority: DOM id > textContent > className patterns
|
|
2054
|
+
for (const el of focusedElements) {
|
|
2055
|
+
elementLocation = findElementLineInFile(content, el);
|
|
2056
|
+
if (elementLocation) {
|
|
2057
|
+
debugLog("Found focused element in file", {
|
|
2058
|
+
matchedBy: elementLocation.matchedBy,
|
|
2059
|
+
lineNumber: elementLocation.lineNumber,
|
|
2060
|
+
confidence: elementLocation.confidence,
|
|
2061
|
+
file: actualTargetFile.path,
|
|
2062
|
+
});
|
|
2063
|
+
break;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
// TEXT SCORING REDIRECT: Score ALL imported files by how many discovered
|
|
2068
|
+
// text strings they contain. The file with the highest score is the TRUE target.
|
|
2069
|
+
if (elementLocation && elementLocation.confidence !== 'high') {
|
|
2070
|
+
const allTextContent = focusedElements
|
|
2071
|
+
.filter(e => e.textContent && e.textContent.length > 5)
|
|
2072
|
+
.map(e => e.textContent!);
|
|
2073
|
+
|
|
2074
|
+
if (allTextContent.length > 0) {
|
|
2075
|
+
debugLog("Medium/low confidence match - scoring imports for all text", {
|
|
2076
|
+
currentFile: actualTargetFile.path,
|
|
2077
|
+
currentConfidence: elementLocation.confidence,
|
|
2078
|
+
textCount: allTextContent.length,
|
|
2079
|
+
texts: allTextContent.slice(0, 5).map(t => t.substring(0, 25))
|
|
2080
|
+
});
|
|
2081
|
+
|
|
2082
|
+
const bestMatch = scoreFilesForTextContent(
|
|
2083
|
+
allTextContent,
|
|
2084
|
+
pageContext.componentSources
|
|
2085
|
+
);
|
|
2086
|
+
|
|
2087
|
+
const currentFileScore = allTextContent.filter(text =>
|
|
2088
|
+
actualTargetFile!.content.includes(text.substring(0, 20))
|
|
2089
|
+
).length;
|
|
2090
|
+
|
|
2091
|
+
debugLog("Text scoring comparison", {
|
|
2092
|
+
currentFile: actualTargetFile.path,
|
|
2093
|
+
currentScore: currentFileScore,
|
|
2094
|
+
bestImport: bestMatch?.path || 'none',
|
|
2095
|
+
bestImportScore: bestMatch?.score || 0
|
|
2096
|
+
});
|
|
2097
|
+
|
|
2098
|
+
if (bestMatch && bestMatch.score > currentFileScore) {
|
|
2099
|
+
debugLog("TEXT REDIRECT: Imported file has more text matches", {
|
|
2100
|
+
originalFile: actualTargetFile.path,
|
|
2101
|
+
originalScore: currentFileScore,
|
|
2102
|
+
redirectTo: bestMatch.path,
|
|
2103
|
+
redirectScore: bestMatch.score,
|
|
2104
|
+
matchedTexts: bestMatch.matchedTexts
|
|
2105
|
+
});
|
|
2106
|
+
|
|
2107
|
+
actualTargetFile = {
|
|
2108
|
+
path: bestMatch.path,
|
|
2109
|
+
content: bestMatch.content
|
|
2110
|
+
};
|
|
2111
|
+
|
|
2112
|
+
const lines = bestMatch.content.split('\n');
|
|
2113
|
+
const snippetStart = Math.max(0, bestMatch.firstMatchLine - 4);
|
|
2114
|
+
const snippetEnd = Math.min(lines.length, bestMatch.firstMatchLine + 5);
|
|
2115
|
+
|
|
2116
|
+
elementLocation = {
|
|
2117
|
+
lineNumber: bestMatch.firstMatchLine,
|
|
2118
|
+
snippet: lines.slice(snippetStart, snippetEnd).join('\n'),
|
|
2119
|
+
confidence: 'high',
|
|
2120
|
+
matchedBy: `text scoring: ${bestMatch.score} matches in imported file`
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
// DYNAMIC IMPORT SEARCH: If not found in main file, search imported components
|
|
2127
|
+
if (!elementLocation) {
|
|
2128
|
+
debugLog("Element not in main file, searching imported components...", {
|
|
2129
|
+
mainFile: actualTargetFile.path,
|
|
2130
|
+
importedFilesCount: pageContext.componentSources.length
|
|
2131
|
+
});
|
|
2132
|
+
|
|
2133
|
+
const importedMatch = findElementInImportedFiles(
|
|
2134
|
+
focusedElements[0],
|
|
2135
|
+
pageContext.componentSources
|
|
2136
|
+
);
|
|
2137
|
+
|
|
2138
|
+
if (importedMatch) {
|
|
2139
|
+
debugLog("REDIRECT: Element found in imported component", {
|
|
2140
|
+
originalFile: actualTargetFile.path,
|
|
2141
|
+
redirectTo: importedMatch.path,
|
|
2142
|
+
matchedBy: importedMatch.matchedBy,
|
|
2143
|
+
lineNumber: importedMatch.lineNumber
|
|
2144
|
+
});
|
|
2145
|
+
|
|
2146
|
+
actualTargetFile = {
|
|
2147
|
+
path: importedMatch.path,
|
|
2148
|
+
content: importedMatch.content
|
|
2149
|
+
};
|
|
2150
|
+
elementLocation = findElementLineInFile(importedMatch.content, focusedElements[0]);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
debugLog("File redirect complete", {
|
|
2156
|
+
originalRecommended: recommendedFileContent?.path || 'none',
|
|
2157
|
+
actualTarget: actualTargetFile?.path || 'none',
|
|
2158
|
+
wasRedirected: actualTargetFile?.path !== recommendedFileContent?.path
|
|
2159
|
+
});
|
|
2160
|
+
|
|
2059
2161
|
// Build text content
|
|
2060
2162
|
let textContent = `VISION MODE EDIT REQUEST
|
|
2061
2163
|
|
|
@@ -2079,7 +2181,8 @@ User Request: "${userPrompt}"
|
|
|
2079
2181
|
|
|
2080
2182
|
// Phase 1: Analyze the screenshot to identify specific problems
|
|
2081
2183
|
// This is a SEPARATE LLM call that only identifies problems
|
|
2082
|
-
|
|
2184
|
+
// Use actualTargetFile since redirect has already happened
|
|
2185
|
+
const fileContentForAnalysis = actualTargetFile?.content || pageContext.pageContent || '';
|
|
2083
2186
|
identifiedProblems = await analyzeDesignProblems(
|
|
2084
2187
|
screenshot,
|
|
2085
2188
|
fileContentForAnalysis,
|
|
@@ -2104,124 +2207,8 @@ User Request: "${userPrompt}"
|
|
|
2104
2207
|
|
|
2105
2208
|
// ========== TARGET COMPONENT ONLY (with line numbers) ==========
|
|
2106
2209
|
// CRITICAL: Only include the TARGET file to avoid overwhelming the LLM with noise
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
// Search for focused element in the file using multiple strategies
|
|
2111
|
-
// Priority: DOM id > textContent > className patterns
|
|
2112
|
-
let elementLocation: { lineNumber: number; snippet: string; confidence: 'high' | 'medium' | 'low'; matchedBy: string } | null = null;
|
|
2113
|
-
let actualTargetFile = recommendedFileContent; // May change if we redirect
|
|
2114
|
-
|
|
2115
|
-
if (focusedElements && focusedElements.length > 0) {
|
|
2116
|
-
for (const el of focusedElements) {
|
|
2117
|
-
elementLocation = findElementLineInFile(content, el);
|
|
2118
|
-
if (elementLocation) {
|
|
2119
|
-
debugLog("Found focused element in file", {
|
|
2120
|
-
matchedBy: elementLocation.matchedBy,
|
|
2121
|
-
lineNumber: elementLocation.lineNumber,
|
|
2122
|
-
confidence: elementLocation.confidence,
|
|
2123
|
-
file: recommendedFileContent.path,
|
|
2124
|
-
});
|
|
2125
|
-
break;
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
|
|
2129
|
-
// TEXT SCORING REDIRECT: Score ALL imported files by how many discovered
|
|
2130
|
-
// text strings they contain. The file with the highest score is the TRUE target.
|
|
2131
|
-
// This handles parent/child component cases accurately.
|
|
2132
|
-
if (elementLocation && elementLocation.confidence !== 'high') {
|
|
2133
|
-
// Collect ALL text content from focused elements
|
|
2134
|
-
const allTextContent = focusedElements
|
|
2135
|
-
.filter(e => e.textContent && e.textContent.length > 5)
|
|
2136
|
-
.map(e => e.textContent!);
|
|
2137
|
-
|
|
2138
|
-
if (allTextContent.length > 0) {
|
|
2139
|
-
debugLog("Medium/low confidence match - scoring imports for all text", {
|
|
2140
|
-
currentFile: recommendedFileContent.path,
|
|
2141
|
-
currentConfidence: elementLocation.confidence,
|
|
2142
|
-
textCount: allTextContent.length,
|
|
2143
|
-
texts: allTextContent.slice(0, 5).map(t => t.substring(0, 25))
|
|
2144
|
-
});
|
|
2145
|
-
|
|
2146
|
-
// Score all imported files
|
|
2147
|
-
const bestMatch = scoreFilesForTextContent(
|
|
2148
|
-
allTextContent,
|
|
2149
|
-
pageContext.componentSources
|
|
2150
|
-
);
|
|
2151
|
-
|
|
2152
|
-
// Also score the current file for comparison
|
|
2153
|
-
const currentFileScore = allTextContent.filter(text =>
|
|
2154
|
-
recommendedFileContent.content.includes(text.substring(0, 20))
|
|
2155
|
-
).length;
|
|
2156
|
-
|
|
2157
|
-
debugLog("Text scoring comparison", {
|
|
2158
|
-
currentFile: recommendedFileContent.path,
|
|
2159
|
-
currentScore: currentFileScore,
|
|
2160
|
-
bestImport: bestMatch?.path || 'none',
|
|
2161
|
-
bestImportScore: bestMatch?.score || 0
|
|
2162
|
-
});
|
|
2163
|
-
|
|
2164
|
-
// Redirect only if imported file has MORE matches than current file
|
|
2165
|
-
if (bestMatch && bestMatch.score > currentFileScore) {
|
|
2166
|
-
debugLog("TEXT REDIRECT: Imported file has more text matches", {
|
|
2167
|
-
originalFile: recommendedFileContent.path,
|
|
2168
|
-
originalScore: currentFileScore,
|
|
2169
|
-
redirectTo: bestMatch.path,
|
|
2170
|
-
redirectScore: bestMatch.score,
|
|
2171
|
-
matchedTexts: bestMatch.matchedTexts
|
|
2172
|
-
});
|
|
2173
|
-
|
|
2174
|
-
// Switch target file to where most text content lives
|
|
2175
|
-
actualTargetFile = {
|
|
2176
|
-
path: bestMatch.path,
|
|
2177
|
-
content: bestMatch.content
|
|
2178
|
-
};
|
|
2179
|
-
|
|
2180
|
-
// Find a line with matched text for element location
|
|
2181
|
-
const lines = bestMatch.content.split('\n');
|
|
2182
|
-
const snippetStart = Math.max(0, bestMatch.firstMatchLine - 4);
|
|
2183
|
-
const snippetEnd = Math.min(lines.length, bestMatch.firstMatchLine + 5);
|
|
2184
|
-
|
|
2185
|
-
elementLocation = {
|
|
2186
|
-
lineNumber: bestMatch.firstMatchLine,
|
|
2187
|
-
snippet: lines.slice(snippetStart, snippetEnd).join('\n'),
|
|
2188
|
-
confidence: 'high',
|
|
2189
|
-
matchedBy: `text scoring: ${bestMatch.score} matches in imported file`
|
|
2190
|
-
};
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
// DYNAMIC IMPORT SEARCH: If not found in main file, search imported components
|
|
2196
|
-
if (!elementLocation) {
|
|
2197
|
-
debugLog("Element not in main file, searching imported components...", {
|
|
2198
|
-
mainFile: recommendedFileContent.path,
|
|
2199
|
-
importedFilesCount: pageContext.componentSources.length
|
|
2200
|
-
});
|
|
2201
|
-
|
|
2202
|
-
const importedMatch = findElementInImportedFiles(
|
|
2203
|
-
focusedElements[0],
|
|
2204
|
-
pageContext.componentSources
|
|
2205
|
-
);
|
|
2206
|
-
|
|
2207
|
-
if (importedMatch) {
|
|
2208
|
-
debugLog("REDIRECT: Element found in imported component", {
|
|
2209
|
-
originalFile: recommendedFileContent.path,
|
|
2210
|
-
redirectTo: importedMatch.path,
|
|
2211
|
-
matchedBy: importedMatch.matchedBy,
|
|
2212
|
-
lineNumber: importedMatch.lineNumber
|
|
2213
|
-
});
|
|
2214
|
-
|
|
2215
|
-
// Switch target file to where element actually is
|
|
2216
|
-
actualTargetFile = {
|
|
2217
|
-
path: importedMatch.path,
|
|
2218
|
-
content: importedMatch.content
|
|
2219
|
-
};
|
|
2220
|
-
elementLocation = findElementLineInFile(importedMatch.content, focusedElements[0]);
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2210
|
+
// Note: Redirect logic already ran above, so actualTargetFile is the correct file
|
|
2211
|
+
if (actualTargetFile) {
|
|
2225
2212
|
// Build focused elements section with precise targeting info
|
|
2226
2213
|
if (focusedElements && focusedElements.length > 0) {
|
|
2227
2214
|
textContent += `FOCUSED ELEMENTS (user clicked on these):\n`;
|
|
@@ -2285,7 +2272,7 @@ ${elementLocation.snippet}
|
|
|
2285
2272
|
// Element NOT found in main file OR any imported components
|
|
2286
2273
|
// BLOCK the LLM from guessing - require empty modifications
|
|
2287
2274
|
debugLog("BLOCK: Could not locate focused element anywhere", {
|
|
2288
|
-
mainFile:
|
|
2275
|
+
mainFile: actualTargetFile.path,
|
|
2289
2276
|
searchedImports: pageContext.componentSources.length,
|
|
2290
2277
|
focusedElements: focusedElements.map(el => ({
|
|
2291
2278
|
name: el.name,
|
|
@@ -2301,7 +2288,7 @@ ${elementLocation.snippet}
|
|
|
2301
2288
|
⛔ STOP: CANNOT LOCATE THE CLICKED ELEMENT
|
|
2302
2289
|
|
|
2303
2290
|
The user clicked on a specific element, but it could NOT be found in:
|
|
2304
|
-
- ${
|
|
2291
|
+
- ${actualTargetFile.path} (main target file)
|
|
2305
2292
|
- Any of the ${pageContext.componentSources.length} imported component files
|
|
2306
2293
|
|
|
2307
2294
|
The element may be:
|
|
@@ -2343,7 +2330,7 @@ ${linesWithNumbers}
|
|
|
2343
2330
|
path: actualTargetFile.path,
|
|
2344
2331
|
lines: targetContent.split('\n').length,
|
|
2345
2332
|
size: targetContent.length,
|
|
2346
|
-
wasRedirected: actualTargetFile.path !== recommendedFileContent
|
|
2333
|
+
wasRedirected: actualTargetFile.path !== recommendedFileContent?.path
|
|
2347
2334
|
});
|
|
2348
2335
|
} else if (pageContext.pageContent) {
|
|
2349
2336
|
// Fallback: use page file if no recommended file
|
|
@@ -2416,7 +2403,8 @@ ${variantsMatch[0]}
|
|
|
2416
2403
|
}
|
|
2417
2404
|
|
|
2418
2405
|
// ========== SIMPLIFIED INSTRUCTIONS ==========
|
|
2419
|
-
|
|
2406
|
+
// Use actualTargetFile which may have been redirected to an imported component
|
|
2407
|
+
const targetPath = actualTargetFile?.path || pageContext.pageFile || "unknown";
|
|
2420
2408
|
|
|
2421
2409
|
textContent += `═══════════════════════════════════════════════════════════════════════════════
|
|
2422
2410
|
HOW TO MAKE YOUR EDIT
|
|
@@ -2628,41 +2616,17 @@ This is better than generating patches with made-up code.`,
|
|
|
2628
2616
|
// New patch-based approach
|
|
2629
2617
|
console.log(`[Vision Mode] Applying ${mod.patches.length} patches to ${mod.filePath}`);
|
|
2630
2618
|
|
|
2631
|
-
//
|
|
2632
|
-
|
|
2619
|
+
// Use patches directly - simplified approach focuses on exact code matching
|
|
2620
|
+
// rather than problemId validation (which was causing LLM to prioritize
|
|
2621
|
+
// problem-solving over exact code copying)
|
|
2622
|
+
const patchesToApply = mod.patches;
|
|
2623
|
+
|
|
2633
2624
|
if (isDesignRequest && identifiedProblems.length > 0) {
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
if (validationResult.rejected.length > 0) {
|
|
2637
|
-
debugLog("Design patch validation rejected some patches", {
|
|
2638
|
-
filePath: mod.filePath,
|
|
2639
|
-
validCount: validationResult.valid.length,
|
|
2640
|
-
rejectedCount: validationResult.rejected.length,
|
|
2641
|
-
rejectedReasons: validationResult.rejected.map(r => r.reason)
|
|
2642
|
-
});
|
|
2643
|
-
console.warn(`[Vision Mode] ${validationResult.rejected.length} patches rejected for missing/invalid problemId`);
|
|
2644
|
-
}
|
|
2645
|
-
|
|
2646
|
-
// Only use validated patches
|
|
2647
|
-
if (validationResult.valid.length === 0) {
|
|
2648
|
-
patchErrors.push(`${mod.filePath}: All patches were rejected - they don't reference identified problems`);
|
|
2649
|
-
continue;
|
|
2650
|
-
}
|
|
2651
|
-
|
|
2652
|
-
// Log which problems are being fixed
|
|
2653
|
-
const problemsBeingFixed = [...new Set(validationResult.valid.map(p => p.problemId))];
|
|
2654
|
-
debugLog("Design patches validated", {
|
|
2625
|
+
debugLog("Design patches received", {
|
|
2655
2626
|
filePath: mod.filePath,
|
|
2656
|
-
|
|
2657
|
-
|
|
2627
|
+
patchCount: patchesToApply.length,
|
|
2628
|
+
problemsIdentified: identifiedProblems.length
|
|
2658
2629
|
});
|
|
2659
|
-
|
|
2660
|
-
// Map validated patches to Patch interface (rationale -> explanation)
|
|
2661
|
-
patchesToApply = validationResult.valid.map(p => ({
|
|
2662
|
-
search: p.search,
|
|
2663
|
-
replace: p.replace,
|
|
2664
|
-
explanation: `[${p.problemId}] ${p.rationale}`
|
|
2665
|
-
}));
|
|
2666
2630
|
}
|
|
2667
2631
|
|
|
2668
2632
|
// PRE-VALIDATION: Check if all search strings exist in the file BEFORE applying
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonance-brand-mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.102",
|
|
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",
|