@szc-ft/mcp-szcd-client 0.20.0 → 0.22.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/agents/build.js +152 -130
- package/agents/opencode-extension/agents/szcd-component-expert.md +96 -12
- package/agents/platforms.json +17 -7
- package/agents/qwen-extension/agents/szcd-component-expert.md +95 -12
- package/agents/src/szcd-component-expert.md +169 -6
- package/agents/src/tools.json +10 -5
- package/agents/szcd-component-expert.md +97 -14
- package/agents/szcd-component-expert.qoder.md +185 -15
- package/agents/szcd-component-expert.trae.md +175 -13
- package/opencode-extension/agents/szcd-component-expert.md +308 -0
- package/opencode-extension/commands/szcd-mcp-api-config.md +48 -0
- package/opencode-extension/commands/szcd-mcp-auth.md +39 -0
- package/opencode-extension/commands/szcd-mcp-browser-test.md +57 -0
- package/opencode-extension/commands/szcd-mcp-coding-config.md +40 -0
- package/opencode-extension/commands/szcd-mcp-feedback.md +42 -0
- package/opencode-extension/commands/szcd-mcp-url.md +37 -0
- package/opencode-extension/opencode.json +15 -0
- package/opencode-extension/skills/local-api-tool/SKILL.md +246 -0
- package/opencode-extension/skills/local-browser-test/SKILL.md +249 -0
- package/opencode-extension/skills/szcd-component-helper/SKILL.md +523 -0
- package/opencode-extension/skills/szcd-design-to-code/SKILL.md +168 -0
- package/package.json +4 -2
- package/qwen-extension/QWEN.md +29 -8
- package/qwen-extension/agents/szcd-component-expert.md +95 -12
- package/qwen-extension/qwen-extension.json +6 -1
- package/qwen-extension/skills/szcd-component-helper/SKILL.md +81 -1
- package/qwen-extension/skills/szcd-design-to-code/SKILL.md +168 -0
- package/scripts/lib/claude-code.js +45 -1
- package/scripts/lib/common.js +17 -0
- package/scripts/lib/opencode.js +108 -12
- package/scripts/lib/qoder.js +42 -1
- package/scripts/lib/trae-cli.js +39 -1
- package/scripts/lib/trae-ide.js +40 -1
- package/scripts/postinstall.js +28 -0
- package/standard-skill/szcd-component-helper/SKILL.md +81 -1
- package/standard-skill/szcd-design-to-code/SKILL.md +168 -0
|
@@ -9,7 +9,7 @@ import fs from "node:fs";
|
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import { execSync } from "node:child_process";
|
|
12
|
-
import { getClientConfigHeader as getClientConfigHeaderDirect, getApiKey as getApiKeyDirect } from "./common.js";
|
|
12
|
+
import { getClientConfigHeader as getClientConfigHeaderDirect, getApiKey as getApiKeyDirect, getSketchMcpServerUrl, ADDITIONAL_MCP_SERVERS } from "./common.js";
|
|
13
13
|
|
|
14
14
|
// ==================== 路径工具 ====================
|
|
15
15
|
|
|
@@ -363,9 +363,53 @@ export function setupClaudeCode(deps) {
|
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
// 最后注册额外的 MCP Server,避免被 CLI 操作覆盖
|
|
367
|
+
registerAdditionalMcpServers();
|
|
368
|
+
|
|
366
369
|
return {
|
|
367
370
|
claudeProjectInstalled,
|
|
368
371
|
skillsDirectory: getClaudeCodeSkillsDirectory(),
|
|
369
372
|
agentsDirectory: getClaudeCodeAgentsDirectory(),
|
|
370
373
|
};
|
|
371
374
|
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* 注册额外的 MCP Server 到 Claude Code
|
|
378
|
+
* HTTP 类型: { type: "http", url: "<httpUrl>" }
|
|
379
|
+
* stdio 类型: { type: "stdio", command, args }
|
|
380
|
+
* 注意:仅通过文件操作写入 ~/.claude.json,不使用 CLI,
|
|
381
|
+
* 因为 `claude mcp add` 会重写整个文件并丢失其他 server 条目
|
|
382
|
+
*/
|
|
383
|
+
function registerAdditionalMcpServers() {
|
|
384
|
+
const configPath = getClaudeCodeConfigPath();
|
|
385
|
+
let config = {};
|
|
386
|
+
if (fs.existsSync(configPath)) {
|
|
387
|
+
try {
|
|
388
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
389
|
+
} catch { /* 忽略 */ }
|
|
390
|
+
}
|
|
391
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
392
|
+
|
|
393
|
+
let changed = false;
|
|
394
|
+
for (const server of ADDITIONAL_MCP_SERVERS) {
|
|
395
|
+
let expected;
|
|
396
|
+
if (server.type === "http") {
|
|
397
|
+
const sketchUrl = `${getSketchMcpServerUrl()}/mcp`;
|
|
398
|
+
expected = { type: "http", url: sketchUrl };
|
|
399
|
+
} else {
|
|
400
|
+
expected = { type: server.type, command: server.command, args: server.args };
|
|
401
|
+
}
|
|
402
|
+
const current = config.mcpServers[server.name];
|
|
403
|
+
if (current && JSON.stringify(current) === JSON.stringify(expected)) {
|
|
404
|
+
console.log(`✓ Claude Code ~/.claude.json already has: ${server.name}`);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
config.mcpServers[server.name] = expected;
|
|
408
|
+
console.log(`✓ Added ${server.name} to Claude Code ~/.claude.json (${server.type})`);
|
|
409
|
+
changed = true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (changed) {
|
|
413
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
414
|
+
}
|
|
415
|
+
}
|
package/scripts/lib/common.js
CHANGED
|
@@ -17,6 +17,18 @@ const __dirname = path.dirname(__filename);
|
|
|
17
17
|
// 默认值(需在 SKILL_SOURCE 之前定义)
|
|
18
18
|
export const DEFAULT_MCP_SERVER_URL = "http://localhost:3456";
|
|
19
19
|
export const DEFAULT_MCP_SERVER_NAME = "szcd-component-helper";
|
|
20
|
+
export const DEFAULT_SKETCH_MCP_SERVER_URL = "http://localhost:3457";
|
|
21
|
+
|
|
22
|
+
// 额外的 MCP Server(HTTP 模式,由独立进程或代理提供 HTTP 接口)
|
|
23
|
+
export const ADDITIONAL_MCP_SERVERS = [
|
|
24
|
+
{
|
|
25
|
+
name: "sketch-mcp-server",
|
|
26
|
+
type: "stdio",
|
|
27
|
+
command: "sketch-mcp-server",
|
|
28
|
+
args: [],
|
|
29
|
+
description: "Sketch 设计文件解析(szcd 维护版),解析 .sketch 文件提取页面结构、图层信息和导出图片。postinstall 会尝试全局安装以加速启动",
|
|
30
|
+
},
|
|
31
|
+
];
|
|
20
32
|
|
|
21
33
|
export const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
|
|
22
34
|
export const SKILLS_DIR = path.join(PACKAGE_ROOT, "standard-skill");
|
|
@@ -184,6 +196,11 @@ export function getMcpServerName() {
|
|
|
184
196
|
return config.MCP_SERVER_NAME || DEFAULT_MCP_SERVER_NAME;
|
|
185
197
|
}
|
|
186
198
|
|
|
199
|
+
export function getSketchMcpServerUrl() {
|
|
200
|
+
const config = loadExistingConfig();
|
|
201
|
+
return config.SKETCH_MCP_SERVER_URL || DEFAULT_SKETCH_MCP_SERVER_URL;
|
|
202
|
+
}
|
|
203
|
+
|
|
187
204
|
// ==================== 客户端 CODING 配置透传 ====================
|
|
188
205
|
|
|
189
206
|
/**
|
package/scripts/lib/opencode.js
CHANGED
|
@@ -39,7 +39,7 @@ import fs from "node:fs";
|
|
|
39
39
|
import path from "node:path";
|
|
40
40
|
import os from "node:os";
|
|
41
41
|
import { execSync } from "node:child_process";
|
|
42
|
-
import { getClientConfigHeader as _getClientConfigHeader, getApiKey as _getApiKey } from "./common.js";
|
|
42
|
+
import { getClientConfigHeader as _getClientConfigHeader, getApiKey as _getApiKey, getSketchMcpServerUrl, ADDITIONAL_MCP_SERVERS } from "./common.js";
|
|
43
43
|
|
|
44
44
|
// ==================== 路径工具 ====================
|
|
45
45
|
|
|
@@ -97,10 +97,40 @@ function readJsonFile(filePath) {
|
|
|
97
97
|
if (!fs.existsSync(filePath)) return {};
|
|
98
98
|
try {
|
|
99
99
|
const content = fs.readFileSync(filePath, "utf8");
|
|
100
|
-
// jsonc
|
|
101
|
-
|
|
100
|
+
// jsonc 支持:去除注释,但需跳过字符串内的 //
|
|
101
|
+
// 策略:逐字符扫描,追踪是否在字符串内
|
|
102
|
+
const lines = content.split("\n");
|
|
103
|
+
const result = [];
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
let inString = false;
|
|
106
|
+
let escape = false;
|
|
107
|
+
let cleanLine = "";
|
|
108
|
+
for (let i = 0; i < line.length; i++) {
|
|
109
|
+
const ch = line[i];
|
|
110
|
+
if (escape) {
|
|
111
|
+
cleanLine += ch;
|
|
112
|
+
escape = false;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (ch === "\\") {
|
|
116
|
+
cleanLine += ch;
|
|
117
|
+
escape = true;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (ch === '"') {
|
|
121
|
+
inString = !inString;
|
|
122
|
+
cleanLine += ch;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (!inString && ch === "/" && i + 1 < line.length && line[i + 1] === "/") {
|
|
126
|
+
break; // 行内注释开始,跳过剩余内容
|
|
127
|
+
}
|
|
128
|
+
cleanLine += ch;
|
|
129
|
+
}
|
|
130
|
+
result.push(cleanLine);
|
|
131
|
+
}
|
|
132
|
+
const cleaned = result.join("\n")
|
|
102
133
|
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
103
|
-
.replace(/\/\/.*$/gm, "")
|
|
104
134
|
.replace(/,(\s*[}\]])/g, "$1")
|
|
105
135
|
.trim();
|
|
106
136
|
return JSON.parse(cleaned);
|
|
@@ -164,16 +194,20 @@ function syncOpenCodeConfig(deps) {
|
|
|
164
194
|
|
|
165
195
|
const current = config.mcp[serverName];
|
|
166
196
|
const apiKey = deps.getApiKey ? deps.getApiKey() : "";
|
|
197
|
+
const clientConfigHeader = deps.getClientConfigHeader ? deps.getClientConfigHeader() : null;
|
|
167
198
|
const currentHeaders = current && current.headers ? current.headers : {};
|
|
168
|
-
const
|
|
199
|
+
const needsUpdate = !current || current.type !== "remote" || current.url !== mcpUrl
|
|
200
|
+
|| (apiKey && currentHeaders["X-API-Key"] !== apiKey)
|
|
201
|
+
|| (clientConfigHeader && currentHeaders["X-Client-Config"] !== clientConfigHeader);
|
|
169
202
|
|
|
170
|
-
if (current && current.type === "remote" && current.url === mcpUrl && !
|
|
203
|
+
if (current && current.type === "remote" && current.url === mcpUrl && !needsUpdate) {
|
|
171
204
|
console.log(`✓ OpenCode ${path.basename(actualPath)} already up-to-date: ${mcpUrl}`);
|
|
172
205
|
return;
|
|
173
206
|
}
|
|
174
207
|
|
|
175
208
|
const headers = {};
|
|
176
|
-
if (
|
|
209
|
+
if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
|
|
210
|
+
if (apiKey) headers["X-API-Key"] = apiKey;
|
|
177
211
|
|
|
178
212
|
config.mcp[serverName] = {
|
|
179
213
|
type: "remote",
|
|
@@ -212,8 +246,11 @@ function createOpenCodeProjectConfig(deps) {
|
|
|
212
246
|
|
|
213
247
|
const current = config.mcp[serverName];
|
|
214
248
|
const apiKey = deps.getApiKey ? deps.getApiKey() : "";
|
|
249
|
+
const clientConfigHeader = deps.getClientConfigHeader ? deps.getClientConfigHeader() : null;
|
|
215
250
|
const currentHeaders = current && current.headers ? current.headers : {};
|
|
216
|
-
const needsUpdate = !current || current.type !== "remote" || current.url !== mcpUrl
|
|
251
|
+
const needsUpdate = !current || current.type !== "remote" || current.url !== mcpUrl
|
|
252
|
+
|| (apiKey && currentHeaders["X-API-Key"] !== apiKey)
|
|
253
|
+
|| (clientConfigHeader && currentHeaders["X-Client-Config"] !== clientConfigHeader);
|
|
217
254
|
|
|
218
255
|
if (!needsUpdate) {
|
|
219
256
|
console.log(`✓ OpenCode project opencode.json already up-to-date: ${mcpUrl}`);
|
|
@@ -221,7 +258,8 @@ function createOpenCodeProjectConfig(deps) {
|
|
|
221
258
|
}
|
|
222
259
|
|
|
223
260
|
const headers = {};
|
|
224
|
-
if (
|
|
261
|
+
if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
|
|
262
|
+
if (apiKey) headers["X-API-Key"] = apiKey;
|
|
225
263
|
|
|
226
264
|
config.mcp[serverName] = {
|
|
227
265
|
type: "remote",
|
|
@@ -468,16 +506,20 @@ function syncOpenCodeConfigDirect(targetUrl, serverName) {
|
|
|
468
506
|
|
|
469
507
|
const current = config.mcp[serverName];
|
|
470
508
|
const apiKey = _getApiKey();
|
|
509
|
+
const clientConfigHeader = _getClientConfigHeader();
|
|
471
510
|
const currentHeaders = current && current.headers ? current.headers : {};
|
|
472
|
-
const
|
|
511
|
+
const needsUpdate = !current || current.type !== "remote" || current.url !== mcpUrl
|
|
512
|
+
|| (apiKey && currentHeaders["X-API-Key"] !== apiKey)
|
|
513
|
+
|| (clientConfigHeader && currentHeaders["X-Client-Config"] !== clientConfigHeader);
|
|
473
514
|
|
|
474
|
-
if (current && current.type === "remote" && current.url === mcpUrl && !
|
|
515
|
+
if (current && current.type === "remote" && current.url === mcpUrl && !needsUpdate) {
|
|
475
516
|
console.log(`⏭️ OpenCode ${path.basename(actualPath)} already up-to-date: ${mcpUrl}`);
|
|
476
517
|
return;
|
|
477
518
|
}
|
|
478
519
|
|
|
479
520
|
const headers = {};
|
|
480
|
-
if (
|
|
521
|
+
if (clientConfigHeader) headers["X-Client-Config"] = clientConfigHeader;
|
|
522
|
+
if (apiKey) headers["X-API-Key"] = apiKey;
|
|
481
523
|
|
|
482
524
|
config.mcp[serverName] = {
|
|
483
525
|
type: "remote",
|
|
@@ -493,6 +535,57 @@ function syncOpenCodeConfigDirect(targetUrl, serverName) {
|
|
|
493
535
|
console.log(`✓ Updated OpenCode ${path.basename(actualPath)}: ${mcpUrl}`);
|
|
494
536
|
}
|
|
495
537
|
|
|
538
|
+
// ==================== 额外 MCP Server(local 模式) ====================
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* 将 ADDITIONAL_MCP_SERVERS 中的 MCP server 注册到 ~/.config/opencode/opencode.jsonc 的 mcp 中。
|
|
542
|
+
* HTTP 类型: { type: "remote", url: "<httpUrl>" }
|
|
543
|
+
* stdio 类型: { type: "local", command: ["sketch-mcp-server"] }
|
|
544
|
+
* 如果已存在同名 server 则跳过,不覆盖已有配置。
|
|
545
|
+
*/
|
|
546
|
+
function registerAdditionalMcpServers() {
|
|
547
|
+
const configPath = getOpenCodeConfigPath();
|
|
548
|
+
const fallbackPath = getOpenCodeConfigPathJson();
|
|
549
|
+
let actualPath = fs.existsSync(configPath) ? configPath : fallbackPath;
|
|
550
|
+
|
|
551
|
+
let config = {};
|
|
552
|
+
if (fs.existsSync(actualPath)) {
|
|
553
|
+
config = readJsonFile(actualPath);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (!config.mcp) config.mcp = {};
|
|
557
|
+
|
|
558
|
+
let changed = false;
|
|
559
|
+
for (const server of ADDITIONAL_MCP_SERVERS) {
|
|
560
|
+
if (config.mcp[server.name]) {
|
|
561
|
+
console.log(`✓ OpenCode ${path.basename(actualPath)} already has MCP server: ${server.name}, skipping`);
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (server.type === "http") {
|
|
565
|
+
const sketchUrl = `${getSketchMcpServerUrl()}/mcp`;
|
|
566
|
+
config.mcp[server.name] = {
|
|
567
|
+
type: "remote",
|
|
568
|
+
url: sketchUrl,
|
|
569
|
+
};
|
|
570
|
+
console.log(`✓ Registered additional MCP server to OpenCode: ${server.name} (remote, ${sketchUrl})`);
|
|
571
|
+
} else {
|
|
572
|
+
config.mcp[server.name] = {
|
|
573
|
+
type: "local",
|
|
574
|
+
command: [server.command, ...server.args],
|
|
575
|
+
};
|
|
576
|
+
console.log(`✓ Registered additional MCP server to OpenCode: ${server.name} (local)`);
|
|
577
|
+
}
|
|
578
|
+
changed = true;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (changed) {
|
|
582
|
+
if (!config.$schema) {
|
|
583
|
+
config.$schema = "https://opencode.ai/config.json";
|
|
584
|
+
}
|
|
585
|
+
writeJsonFile(actualPath, config);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
496
589
|
// ==================== 导出 ====================
|
|
497
590
|
|
|
498
591
|
export function setupOpenCode(deps) {
|
|
@@ -517,6 +610,9 @@ export function setupOpenCode(deps) {
|
|
|
517
610
|
}
|
|
518
611
|
*/
|
|
519
612
|
|
|
613
|
+
// ---- 额外 MCP Server(local 模式) ----
|
|
614
|
+
registerAdditionalMcpServers();
|
|
615
|
+
|
|
520
616
|
return {
|
|
521
617
|
openCodeProjectInstalled: false,
|
|
522
618
|
skillsDirectory: getOpenCodeSkillsDirectory(),
|
package/scripts/lib/qoder.js
CHANGED
|
@@ -29,7 +29,7 @@ import fs from "node:fs";
|
|
|
29
29
|
import path from "node:path";
|
|
30
30
|
import os from "node:os";
|
|
31
31
|
import { execSync } from "node:child_process";
|
|
32
|
-
import { getClientConfigHeader as _getClientConfigHeader, getApiKey as _getApiKey } from "./common.js";
|
|
32
|
+
import { getClientConfigHeader as _getClientConfigHeader, getApiKey as _getApiKey, getSketchMcpServerUrl, ADDITIONAL_MCP_SERVERS } from "./common.js";
|
|
33
33
|
|
|
34
34
|
// ==================== 路径工具 ====================
|
|
35
35
|
|
|
@@ -474,6 +474,44 @@ function syncQoderSettingsDirect(targetUrl, serverName) {
|
|
|
474
474
|
console.log(`✓ Updated Qoder ~/.qoder/settings.json: ${serverName} (${mcpUrl})`);
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
+
// ==================== 额外 MCP Server(stdio 模式) ====================
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* 将 ADDITIONAL_MCP_SERVERS 中的 MCP server 注册到 ~/.qoder/settings.json 的 mcpServers 中。
|
|
481
|
+
* HTTP 类型: { type: "http", url: "<httpUrl>" }
|
|
482
|
+
* stdio 类型: { type: "stdio", command, args }
|
|
483
|
+
* 如果已存在同名 server 则跳过,不覆盖已有配置。
|
|
484
|
+
*/
|
|
485
|
+
function registerAdditionalMcpServers() {
|
|
486
|
+
const settingsPath = getQoderSettingsPath();
|
|
487
|
+
let settings = readJsonFile(settingsPath);
|
|
488
|
+
|
|
489
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
490
|
+
|
|
491
|
+
for (const server of ADDITIONAL_MCP_SERVERS) {
|
|
492
|
+
if (settings.mcpServers[server.name]) {
|
|
493
|
+
console.log(`✓ Qoder ~/.qoder/settings.json already has MCP server: ${server.name}, skipping`);
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
if (server.type === "http") {
|
|
497
|
+
const sketchUrl = `${getSketchMcpServerUrl()}/mcp`;
|
|
498
|
+
settings.mcpServers[server.name] = {
|
|
499
|
+
type: "http",
|
|
500
|
+
url: sketchUrl,
|
|
501
|
+
};
|
|
502
|
+
} else {
|
|
503
|
+
settings.mcpServers[server.name] = {
|
|
504
|
+
type: server.type,
|
|
505
|
+
command: server.command,
|
|
506
|
+
args: server.args,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
console.log(`✓ Registered additional MCP server to Qoder: ${server.name} (${server.type})`);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
writeJsonFile(settingsPath, settings);
|
|
513
|
+
}
|
|
514
|
+
|
|
477
515
|
// ==================== 导出 ====================
|
|
478
516
|
|
|
479
517
|
export function setupQoder(deps) {
|
|
@@ -504,6 +542,9 @@ export function setupQoder(deps) {
|
|
|
504
542
|
// }
|
|
505
543
|
// }
|
|
506
544
|
|
|
545
|
+
// ---- 额外 MCP Server(stdio 模式) ----
|
|
546
|
+
registerAdditionalMcpServers();
|
|
547
|
+
|
|
507
548
|
return {
|
|
508
549
|
qoderProjectInstalled: false,
|
|
509
550
|
skillsDirectory: getQoderSkillsDirectory(),
|
package/scripts/lib/trae-cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import fs from "node:fs";
|
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import os from "node:os";
|
|
16
16
|
import { execSync } from "node:child_process";
|
|
17
|
-
import { loadClientCodingConfig, encodeClientConfig, getApiKey } from "./common.js";
|
|
17
|
+
import { loadClientCodingConfig, encodeClientConfig, getApiKey, getSketchMcpServerUrl, ADDITIONAL_MCP_SERVERS } from "./common.js";
|
|
18
18
|
|
|
19
19
|
// ==================== 路径工具 ====================
|
|
20
20
|
|
|
@@ -163,10 +163,48 @@ export function createTraeCliConfig(deps) {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
syncTraeCliYaml(deps);
|
|
166
|
+
registerAdditionalMcpServersToYaml();
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
// ==================== Skill 复制(Trae CLI) ====================
|
|
169
170
|
|
|
171
|
+
/**
|
|
172
|
+
* 注册额外的 MCP Server 到 Trae CLI YAML
|
|
173
|
+
* HTTP 类型: { type: http, url: "<httpUrl>" }
|
|
174
|
+
* stdio 类型: { type: stdio, command, args }
|
|
175
|
+
*/
|
|
176
|
+
function registerAdditionalMcpServersToYaml() {
|
|
177
|
+
const yamlPath = getTraeCliYamlPath();
|
|
178
|
+
if (!fs.existsSync(yamlPath)) return;
|
|
179
|
+
|
|
180
|
+
let content = fs.readFileSync(yamlPath, "utf8");
|
|
181
|
+
const { servers } = parseYamlMcpServers(content);
|
|
182
|
+
|
|
183
|
+
for (const server of ADDITIONAL_MCP_SERVERS) {
|
|
184
|
+
const existing = servers.find(s => s.name === server.name);
|
|
185
|
+
if (existing) {
|
|
186
|
+
console.log(`✓ Trae CLI YAML already has: ${server.name}`);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
let entry;
|
|
190
|
+
if (server.type === "http") {
|
|
191
|
+
const sketchUrl = `${getSketchMcpServerUrl()}/mcp`;
|
|
192
|
+
entry = ` - name: ${server.name}\n type: http\n url: ${sketchUrl}\n`;
|
|
193
|
+
} else {
|
|
194
|
+
const argsStr = server.args ? server.args.map(a => `"${a}"`).join(", ") : "";
|
|
195
|
+
entry = ` - name: ${server.name}\n type: ${server.type}\n command: ${server.command}\n args: [${argsStr}]\n`;
|
|
196
|
+
}
|
|
197
|
+
if (/^mcp_servers\s*:/m.test(content)) {
|
|
198
|
+
content = content.replace(/^(mcp_servers\s*:)/m, `$1\n${entry}`);
|
|
199
|
+
} else {
|
|
200
|
+
content = content.trimEnd() + `\n\nmcp_servers:\n${entry}`;
|
|
201
|
+
}
|
|
202
|
+
console.log(`✓ Added ${server.name} to Trae CLI YAML (${server.type})`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fs.writeFileSync(yamlPath, content);
|
|
206
|
+
}
|
|
207
|
+
|
|
170
208
|
export function getTraeCliSkillsDirectory() {
|
|
171
209
|
return path.join(getHomeDir(), ".traecli", "skills");
|
|
172
210
|
}
|
package/scripts/lib/trae-ide.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import os from "node:os";
|
|
16
|
-
import { getClientConfigHeader, getApiKey } from "./common.js";
|
|
16
|
+
import { getClientConfigHeader, getApiKey, getSketchMcpServerUrl, ADDITIONAL_MCP_SERVERS } from "./common.js";
|
|
17
17
|
|
|
18
18
|
// ==================== 路径工具 ====================
|
|
19
19
|
|
|
@@ -101,6 +101,45 @@ export function syncTraeCnMcpJson(deps) {
|
|
|
101
101
|
|
|
102
102
|
export function createTraeIdeConfig(deps) {
|
|
103
103
|
syncTraeCnMcpJson(deps);
|
|
104
|
+
registerAdditionalMcpServers();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 注册额外的 MCP Server,如 sketch-mcp-server
|
|
109
|
+
* HTTP 类型: { type: "http", url: "<httpUrl>" }
|
|
110
|
+
* stdio 类型: { type: "stdio", command, args }
|
|
111
|
+
*/
|
|
112
|
+
function registerAdditionalMcpServers() {
|
|
113
|
+
const mcpJsonPath = getTraeIdeMcpJsonPath();
|
|
114
|
+
|
|
115
|
+
let config = {};
|
|
116
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
117
|
+
try {
|
|
118
|
+
config = JSON.parse(fs.readFileSync(mcpJsonPath, "utf8"));
|
|
119
|
+
} catch { /* 忽略 */ }
|
|
120
|
+
}
|
|
121
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
122
|
+
|
|
123
|
+
for (const server of ADDITIONAL_MCP_SERVERS) {
|
|
124
|
+
let expected;
|
|
125
|
+
if (server.type === "http") {
|
|
126
|
+
const sketchUrl = `${getSketchMcpServerUrl()}/mcp`;
|
|
127
|
+
expected = { type: "http", url: sketchUrl };
|
|
128
|
+
} else {
|
|
129
|
+
expected = { type: server.type, command: server.command, args: server.args };
|
|
130
|
+
}
|
|
131
|
+
const current = config.mcpServers[server.name];
|
|
132
|
+
if (current && JSON.stringify(current) === JSON.stringify(expected)) {
|
|
133
|
+
console.log(`✓ Trae CN mcp.json already has: ${server.name}`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
config.mcpServers[server.name] = expected;
|
|
137
|
+
console.log(`✓ Added ${server.name} to Trae CN mcp.json (${server.type})`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const dir = path.dirname(mcpJsonPath);
|
|
141
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
142
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2));
|
|
104
143
|
}
|
|
105
144
|
|
|
106
145
|
// ==================== MCP URL 同步(供 update-mcp-url.js 调用) ====================
|
package/scripts/postinstall.js
CHANGED
|
@@ -46,6 +46,31 @@ const deps = {
|
|
|
46
46
|
PACKAGE_ROOT: common.PACKAGE_ROOT,
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
// ==================== sketch-mcp-server 全局安装 ====================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 确保 @szc-ft/sketch-mcp-server 已全局安装(提供 sketch-mcp-server 命令行工具)
|
|
53
|
+
* 如果未安装,自动执行 npm install -g @szc-ft/sketch-mcp-server
|
|
54
|
+
* 这样各 IDE 通过全局 bin 调用 sketch-mcp-server,避免 npx 网络延迟
|
|
55
|
+
*/
|
|
56
|
+
function ensureSketchMcpServer(deps) {
|
|
57
|
+
if (deps.isCommandAvailable("sketch-mcp-server")) {
|
|
58
|
+
console.log("✓ sketch-mcp-server already installed globally");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log("📦 Installing @szc-ft/sketch-mcp-server globally (npm install -g @szc-ft/sketch-mcp-server)...");
|
|
63
|
+
console.log(" This avoids npx network delays when IDE starts the MCP server.");
|
|
64
|
+
|
|
65
|
+
const result = deps.safeExecSync("npm install -g @szc-ft/sketch-mcp-server", { timeout: 30000 });
|
|
66
|
+
if (result === true) {
|
|
67
|
+
console.log("✓ @szc-ft/sketch-mcp-server installed globally successfully");
|
|
68
|
+
} else {
|
|
69
|
+
console.warn("⚠️ Failed to install @szc-ft/sketch-mcp-server globally.");
|
|
70
|
+
console.warn(" You can install it manually: npm install -g @szc-ft/sketch-mcp-server");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
49
74
|
// ==================== 快速模式提示 ====================
|
|
50
75
|
|
|
51
76
|
function showQuickMessage() {
|
|
@@ -179,6 +204,9 @@ function main() {
|
|
|
179
204
|
|
|
180
205
|
// ---- 配置文件 ----
|
|
181
206
|
createConfigTemplate();
|
|
207
|
+
console.log("\n🔧 Ensuring sketch-mcp-server is installed globally...\n");
|
|
208
|
+
ensureSketchMcpServer(deps);
|
|
209
|
+
|
|
182
210
|
console.log("\n🔧 Setting up OpenCode compatibility...\n");
|
|
183
211
|
const openCodeResult = setupOpenCode(deps);
|
|
184
212
|
|
|
@@ -78,6 +78,39 @@ AI 助手在处理页面生成需求时,必须按以下流程使用工具:
|
|
|
78
78
|
- 如需样式注入细节,额外调用 `get_style_injection_guide`
|
|
79
79
|
- 如需组合最佳实践,额外调用 `get_best_practices(scenario="...")`
|
|
80
80
|
|
|
81
|
+
### 步骤3.5:Sketch 文件解析(如有 .sketch 设计稿)
|
|
82
|
+
- **当用户提供 .sketch 文件时**,使用 `sketch-mcp-server`(独立 MCP Server,stdio 模式)解析,直接提取结构化数据,结合组件库架构推理组件方案,无需经过视觉模型和 `map_design_data`
|
|
83
|
+
- 工作流(4步直传):
|
|
84
|
+
1. **解析 Sketch 结构**:`loadSketchByPath` → `listPages` → `listNodesByPage(type="artboard")` → `getNodeInfo`/`getPageStructure(maxDepth=2)` 提取结构
|
|
85
|
+
2. **获取组件库架构**(复用步骤1结果):`get_architecture_overview` 的 `templatePatterns` 提供模板组合模式,`llmMappingHints` 提供常见错误修正
|
|
86
|
+
3. **LLM 推理组件范围**:从 Sketch 结构化数据推断画板名称→`pageName`、区域分布→`layoutType`、图层类型→组件映射,对照 `templatePatterns` 选择最匹配的模板,输出组件候选列表
|
|
87
|
+
4. **批量获取组件详情**:调用 `get_component_full_profile(name="Query,TableOrList,LeftTree,TemplateMode", depth="deep")` 一次性获取所有需要的组件 API
|
|
88
|
+
|
|
89
|
+
**Sketch → szcd 工作流(结构化直传,无需视觉模型)**:
|
|
90
|
+
```
|
|
91
|
+
.sketch → loadSketchByPath → listPages → listNodesByPage(type=artboard)
|
|
92
|
+
→ getNodeInfo/getPageStructure → [步骤1架构数据] → LLM推理组件
|
|
93
|
+
→ get_component_full_profile(批量) → 编码
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Sketch 结构 → 组件映射规则**:
|
|
97
|
+
| Sketch 特征 | 推断结果 | 依据 |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| 画板名称(如 "4.1-编目审核-待办") | `pageName` | 直接使用 |
|
|
100
|
+
| 左侧窄区域 + 树形图层 | `LeftTree` 组件 | templatePatterns.TreeQueryTable |
|
|
101
|
+
| 顶部输入框/下拉框/日期选择器 | `Query` 组件 | 搜索区域特征 |
|
|
102
|
+
| 中间表头 + 数据行图层 | `TableOrList` 组件 | 表格区域特征 |
|
|
103
|
+
| 弹窗/抽屉图层 | `ModelOrDrawer` 组件 | 弹窗交互特征 |
|
|
104
|
+
| 页面标题 + 返回箭头 | `TitleAndBack` 组件 | 标题栏特征 |
|
|
105
|
+
| 左右分区布局 | `TemplateMode(templateTpye="LeftRight")` | 区域分布 |
|
|
106
|
+
| 上下分区布局 | `TemplateMode(templateTpye="TopBottom")` | 区域分布 |
|
|
107
|
+
|
|
108
|
+
**提示**:
|
|
109
|
+
- 大型 .sketch 文件用 `getPageStructure(maxDepth=1-2)` 即可获取布局概况,避免深层递归超时
|
|
110
|
+
- `listNodesByPage(type="artboard")` 优先于 `getPageStructure`,更轻量
|
|
111
|
+
- 仅当结构化数据不足以判断视觉细节(颜色、间距、字体)时,才回退到 `renderNodeAsBase64` + `analyze_design_image`
|
|
112
|
+
- 如果 sketch-mcp-server 工具不可用,提示用户安装:`npm install -g sketch-mcp-server`
|
|
113
|
+
|
|
81
114
|
### 步骤4:设计稿分析(如有)
|
|
82
115
|
- 使用 `analyze_design_image` 分析设计稿,建议 `outputFormat="restoration_code"` 直接输出还原代码
|
|
83
116
|
- 使用 `get_style_injection_guide` 处理 CSS 分配
|
|
@@ -91,7 +124,54 @@ AI 助手在处理页面生成需求时,必须按以下流程使用工具:
|
|
|
91
124
|
- 若用户表示不准确或不采纳,收集原因
|
|
92
125
|
- 调用 `submit_feedback` 将反馈提交到服务器
|
|
93
126
|
|
|
94
|
-
## 可用工具(13
|
|
127
|
+
## 可用工具(13 + 17 Sketch)
|
|
128
|
+
|
|
129
|
+
### Sketch 文件解析工具(sketch-mcp-server,独立 MCP Server)
|
|
130
|
+
|
|
131
|
+
sketch-mcp-server 是社区版 MCP Server(npm: `sketch-mcp-server`),通过 stdio 模式运行,提供 .sketch 文件解析能力。
|
|
132
|
+
|
|
133
|
+
#### S1. loadSketchByPath
|
|
134
|
+
**功能**:从文件路径加载 .sketch 文件(本质是 ZIP 压缩包),自动解压解析。
|
|
135
|
+
**参数**:
|
|
136
|
+
- `path` (string, required): .sketch 文件的绝对路径
|
|
137
|
+
|
|
138
|
+
#### S2. listPages
|
|
139
|
+
**功能**:列出 .sketch 文件中所有页面,返回页面 ID、名称和图层计数。
|
|
140
|
+
**参数**:无必填参数
|
|
141
|
+
|
|
142
|
+
#### S3. getPageStructure
|
|
143
|
+
**功能**:获取页面的完整层级结构,包含 artboard、group、layer 等节点。
|
|
144
|
+
**参数**:
|
|
145
|
+
- `pageId` (string, required): 页面 ID(从 listPages 获取)
|
|
146
|
+
- `includeDetails` (boolean, optional, default: true): 是否包含详细信息
|
|
147
|
+
- `maxDepth` (number, optional, default: 10): 最大递归深度
|
|
148
|
+
|
|
149
|
+
#### S4. renderNodeAsBase64
|
|
150
|
+
**功能**:将节点渲染为 SVG 图片的 Base64 编码,可直接传入 analyze_design_image。
|
|
151
|
+
**参数**:
|
|
152
|
+
- `nodeId` (string, required): 节点 ID(artboard 级别效果最佳)
|
|
153
|
+
- `format` (enum, optional, default: "svg"): 输出格式,当前仅支持 svg
|
|
154
|
+
|
|
155
|
+
#### S5. getNodesSummary
|
|
156
|
+
**功能**:获取节点统计摘要,Token 消耗比完整信息减少 80-90%。
|
|
157
|
+
**参数**:
|
|
158
|
+
- `pageId` (string, optional): 页面 ID
|
|
159
|
+
- `groupBy` (enum, optional): type/style/position/size
|
|
160
|
+
- `includeStats` (boolean, optional, default: true)
|
|
161
|
+
|
|
162
|
+
#### S6. findNodesByName
|
|
163
|
+
**功能**:按名称搜索节点,支持模糊匹配。
|
|
164
|
+
**参数**:
|
|
165
|
+
- `name` (string, required): 搜索关键词
|
|
166
|
+
- `limit` (number, optional, default: 10): 最大返回数量
|
|
167
|
+
|
|
168
|
+
#### S7-S17. 其他 Sketch 工具
|
|
169
|
+
- `listNodes` / `listNodesByPage` — 列出节点(含过滤)
|
|
170
|
+
- `getNodeInfo` / `getMultipleNodeInfo` — 获取节点详情
|
|
171
|
+
- `getNodePosition` — 获取节点位置
|
|
172
|
+
- `getDocumentStructure` — 获取文档结构(支持字段过滤和摘要模式)
|
|
173
|
+
- `getSymbolMasters` / `getSymbolInstances` — Symbol 组件管理
|
|
174
|
+
- `getSymbolMasterBySymbolID` / `getSymbolInstanceStyles` — Symbol 详情
|
|
95
175
|
|
|
96
176
|
### 复合组件工具
|
|
97
177
|
|