@gemini-designer/mcp-server 0.1.0
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/.prettierrc +9 -0
- package/dist/components/catalog.d.ts +24 -0
- package/dist/components/catalog.d.ts.map +1 -0
- package/dist/components/catalog.js +186 -0
- package/dist/components/catalog.js.map +1 -0
- package/dist/config/index.d.ts +60 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +199 -0
- package/dist/config/index.js.map +1 -0
- package/dist/context/builder.d.ts +32 -0
- package/dist/context/builder.d.ts.map +1 -0
- package/dist/context/builder.js +194 -0
- package/dist/context/builder.js.map +1 -0
- package/dist/context/filter.d.ts +28 -0
- package/dist/context/filter.d.ts.map +1 -0
- package/dist/context/filter.js +136 -0
- package/dist/context/filter.js.map +1 -0
- package/dist/context/grounding.d.ts +27 -0
- package/dist/context/grounding.d.ts.map +1 -0
- package/dist/context/grounding.js +162 -0
- package/dist/context/grounding.js.map +1 -0
- package/dist/context/guards.d.ts +31 -0
- package/dist/context/guards.d.ts.map +1 -0
- package/dist/context/guards.js +76 -0
- package/dist/context/guards.js.map +1 -0
- package/dist/context/repo-hints.d.ts +12 -0
- package/dist/context/repo-hints.d.ts.map +1 -0
- package/dist/context/repo-hints.js +40 -0
- package/dist/context/repo-hints.js.map +1 -0
- package/dist/generation/gemini-client.d.ts +27 -0
- package/dist/generation/gemini-client.d.ts.map +1 -0
- package/dist/generation/gemini-client.js +64 -0
- package/dist/generation/gemini-client.js.map +1 -0
- package/dist/generation/litellm-client.d.ts +16 -0
- package/dist/generation/litellm-client.d.ts.map +1 -0
- package/dist/generation/litellm-client.js +98 -0
- package/dist/generation/litellm-client.js.map +1 -0
- package/dist/generation/remote-client.d.ts +20 -0
- package/dist/generation/remote-client.d.ts.map +1 -0
- package/dist/generation/remote-client.js +69 -0
- package/dist/generation/remote-client.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/output/file-writer.d.ts +39 -0
- package/dist/output/file-writer.d.ts.map +1 -0
- package/dist/output/file-writer.js +153 -0
- package/dist/output/file-writer.js.map +1 -0
- package/dist/output/formatter.d.ts +26 -0
- package/dist/output/formatter.d.ts.map +1 -0
- package/dist/output/formatter.js +156 -0
- package/dist/output/formatter.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +22 -0
- package/dist/server.js.map +1 -0
- package/dist/stack/detect.d.ts +49 -0
- package/dist/stack/detect.d.ts.map +1 -0
- package/dist/stack/detect.js +157 -0
- package/dist/stack/detect.js.map +1 -0
- package/dist/tokens/sync.d.ts +32 -0
- package/dist/tokens/sync.d.ts.map +1 -0
- package/dist/tokens/sync.js +188 -0
- package/dist/tokens/sync.js.map +1 -0
- package/dist/tools/analyze-screenshot-ui.d.ts +18 -0
- package/dist/tools/analyze-screenshot-ui.d.ts.map +1 -0
- package/dist/tools/analyze-screenshot-ui.js +133 -0
- package/dist/tools/analyze-screenshot-ui.js.map +1 -0
- package/dist/tools/analyze-tokens.d.ts +10 -0
- package/dist/tools/analyze-tokens.d.ts.map +1 -0
- package/dist/tools/analyze-tokens.js +107 -0
- package/dist/tools/analyze-tokens.js.map +1 -0
- package/dist/tools/catalog-components.d.ts +14 -0
- package/dist/tools/catalog-components.d.ts.map +1 -0
- package/dist/tools/catalog-components.js +85 -0
- package/dist/tools/catalog-components.js.map +1 -0
- package/dist/tools/create-ui.d.ts +10 -0
- package/dist/tools/create-ui.d.ts.map +1 -0
- package/dist/tools/create-ui.js +167 -0
- package/dist/tools/create-ui.js.map +1 -0
- package/dist/tools/detect-ui-stack.d.ts +15 -0
- package/dist/tools/detect-ui-stack.d.ts.map +1 -0
- package/dist/tools/detect-ui-stack.js +52 -0
- package/dist/tools/detect-ui-stack.js.map +1 -0
- package/dist/tools/generate-component-variants.d.ts +15 -0
- package/dist/tools/generate-component-variants.d.ts.map +1 -0
- package/dist/tools/generate-component-variants.js +199 -0
- package/dist/tools/generate-component-variants.js.map +1 -0
- package/dist/tools/generate-vibes.d.ts +10 -0
- package/dist/tools/generate-vibes.d.ts.map +1 -0
- package/dist/tools/generate-vibes.js +145 -0
- package/dist/tools/generate-vibes.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +36 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/modify-ui.d.ts +11 -0
- package/dist/tools/modify-ui.d.ts.map +1 -0
- package/dist/tools/modify-ui.js +207 -0
- package/dist/tools/modify-ui.js.map +1 -0
- package/dist/tools/scaffold-project.d.ts +10 -0
- package/dist/tools/scaffold-project.d.ts.map +1 -0
- package/dist/tools/scaffold-project.js +122 -0
- package/dist/tools/scaffold-project.js.map +1 -0
- package/dist/tools/snippet-ui.d.ts +11 -0
- package/dist/tools/snippet-ui.d.ts.map +1 -0
- package/dist/tools/snippet-ui.js +194 -0
- package/dist/tools/snippet-ui.js.map +1 -0
- package/dist/tools/sync-design-tokens.d.ts +14 -0
- package/dist/tools/sync-design-tokens.d.ts.map +1 -0
- package/dist/tools/sync-design-tokens.js +233 -0
- package/dist/tools/sync-design-tokens.js.map +1 -0
- package/dist/utils/walk.d.ts +15 -0
- package/dist/utils/walk.d.ts.map +1 -0
- package/dist/utils/walk.js +63 -0
- package/dist/utils/walk.js.map +1 -0
- package/eslint.config.js +37 -0
- package/package.json +56 -0
- package/src/__tests__/builder.test.ts +31 -0
- package/src/__tests__/config.test.ts +52 -0
- package/src/__tests__/filter.test.ts +109 -0
- package/src/components/catalog.ts +214 -0
- package/src/config/index.ts +237 -0
- package/src/context/builder.ts +233 -0
- package/src/context/filter.ts +164 -0
- package/src/context/grounding.ts +191 -0
- package/src/context/guards.ts +94 -0
- package/src/context/repo-hints.ts +43 -0
- package/src/generation/gemini-client.ts +94 -0
- package/src/generation/litellm-client.ts +121 -0
- package/src/generation/remote-client.ts +103 -0
- package/src/index.ts +36 -0
- package/src/output/file-writer.ts +181 -0
- package/src/output/formatter.ts +186 -0
- package/src/server.ts +28 -0
- package/src/stack/detect.ts +204 -0
- package/src/tokens/sync.ts +212 -0
- package/src/tools/analyze-screenshot-ui.ts +150 -0
- package/src/tools/analyze-tokens.ts +123 -0
- package/src/tools/catalog-components.ts +99 -0
- package/src/tools/create-ui.ts +194 -0
- package/src/tools/detect-ui-stack.ts +64 -0
- package/src/tools/generate-component-variants.ts +218 -0
- package/src/tools/generate-vibes.ts +177 -0
- package/src/tools/index.ts +42 -0
- package/src/tools/modify-ui.ts +230 -0
- package/src/tools/scaffold-project.ts +138 -0
- package/src/tools/snippet-ui.ts +222 -0
- package/src/tools/sync-design-tokens.ts +256 -0
- package/src/utils/walk.ts +75 -0
- package/tsconfig.json +34 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry
|
|
3
|
+
*
|
|
4
|
+
* Registers all MCP tools with the server.
|
|
5
|
+
*/
|
|
6
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import { Config } from '../config/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Register all MCP tools
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerTools(server: McpServer, config: Config): void;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAa5C;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAkBrE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry
|
|
3
|
+
*
|
|
4
|
+
* Registers all MCP tools with the server.
|
|
5
|
+
*/
|
|
6
|
+
import { registerGenerateVibes } from './generate-vibes.js';
|
|
7
|
+
import { registerCreateUI } from './create-ui.js';
|
|
8
|
+
import { registerModifyUI } from './modify-ui.js';
|
|
9
|
+
import { registerSnippetUI } from './snippet-ui.js';
|
|
10
|
+
import { registerScaffoldProject } from './scaffold-project.js';
|
|
11
|
+
import { registerAnalyzeTokens } from './analyze-tokens.js';
|
|
12
|
+
import { registerDetectUIStack } from './detect-ui-stack.js';
|
|
13
|
+
import { registerSyncDesignTokens } from './sync-design-tokens.js';
|
|
14
|
+
import { registerCatalogComponents } from './catalog-components.js';
|
|
15
|
+
import { registerGenerateComponentVariants } from './generate-component-variants.js';
|
|
16
|
+
import { registerAnalyzeScreenshotUI } from './analyze-screenshot-ui.js';
|
|
17
|
+
/**
|
|
18
|
+
* Register all MCP tools
|
|
19
|
+
*/
|
|
20
|
+
export function registerTools(server, config) {
|
|
21
|
+
console.error('[tools] Registering MCP tools...');
|
|
22
|
+
registerGenerateVibes(server, config);
|
|
23
|
+
registerCreateUI(server, config);
|
|
24
|
+
registerModifyUI(server, config);
|
|
25
|
+
registerSnippetUI(server, config);
|
|
26
|
+
registerScaffoldProject(server, config);
|
|
27
|
+
registerAnalyzeTokens(server, config);
|
|
28
|
+
// New grounding + power tools
|
|
29
|
+
registerDetectUIStack(server, config);
|
|
30
|
+
registerSyncDesignTokens(server, config);
|
|
31
|
+
registerCatalogComponents(server, config);
|
|
32
|
+
registerGenerateComponentVariants(server, config);
|
|
33
|
+
registerAnalyzeScreenshotUI(server, config);
|
|
34
|
+
console.error('[tools] All tools registered');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,iCAAiC,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAEzE;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAAiB,EAAE,MAAc;IAC3D,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAElD,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC,8BAA8B;IAC9B,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,wBAAwB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,yBAAyB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,iCAAiC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,2BAA2B,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5C,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modify_ui Tool
|
|
3
|
+
*
|
|
4
|
+
* Surgical edits to existing components with minimal breakage.
|
|
5
|
+
* Preserves logic and only modifies UI-related code.
|
|
6
|
+
* Can optionally apply changes directly if requested.
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { Config } from '../config/index.js';
|
|
10
|
+
export declare function registerModifyUI(server: McpServer, config: Config): void;
|
|
11
|
+
//# sourceMappingURL=modify-ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modify-ui.d.ts","sourceRoot":"","sources":["../../src/tools/modify-ui.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAgE5C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA0JxE"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modify_ui Tool
|
|
3
|
+
*
|
|
4
|
+
* Surgical edits to existing components with minimal breakage.
|
|
5
|
+
* Preserves logic and only modifies UI-related code.
|
|
6
|
+
* Can optionally apply changes directly if requested.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import { buildContext } from '../context/builder.js';
|
|
11
|
+
import { assertReadablePath, assertWritablePath } from '../context/guards.js';
|
|
12
|
+
import { buildRepoGrounding } from '../context/grounding.js';
|
|
13
|
+
import { generateWithGemini } from '../generation/gemini-client.js';
|
|
14
|
+
import { writeFile } from '../output/file-writer.js';
|
|
15
|
+
const inputSchema = {
|
|
16
|
+
targetFile: z.string().describe('Path to the file to modify'),
|
|
17
|
+
instruction: z
|
|
18
|
+
.string()
|
|
19
|
+
.describe('What to change (e.g., "Make the header sticky and add a subtle shadow")'),
|
|
20
|
+
context: z
|
|
21
|
+
.array(z.string())
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Related files for awareness (design tokens, parent components)'),
|
|
24
|
+
preserveLogic: z
|
|
25
|
+
.boolean()
|
|
26
|
+
.default(true)
|
|
27
|
+
.describe('Never touch non-UI code like API calls, state logic, etc.'),
|
|
28
|
+
returnDiff: z.boolean().default(false).describe('Return unified diff instead of full file'),
|
|
29
|
+
applyChanges: z
|
|
30
|
+
.boolean()
|
|
31
|
+
.default(false)
|
|
32
|
+
.describe('If true, apply the modifications directly to the file (only when returnDiff=false)'),
|
|
33
|
+
backup: z.boolean().default(true).describe('If true (default), create a .bak backup before modifying'),
|
|
34
|
+
};
|
|
35
|
+
function getSystemPrompt(returnDiff) {
|
|
36
|
+
const base = `You are a senior frontend engineer performing surgical code modifications.
|
|
37
|
+
|
|
38
|
+
CRITICAL RULES (must follow):
|
|
39
|
+
1. ONLY modify what is explicitly requested.
|
|
40
|
+
2. PRESERVE all existing functionality, imports, and logic.
|
|
41
|
+
3. If preserveLogic is true, NEVER touch:
|
|
42
|
+
- API calls, fetch, axios
|
|
43
|
+
- State management (useState, Redux, Vuex, stores)
|
|
44
|
+
- Event handlers that aren't purely UI
|
|
45
|
+
- Business logic / data transforms
|
|
46
|
+
4. Maintain the existing code style and formatting.
|
|
47
|
+
5. Keep existing comments unless they conflict with the requested change.
|
|
48
|
+
6. Accessibility: do not regress accessibility; keep/repair aria, labels, focus handling.
|
|
49
|
+
7. Prefer reusing existing components/utilities from the repo (you will receive an auto component catalog).
|
|
50
|
+
8. Output MUST NOT contain explanations, markdown, or code fences.`;
|
|
51
|
+
if (returnDiff) {
|
|
52
|
+
return `${base}\n\nOutput a UNIFIED DIFF only.`;
|
|
53
|
+
}
|
|
54
|
+
return `${base}\n\nOutput the COMPLETE modified file only (raw code).`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Extract code from a model response that might contain fences.
|
|
58
|
+
*/
|
|
59
|
+
function cleanResponse(response) {
|
|
60
|
+
let out = response.trim();
|
|
61
|
+
const codeBlockMatch = out.match(/```(?:\w+)?\n([\s\S]*?)```/);
|
|
62
|
+
if (codeBlockMatch) {
|
|
63
|
+
out = codeBlockMatch[1].trim();
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
export function registerModifyUI(server, config) {
|
|
68
|
+
server.registerTool('modify_ui', {
|
|
69
|
+
title: 'Modify UI Component',
|
|
70
|
+
description: 'Make surgical edits to existing UI components. Preserves logic and only modifies UI-related code. Set applyChanges=true to write directly.',
|
|
71
|
+
inputSchema,
|
|
72
|
+
}, async (args) => {
|
|
73
|
+
const targetFile = args.targetFile;
|
|
74
|
+
const instruction = args.instruction;
|
|
75
|
+
const contextPaths = args.context;
|
|
76
|
+
const preserveLogic = args.preserveLogic !== false;
|
|
77
|
+
const returnDiff = args.returnDiff === true;
|
|
78
|
+
const shouldApply = args.applyChanges === true;
|
|
79
|
+
const shouldBackup = args.backup !== false;
|
|
80
|
+
if (shouldApply && returnDiff) {
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: 'Error: applyChanges=true is not supported when returnDiff=true. Set returnDiff=false to apply changes.',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
isError: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// Resolve and validate the target path
|
|
92
|
+
let safeReadPath;
|
|
93
|
+
try {
|
|
94
|
+
safeReadPath = assertReadablePath(targetFile, config);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const message = error instanceof Error ? error.message : 'Invalid targetFile';
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
100
|
+
isError: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Read the target file
|
|
104
|
+
let fileContent;
|
|
105
|
+
try {
|
|
106
|
+
fileContent = fs.readFileSync(safeReadPath, 'utf-8');
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: 'text',
|
|
113
|
+
text: `Error: Could not read file at ${targetFile}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
isError: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// Build context from related files
|
|
120
|
+
let contextContent = '';
|
|
121
|
+
if (contextPaths && contextPaths.length > 0) {
|
|
122
|
+
contextContent = await buildContext(contextPaths, config);
|
|
123
|
+
}
|
|
124
|
+
// Auto-inject deterministic project grounding (stack + reusable components)
|
|
125
|
+
const grounding = await buildRepoGrounding(config, {
|
|
126
|
+
focusFileAbs: safeReadPath,
|
|
127
|
+
instruction,
|
|
128
|
+
});
|
|
129
|
+
const systemPrompt = getSystemPrompt(returnDiff);
|
|
130
|
+
const userPrompt = `Modify this file according to the instruction.
|
|
131
|
+
|
|
132
|
+
${grounding}
|
|
133
|
+
|
|
134
|
+
INSTRUCTION:
|
|
135
|
+
${instruction}
|
|
136
|
+
|
|
137
|
+
PRESERVE LOGIC:
|
|
138
|
+
${preserveLogic}
|
|
139
|
+
|
|
140
|
+
CURRENT FILE (${targetFile}):
|
|
141
|
+
${fileContent}
|
|
142
|
+
|
|
143
|
+
${contextContent ? `\n\nRELATED FILES FOR CONTEXT:\n${contextContent}\n` : ''}
|
|
144
|
+
|
|
145
|
+
${returnDiff ? 'Return a unified diff only.' : 'Return the full modified file only.'}`;
|
|
146
|
+
try {
|
|
147
|
+
const response = await generateWithGemini(config, systemPrompt, userPrompt, { toolName: 'modify_ui' });
|
|
148
|
+
const cleaned = cleanResponse(response);
|
|
149
|
+
// Apply changes if requested (and not returning diff)
|
|
150
|
+
if (shouldApply) {
|
|
151
|
+
let safeWritePath;
|
|
152
|
+
try {
|
|
153
|
+
safeWritePath = assertWritablePath(targetFile, config);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const message = error instanceof Error ? error.message : 'Invalid targetFile';
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const result = await writeFile(safeWritePath, cleaned, {
|
|
163
|
+
format: true,
|
|
164
|
+
backup: shouldBackup,
|
|
165
|
+
});
|
|
166
|
+
if (!result.success) {
|
|
167
|
+
return {
|
|
168
|
+
content: [
|
|
169
|
+
{
|
|
170
|
+
type: 'text',
|
|
171
|
+
text: `❌ Failed to apply changes: ${result.error || 'Unknown error'}`,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
isError: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const msg = [
|
|
178
|
+
`✅ Applied changes successfully.`,
|
|
179
|
+
`File: ${result.filePath}`,
|
|
180
|
+
result.backupPath ? `Backup: ${result.backupPath}` : undefined,
|
|
181
|
+
]
|
|
182
|
+
.filter(Boolean)
|
|
183
|
+
.join('\n');
|
|
184
|
+
return {
|
|
185
|
+
content: [{ type: 'text', text: msg }],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// Return diff or full file ONLY (no wrappers) for best agent compatibility
|
|
189
|
+
return {
|
|
190
|
+
content: [{ type: 'text', text: cleaned }],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
195
|
+
return {
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: `Error modifying UI: ${message}`,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
isError: true,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=modify-ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modify-ui.js","sourceRoot":"","sources":["../../src/tools/modify-ui.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,MAAM,WAAW,GAAG;IAChB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;IAC7D,WAAW,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,CAAC,yEAAyE,CAAC;IACxF,OAAO,EAAE,CAAC;SACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CAAC,gEAAgE,CAAC;IAC/E,aAAa,EAAE,CAAC;SACX,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;SACb,QAAQ,CAAC,2DAA2D,CAAC;IAC1E,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,0CAA0C,CAAC;IAC3F,YAAY,EAAE,CAAC;SACV,OAAO,EAAE;SACT,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,oFAAoF,CAAC;IACnG,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,0DAA0D,CAAC;CACzG,CAAC;AAEF,SAAS,eAAe,CAAC,UAAmB;IACxC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;mEAckD,CAAC;IAEhE,IAAI,UAAU,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,iCAAiC,CAAC;IACpD,CAAC;IAED,OAAO,GAAG,IAAI,wDAAwD,CAAC;AAC3E,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,QAAgB;IACnC,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC/D,IAAI,cAAc,EAAE,CAAC;QACjB,GAAG,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,MAAc;IAC9D,MAAM,CAAC,YAAY,CACf,WAAW,EACX;QACI,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACP,4IAA4I;QAChJ,WAAW;KACd,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACX,MAAM,UAAU,GAAG,IAAI,CAAC,UAAoB,CAAC;QAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,WAAqB,CAAC;QAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,OAA+B,CAAC;QAC1D,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,KAAK,CAAC;QACnD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC;QAC5C,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC;QAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC;QAE3C,IAAI,WAAW,IAAI,UAAU,EAAE,CAAC;YAC5B,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,wGAAwG;qBACjH;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;QAED,uCAAuC;QACvC,IAAI,YAAoB,CAAC;QACzB,IAAI,CAAC;YACD,YAAY,GAAG,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;YAC9E,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;gBAC/D,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;QAED,uBAAuB;QACvB,IAAI,WAAmB,CAAC;QACxB,IAAI,CAAC;YACD,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACL,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,iCAAiC,UAAU,EAAE;qBACtD;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;QAED,mCAAmC;QACnC,IAAI,cAAc,GAAG,EAAE,CAAC;QACxB,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,cAAc,GAAG,MAAM,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC9D,CAAC;QAED,4EAA4E;QAC5E,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE;YAC/C,YAAY,EAAE,YAAY;YAC1B,WAAW;SACd,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QAEjD,MAAM,UAAU,GAAG;;EAE7B,SAAS;;;EAGT,WAAW;;;EAGX,aAAa;;gBAEC,UAAU;EACxB,WAAW;;EAEX,cAAc,CAAC,CAAC,CAAC,mCAAmC,cAAc,IAAI,CAAC,CAAC,CAAC,EAAE;;EAE3E,UAAU,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,qCAAqC,EAAE,CAAC;QAE3E,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;YACvG,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YAExC,sDAAsD;YACtD,IAAI,WAAW,EAAE,CAAC;gBACd,IAAI,aAAqB,CAAC;gBAC1B,IAAI,CAAC;oBACD,aAAa,GAAG,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;gBAC3D,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACb,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;oBAC9E,OAAO;wBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;wBAC/D,OAAO,EAAE,IAAI;qBAChB,CAAC;gBACN,CAAC;gBAED,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,aAAa,EAAE,OAAO,EAAE;oBACnD,MAAM,EAAE,IAAI;oBACZ,MAAM,EAAE,YAAY;iBACvB,CAAC,CAAC;gBAEH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBAClB,OAAO;wBACH,OAAO,EAAE;4BACL;gCACI,IAAI,EAAE,MAAe;gCACrB,IAAI,EAAE,8BAA8B,MAAM,CAAC,KAAK,IAAI,eAAe,EAAE;6BACxE;yBACJ;wBACD,OAAO,EAAE,IAAI;qBAChB,CAAC;gBACN,CAAC;gBAED,MAAM,GAAG,GAAG;oBACR,iCAAiC;oBACjC,SAAS,MAAM,CAAC,QAAQ,EAAE;oBAC1B,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS;iBACjE;qBACI,MAAM,CAAC,OAAO,CAAC;qBACf,IAAI,CAAC,IAAI,CAAC,CAAC;gBAEhB,OAAO;oBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;iBAClD,CAAC;YACN,CAAC;YAED,2EAA2E;YAC3E,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;aACtD,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACzE,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,uBAAuB,OAAO,EAAE;qBACzC;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scaffold_project Tool
|
|
3
|
+
*
|
|
4
|
+
* Creates a complete UI plan for a new project.
|
|
5
|
+
* Outputs component hierarchy, page layouts, and design tokens.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { Config } from '../config/index.js';
|
|
9
|
+
export declare function registerScaffoldProject(server: McpServer, config: Config): void;
|
|
10
|
+
//# sourceMappingURL=scaffold-project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-project.d.ts","sourceRoot":"","sources":["../../src/tools/scaffold-project.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAoE5C,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA4D/E"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scaffold_project Tool
|
|
3
|
+
*
|
|
4
|
+
* Creates a complete UI plan for a new project.
|
|
5
|
+
* Outputs component hierarchy, page layouts, and design tokens.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { generateWithGemini } from '../generation/gemini-client.js';
|
|
9
|
+
const inputSchema = {
|
|
10
|
+
projectType: z
|
|
11
|
+
.enum(['landing', 'dashboard', 'ecommerce', 'saas', 'blog', 'portfolio', 'docs', 'custom'])
|
|
12
|
+
.describe('Type of project to scaffold'),
|
|
13
|
+
name: z.string().describe('Project name'),
|
|
14
|
+
pages: z
|
|
15
|
+
.array(z.string())
|
|
16
|
+
.describe('List of pages to plan (e.g., ["Home", "Pricing", "About", "Contact"])'),
|
|
17
|
+
vibe: z.string().optional().describe('Selected design vibe from generate_vibes'),
|
|
18
|
+
features: z
|
|
19
|
+
.array(z.string())
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Special features (e.g., ["dark mode", "i18n", "auth", "animations"])'),
|
|
22
|
+
framework: z
|
|
23
|
+
.enum(['vanilla', 'react', 'vue', 'svelte', 'nextjs'])
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Target framework'),
|
|
26
|
+
};
|
|
27
|
+
const SYSTEM_PROMPT = `You are a senior frontend architect creating a comprehensive UI plan for a new project.
|
|
28
|
+
|
|
29
|
+
Return ONE valid JSON object only. No markdown. No code fences. No extra commentary.
|
|
30
|
+
|
|
31
|
+
Output structure (example):
|
|
32
|
+
{
|
|
33
|
+
"projectName": "name",
|
|
34
|
+
"projectType": "saas",
|
|
35
|
+
"framework": "nextjs",
|
|
36
|
+
"structure": {
|
|
37
|
+
"components": {
|
|
38
|
+
"common": ["Button", "Input", "Card"],
|
|
39
|
+
"layout": ["Header", "Footer", "Sidebar"],
|
|
40
|
+
"features": { "auth": ["LoginForm", "SignupForm"] }
|
|
41
|
+
},
|
|
42
|
+
"pages": {
|
|
43
|
+
"Home": {
|
|
44
|
+
"path": "/",
|
|
45
|
+
"layout": "MarketingLayout",
|
|
46
|
+
"components": ["Hero", "Features", "CTA"],
|
|
47
|
+
"description": "Page purpose"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"designTokens": {
|
|
52
|
+
"colors": {},
|
|
53
|
+
"typography": {},
|
|
54
|
+
"spacing": {},
|
|
55
|
+
"breakpoints": {}
|
|
56
|
+
},
|
|
57
|
+
"fileStructure": [
|
|
58
|
+
"src/components/common/Button.tsx",
|
|
59
|
+
"src/components/layout/Header.tsx",
|
|
60
|
+
"src/pages/Home.tsx"
|
|
61
|
+
],
|
|
62
|
+
"dependencies": [],
|
|
63
|
+
"setupInstructions": []
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Focus on:
|
|
67
|
+
- Reusable component identification
|
|
68
|
+
- Consistent naming conventions
|
|
69
|
+
- Logical file organization
|
|
70
|
+
- Clear responsibilities (avoid bloated god-components)
|
|
71
|
+
- Accessibility defaults (WCAG AA) for interactive components`;
|
|
72
|
+
export function registerScaffoldProject(server, config) {
|
|
73
|
+
server.registerTool('scaffold_project', {
|
|
74
|
+
title: 'Scaffold Project',
|
|
75
|
+
description: 'Create a complete UI architecture plan for a new project including component hierarchy, pages, and design tokens.',
|
|
76
|
+
inputSchema,
|
|
77
|
+
}, async (args) => {
|
|
78
|
+
const projectType = args.projectType;
|
|
79
|
+
const name = args.name;
|
|
80
|
+
const pages = args.pages;
|
|
81
|
+
const vibe = args.vibe;
|
|
82
|
+
const features = args.features;
|
|
83
|
+
const framework = args.framework ||
|
|
84
|
+
config.defaultFramework;
|
|
85
|
+
let userPrompt = `Create a UI scaffold for a ${projectType} project called "${name}".
|
|
86
|
+
|
|
87
|
+
Framework: ${framework}
|
|
88
|
+
Pages: ${pages.join(', ')}`;
|
|
89
|
+
if (features && features.length > 0) {
|
|
90
|
+
userPrompt += `\nFeatures: ${features.join(', ')}`;
|
|
91
|
+
}
|
|
92
|
+
if (vibe) {
|
|
93
|
+
userPrompt += `\n\nDesign vibe/tokens:\n${vibe}`;
|
|
94
|
+
}
|
|
95
|
+
userPrompt += '\n\nReturn valid JSON only.';
|
|
96
|
+
try {
|
|
97
|
+
const response = await generateWithGemini(config, SYSTEM_PROMPT, userPrompt, { toolName: 'scaffold_project' });
|
|
98
|
+
// IMPORTANT: Return raw JSON ONLY (no wrappers) so agents can parse it reliably.
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: 'text',
|
|
103
|
+
text: response.trim(),
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
110
|
+
return {
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: 'text',
|
|
114
|
+
text: `Error scaffolding project: ${message}`,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
isError: true,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=scaffold-project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-project.js","sourceRoot":"","sources":["../../src/tools/scaffold-project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snippet_ui Tool
|
|
3
|
+
*
|
|
4
|
+
* Generate standalone UI components (modals, tables, forms, etc.)
|
|
5
|
+
* for injection into an existing architecture.
|
|
6
|
+
* Can optionally write directly to a file if requested.
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { Config } from '../config/index.js';
|
|
10
|
+
export declare function registerSnippetUI(server: McpServer, config: Config): void;
|
|
11
|
+
//# sourceMappingURL=snippet-ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet-ui.d.ts","sourceRoot":"","sources":["../../src/tools/snippet-ui.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAmG5C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAgHzE"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* snippet_ui Tool
|
|
3
|
+
*
|
|
4
|
+
* Generate standalone UI components (modals, tables, forms, etc.)
|
|
5
|
+
* for injection into an existing architecture.
|
|
6
|
+
* Can optionally write directly to a file if requested.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { generateWithGemini } from '../generation/gemini-client.js';
|
|
10
|
+
import { buildContext } from '../context/builder.js';
|
|
11
|
+
import { buildRepoHints } from '../context/repo-hints.js';
|
|
12
|
+
import { assertWritablePath } from '../context/guards.js';
|
|
13
|
+
import { writeFile } from '../output/file-writer.js';
|
|
14
|
+
const inputSchema = {
|
|
15
|
+
type: z
|
|
16
|
+
.enum(['modal', 'table', 'chart', 'form', 'card', 'nav', 'hero', 'footer', 'sidebar', 'custom'])
|
|
17
|
+
.describe('Type of UI component to generate'),
|
|
18
|
+
description: z
|
|
19
|
+
.string()
|
|
20
|
+
.describe('Detailed description of the component (e.g., "A data table with sorting, filtering, and pagination")'),
|
|
21
|
+
framework: z
|
|
22
|
+
.enum(['vanilla', 'react', 'vue', 'svelte', 'nextjs'])
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Target framework'),
|
|
25
|
+
context: z
|
|
26
|
+
.array(z.string())
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Paths to design tokens or existing components to match style'),
|
|
29
|
+
propsInterface: z
|
|
30
|
+
.string()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('TypeScript interface for component props (if applicable)'),
|
|
33
|
+
animated: z.boolean().default(false).describe('Include animations/transitions'),
|
|
34
|
+
targetPath: z.string().optional().describe('Where to write the output file (if writeToFile=true)'),
|
|
35
|
+
writeToFile: z.boolean().default(false).describe('If true, write the generated code to targetPath'),
|
|
36
|
+
};
|
|
37
|
+
function getSystemPrompt(type, framework) {
|
|
38
|
+
return `You are a senior frontend engineer and UI designer.
|
|
39
|
+
|
|
40
|
+
Generate a standalone ${type} component in ${framework}.
|
|
41
|
+
|
|
42
|
+
Hard rules:
|
|
43
|
+
- Output ONLY the code. No explanations. No markdown. No code fences.
|
|
44
|
+
- Accessible by default (WCAG AA): semantic HTML, labels, aria-* where needed, keyboard support, focus-visible.
|
|
45
|
+
- Responsive by default (mobile-first).
|
|
46
|
+
- Do not add new dependencies unless explicitly requested.
|
|
47
|
+
- Avoid placeholder TODOs. Provide complete, production-ready implementation.
|
|
48
|
+
|
|
49
|
+
Type-specific requirements:
|
|
50
|
+
${getComponentGuidelines(type)}`;
|
|
51
|
+
}
|
|
52
|
+
function getComponentGuidelines(type) {
|
|
53
|
+
const guidelines = {
|
|
54
|
+
modal: [
|
|
55
|
+
'- Trap focus inside the modal',
|
|
56
|
+
'- Close on Escape',
|
|
57
|
+
'- Close on backdrop click',
|
|
58
|
+
'- Ensure aria-modal, role=dialog, accessible title/description wiring',
|
|
59
|
+
].join('\n'),
|
|
60
|
+
table: [
|
|
61
|
+
'- Optional sortable columns (no external deps)',
|
|
62
|
+
'- Responsive: horizontal scroll or stacked layout on small screens',
|
|
63
|
+
'- Accessible headers and caption support',
|
|
64
|
+
].join('\n'),
|
|
65
|
+
form: [
|
|
66
|
+
'- Accessible labels and error messages',
|
|
67
|
+
'- Validation states and disabled/loading submit state',
|
|
68
|
+
'- Keyboard submit behavior',
|
|
69
|
+
].join('\n'),
|
|
70
|
+
card: ['- Flexible content slots', '- Hover/focus states', '- Optional media support'].join('\n'),
|
|
71
|
+
nav: [
|
|
72
|
+
'- Mobile hamburger pattern if needed',
|
|
73
|
+
'- Active state indication',
|
|
74
|
+
'- Keyboard navigation (tab, arrow keys if menus)',
|
|
75
|
+
].join('\n'),
|
|
76
|
+
hero: ['- Responsive layout', '- CTA buttons', '- Ensure text contrast/legibility'].join('\n'),
|
|
77
|
+
footer: ['- Responsive multi-column layout', '- Social links', '- Legal/copyright'].join('\n'),
|
|
78
|
+
sidebar: ['- Collapsible (optional)', '- Active item highlighting', '- Nested items support'].join('\n'),
|
|
79
|
+
chart: [
|
|
80
|
+
'- Responsive sizing',
|
|
81
|
+
'- Provide an accessible table fallback for data',
|
|
82
|
+
'- Avoid heavy chart deps unless explicitly requested',
|
|
83
|
+
].join('\n'),
|
|
84
|
+
custom: '- Follow best practices for the described component type',
|
|
85
|
+
};
|
|
86
|
+
return guidelines[type] || guidelines.custom;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extract code from markdown code blocks.
|
|
90
|
+
* (We still instruct the model not to use fences; this is a safety net.)
|
|
91
|
+
*/
|
|
92
|
+
function extractCode(response) {
|
|
93
|
+
const codeBlockMatch = response.match(/```(?:\w+)?\n([\s\S]*?)```/);
|
|
94
|
+
if (codeBlockMatch) {
|
|
95
|
+
return codeBlockMatch[1].trim();
|
|
96
|
+
}
|
|
97
|
+
return response.trim();
|
|
98
|
+
}
|
|
99
|
+
export function registerSnippetUI(server, config) {
|
|
100
|
+
server.registerTool('snippet_ui', {
|
|
101
|
+
title: 'Generate UI Snippet',
|
|
102
|
+
description: 'Generate standalone UI components (modals, tables, forms, etc.) ready to inject. Set writeToFile=true to write directly.',
|
|
103
|
+
inputSchema,
|
|
104
|
+
}, async (args) => {
|
|
105
|
+
const type = args.type;
|
|
106
|
+
const description = args.description;
|
|
107
|
+
const framework = args.framework ||
|
|
108
|
+
config.defaultFramework;
|
|
109
|
+
const contextPaths = args.context;
|
|
110
|
+
const propsInterface = args.propsInterface;
|
|
111
|
+
const animated = args.animated === true;
|
|
112
|
+
const targetPath = args.targetPath;
|
|
113
|
+
const shouldWrite = args.writeToFile === true;
|
|
114
|
+
// Build context from existing files
|
|
115
|
+
let contextContent = '';
|
|
116
|
+
if (contextPaths && contextPaths.length > 0) {
|
|
117
|
+
contextContent = await buildContext(contextPaths, config);
|
|
118
|
+
}
|
|
119
|
+
const systemPrompt = getSystemPrompt(type, framework);
|
|
120
|
+
let userPrompt = `Create a ${type} component.
|
|
121
|
+
|
|
122
|
+
${buildRepoHints(config)}
|
|
123
|
+
|
|
124
|
+
Description:
|
|
125
|
+
${description}`;
|
|
126
|
+
if (propsInterface) {
|
|
127
|
+
userPrompt += `\n\nProps interface:\n${propsInterface}`;
|
|
128
|
+
}
|
|
129
|
+
if (animated) {
|
|
130
|
+
userPrompt += `\n\nInclude smooth, subtle animations/transitions (respect reduced motion).`;
|
|
131
|
+
}
|
|
132
|
+
if (contextContent) {
|
|
133
|
+
userPrompt += `\n\nMatch the style/tokens from these existing files:\n${contextContent}`;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const response = await generateWithGemini(config, systemPrompt, userPrompt, { toolName: 'snippet_ui' });
|
|
137
|
+
const code = extractCode(response);
|
|
138
|
+
// Write to file if requested
|
|
139
|
+
if (shouldWrite) {
|
|
140
|
+
if (!targetPath) {
|
|
141
|
+
return {
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: 'text',
|
|
145
|
+
text: `Error: writeToFile=true but no targetPath was provided.`,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
isError: true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const safePath = assertWritablePath(targetPath, config);
|
|
152
|
+
const result = await writeFile(safePath, code, { format: true, backup: true });
|
|
153
|
+
if (!result.success) {
|
|
154
|
+
return {
|
|
155
|
+
content: [
|
|
156
|
+
{
|
|
157
|
+
type: 'text',
|
|
158
|
+
text: `❌ Failed to write file: ${result.error || 'Unknown error'}`,
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
isError: true,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const msg = [
|
|
165
|
+
`✅ Snippet written successfully.`,
|
|
166
|
+
`File: ${result.filePath}`,
|
|
167
|
+
result.backupPath ? `Backup: ${result.backupPath}` : undefined,
|
|
168
|
+
]
|
|
169
|
+
.filter(Boolean)
|
|
170
|
+
.join('\n');
|
|
171
|
+
return {
|
|
172
|
+
content: [{ type: 'text', text: msg }],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
// Return code only (no wrappers) for best agent compatibility
|
|
176
|
+
return {
|
|
177
|
+
content: [{ type: 'text', text: code }],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
182
|
+
return {
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: `Error generating snippet: ${message}`,
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
isError: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=snippet-ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippet-ui.js","sourceRoot":"","sources":["../../src/tools/snippet-ui.ts"],"names":[],"mappings":"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"}
|