gemini-design-mcp 3.6.14 ā 3.7.1
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.
package/build/index.js
CHANGED
|
@@ -59,7 +59,7 @@ Present options to user, they select one, then pass it here via designSystem.vib
|
|
|
59
59
|
š¤ OUTPUT
|
|
60
60
|
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
61
61
|
|
|
62
|
-
Returns
|
|
62
|
+
Returns the complete code. YOU (the agent) are responsible for writing it to disk.`, createFrontendSchema, createFrontend);
|
|
63
63
|
// =============================================================================
|
|
64
64
|
// TOOL 2: MODIFY_FRONTEND
|
|
65
65
|
// =============================================================================
|
|
@@ -112,7 +112,7 @@ import { X } from "y";
|
|
|
112
112
|
// REPLACE WITH:
|
|
113
113
|
<new redesigned code>
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
YOU (the agent) are responsible for applying this find/replace to the file.`, modifyFrontendSchema, modifyFrontend);
|
|
116
116
|
// =============================================================================
|
|
117
117
|
// TOOL 3: SNIPPET_FRONTEND
|
|
118
118
|
// =============================================================================
|
|
@@ -234,11 +234,9 @@ import { Search } from "lucide-react";
|
|
|
234
234
|
)}
|
|
235
235
|
</div>
|
|
236
236
|
|
|
237
|
-
|
|
237
|
+
YOU (the agent) are responsible for:
|
|
238
238
|
- Adding the logic (useState, handlers) BEFORE calling this tool
|
|
239
|
-
-
|
|
240
|
-
|
|
241
|
-
The snippet is inserted directly to disk by default (requires insertAtLine OR insertAfterPattern).`, snippetFrontendSchema, snippetFrontend);
|
|
239
|
+
- Inserting the returned snippet into the correct location in the file`, snippetFrontendSchema, snippetFrontend);
|
|
242
240
|
// =============================================================================
|
|
243
241
|
// TOOL 4: GENERATE_VIBES
|
|
244
242
|
// =============================================================================
|
package/build/lib/gemini.js
CHANGED
|
@@ -7,11 +7,11 @@ function getApiKey() {
|
|
|
7
7
|
const apiKey = process.env.API_KEY;
|
|
8
8
|
if (!apiKey) {
|
|
9
9
|
throw new Error("Missing API_KEY environment variable. " +
|
|
10
|
-
"Get your API key at https://gemini-design.com/dashboard/api-keys");
|
|
10
|
+
"Get your API key at https://gemini-design-mcp.com/dashboard/api-keys");
|
|
11
11
|
}
|
|
12
12
|
if (!apiKey.startsWith("gd_")) {
|
|
13
13
|
throw new Error("Invalid API key format. API keys must start with 'gd_'. " +
|
|
14
|
-
"Get your API key at https://gemini-design.com/dashboard/api-keys");
|
|
14
|
+
"Get your API key at https://gemini-design-mcp.com/dashboard/api-keys");
|
|
15
15
|
}
|
|
16
16
|
return apiKey;
|
|
17
17
|
}
|
|
@@ -45,7 +45,17 @@ export async function generateWithGemini(systemPrompt, userPrompt, model = DEFAU
|
|
|
45
45
|
});
|
|
46
46
|
const result = await response.json();
|
|
47
47
|
if (!response.ok) {
|
|
48
|
-
|
|
48
|
+
const errorMsg = result.error || `API error: ${response.status}`;
|
|
49
|
+
// Check for credit/quota related errors
|
|
50
|
+
if (errorMsg.toLowerCase().includes('credit') ||
|
|
51
|
+
errorMsg.toLowerCase().includes('quota') ||
|
|
52
|
+
errorMsg.toLowerCase().includes('insufficient') ||
|
|
53
|
+
errorMsg.toLowerCase().includes('limit') ||
|
|
54
|
+
response.status === 402 ||
|
|
55
|
+
response.status === 429) {
|
|
56
|
+
throw new Error(`${errorMsg}\n\nš³ Top up your credits here: https://gemini-design-mcp.com/settings/billing`);
|
|
57
|
+
}
|
|
58
|
+
throw new Error(errorMsg);
|
|
49
59
|
}
|
|
50
60
|
// Extract text from Gemini response format
|
|
51
61
|
const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { generateWithGemini } from "../lib/gemini.js";
|
|
3
3
|
import { CREATE_FRONTEND_PROMPT } from "../prompts/system.js";
|
|
4
|
-
import {
|
|
4
|
+
import { stripCodeFences } from "../lib/filesystem.js";
|
|
5
5
|
import { scaleSchema, scaleDescriptions } from "../lib/scale.js";
|
|
6
6
|
export const createFrontendSchema = {
|
|
7
7
|
request: z.string().describe("What to create: describe the page, component, or section. " +
|
|
@@ -24,12 +24,9 @@ export const createFrontendSchema = {
|
|
|
24
24
|
}).describe("The selected design vibe"),
|
|
25
25
|
}).optional().describe("Design system with selected vibe. REQUIRED for new projects without existing design. " +
|
|
26
26
|
"Call generate_vibes first, user selects, then pass the selection here."),
|
|
27
|
-
writeFile: z.boolean().default(false).describe("Write the file directly to disk instead of returning the code. " +
|
|
28
|
-
"Returns a confirmation message instead of the full code, keeping context lightweight. " +
|
|
29
|
-
"Defaults to false."),
|
|
30
27
|
};
|
|
31
28
|
export async function createFrontend(params) {
|
|
32
|
-
const { request, filePath, techStack, context, designSystem
|
|
29
|
+
const { request, filePath, techStack, context, designSystem } = params;
|
|
33
30
|
// Build design system instructions if provided
|
|
34
31
|
let designSystemInstructions = '';
|
|
35
32
|
if (designSystem?.vibe) {
|
|
@@ -74,18 +71,7 @@ Remember: Return a COMPLETE file ready to save at ${filePath}`.trim();
|
|
|
74
71
|
const rawResult = await generateWithGemini(systemPrompt, request, undefined, "high", "create_frontend");
|
|
75
72
|
// Strip markdown code fences from the result
|
|
76
73
|
const result = stripCodeFences(rawResult);
|
|
77
|
-
//
|
|
78
|
-
if (writeFile) {
|
|
79
|
-
writeFileWithDirs(filePath, result);
|
|
80
|
-
const size = getFileSize(result);
|
|
81
|
-
return {
|
|
82
|
-
content: [{
|
|
83
|
-
type: "text",
|
|
84
|
-
text: `ā
Created: ${filePath} (${size})`,
|
|
85
|
-
}],
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
// Default: return the code
|
|
74
|
+
// Return the code for the agent to write
|
|
89
75
|
return {
|
|
90
76
|
content: [{ type: "text", text: result }],
|
|
91
77
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { generateWithGemini } from "../lib/gemini.js";
|
|
3
3
|
import { MODIFY_FRONTEND_PROMPT } from "../prompts/system.js";
|
|
4
|
-
import { readFileIfExists, writeFileWithDirs, parseFindReplace, applyFindReplace, mergeImports, } from "../lib/filesystem.js";
|
|
5
4
|
import { scaleSchema, getScaleInstructions } from "../lib/scale.js";
|
|
6
5
|
export const modifyFrontendSchema = {
|
|
7
6
|
modification: z.string().describe("The SINGLE design modification to make. Be specific. " +
|
|
@@ -20,13 +19,9 @@ export const modifyFrontendSchema = {
|
|
|
20
19
|
"Example: 'Project uses: var(--font-heading), var(--bg-primary), .section-padding class'"),
|
|
21
20
|
scale: scaleSchema.optional().describe("Element sizing: 'refined' (small, elegant), 'balanced' (standard), 'zoomed' (large). " +
|
|
22
21
|
"Controls button sizes, typography, spacing, icons."),
|
|
23
|
-
writeFile: z.boolean().default(true).describe("Apply the modification directly to the file on disk. " +
|
|
24
|
-
"Reads the file, applies find/replace, and writes back. " +
|
|
25
|
-
"Returns a confirmation instead of the find/replace instructions. " +
|
|
26
|
-
"Defaults to true."),
|
|
27
22
|
};
|
|
28
23
|
export async function modifyFrontend(params) {
|
|
29
|
-
const { modification, targetCode, filePath, context, scale
|
|
24
|
+
const { modification, targetCode, filePath, context, scale } = params;
|
|
30
25
|
// Build context instructions
|
|
31
26
|
let contextInstructions = '';
|
|
32
27
|
if (context) {
|
|
@@ -51,52 +46,7 @@ MODIFICATION REQUESTED: ${modification}
|
|
|
51
46
|
|
|
52
47
|
Remember: Return ONLY the find/replace block. ONE modification, surgical precision.`.trim();
|
|
53
48
|
const result = await generateWithGemini(systemPrompt, modification, undefined, "minimal", "modify_frontend");
|
|
54
|
-
//
|
|
55
|
-
if (writeFile) {
|
|
56
|
-
const fileContent = readFileIfExists(filePath);
|
|
57
|
-
if (!fileContent) {
|
|
58
|
-
return {
|
|
59
|
-
content: [{
|
|
60
|
-
type: "text",
|
|
61
|
-
text: `ā Error: File not found: ${filePath}`,
|
|
62
|
-
}],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
const parsed = parseFindReplace(result);
|
|
66
|
-
if (!parsed) {
|
|
67
|
-
// Fallback: return raw result if parsing fails
|
|
68
|
-
return {
|
|
69
|
-
content: [{
|
|
70
|
-
type: "text",
|
|
71
|
-
text: `ā ļø Could not parse Gemini output. Raw result:\n\n${result}`,
|
|
72
|
-
}],
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
// Apply the find/replace
|
|
76
|
-
const { success, newContent, error } = applyFindReplace(fileContent, parsed.find, parsed.replace);
|
|
77
|
-
if (!success) {
|
|
78
|
-
return {
|
|
79
|
-
content: [{
|
|
80
|
-
type: "text",
|
|
81
|
-
text: `ā Error: ${error}\n\nExpected to find:\n${parsed.find.slice(0, 200)}...`,
|
|
82
|
-
}],
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
// Merge imports if any
|
|
86
|
-
let finalContent = newContent;
|
|
87
|
-
if (parsed.imports) {
|
|
88
|
-
finalContent = mergeImports(newContent, parsed.imports);
|
|
89
|
-
}
|
|
90
|
-
// Write the file
|
|
91
|
-
writeFileWithDirs(filePath, finalContent);
|
|
92
|
-
return {
|
|
93
|
-
content: [{
|
|
94
|
-
type: "text",
|
|
95
|
-
text: `ā
Modified: ${filePath}${parsed.imports ? `\n Added imports` : ""}`,
|
|
96
|
-
}],
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
// Default: return the find/replace instructions
|
|
49
|
+
// Return the find/replace instructions for the agent to apply
|
|
100
50
|
return {
|
|
101
51
|
content: [{ type: "text", text: result }],
|
|
102
52
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { generateWithGemini } from "../lib/gemini.js";
|
|
3
3
|
import { SNIPPET_FRONTEND_PROMPT } from "../prompts/system.js";
|
|
4
|
-
import { readFileIfExists, writeFileWithDirs, parseSnippet, mergeImports, insertAfterLine, insertAfterPattern, } from "../lib/filesystem.js";
|
|
5
4
|
import { scaleSchema, getScaleInstructions } from "../lib/scale.js";
|
|
6
5
|
export const snippetFrontendSchema = {
|
|
7
6
|
request: z.string().describe("What code snippet to generate. Be specific about what you need. " +
|
|
@@ -23,18 +22,9 @@ export const snippetFrontendSchema = {
|
|
|
23
22
|
"Without this, Gemini will create standalone styles that won't match your design system."),
|
|
24
23
|
scale: scaleSchema.optional().describe("Element sizing: 'refined' (small, elegant), 'balanced' (standard), 'zoomed' (large). " +
|
|
25
24
|
"Controls button sizes, typography, spacing, icons."),
|
|
26
|
-
writeFile: z.boolean().default(true).describe("Insert the snippet directly into the file on disk. " +
|
|
27
|
-
"Requires insertAtLine OR insertAfterPattern to know where to insert. " +
|
|
28
|
-
"Defaults to true."),
|
|
29
|
-
insertAtLine: z.number().optional().describe("Line number (1-indexed) where to insert the snippet. " +
|
|
30
|
-
"The snippet will be inserted AFTER this line. " +
|
|
31
|
-
"Required if writeFile is true and insertAfterPattern is not provided."),
|
|
32
|
-
insertAfterPattern: z.string().optional().describe("A string pattern to find in the file. The snippet will be inserted after this pattern. " +
|
|
33
|
-
"Example: 'return (' or '<main>' or '</header>'. " +
|
|
34
|
-
"Required if writeFile is true and insertAtLine is not provided."),
|
|
35
25
|
};
|
|
36
26
|
export async function snippetFrontend(params) {
|
|
37
|
-
const { request, targetFile, techStack, insertionContext, context, scale
|
|
27
|
+
const { request, targetFile, techStack, insertionContext, context, scale } = params;
|
|
38
28
|
// Build context instructions
|
|
39
29
|
let contextInstructions = '';
|
|
40
30
|
if (context) {
|
|
@@ -56,69 +46,7 @@ ${insertionContext}
|
|
|
56
46
|
|
|
57
47
|
Generate a snippet that will integrate smoothly at this location.`.trim();
|
|
58
48
|
const result = await generateWithGemini(systemPrompt, request, undefined, "minimal", "snippet_frontend");
|
|
59
|
-
//
|
|
60
|
-
if (writeFile) {
|
|
61
|
-
if (!lineNumber && !pattern) {
|
|
62
|
-
return {
|
|
63
|
-
content: [{
|
|
64
|
-
type: "text",
|
|
65
|
-
text: `ā Error: writeFile requires either insertAtLine or insertAfterPattern`,
|
|
66
|
-
}],
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
const fileContent = readFileIfExists(targetFile);
|
|
70
|
-
if (!fileContent) {
|
|
71
|
-
return {
|
|
72
|
-
content: [{
|
|
73
|
-
type: "text",
|
|
74
|
-
text: `ā Error: File not found: ${targetFile}`,
|
|
75
|
-
}],
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
const parsed = parseSnippet(result);
|
|
79
|
-
if (!parsed) {
|
|
80
|
-
return {
|
|
81
|
-
content: [{
|
|
82
|
-
type: "text",
|
|
83
|
-
text: `ā ļø Could not parse snippet. Raw result:\n\n${result}`,
|
|
84
|
-
}],
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
let newContent;
|
|
88
|
-
// Insert using line number or pattern
|
|
89
|
-
if (lineNumber) {
|
|
90
|
-
newContent = insertAfterLine(fileContent, parsed.snippet, lineNumber);
|
|
91
|
-
}
|
|
92
|
-
else if (pattern) {
|
|
93
|
-
const insertResult = insertAfterPattern(fileContent, parsed.snippet, pattern);
|
|
94
|
-
if (!insertResult.success) {
|
|
95
|
-
return {
|
|
96
|
-
content: [{
|
|
97
|
-
type: "text",
|
|
98
|
-
text: `ā Error: ${insertResult.error}`,
|
|
99
|
-
}],
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
newContent = insertResult.newContent;
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
// Should never reach here due to earlier check
|
|
106
|
-
newContent = fileContent;
|
|
107
|
-
}
|
|
108
|
-
// Merge imports if any
|
|
109
|
-
if (parsed.imports) {
|
|
110
|
-
newContent = mergeImports(newContent, parsed.imports);
|
|
111
|
-
}
|
|
112
|
-
// Write the file
|
|
113
|
-
writeFileWithDirs(targetFile, newContent);
|
|
114
|
-
return {
|
|
115
|
-
content: [{
|
|
116
|
-
type: "text",
|
|
117
|
-
text: `ā
Inserted snippet in: ${targetFile}${parsed.imports ? `\n Added imports` : ""}${lineNumber ? `\n After line ${lineNumber}` : ""}${pattern ? `\n After pattern: ${pattern}` : ""}`,
|
|
118
|
-
}],
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
// Default: return the snippet
|
|
49
|
+
// Return the snippet for the agent to insert
|
|
122
50
|
return {
|
|
123
51
|
content: [{ type: "text", text: result }],
|
|
124
52
|
};
|