@gemini-designer/mcp-server 0.1.2 → 0.1.29
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/dist/components/catalog.d.ts.map +1 -1
- package/dist/components/catalog.js +10 -4
- package/dist/components/catalog.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +11 -6
- package/dist/config/index.js.map +1 -1
- package/dist/context/builder.d.ts.map +1 -1
- package/dist/context/builder.js.map +1 -1
- package/dist/context/filter.d.ts.map +1 -1
- package/dist/context/filter.js +5 -1
- package/dist/context/filter.js.map +1 -1
- package/dist/context/grounding.d.ts.map +1 -1
- package/dist/context/grounding.js +7 -3
- package/dist/context/grounding.js.map +1 -1
- package/dist/context/guards.d.ts.map +1 -1
- package/dist/context/guards.js +53 -0
- package/dist/context/guards.js.map +1 -1
- package/dist/context/repo-hints.js.map +1 -1
- package/dist/context/styling-detector.d.ts +24 -0
- package/dist/context/styling-detector.d.ts.map +1 -0
- package/dist/context/styling-detector.js +337 -0
- package/dist/context/styling-detector.js.map +1 -0
- package/dist/design/principles.js.map +1 -1
- package/dist/generation/gemini-client.d.ts.map +1 -1
- package/dist/generation/gemini-client.js.map +1 -1
- package/dist/generation/litellm-client.d.ts.map +1 -1
- package/dist/generation/litellm-client.js +14 -7
- package/dist/generation/litellm-client.js.map +1 -1
- package/dist/generation/remote-client.d.ts +10 -5
- package/dist/generation/remote-client.d.ts.map +1 -1
- package/dist/generation/remote-client.js +13 -2
- package/dist/generation/remote-client.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/output/file-writer.d.ts.map +1 -1
- package/dist/output/file-writer.js +4 -4
- package/dist/output/file-writer.js.map +1 -1
- package/dist/output/formatter.d.ts.map +1 -1
- package/dist/output/formatter.js +5 -2
- package/dist/output/formatter.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/stack/detect.d.ts.map +1 -1
- package/dist/stack/detect.js +42 -9
- package/dist/stack/detect.js.map +1 -1
- package/dist/tokens/sync.d.ts.map +1 -1
- package/dist/tokens/sync.js +22 -5
- package/dist/tokens/sync.js.map +1 -1
- package/dist/tools/analyze-screenshot-ui.d.ts.map +1 -1
- package/dist/tools/analyze-screenshot-ui.js +5 -5
- package/dist/tools/analyze-screenshot-ui.js.map +1 -1
- package/dist/tools/analyze-tokens.d.ts.map +1 -1
- package/dist/tools/analyze-tokens.js +3 -1
- package/dist/tools/analyze-tokens.js.map +1 -1
- package/dist/tools/catalog-components.d.ts.map +1 -1
- package/dist/tools/catalog-components.js +1 -4
- package/dist/tools/catalog-components.js.map +1 -1
- package/dist/tools/create-ui.d.ts +3 -0
- package/dist/tools/create-ui.d.ts.map +1 -1
- package/dist/tools/create-ui.js +203 -75
- package/dist/tools/create-ui.js.map +1 -1
- package/dist/tools/detect-ui-stack.js.map +1 -1
- package/dist/tools/generate-component-variants.d.ts.map +1 -1
- package/dist/tools/generate-component-variants.js +15 -4
- package/dist/tools/generate-component-variants.js.map +1 -1
- package/dist/tools/generate-vibes.d.ts.map +1 -1
- package/dist/tools/generate-vibes.js +7 -3
- package/dist/tools/generate-vibes.js.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/modify-ui.d.ts.map +1 -1
- package/dist/tools/modify-ui.js +7 -2
- package/dist/tools/modify-ui.js.map +1 -1
- package/dist/tools/scaffold-project.d.ts.map +1 -1
- package/dist/tools/scaffold-project.js +3 -1
- package/dist/tools/scaffold-project.js.map +1 -1
- package/dist/tools/snippet-ui.d.ts +3 -1
- package/dist/tools/snippet-ui.d.ts.map +1 -1
- package/dist/tools/snippet-ui.js +219 -88
- package/dist/tools/snippet-ui.js.map +1 -1
- package/dist/tools/sync-design-tokens.d.ts.map +1 -1
- package/dist/tools/sync-design-tokens.js +26 -11
- package/dist/tools/sync-design-tokens.js.map +1 -1
- package/dist/utils/walk.d.ts.map +1 -1
- package/dist/utils/walk.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +55 -55
- package/src/__tests__/builder.test.ts +19 -19
- package/src/__tests__/config.test.ts +63 -31
- package/src/__tests__/filter.test.ts +98 -92
- package/src/__tests__/remote-client.test.ts +179 -0
- package/src/components/catalog.ts +170 -166
- package/src/config/index.ts +185 -177
- package/src/context/builder.ts +157 -157
- package/src/context/filter.ts +110 -104
- package/src/context/grounding.ts +143 -129
- package/src/context/guards.ts +97 -38
- package/src/context/repo-hints.ts +24 -24
- package/src/context/styling-detector.ts +460 -0
- package/src/design/principles.ts +14 -14
- package/src/generation/gemini-client.ts +53 -56
- package/src/generation/litellm-client.ts +102 -86
- package/src/generation/remote-client.ts +100 -77
- package/src/index.ts +16 -16
- package/src/output/file-writer.ts +123 -123
- package/src/output/formatter.ts +139 -132
- package/src/server.ts +12 -11
- package/src/stack/detect.ts +226 -175
- package/src/tokens/sync.ts +189 -155
- package/src/tools/analyze-screenshot-ui.ts +89 -88
- package/src/tools/analyze-tokens.ts +80 -78
- package/src/tools/catalog-components.ts +68 -68
- package/src/tools/create-ui.ts +295 -142
- package/src/tools/detect-ui-stack.ts +36 -36
- package/src/tools/generate-component-variants.ts +155 -135
- package/src/tools/generate-vibes.ts +121 -117
- package/src/tools/index.ts +14 -14
- package/src/tools/modify-ui.ts +170 -165
- package/src/tools/scaffold-project.ts +68 -66
- package/src/tools/snippet-ui.ts +323 -172
- package/src/tools/sync-design-tokens.ts +217 -195
- package/src/utils/walk.ts +47 -45
- package/src/version.ts +6 -0
- package/tsconfig.json +23 -33
- package/vitest.config.ts +10 -10
- package/.prettierrc +0 -9
- package/eslint.config.js +0 -37
|
@@ -7,175 +7,175 @@
|
|
|
7
7
|
|
|
8
8
|
import * as fs from 'node:fs';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
|
-
import {
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
11
|
import { formatCode } from './formatter.js';
|
|
12
12
|
|
|
13
13
|
export interface WriteOptions {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
backup?: boolean;
|
|
15
|
+
format?: boolean;
|
|
16
|
+
createDirs?: boolean;
|
|
17
|
+
gitAdd?: boolean;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface WriteResult {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
success: boolean;
|
|
22
|
+
filePath: string;
|
|
23
|
+
backupPath?: string;
|
|
24
|
+
error?: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Write content to file with safety features
|
|
29
29
|
*/
|
|
30
30
|
export async function writeFile(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
filePath: string,
|
|
32
|
+
content: string,
|
|
33
|
+
options: WriteOptions = {}
|
|
34
34
|
): Promise<WriteResult> {
|
|
35
|
-
|
|
35
|
+
const { backup = true, format = true, createDirs = true, gitAdd = false } = options;
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
success: true,
|
|
79
|
-
filePath: absPath,
|
|
80
|
-
backupPath,
|
|
81
|
-
};
|
|
82
|
-
} catch (error) {
|
|
83
|
-
return {
|
|
84
|
-
success: false,
|
|
85
|
-
filePath: absPath,
|
|
86
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
87
|
-
};
|
|
39
|
+
try {
|
|
40
|
+
// Create parent directories if needed
|
|
41
|
+
if (createDirs) {
|
|
42
|
+
const dir = path.dirname(absPath);
|
|
43
|
+
if (!fs.existsSync(dir)) {
|
|
44
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Create backup if file exists
|
|
49
|
+
let backupPath: string | undefined;
|
|
50
|
+
if (backup && fs.existsSync(absPath)) {
|
|
51
|
+
backupPath = `${absPath}.bak`;
|
|
52
|
+
fs.copyFileSync(absPath, backupPath);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Format content if requested
|
|
56
|
+
let finalContent = content;
|
|
57
|
+
if (format) {
|
|
58
|
+
try {
|
|
59
|
+
finalContent = await formatCode(content, { filePath: absPath });
|
|
60
|
+
} catch {
|
|
61
|
+
// Keep original if formatting fails
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Write the file
|
|
66
|
+
fs.writeFileSync(absPath, finalContent, 'utf-8');
|
|
67
|
+
|
|
68
|
+
// Git add if requested and in a git repo
|
|
69
|
+
if (gitAdd && isGitRepo(absPath)) {
|
|
70
|
+
try {
|
|
71
|
+
execFileSync('git', ['add', absPath], { cwd: path.dirname(absPath), stdio: 'ignore' });
|
|
72
|
+
} catch {
|
|
73
|
+
// Ignore git errors
|
|
74
|
+
}
|
|
88
75
|
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
filePath: absPath,
|
|
80
|
+
backupPath,
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
filePath: absPath,
|
|
86
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* Restore file from backup
|
|
93
93
|
*/
|
|
94
94
|
export function restoreFromBackup(filePath: string): boolean {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
95
|
+
const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
96
|
+
const backupPath = `${absPath}.bak`;
|
|
97
|
+
|
|
98
|
+
if (!fs.existsSync(backupPath)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
fs.copyFileSync(backupPath, absPath);
|
|
104
|
+
fs.unlinkSync(backupPath);
|
|
105
|
+
return true;
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Check if path is in a git repository
|
|
113
113
|
*/
|
|
114
114
|
function isGitRepo(filePath: string): boolean {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
try {
|
|
116
|
+
execFileSync('git', ['rev-parse', '--git-dir'], {
|
|
117
|
+
cwd: path.dirname(filePath),
|
|
118
|
+
stdio: 'ignore',
|
|
119
|
+
});
|
|
120
|
+
return true;
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
127
|
* Get git status for a file
|
|
128
128
|
*/
|
|
129
129
|
export function getGitStatus(filePath: string): 'modified' | 'untracked' | 'clean' | 'not-git' {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
130
|
+
const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
131
|
+
|
|
132
|
+
if (!isGitRepo(absPath)) {
|
|
133
|
+
return 'not-git';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const status = execFileSync('git', ['status', '--porcelain', absPath], {
|
|
138
|
+
cwd: path.dirname(absPath),
|
|
139
|
+
encoding: 'utf-8',
|
|
140
|
+
}).trim();
|
|
141
|
+
|
|
142
|
+
if (!status) return 'clean';
|
|
143
|
+
if (status.startsWith('??')) return 'untracked';
|
|
144
|
+
return 'modified';
|
|
145
|
+
} catch {
|
|
146
|
+
return 'not-git';
|
|
147
|
+
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
151
|
* List all backup files in a directory
|
|
152
152
|
*/
|
|
153
153
|
export function listBackups(directory: string): string[] {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
154
|
+
try {
|
|
155
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
156
|
+
return entries
|
|
157
|
+
.filter((e) => e.isFile() && e.name.endsWith('.bak'))
|
|
158
|
+
.map((e) => path.join(directory, e.name));
|
|
159
|
+
} catch {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
165
|
* Clean up old backup files
|
|
166
166
|
*/
|
|
167
167
|
export function cleanBackups(directory: string): number {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
168
|
+
const backups = listBackups(directory);
|
|
169
|
+
let cleaned = 0;
|
|
170
|
+
|
|
171
|
+
for (const backup of backups) {
|
|
172
|
+
try {
|
|
173
|
+
fs.unlinkSync(backup);
|
|
174
|
+
cleaned++;
|
|
175
|
+
} catch {
|
|
176
|
+
// Ignore errors
|
|
178
177
|
}
|
|
178
|
+
}
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
return cleaned;
|
|
181
181
|
}
|
package/src/output/formatter.ts
CHANGED
|
@@ -10,177 +10,184 @@ import * as prettier from 'prettier';
|
|
|
10
10
|
export type SupportedLanguage = 'typescript' | 'javascript' | 'css' | 'html' | 'json' | 'markdown';
|
|
11
11
|
|
|
12
12
|
interface FormatOptions {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
language?: SupportedLanguage;
|
|
14
|
+
filePath?: string;
|
|
15
|
+
printWidth?: number;
|
|
16
|
+
tabWidth?: number;
|
|
17
|
+
useTabs?: boolean;
|
|
18
|
+
semi?: boolean;
|
|
19
|
+
singleQuote?: boolean;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Format code using Prettier
|
|
24
24
|
*/
|
|
25
25
|
export async function formatCode(code: string, options: FormatOptions = {}): Promise<string> {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
26
|
+
const language = options.language || detectLanguage(code, options.filePath);
|
|
27
|
+
const parser = getParser(language);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const formatted = await prettier.format(code, {
|
|
31
|
+
parser,
|
|
32
|
+
printWidth: options.printWidth ?? 100,
|
|
33
|
+
tabWidth: options.tabWidth ?? 2,
|
|
34
|
+
useTabs: options.useTabs ?? false,
|
|
35
|
+
semi: options.semi ?? true,
|
|
36
|
+
singleQuote: options.singleQuote ?? true,
|
|
37
|
+
trailingComma: 'es5',
|
|
38
|
+
bracketSpacing: true,
|
|
39
|
+
arrowParens: 'always',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return formatted;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// If Prettier fails, return original code
|
|
45
|
+
console.error('[formatter] Prettier error, returning unformatted:', error);
|
|
46
|
+
return code;
|
|
47
|
+
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Extract code blocks from LLM response and format them
|
|
52
52
|
*/
|
|
53
53
|
export async function formatLLMResponse(response: string): Promise<string> {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
54
|
+
// Match code blocks with language identifier
|
|
55
|
+
const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
|
|
56
|
+
let result = response;
|
|
57
|
+
let match;
|
|
58
|
+
|
|
59
|
+
const replacements: Array<{ original: string; formatted: string }> = [];
|
|
60
|
+
|
|
61
|
+
while ((match = codeBlockRegex.exec(response)) !== null) {
|
|
62
|
+
const [fullMatch, lang, code] = match;
|
|
63
|
+
const language = mapLanguageIdentifier(lang);
|
|
64
|
+
|
|
65
|
+
if (language) {
|
|
66
|
+
try {
|
|
67
|
+
const formatted = await formatCode(code.trim(), { language });
|
|
68
|
+
replacements.push({
|
|
69
|
+
original: fullMatch,
|
|
70
|
+
formatted: `\`\`\`${lang}\n${formatted}\`\`\``,
|
|
71
|
+
});
|
|
72
|
+
} catch {
|
|
73
|
+
// Keep original if formatting fails
|
|
74
|
+
}
|
|
76
75
|
}
|
|
76
|
+
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
// Apply replacements
|
|
79
|
+
for (const { original, formatted } of replacements) {
|
|
80
|
+
result = result.replace(original, formatted);
|
|
81
|
+
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
return result;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* Detect language from code content or file path
|
|
88
88
|
*/
|
|
89
89
|
function detectLanguage(code: string, filePath?: string): SupportedLanguage {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return 'typescript';
|
|
96
|
-
case 'js':
|
|
97
|
-
case 'jsx':
|
|
98
|
-
return 'javascript';
|
|
99
|
-
case 'css':
|
|
100
|
-
case 'scss':
|
|
101
|
-
case 'less':
|
|
102
|
-
return 'css';
|
|
103
|
-
case 'html':
|
|
104
|
-
return 'html';
|
|
105
|
-
case 'json':
|
|
106
|
-
return 'json';
|
|
107
|
-
case 'md':
|
|
108
|
-
return 'markdown';
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Detect from content
|
|
113
|
-
if (code.includes('import React') || code.includes('from "react"') || code.includes("from 'react'")) {
|
|
114
|
-
return code.includes(': React.') || code.includes('<') ? 'typescript' : 'javascript';
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (code.includes('interface ') || code.includes(': string') || code.includes(': number')) {
|
|
90
|
+
if (filePath) {
|
|
91
|
+
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
92
|
+
switch (ext) {
|
|
93
|
+
case 'ts':
|
|
94
|
+
case 'tsx':
|
|
118
95
|
return 'typescript';
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (code.includes('function ') || code.includes('const ') || code.includes('let ')) {
|
|
96
|
+
case 'js':
|
|
97
|
+
case 'jsx':
|
|
122
98
|
return 'javascript';
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
99
|
+
case 'css':
|
|
100
|
+
case 'scss':
|
|
101
|
+
case 'less':
|
|
126
102
|
return 'css';
|
|
103
|
+
case 'html':
|
|
104
|
+
return 'html';
|
|
105
|
+
case 'json':
|
|
106
|
+
return 'json';
|
|
107
|
+
case 'md':
|
|
108
|
+
return 'markdown';
|
|
127
109
|
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Detect from content
|
|
113
|
+
if (
|
|
114
|
+
code.includes('import React') ||
|
|
115
|
+
code.includes('from "react"') ||
|
|
116
|
+
code.includes("from 'react'")
|
|
117
|
+
) {
|
|
118
|
+
return code.includes(': React.') || code.includes('<') ? 'typescript' : 'javascript';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (code.includes('interface ') || code.includes(': string') || code.includes(': number')) {
|
|
122
|
+
return 'typescript';
|
|
123
|
+
}
|
|
128
124
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return 'json';
|
|
133
|
-
} catch {
|
|
134
|
-
// Not JSON
|
|
135
|
-
}
|
|
136
|
-
}
|
|
125
|
+
if (code.includes('function ') || code.includes('const ') || code.includes('let ')) {
|
|
126
|
+
return 'javascript';
|
|
127
|
+
}
|
|
137
128
|
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
if (
|
|
130
|
+
code.includes('{') &&
|
|
131
|
+
(code.includes('color:') || code.includes('margin:') || code.includes('display:'))
|
|
132
|
+
) {
|
|
133
|
+
return 'css';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (code.startsWith('{') || code.startsWith('[')) {
|
|
137
|
+
try {
|
|
138
|
+
JSON.parse(code);
|
|
139
|
+
return 'json';
|
|
140
|
+
} catch {
|
|
141
|
+
// Not JSON
|
|
140
142
|
}
|
|
143
|
+
}
|
|
141
144
|
|
|
142
|
-
|
|
143
|
-
return '
|
|
145
|
+
if (code.includes('<html') || code.includes('<!DOCTYPE')) {
|
|
146
|
+
return 'html';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Default to TypeScript for better JSX support
|
|
150
|
+
return 'typescript';
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
/**
|
|
147
154
|
* Get Prettier parser for language
|
|
148
155
|
*/
|
|
149
156
|
function getParser(language: SupportedLanguage): string {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
const parsers: Record<SupportedLanguage, string> = {
|
|
158
|
+
typescript: 'typescript',
|
|
159
|
+
javascript: 'babel',
|
|
160
|
+
css: 'css',
|
|
161
|
+
html: 'html',
|
|
162
|
+
json: 'json',
|
|
163
|
+
markdown: 'markdown',
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return parsers[language];
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
/**
|
|
163
170
|
* Map common language identifiers to supported types
|
|
164
171
|
*/
|
|
165
172
|
function mapLanguageIdentifier(identifier?: string): SupportedLanguage | null {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
173
|
+
if (!identifier) return null;
|
|
174
|
+
|
|
175
|
+
const lower = identifier.toLowerCase();
|
|
176
|
+
const mapping: Record<string, SupportedLanguage> = {
|
|
177
|
+
ts: 'typescript',
|
|
178
|
+
tsx: 'typescript',
|
|
179
|
+
typescript: 'typescript',
|
|
180
|
+
js: 'javascript',
|
|
181
|
+
jsx: 'javascript',
|
|
182
|
+
javascript: 'javascript',
|
|
183
|
+
css: 'css',
|
|
184
|
+
scss: 'css',
|
|
185
|
+
less: 'css',
|
|
186
|
+
html: 'html',
|
|
187
|
+
json: 'json',
|
|
188
|
+
md: 'markdown',
|
|
189
|
+
markdown: 'markdown',
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return mapping[lower] || null;
|
|
186
193
|
}
|
package/src/server.ts
CHANGED
|
@@ -8,21 +8,22 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
8
8
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
9
|
import { Config } from './config/index.js';
|
|
10
10
|
import { registerTools } from './tools/index.js';
|
|
11
|
+
import { VERSION } from './version.js';
|
|
11
12
|
|
|
12
13
|
export async function createServer(config: Config) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const server = new McpServer({
|
|
15
|
+
name: 'gemini-designer-mcp',
|
|
16
|
+
version: VERSION,
|
|
17
|
+
});
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
// Register all MCP tools
|
|
20
|
+
registerTools(server, config);
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// Use stdio transport (standard for MCP)
|
|
23
|
+
const transport = new StdioServerTransport();
|
|
24
|
+
await server.connect(transport);
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
console.error('[gemini-designer-mcp] Server connected via stdio');
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
return server;
|
|
28
29
|
}
|