sonance-brand-mcp 1.3.21 → 1.3.24
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.
|
@@ -60,27 +60,39 @@ const VISION_SYSTEM_PROMPT = `You are a React/Tailwind CSS expert with vision ca
|
|
|
60
60
|
You can see screenshots of web pages and understand their visual layout, then modify code to implement requested changes.
|
|
61
61
|
|
|
62
62
|
═══════════════════════════════════════════════════════════════════════════════
|
|
63
|
-
|
|
63
|
+
ABSOLUTE RULES - VIOLATION WILL BREAK THE APPLICATION
|
|
64
64
|
═══════════════════════════════════════════════════════════════════════════════
|
|
65
65
|
|
|
66
|
-
**
|
|
67
|
-
1.
|
|
68
|
-
2.
|
|
69
|
-
3.
|
|
70
|
-
4.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
6.
|
|
75
|
-
7. NEVER
|
|
76
|
-
8.
|
|
66
|
+
**SURGICAL EDITING (MOST CRITICAL):**
|
|
67
|
+
1. You are making SURGICAL EDITS, not rewriting files
|
|
68
|
+
2. Change ONLY the specific lines that need modification
|
|
69
|
+
3. The modified file MUST have approximately the same line count as the original
|
|
70
|
+
4. If original file has 200 lines, modified file should have ~195-210 lines
|
|
71
|
+
5. NEVER rewrite a file from scratch - this destroys the application
|
|
72
|
+
|
|
73
|
+
**FILE RULES:**
|
|
74
|
+
6. You may ONLY edit files that are provided in the PAGE CONTEXT section
|
|
75
|
+
7. NEVER create new files - only modify existing ones shown to you
|
|
76
|
+
8. The filePath in your response MUST exactly match one of the provided file paths
|
|
77
|
+
|
|
78
|
+
**PRESERVATION RULES (ZERO TOLERANCE):**
|
|
79
|
+
9. NEVER delete or remove existing functions, hooks, state, or logic
|
|
80
|
+
10. NEVER remove imports - you may only ADD imports if needed
|
|
81
|
+
11. NEVER remove useEffect, useState, useCallback, or other React hooks
|
|
82
|
+
12. NEVER remove API calls, fetch requests, or data loading logic
|
|
83
|
+
13. NEVER remove error handling, loading states, or conditional rendering
|
|
84
|
+
14. NEVER remove data-sonance-* attributes
|
|
85
|
+
15. NEVER change component structure unless specifically requested
|
|
86
|
+
16. NEVER modify TypeScript types or interfaces unless specifically requested
|
|
77
87
|
|
|
78
88
|
**CHANGE RULES:**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
17. Make ONLY the changes requested by the user
|
|
90
|
+
18. Modify the MINIMUM amount of code necessary
|
|
91
|
+
19. Keep all existing className values and ADD to them if needed
|
|
92
|
+
20. Use semantic Tailwind classes (bg-primary, text-foreground, etc.)
|
|
93
|
+
21. Maintain dark mode compatibility with CSS variables
|
|
94
|
+
22. Keep the cn() utility for className merging
|
|
95
|
+
23. CRITICAL: You MUST return the FULL file content in "modifiedContent". Do NOT use comments like "// ... existing code ..." or "// ... rest of file ...". Return every single line of code, even if unchanged.
|
|
84
96
|
|
|
85
97
|
**SONANCE BRAND COLORS:**
|
|
86
98
|
- Charcoal: #333F48, #343D46 (primary)
|
|
@@ -96,9 +108,16 @@ The JSON must include:
|
|
|
96
108
|
- "reasoning": Brief explanation of what you see in the screenshot and your plan
|
|
97
109
|
- "modifications": Array of file modifications, each with:
|
|
98
110
|
- "filePath": Path to the file
|
|
99
|
-
- "modifiedContent": Complete updated file content
|
|
111
|
+
- "modifiedContent": Complete updated file content (MUST BE FULL CONTENT, NO TRUNCATION)
|
|
100
112
|
- "explanation": What changed in this file
|
|
101
|
-
- "explanation": Overall summary of changes
|
|
113
|
+
- "explanation": Overall summary of changes
|
|
114
|
+
|
|
115
|
+
**EXAMPLE OF CORRECT EDIT:**
|
|
116
|
+
If user asks to "make buttons smaller", you should:
|
|
117
|
+
- Find the button elements in the code
|
|
118
|
+
- Change ONLY the size-related classes (e.g., h-10 -> h-8, px-4 -> px-3)
|
|
119
|
+
- Keep ALL other code exactly the same
|
|
120
|
+
- Return the FULL file with this tiny change`;
|
|
102
121
|
|
|
103
122
|
export async function POST(request: Request) {
|
|
104
123
|
// Only allow in development
|
|
@@ -234,12 +253,19 @@ GLOBALS.CSS (relevant theme variables):
|
|
|
234
253
|
${pageContext.globalsCSS.substring(0, 2000)}${pageContext.globalsCSS.length > 2000 ? "\n/* ... (truncated) */" : ""}
|
|
235
254
|
\`\`\`
|
|
236
255
|
|
|
256
|
+
VALID FILES YOU MAY EDIT:
|
|
257
|
+
${pageContext.pageFile ? `- ${pageContext.pageFile}` : ""}
|
|
258
|
+
${pageContext.componentSources.map((c) => `- ${c.path}`).join("\n")}
|
|
259
|
+
|
|
237
260
|
INSTRUCTIONS:
|
|
238
261
|
1. Look at the screenshot and identify elements mentioned in the user's request
|
|
239
262
|
2. Review the code to understand current implementation
|
|
240
|
-
3.
|
|
241
|
-
4.
|
|
242
|
-
5. Return
|
|
263
|
+
3. Make SURGICAL EDITS - change only the specific lines needed
|
|
264
|
+
4. PRESERVE all existing logic, hooks, API calls, and error handling
|
|
265
|
+
5. Return the FULL file content (no truncation, no "// ... existing ..." comments)
|
|
266
|
+
6. Only use file paths from the VALID FILES list above
|
|
267
|
+
|
|
268
|
+
CRITICAL: Your modified file should have approximately the same number of lines as the original. If the original has 200 lines and your output has 50 lines, you have made a mistake.`;
|
|
243
269
|
|
|
244
270
|
messageContent.push({
|
|
245
271
|
type: "text",
|
|
@@ -320,6 +346,9 @@ INSTRUCTIONS:
|
|
|
320
346
|
|
|
321
347
|
// Read original content and prepare modifications
|
|
322
348
|
const modifications: VisionFileModification[] = [];
|
|
349
|
+
const validationErrors: string[] = [];
|
|
350
|
+
const validationWarnings: string[] = [];
|
|
351
|
+
|
|
323
352
|
for (const mod of aiResponse.modifications) {
|
|
324
353
|
const fullPath = path.join(projectRoot, mod.filePath);
|
|
325
354
|
let originalContent = "";
|
|
@@ -327,6 +356,18 @@ INSTRUCTIONS:
|
|
|
327
356
|
originalContent = fs.readFileSync(fullPath, "utf-8");
|
|
328
357
|
}
|
|
329
358
|
|
|
359
|
+
// Validate the modification
|
|
360
|
+
const validation = validateModification(originalContent, mod.modifiedContent, mod.filePath);
|
|
361
|
+
|
|
362
|
+
if (!validation.valid) {
|
|
363
|
+
validationErrors.push(validation.error || "Unknown validation error");
|
|
364
|
+
continue; // Skip this modification
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (validation.warnings.length > 0) {
|
|
368
|
+
validationWarnings.push(...validation.warnings.map(w => `${mod.filePath}: ${w}`));
|
|
369
|
+
}
|
|
370
|
+
|
|
330
371
|
modifications.push({
|
|
331
372
|
filePath: mod.filePath,
|
|
332
373
|
originalContent,
|
|
@@ -336,6 +377,23 @@ INSTRUCTIONS:
|
|
|
336
377
|
});
|
|
337
378
|
}
|
|
338
379
|
|
|
380
|
+
// If all modifications failed validation, return error
|
|
381
|
+
if (validationErrors.length > 0 && modifications.length === 0) {
|
|
382
|
+
console.error("All AI modifications failed validation:", validationErrors);
|
|
383
|
+
return NextResponse.json(
|
|
384
|
+
{
|
|
385
|
+
success: false,
|
|
386
|
+
error: validationErrors.join("\n\n"),
|
|
387
|
+
},
|
|
388
|
+
{ status: 400 }
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Log warnings for review
|
|
393
|
+
if (validationWarnings.length > 0) {
|
|
394
|
+
console.warn("Vision apply validation warnings:", validationWarnings);
|
|
395
|
+
}
|
|
396
|
+
|
|
339
397
|
// Create backups and apply changes atomically
|
|
340
398
|
const applyResult = await applyChangesWithBackup(
|
|
341
399
|
modifications,
|
|
@@ -650,3 +708,105 @@ function generateSimpleDiff(original: string, modified: string): string {
|
|
|
650
708
|
|
|
651
709
|
return diff.join("\n");
|
|
652
710
|
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Validate that AI modifications are surgical edits, not complete rewrites
|
|
714
|
+
*/
|
|
715
|
+
interface ValidationResult {
|
|
716
|
+
valid: boolean;
|
|
717
|
+
error?: string;
|
|
718
|
+
warnings: string[];
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function validateModification(
|
|
722
|
+
originalContent: string,
|
|
723
|
+
modifiedContent: string,
|
|
724
|
+
filePath: string
|
|
725
|
+
): ValidationResult {
|
|
726
|
+
const warnings: string[] = [];
|
|
727
|
+
|
|
728
|
+
// Skip validation for new files (no original content)
|
|
729
|
+
if (!originalContent || originalContent.trim() === "") {
|
|
730
|
+
return { valid: true, warnings: ["New file - no original to compare"] };
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const originalLines = originalContent.split("\n");
|
|
734
|
+
const modifiedLines = modifiedContent.split("\n");
|
|
735
|
+
|
|
736
|
+
// Check 1: Truncation detection - look for placeholder comments
|
|
737
|
+
const truncationPatterns = [
|
|
738
|
+
/\/\/\s*\.\.\.\s*existing/i,
|
|
739
|
+
/\/\/\s*\.\.\.\s*rest\s*of/i,
|
|
740
|
+
/\/\/\s*\.\.\.\s*more\s*code/i,
|
|
741
|
+
/\/\*\s*\.\.\.\s*\*\//,
|
|
742
|
+
/\/\/\s*\.\.\./,
|
|
743
|
+
];
|
|
744
|
+
|
|
745
|
+
for (const pattern of truncationPatterns) {
|
|
746
|
+
if (pattern.test(modifiedContent)) {
|
|
747
|
+
return {
|
|
748
|
+
valid: false,
|
|
749
|
+
error: `File ${filePath} contains truncation placeholder (e.g., "// ... existing code"). The AI must return the complete file content. Please try again.`,
|
|
750
|
+
warnings,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Check 2: Line count shrinkage - reject if file shrinks by more than 30%
|
|
756
|
+
const lineDelta = modifiedLines.length - originalLines.length;
|
|
757
|
+
const shrinkagePercent = (lineDelta / originalLines.length) * 100;
|
|
758
|
+
|
|
759
|
+
if (shrinkagePercent < -30) {
|
|
760
|
+
return {
|
|
761
|
+
valid: false,
|
|
762
|
+
error: `File ${filePath} shrank from ${originalLines.length} to ${modifiedLines.length} lines (${Math.abs(shrinkagePercent).toFixed(0)}% reduction). This suggests the AI rewrote the file instead of making surgical edits. Please try a more specific request.`,
|
|
763
|
+
warnings,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (shrinkagePercent < -15) {
|
|
768
|
+
warnings.push(`File shrank by ${Math.abs(shrinkagePercent).toFixed(0)}% - review carefully`);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Check 3: Change percentage - warn if too many lines are different
|
|
772
|
+
let changedLines = 0;
|
|
773
|
+
const minLines = Math.min(originalLines.length, modifiedLines.length);
|
|
774
|
+
|
|
775
|
+
for (let i = 0; i < minLines; i++) {
|
|
776
|
+
if (originalLines[i] !== modifiedLines[i]) {
|
|
777
|
+
changedLines++;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Add lines that were added or removed
|
|
782
|
+
changedLines += Math.abs(originalLines.length - modifiedLines.length);
|
|
783
|
+
|
|
784
|
+
const changePercent = (changedLines / originalLines.length) * 100;
|
|
785
|
+
|
|
786
|
+
if (changePercent > 50) {
|
|
787
|
+
return {
|
|
788
|
+
valid: false,
|
|
789
|
+
error: `File ${filePath} has ${changePercent.toFixed(0)}% of lines changed. This suggests the AI rewrote the file instead of making surgical edits. For safety, this change has been rejected. Please try a more specific request.`,
|
|
790
|
+
warnings,
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (changePercent > 30) {
|
|
795
|
+
warnings.push(`${changePercent.toFixed(0)}% of lines changed - larger than expected for a surgical edit`);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Check 4: Import preservation - ensure imports aren't removed
|
|
799
|
+
const importRegex = /^import\s+/gm;
|
|
800
|
+
const originalImports = (originalContent.match(importRegex) || []).length;
|
|
801
|
+
const modifiedImports = (modifiedContent.match(importRegex) || []).length;
|
|
802
|
+
|
|
803
|
+
if (modifiedImports < originalImports * 0.5 && originalImports > 2) {
|
|
804
|
+
return {
|
|
805
|
+
valid: false,
|
|
806
|
+
error: `File ${filePath} went from ${originalImports} imports to ${modifiedImports}. Imports should not be removed. Please try again.`,
|
|
807
|
+
warnings,
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
return { valid: true, warnings };
|
|
812
|
+
}
|
|
@@ -61,33 +61,40 @@ const VISION_SYSTEM_PROMPT = `You are a React/Tailwind CSS expert with vision ca
|
|
|
61
61
|
You can see screenshots of web pages and understand their visual layout, then modify code to implement requested changes.
|
|
62
62
|
|
|
63
63
|
═══════════════════════════════════════════════════════════════════════════════
|
|
64
|
-
|
|
64
|
+
ABSOLUTE RULES - VIOLATION WILL BREAK THE APPLICATION
|
|
65
65
|
═══════════════════════════════════════════════════════════════════════════════
|
|
66
66
|
|
|
67
|
-
**
|
|
68
|
-
1. You
|
|
69
|
-
2.
|
|
70
|
-
3. The
|
|
71
|
-
4. If
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
6.
|
|
76
|
-
7.
|
|
77
|
-
8.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
10. NEVER
|
|
82
|
-
11. NEVER
|
|
83
|
-
12. NEVER remove
|
|
67
|
+
**SURGICAL EDITING (MOST CRITICAL):**
|
|
68
|
+
1. You are making SURGICAL EDITS, not rewriting files
|
|
69
|
+
2. Change ONLY the specific lines that need modification
|
|
70
|
+
3. The modified file MUST have approximately the same line count as the original
|
|
71
|
+
4. If original file has 200 lines, modified file should have ~195-210 lines
|
|
72
|
+
5. NEVER rewrite a file from scratch - this destroys the application
|
|
73
|
+
|
|
74
|
+
**FILE RULES:**
|
|
75
|
+
6. You may ONLY edit files that are provided in the PAGE CONTEXT section
|
|
76
|
+
7. NEVER create new files - only modify existing ones shown to you
|
|
77
|
+
8. The filePath in your response MUST exactly match one of the provided file paths
|
|
78
|
+
9. If you cannot find the right file to edit, explain this in your response instead of creating a new file
|
|
79
|
+
|
|
80
|
+
**PRESERVATION RULES (ZERO TOLERANCE):**
|
|
81
|
+
10. NEVER delete or remove existing functions, hooks, state, or logic
|
|
82
|
+
11. NEVER remove imports - you may only ADD imports if needed
|
|
83
|
+
12. NEVER remove useEffect, useState, useCallback, or other React hooks
|
|
84
|
+
13. NEVER remove API calls, fetch requests, or data loading logic
|
|
85
|
+
14. NEVER remove error handling, loading states, or conditional rendering
|
|
86
|
+
15. NEVER remove data-sonance-* attributes
|
|
87
|
+
16. NEVER change component structure unless specifically requested
|
|
88
|
+
17. NEVER modify TypeScript types or interfaces unless specifically requested
|
|
84
89
|
|
|
85
90
|
**CHANGE RULES:**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
18. Make ONLY the changes requested by the user
|
|
92
|
+
19. Modify the MINIMUM amount of code necessary
|
|
93
|
+
20. Keep all existing className values and ADD to them if needed
|
|
94
|
+
21. Use semantic Tailwind classes (bg-primary, text-foreground, etc.)
|
|
95
|
+
22. Maintain dark mode compatibility with CSS variables
|
|
96
|
+
23. Keep the cn() utility for className merging
|
|
97
|
+
24. CRITICAL: You MUST return the FULL file content in "modifiedContent". Do NOT use comments like "// ... existing code ..." or "// ... rest of file ...". Return every single line of code, even if unchanged.
|
|
91
98
|
|
|
92
99
|
**SONANCE BRAND COLORS:**
|
|
93
100
|
- Charcoal: #333F48, #343D46 (primary)
|
|
@@ -103,11 +110,18 @@ The JSON must include:
|
|
|
103
110
|
- "reasoning": Brief explanation of what you see in the screenshot and your plan
|
|
104
111
|
- "modifications": Array of file modifications, each with:
|
|
105
112
|
- "filePath": Path to the file
|
|
106
|
-
- "modifiedContent": Complete updated file content
|
|
113
|
+
- "modifiedContent": Complete updated file content (MUST BE FULL CONTENT, NO TRUNCATION)
|
|
107
114
|
- "explanation": What changed in this file
|
|
108
115
|
- "previewCSS": CSS for live preview (use [data-sonance-name="ComponentName"] selectors)
|
|
109
116
|
- "aggregatedPreviewCSS": Combined CSS for all changes
|
|
110
|
-
- "explanation": Overall summary of changes
|
|
117
|
+
- "explanation": Overall summary of changes
|
|
118
|
+
|
|
119
|
+
**EXAMPLE OF CORRECT EDIT:**
|
|
120
|
+
If user asks to "make buttons smaller", you should:
|
|
121
|
+
- Find the button elements in the code
|
|
122
|
+
- Change ONLY the size-related classes (e.g., h-10 -> h-8, px-4 -> px-3)
|
|
123
|
+
- Keep ALL other code exactly the same
|
|
124
|
+
- Return the FULL file with this tiny change`;
|
|
111
125
|
|
|
112
126
|
export async function POST(request: Request) {
|
|
113
127
|
// Only allow in development
|
|
@@ -369,6 +383,9 @@ CRITICAL: Only use file paths from the VALID FILES list above. Do NOT create new
|
|
|
369
383
|
|
|
370
384
|
// Read original content and generate diffs for each modification
|
|
371
385
|
const modificationsWithOriginals: VisionFileModification[] = [];
|
|
386
|
+
const validationErrors: string[] = [];
|
|
387
|
+
const validationWarnings: string[] = [];
|
|
388
|
+
|
|
372
389
|
for (const mod of aiResponse.modifications || []) {
|
|
373
390
|
const fullPath = path.join(projectRoot, mod.filePath);
|
|
374
391
|
let originalContent = "";
|
|
@@ -376,6 +393,18 @@ CRITICAL: Only use file paths from the VALID FILES list above. Do NOT create new
|
|
|
376
393
|
originalContent = fs.readFileSync(fullPath, "utf-8");
|
|
377
394
|
}
|
|
378
395
|
|
|
396
|
+
// Validate the modification
|
|
397
|
+
const validation = validateModification(originalContent, mod.modifiedContent, mod.filePath);
|
|
398
|
+
|
|
399
|
+
if (!validation.valid) {
|
|
400
|
+
validationErrors.push(validation.error || "Unknown validation error");
|
|
401
|
+
continue; // Skip this modification
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (validation.warnings.length > 0) {
|
|
405
|
+
validationWarnings.push(...validation.warnings.map(w => `${mod.filePath}: ${w}`));
|
|
406
|
+
}
|
|
407
|
+
|
|
379
408
|
modificationsWithOriginals.push({
|
|
380
409
|
filePath: mod.filePath,
|
|
381
410
|
originalContent,
|
|
@@ -386,6 +415,23 @@ CRITICAL: Only use file paths from the VALID FILES list above. Do NOT create new
|
|
|
386
415
|
});
|
|
387
416
|
}
|
|
388
417
|
|
|
418
|
+
// If all modifications failed validation, return error
|
|
419
|
+
if (validationErrors.length > 0 && modificationsWithOriginals.length === 0) {
|
|
420
|
+
console.error("All AI modifications failed validation:", validationErrors);
|
|
421
|
+
return NextResponse.json(
|
|
422
|
+
{
|
|
423
|
+
success: false,
|
|
424
|
+
error: validationErrors.join("\n\n"),
|
|
425
|
+
} as VisionEditResponse,
|
|
426
|
+
{ status: 400 }
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Log warnings for review
|
|
431
|
+
if (validationWarnings.length > 0) {
|
|
432
|
+
console.warn("Vision edit validation warnings:", validationWarnings);
|
|
433
|
+
}
|
|
434
|
+
|
|
389
435
|
// Aggregate preview CSS
|
|
390
436
|
const aggregatedCSS = modificationsWithOriginals
|
|
391
437
|
.filter((m) => m.previewCSS)
|
|
@@ -654,3 +700,105 @@ function generateSimpleDiff(original: string, modified: string): string {
|
|
|
654
700
|
|
|
655
701
|
return diff.join("\n");
|
|
656
702
|
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Validate that AI modifications are surgical edits, not complete rewrites
|
|
706
|
+
*/
|
|
707
|
+
interface ValidationResult {
|
|
708
|
+
valid: boolean;
|
|
709
|
+
error?: string;
|
|
710
|
+
warnings: string[];
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function validateModification(
|
|
714
|
+
originalContent: string,
|
|
715
|
+
modifiedContent: string,
|
|
716
|
+
filePath: string
|
|
717
|
+
): ValidationResult {
|
|
718
|
+
const warnings: string[] = [];
|
|
719
|
+
|
|
720
|
+
// Skip validation for new files (no original content)
|
|
721
|
+
if (!originalContent || originalContent.trim() === "") {
|
|
722
|
+
return { valid: true, warnings: ["New file - no original to compare"] };
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const originalLines = originalContent.split("\n");
|
|
726
|
+
const modifiedLines = modifiedContent.split("\n");
|
|
727
|
+
|
|
728
|
+
// Check 1: Truncation detection - look for placeholder comments
|
|
729
|
+
const truncationPatterns = [
|
|
730
|
+
/\/\/\s*\.\.\.\s*existing/i,
|
|
731
|
+
/\/\/\s*\.\.\.\s*rest\s*of/i,
|
|
732
|
+
/\/\/\s*\.\.\.\s*more\s*code/i,
|
|
733
|
+
/\/\*\s*\.\.\.\s*\*\//,
|
|
734
|
+
/\/\/\s*\.\.\./,
|
|
735
|
+
];
|
|
736
|
+
|
|
737
|
+
for (const pattern of truncationPatterns) {
|
|
738
|
+
if (pattern.test(modifiedContent)) {
|
|
739
|
+
return {
|
|
740
|
+
valid: false,
|
|
741
|
+
error: `File ${filePath} contains truncation placeholder (e.g., "// ... existing code"). The AI must return the complete file content. Please try again.`,
|
|
742
|
+
warnings,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Check 2: Line count shrinkage - reject if file shrinks by more than 30%
|
|
748
|
+
const lineDelta = modifiedLines.length - originalLines.length;
|
|
749
|
+
const shrinkagePercent = (lineDelta / originalLines.length) * 100;
|
|
750
|
+
|
|
751
|
+
if (shrinkagePercent < -30) {
|
|
752
|
+
return {
|
|
753
|
+
valid: false,
|
|
754
|
+
error: `File ${filePath} shrank from ${originalLines.length} to ${modifiedLines.length} lines (${Math.abs(shrinkagePercent).toFixed(0)}% reduction). This suggests the AI rewrote the file instead of making surgical edits. Please try a more specific request.`,
|
|
755
|
+
warnings,
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (shrinkagePercent < -15) {
|
|
760
|
+
warnings.push(`File shrank by ${Math.abs(shrinkagePercent).toFixed(0)}% - review carefully`);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Check 3: Change percentage - warn if too many lines are different
|
|
764
|
+
let changedLines = 0;
|
|
765
|
+
const minLines = Math.min(originalLines.length, modifiedLines.length);
|
|
766
|
+
|
|
767
|
+
for (let i = 0; i < minLines; i++) {
|
|
768
|
+
if (originalLines[i] !== modifiedLines[i]) {
|
|
769
|
+
changedLines++;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Add lines that were added or removed
|
|
774
|
+
changedLines += Math.abs(originalLines.length - modifiedLines.length);
|
|
775
|
+
|
|
776
|
+
const changePercent = (changedLines / originalLines.length) * 100;
|
|
777
|
+
|
|
778
|
+
if (changePercent > 50) {
|
|
779
|
+
return {
|
|
780
|
+
valid: false,
|
|
781
|
+
error: `File ${filePath} has ${changePercent.toFixed(0)}% of lines changed. This suggests the AI rewrote the file instead of making surgical edits. For safety, this change has been rejected. Please try a more specific request.`,
|
|
782
|
+
warnings,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (changePercent > 30) {
|
|
787
|
+
warnings.push(`${changePercent.toFixed(0)}% of lines changed - larger than expected for a surgical edit`);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Check 4: Import preservation - ensure imports aren't removed
|
|
791
|
+
const importRegex = /^import\s+/gm;
|
|
792
|
+
const originalImports = (originalContent.match(importRegex) || []).length;
|
|
793
|
+
const modifiedImports = (modifiedContent.match(importRegex) || []).length;
|
|
794
|
+
|
|
795
|
+
if (modifiedImports < originalImports * 0.5 && originalImports > 2) {
|
|
796
|
+
return {
|
|
797
|
+
valid: false,
|
|
798
|
+
error: `File ${filePath} went from ${originalImports} imports to ${modifiedImports}. Imports should not be removed. Please try again.`,
|
|
799
|
+
warnings,
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
return { valid: true, warnings };
|
|
804
|
+
}
|
|
@@ -582,6 +582,9 @@ export function ComponentsPanel({
|
|
|
582
582
|
)}
|
|
583
583
|
|
|
584
584
|
{/* AI Chat Interface - hide when any pending edit is present */}
|
|
585
|
+
{/* Apply-First mode: Files written immediately with backups for instant HMR preview
|
|
586
|
+
User sees structural + CSS changes live, then clicks Accept or Revert
|
|
587
|
+
Original files are always backed up and can be restored */}
|
|
585
588
|
{!pendingEdit && !visionPendingEdit && !applyFirstSession && (
|
|
586
589
|
<ChatInterface
|
|
587
590
|
componentType={selectedComponentType}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonance-brand-mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.24",
|
|
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",
|