@szc-ft/mcp-szcd-client 0.11.5 → 0.12.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/README.md +143 -0
- package/agents/build.js +331 -0
- package/agents/platforms.json +47 -0
- package/agents/qwen-extension/agents/szcd-component-expert.md +165 -0
- package/agents/src/szcd-component-expert.md +352 -0
- package/agents/src/tools.json +112 -0
- package/agents/szcd-component-expert.md +25 -19
- package/agents/szcd-component-expert.qoder.md +226 -0
- package/agents/szcd-component-expert.trae.md +113 -93
- package/commands/szcd-mcp-url.md +2 -2
- package/package.json +8 -5
- package/scripts/lib/claude-code.js +30 -30
- package/scripts/lib/qoder.js +43 -32
- package/scripts/lib/qwen-code.js +14 -14
- package/scripts/lib/trae-cli.js +22 -22
- package/scripts/lib/trae-ide.js +12 -12
- package/scripts/postinstall.js +4 -4
- package/skill/SKILL.md +288 -358
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# @szc-ft/mcp-szcd-client
|
|
2
|
+
|
|
3
|
+
szcd 组件库 MCP 客户端 — 自动配置 AI 编码工具的 MCP 服务器连接、Skills、Agents 和 Commands。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @szc-ft/mcp-szcd-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
安装后自动执行配置,支持以下 AI 编码工具:
|
|
12
|
+
|
|
13
|
+
| 工具 | 配置方式 |
|
|
14
|
+
|------|---------|
|
|
15
|
+
| Claude Code | `~/.claude.json` + `~/.claude/agents/` + `~/.claude/skills/` |
|
|
16
|
+
| Trae CLI | `~/.traecli/trae_cli.yaml` |
|
|
17
|
+
| Trae IDE | `~/.trae-cn/mcp.json` + `~/.trae-cn/agents/` |
|
|
18
|
+
| Qoder CLI | `~/.qoder/settings.json` + `~/.qoder/agents/` + `~/.qoder/skills/` |
|
|
19
|
+
| Qwen Code | `~/.qwen/extensions/szcd-component-helper/`(扩展机制) |
|
|
20
|
+
|
|
21
|
+
## 连接方式
|
|
22
|
+
|
|
23
|
+
### 方式1: 本地直连 (stdio)
|
|
24
|
+
|
|
25
|
+
无需远程服务器,每次启动时本地运行 MCP 服务器进程:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"szcd-component-helper": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["szcd-mcp-server", "--stdio"]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 方式2: Streamable HTTP 远程连接(推荐)
|
|
39
|
+
|
|
40
|
+
连接到远程运行的 MCP 服务器,多客户端共享:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"szcd-component-helper": {
|
|
46
|
+
"type": "http",
|
|
47
|
+
"url": "http://localhost:3456/mcp"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 方式3: SSE 远程连接(旧客户端向后兼容)
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"szcd-component-helper": {
|
|
59
|
+
"type": "sse",
|
|
60
|
+
"url": "http://localhost:3456/sse"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## CLI 命令
|
|
67
|
+
|
|
68
|
+
| 命令 | 功能 |
|
|
69
|
+
|------|------|
|
|
70
|
+
| `szcd-mcp-setup` | 运行安装配置(同 `npm run setup`) |
|
|
71
|
+
| `szcd-mcp-update-url [url]` | 更新 MCP 服务器地址并同步所有 IDE 配置 |
|
|
72
|
+
| `szcd-mcp-coding-config` | 配置 CODING 平台连接信息 |
|
|
73
|
+
|
|
74
|
+
## 智能体(Agent)
|
|
75
|
+
|
|
76
|
+
提供 `szcd-component-expert` 智能体,帮助开发者查询组件信息、匹配需求到组件、生成基于 szcd 组件库的 React 代码。
|
|
77
|
+
|
|
78
|
+
各工具自动获取对应详细度的版本:
|
|
79
|
+
|
|
80
|
+
| 工具 | Agent 版本 | 特点 |
|
|
81
|
+
|------|-----------|------|
|
|
82
|
+
| Claude Code / Qwen | base | 基础工作流,无工具名前缀 |
|
|
83
|
+
| Trae | enhanced | 增强工作流,`mcp__szcd-component-helper__` 工具名前缀 |
|
|
84
|
+
| Qoder | full | 完整工作流 + MCP 工具表 + AskUserQuestion 交互 |
|
|
85
|
+
|
|
86
|
+
### Agent 构建
|
|
87
|
+
|
|
88
|
+
Agent 配置采用单一源模板 + 条件标记 + 构建脚本的方案:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm run build:agents
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
修改 `agents/src/` 下的模板后运行构建,自动生成各平台适配文件。
|
|
95
|
+
|
|
96
|
+
## Skill
|
|
97
|
+
|
|
98
|
+
提供 `szcd-component-helper` Skill,包含 22 个 MCP 工具的使用说明。
|
|
99
|
+
|
|
100
|
+
## 项目结构
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
├── agents/
|
|
104
|
+
│ ├── src/ # 源模板(唯一编辑入口)
|
|
105
|
+
│ │ ├── szcd-component-expert.md
|
|
106
|
+
│ │ └── tools.json
|
|
107
|
+
│ ├── platforms.json # 平台适配配置
|
|
108
|
+
│ ├── build.js # 构建脚本
|
|
109
|
+
│ ├── szcd-component-expert.md # [生成] Claude / Qwen
|
|
110
|
+
│ ├── szcd-component-expert.trae.md # [生成] Trae
|
|
111
|
+
│ └── szcd-component-expert.qoder.md # [生成] Qoder
|
|
112
|
+
├── commands/ # 自定义命令
|
|
113
|
+
├── skill/ # Skill 定义
|
|
114
|
+
├── qwen-extension/ # Qwen Code 扩展包
|
|
115
|
+
├── scripts/
|
|
116
|
+
│ ├── postinstall.js # 安装入口
|
|
117
|
+
│ ├── update-mcp-url.js # URL 同步工具
|
|
118
|
+
│ ├── update-coding-config.js # CODING 配置工具
|
|
119
|
+
│ └── lib/ # 各平台适配模块
|
|
120
|
+
│ ├── claude-code.js
|
|
121
|
+
│ ├── trae-cli.js
|
|
122
|
+
│ ├── trae-ide.js
|
|
123
|
+
│ ├── qoder.js
|
|
124
|
+
│ ├── qwen-code.js
|
|
125
|
+
│ └── opencode.js
|
|
126
|
+
└── AGENTS.md # 项目级指令
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 配置文件
|
|
130
|
+
|
|
131
|
+
MCP 服务器地址保存在 `~/.szcd-mcp-config.json`:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"MCP_SERVER_URL": "http://localhost:3456",
|
|
136
|
+
"MCP_SERVER_NAME": "szcd-component-helper",
|
|
137
|
+
"MCP_TIMEOUT": 30000
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
package/agents/build.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* agents/build.js — 从源模板生成多平台智能体配置文件
|
|
5
|
+
*
|
|
6
|
+
* 用法: node agents/build.js
|
|
7
|
+
*
|
|
8
|
+
* 逻辑:
|
|
9
|
+
* 1. 读取 src/ 下的主模板和 tools.json
|
|
10
|
+
* 2. 读取 platforms.json 平台配置
|
|
11
|
+
* 3. 将模板整体(含 frontmatter)按 INCLUDE/EXCLUDE 过滤
|
|
12
|
+
* 4. 替换 {{TOOL:name}} 和 {{MCP_TOOL_TABLE}} 占位符
|
|
13
|
+
* 5. 写入目标文件
|
|
14
|
+
* 6. 同步到 mcp-server/agents/ 目录
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
23
|
+
|
|
24
|
+
const AGENTS_DIR = __dirname;
|
|
25
|
+
const SRC_DIR = path.join(AGENTS_DIR, "src");
|
|
26
|
+
|
|
27
|
+
// ==================== 工具函数 ====================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 内容级别层级:base < enhanced < full
|
|
31
|
+
*/
|
|
32
|
+
const LEVEL_HIERARCHY = ["base", "enhanced", "full"];
|
|
33
|
+
|
|
34
|
+
function getLevelIndex(level) {
|
|
35
|
+
const idx = LEVEL_HIERARCHY.indexOf(level);
|
|
36
|
+
if (idx === -1) throw new Error(`Unknown content level: ${level}`);
|
|
37
|
+
return idx;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 按内容级别过滤 INCLUDE/EXCLUDE 段
|
|
42
|
+
*
|
|
43
|
+
* 规则:
|
|
44
|
+
* - 无标记段:所有级别都包含
|
|
45
|
+
* - <!-- INCLUDE:X --> ... <!-- /INCLUDE:X -->: 仅 levelIndex >= X 的级别包含
|
|
46
|
+
* - <!-- EXCLUDE:X --> ... <!-- /EXCLUDE:X -->: 仅 levelIndex < X 的级别包含
|
|
47
|
+
*
|
|
48
|
+
* 同时处理 frontmatter 中的条件标记(如 <!-- EXCLUDE:enhanced -->tools: ["*"]<!-- /EXCLUDE:enhanced -->)
|
|
49
|
+
*/
|
|
50
|
+
function filterByContentLevel(content, contentLevel) {
|
|
51
|
+
const levelIndex = getLevelIndex(contentLevel);
|
|
52
|
+
const lines = content.split("\n");
|
|
53
|
+
const result = [];
|
|
54
|
+
let skipStack = []; // { type: 'INCLUDE'|'EXCLUDE', levelIndex }
|
|
55
|
+
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
// 检查行内标记(前后在同一行)
|
|
58
|
+
const inlineInclude = line.match(/^(.*)<!--\s*INCLUDE:(\w+)\s*-->(.*?)<!--\s*\/INCLUDE:\2\s*-->(.*)$/);
|
|
59
|
+
const inlineExclude = line.match(/^(.*)<!--\s*EXCLUDE:(\w+)\s*-->(.*?)<!--\s*\/EXCLUDE:\2\s*-->(.*)$/);
|
|
60
|
+
|
|
61
|
+
if (inlineInclude) {
|
|
62
|
+
const [, before, lvl, inner, after] = inlineInclude;
|
|
63
|
+
const lvlIdx = getLevelIndex(lvl);
|
|
64
|
+
if (levelIndex >= lvlIdx) {
|
|
65
|
+
result.push(before + inner + after);
|
|
66
|
+
} else {
|
|
67
|
+
result.push(before + after);
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (inlineExclude) {
|
|
73
|
+
const [, before, lvl, inner, after] = inlineExclude;
|
|
74
|
+
const lvlIdx = getLevelIndex(lvl);
|
|
75
|
+
if (levelIndex < lvlIdx) {
|
|
76
|
+
result.push(before + inner + after);
|
|
77
|
+
} else {
|
|
78
|
+
result.push(before + after);
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 检查块级开始标记
|
|
84
|
+
const includeStart = line.match(/<!--\s*INCLUDE:(\w+)\s*-->/);
|
|
85
|
+
const excludeStart = line.match(/<!--\s*EXCLUDE:(\w+)\s*-->/);
|
|
86
|
+
const includeEnd = line.match(/<!--\s*\/INCLUDE:(\w+)\s*-->/);
|
|
87
|
+
const excludeEnd = line.match(/<!--\s*\/EXCLUDE:(\w+)\s*-->/);
|
|
88
|
+
|
|
89
|
+
if (includeStart) {
|
|
90
|
+
const lvlIdx = getLevelIndex(includeStart[1]);
|
|
91
|
+
skipStack.push({ type: "INCLUDE", levelIndex: lvlIdx });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (excludeStart) {
|
|
96
|
+
const lvlIdx = getLevelIndex(excludeStart[1]);
|
|
97
|
+
skipStack.push({ type: "EXCLUDE", levelIndex: lvlIdx });
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (includeEnd || excludeEnd) {
|
|
102
|
+
skipStack.pop();
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 判断当前行是否应该跳过
|
|
107
|
+
let shouldSkip = false;
|
|
108
|
+
for (const rule of skipStack) {
|
|
109
|
+
if (rule.type === "INCLUDE" && levelIndex < rule.levelIndex) {
|
|
110
|
+
shouldSkip = true;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (rule.type === "EXCLUDE" && levelIndex >= rule.levelIndex) {
|
|
114
|
+
shouldSkip = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!shouldSkip) {
|
|
120
|
+
result.push(line);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result.join("\n");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 替换 {{TOOL:name}} 占位符
|
|
129
|
+
*/
|
|
130
|
+
function replaceToolPlaceholders(content, toolPrefix) {
|
|
131
|
+
return content.replace(/\{\{TOOL:(\w+)\}\}/g, (_match, toolName) => {
|
|
132
|
+
return toolPrefix ? `${toolPrefix}${toolName}` : toolName;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 从 tools.json 生成 MCP 工具表格
|
|
138
|
+
* 按功能分组,同一 whenToUse 的工具合并在一行
|
|
139
|
+
*/
|
|
140
|
+
function generateMcpToolTable(tools, toolPrefix) {
|
|
141
|
+
const rows = [];
|
|
142
|
+
const grouped = new Map();
|
|
143
|
+
|
|
144
|
+
for (const tool of tools) {
|
|
145
|
+
const key = tool.whenToUse;
|
|
146
|
+
if (!grouped.has(key)) {
|
|
147
|
+
grouped.set(key, []);
|
|
148
|
+
}
|
|
149
|
+
grouped.get(key).push(tool);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const [, group] of grouped) {
|
|
153
|
+
const names = group.map((t) => {
|
|
154
|
+
const fullName = toolPrefix ? `${toolPrefix}${t.name}` : t.name;
|
|
155
|
+
return `\`${fullName}\``;
|
|
156
|
+
});
|
|
157
|
+
rows.push(`| ${names.join(" / ")} | ${group[0].description} | ${group[0].whenToUse} |`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const header = "| 工具名 | 功能 | 何时使用 |\n|--------|------|---------|";
|
|
161
|
+
return header + "\n" + rows.join("\n");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 从 frontmatter 中移除指定字段
|
|
166
|
+
*/
|
|
167
|
+
function stripFrontmatterFields(content, fields) {
|
|
168
|
+
const fmMatch = content.match(/^(---\n)([\s\S]*?)(\n---)/);
|
|
169
|
+
if (!fmMatch) return content;
|
|
170
|
+
|
|
171
|
+
const prefix = fmMatch[1];
|
|
172
|
+
let fmBody = fmMatch[2];
|
|
173
|
+
const suffix = fmMatch[3];
|
|
174
|
+
|
|
175
|
+
for (const field of fields) {
|
|
176
|
+
// 匹配字段行(支持简单值、多行值、YAML列表等)
|
|
177
|
+
// 简单行:tools: ["*"]
|
|
178
|
+
// YAML列表:tools:\n - "*"
|
|
179
|
+
const simpleLineRegex = new RegExp(`^${field}:.*$\\n?`, "m");
|
|
180
|
+
fmBody = fmBody.replace(simpleLineRegex, "");
|
|
181
|
+
|
|
182
|
+
const yamlListRegex = new RegExp(`^${field}:\\s*\\n(?:^\\s+.*$\\n?)*`, "m");
|
|
183
|
+
fmBody = fmBody.replace(yamlListRegex, "");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 清理空行
|
|
187
|
+
fmBody = fmBody.replace(/\n{2,}/g, "\n");
|
|
188
|
+
|
|
189
|
+
return prefix + fmBody + suffix + content.slice(fmMatch[0].length);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 清理输出内容中的多余空行和残留空行
|
|
194
|
+
*/
|
|
195
|
+
function cleanupContent(content) {
|
|
196
|
+
// 移除连续3个以上空行 → 2个空行
|
|
197
|
+
let result = content.replace(/\n{4,}/g, "\n\n\n");
|
|
198
|
+
// 移除 frontmatter 后多余空行(--- 后最多1个空行)
|
|
199
|
+
result = result.replace(/(---\n)\n{2,}/g, "$1\n");
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ==================== 主构建逻辑 ====================
|
|
204
|
+
|
|
205
|
+
function build() {
|
|
206
|
+
console.log("🔧 Building agent configurations...\n");
|
|
207
|
+
|
|
208
|
+
// 1. 读取源文件
|
|
209
|
+
const templatePath = path.join(SRC_DIR, "szcd-component-expert.md");
|
|
210
|
+
const toolsPath = path.join(SRC_DIR, "tools.json");
|
|
211
|
+
const platformsPath = path.join(AGENTS_DIR, "platforms.json");
|
|
212
|
+
|
|
213
|
+
const templateContent = fs.readFileSync(templatePath, "utf8");
|
|
214
|
+
const tools = JSON.parse(fs.readFileSync(toolsPath, "utf8"));
|
|
215
|
+
const platformsConfig = JSON.parse(fs.readFileSync(platformsPath, "utf8"));
|
|
216
|
+
|
|
217
|
+
// 2. 按平台生成
|
|
218
|
+
for (const [platformName, platformConfig] of Object.entries(platformsConfig.platforms)) {
|
|
219
|
+
console.log(` Generating for ${platformName} (level: ${platformConfig.contentLevel})...`);
|
|
220
|
+
|
|
221
|
+
// 对整个模板内容(含 frontmatter)进行条件过滤
|
|
222
|
+
let filtered = filterByContentLevel(templateContent, platformConfig.contentLevel);
|
|
223
|
+
|
|
224
|
+
// 剥离 frontmatter 中指定字段(如 Qwen 不需要 tools/model/type)
|
|
225
|
+
if (platformConfig.stripFrontmatterFields && platformConfig.stripFrontmatterFields.length > 0) {
|
|
226
|
+
filtered = stripFrontmatterFields(filtered, platformConfig.stripFrontmatterFields);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 替换 {{TOOL:}} 占位符
|
|
230
|
+
filtered = replaceToolPlaceholders(filtered, platformConfig.toolPrefix);
|
|
231
|
+
|
|
232
|
+
// 替换 {{MCP_TOOL_TABLE}}
|
|
233
|
+
if (filtered.includes("{{MCP_TOOL_TABLE}}")) {
|
|
234
|
+
const table = generateMcpToolTable(tools, platformConfig.toolPrefix);
|
|
235
|
+
filtered = filtered.replace("{{MCP_TOOL_TABLE}}", table);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 清理
|
|
239
|
+
const output = cleanupContent(filtered);
|
|
240
|
+
|
|
241
|
+
// 确定输出路径
|
|
242
|
+
const outputDir = platformConfig.outputDir
|
|
243
|
+
? path.join(AGENTS_DIR, platformConfig.outputDir)
|
|
244
|
+
: AGENTS_DIR;
|
|
245
|
+
const outputPath = path.join(outputDir, platformConfig.outputFile);
|
|
246
|
+
|
|
247
|
+
if (!fs.existsSync(outputDir)) {
|
|
248
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fs.writeFileSync(outputPath, output);
|
|
252
|
+
console.log(` ✓ Written: ${path.relative(AGENTS_DIR, outputPath)}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 3. 同步到 mcp-server/agents/(同级 mcp/szcd-mcp-server 目录)
|
|
256
|
+
const mcpServerAgentsDir = path.join(AGENTS_DIR, "..", "..", "szcd-mcp-server", "agents");
|
|
257
|
+
if (fs.existsSync(path.dirname(mcpServerAgentsDir))) {
|
|
258
|
+
if (!fs.existsSync(mcpServerAgentsDir)) {
|
|
259
|
+
fs.mkdirSync(mcpServerAgentsDir, { recursive: true });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const outputFiles = [
|
|
263
|
+
"szcd-component-expert.md",
|
|
264
|
+
"szcd-component-expert.trae.md",
|
|
265
|
+
"szcd-component-expert.qoder.md",
|
|
266
|
+
];
|
|
267
|
+
for (const file of outputFiles) {
|
|
268
|
+
const src = path.join(AGENTS_DIR, file);
|
|
269
|
+
const dest = path.join(mcpServerAgentsDir, file);
|
|
270
|
+
if (fs.existsSync(src)) {
|
|
271
|
+
fs.copyFileSync(src, dest);
|
|
272
|
+
console.log(` ✓ Synced to mcp-server: ${file}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 4. 同步到 qwen-extension/agents/(供 Qwen Code 扩展安装使用)
|
|
278
|
+
const qwenExtDir = path.join(AGENTS_DIR, "..", "qwen-extension");
|
|
279
|
+
const qwenGeneratedAgent = path.join(AGENTS_DIR, "qwen-extension", "agents", "szcd-component-expert.md");
|
|
280
|
+
if (fs.existsSync(qwenGeneratedAgent)) {
|
|
281
|
+
const qwenExtAgentsDir = path.join(qwenExtDir, "agents");
|
|
282
|
+
if (!fs.existsSync(qwenExtAgentsDir)) {
|
|
283
|
+
fs.mkdirSync(qwenExtAgentsDir, { recursive: true });
|
|
284
|
+
}
|
|
285
|
+
fs.copyFileSync(qwenGeneratedAgent, path.join(qwenExtAgentsDir, "szcd-component-expert.md"));
|
|
286
|
+
console.log(` ✓ Synced to qwen-extension/agents/`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 5. 同步 SKILL.md 和 commands 到 qwen-extension/
|
|
290
|
+
const packageRoot = path.join(AGENTS_DIR, "..");
|
|
291
|
+
|
|
292
|
+
// 同步 SKILL.md
|
|
293
|
+
const skillSource = path.join(packageRoot, "skill", "SKILL.md");
|
|
294
|
+
const skillDest = path.join(qwenExtDir, "skills", "szcd-component-helper", "SKILL.md");
|
|
295
|
+
if (fs.existsSync(skillSource)) {
|
|
296
|
+
if (!fs.existsSync(path.dirname(skillDest))) {
|
|
297
|
+
fs.mkdirSync(path.dirname(skillDest), { recursive: true });
|
|
298
|
+
}
|
|
299
|
+
fs.copyFileSync(skillSource, skillDest);
|
|
300
|
+
console.log(` ✓ Synced to qwen-extension/skills/`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 同步 commands
|
|
304
|
+
const commandsSourceDir = path.join(packageRoot, "commands");
|
|
305
|
+
const commandsDestDir = path.join(qwenExtDir, "commands");
|
|
306
|
+
if (fs.existsSync(commandsSourceDir)) {
|
|
307
|
+
if (!fs.existsSync(commandsDestDir)) {
|
|
308
|
+
fs.mkdirSync(commandsDestDir, { recursive: true });
|
|
309
|
+
}
|
|
310
|
+
const cmdFiles = fs.readdirSync(commandsSourceDir).filter((f) => f.endsWith(".md"));
|
|
311
|
+
for (const cmdFile of cmdFiles) {
|
|
312
|
+
fs.copyFileSync(
|
|
313
|
+
path.join(commandsSourceDir, cmdFile),
|
|
314
|
+
path.join(commandsDestDir, cmdFile),
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
if (cmdFiles.length > 0) {
|
|
318
|
+
console.log(` ✓ Synced ${cmdFiles.length} command(s) to qwen-extension/commands/`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
console.log("\n✅ Agent build complete!");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
build();
|
|
327
|
+
} catch (error) {
|
|
328
|
+
console.error("❌ Build failed:", error.message);
|
|
329
|
+
console.error(error.stack);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServerName": "szcd-component-helper",
|
|
3
|
+
"platforms": {
|
|
4
|
+
"claude": {
|
|
5
|
+
"outputFile": "szcd-component-expert.md",
|
|
6
|
+
"contentLevel": "base",
|
|
7
|
+
"frontmatter": {
|
|
8
|
+
"name": "{{name}}",
|
|
9
|
+
"description": "{{description}}",
|
|
10
|
+
"tools": "[\"*\"]",
|
|
11
|
+
"model": "sonnet",
|
|
12
|
+
"type": "agent"
|
|
13
|
+
},
|
|
14
|
+
"toolPrefix": ""
|
|
15
|
+
},
|
|
16
|
+
"trae": {
|
|
17
|
+
"outputFile": "szcd-component-expert.trae.md",
|
|
18
|
+
"contentLevel": "enhanced",
|
|
19
|
+
"frontmatter": {
|
|
20
|
+
"name": "{{name}}",
|
|
21
|
+
"tools": "- \"*\"",
|
|
22
|
+
"description": "{{description}}"
|
|
23
|
+
},
|
|
24
|
+
"toolPrefix": "mcp__szcd-component-helper__"
|
|
25
|
+
},
|
|
26
|
+
"qoder": {
|
|
27
|
+
"outputFile": "szcd-component-expert.qoder.md",
|
|
28
|
+
"contentLevel": "full",
|
|
29
|
+
"frontmatter": {
|
|
30
|
+
"name": "{{name}}",
|
|
31
|
+
"description": "{{description}}"
|
|
32
|
+
},
|
|
33
|
+
"toolPrefix": ""
|
|
34
|
+
},
|
|
35
|
+
"qwen": {
|
|
36
|
+
"outputFile": "szcd-component-expert.md",
|
|
37
|
+
"outputDir": "qwen-extension/agents",
|
|
38
|
+
"contentLevel": "base",
|
|
39
|
+
"frontmatter": {
|
|
40
|
+
"name": "{{name}}",
|
|
41
|
+
"description": "{{description}}"
|
|
42
|
+
},
|
|
43
|
+
"stripFrontmatterFields": ["tools", "model", "type"],
|
|
44
|
+
"toolPrefix": ""
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: szcd-component-expert
|
|
3
|
+
description: |
|
|
4
|
+
szcd 组件库专家,用于查询组件信息、匹配需求到组件、生成基于 szcd 组件库的 React 代码。
|
|
5
|
+
当用户需要使用 @szc-ft/szcd 组件库开发页面、选择组件、查询组件 API、根据设计稿生成代码时,应使用此 agent。
|
|
6
|
+
典型触发场景:
|
|
7
|
+
- 用户想查询 szcd 组件库中有哪些组件
|
|
8
|
+
- 用户想获取某个组件的 Props 和使用示例
|
|
9
|
+
- 用户提供了设计稿截图/描述,要求生成页面代码
|
|
10
|
+
- 用户描述页面需求(如"做一个左侧树+右侧表格的页面"),要求生成代码
|
|
11
|
+
- 用户提到"根据设计稿生成页面"、"设计稿转代码"、"需求转组件"等关键词
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# szcd-component-expert: szcd 组件库专家
|
|
16
|
+
|
|
17
|
+
## 角色定位
|
|
18
|
+
|
|
19
|
+
你是 szcd 组件库(@szc-ft/szcd)的专家助手。你的职责是帮助开发者快速找到合适的组件并生成可直接使用的代码。
|
|
20
|
+
|
|
21
|
+
szcd 是基于 Ant Design 5.27 封装的企业级 React 组件库,采用分层架构:
|
|
22
|
+
|
|
23
|
+
| 层级 | 路径 | 说明 |
|
|
24
|
+
|------|------|------|
|
|
25
|
+
| Cover 层 | src/components/cover/ | 对 antd 组件的样式和交互定制 |
|
|
26
|
+
| Wrapper 层 | src/components/wrappers/ | 封装业务逻辑的包装组件 |
|
|
27
|
+
| 复合组件 | src/other/components/ | 11 个高级业务组件(Query/LeftTree/TableOrList 等) |
|
|
28
|
+
| ProPackages | pro-packages/ | ProComponents 封装 |
|
|
29
|
+
| 模板组件 | mode-packages/ | 页面级模板(LeftTree+Query+Table 等) |
|
|
30
|
+
|
|
31
|
+
**关键原则**: 能用复合组件就用复合组件,能用 Wrapper 就不用 Cover,能用 Cover 就不直接用 Ant Design。
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## 完整工作流程(页面生成类任务)
|
|
35
|
+
|
|
36
|
+
### 步骤1:架构认知 + 工具自检(必做)
|
|
37
|
+
|
|
38
|
+
**在正式开始之前,获取项目架构全局视图,同时验证 MCP 连接。**
|
|
39
|
+
|
|
40
|
+
执行动作:
|
|
41
|
+
1. 调用 `get_architecture_overview`(detail="summary")获取7层架构图、模板组合模式、推荐使用顺序和 LLM 映射错误提示
|
|
42
|
+
2. 如果连接失败,**立即停止并返回错误信息**,告知用户需要先配置 MCP 服务器
|
|
43
|
+
3. 如果成功,根据返回的 `templatePatterns` 初步判断用户需求匹配哪个模板
|
|
44
|
+
|
|
45
|
+
- 如果仍失败,说明当前环境没有配置 szcd-component-helper MCP 服务器,请直接告知用户需要先配置 MCP 服务器
|
|
46
|
+
- **不要假设工具一定存在,务必先验证再使用**
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### 步骤2:理解需求(必做)
|
|
50
|
+
|
|
51
|
+
分析用户要实现的页面或功能,梳理以下维度:
|
|
52
|
+
|
|
53
|
+
1. **页面布局类型**: 左右布局 / 上下布局 / 表单页 / 详情页
|
|
54
|
+
2. **查询条件**: 有哪些搜索字段,每个字段的类型
|
|
55
|
+
3. **操作按钮**: 新增/编辑/删除/批量操作等
|
|
56
|
+
4. **表格列**: 列名、数据类型、是否可排序/筛选
|
|
57
|
+
5. **左侧树**: 是否需要,树的数据来源
|
|
58
|
+
6. **弹窗/抽屉**: 新增/编辑用的弹窗或抽屉
|
|
59
|
+
|
|
60
|
+
**交互节点**:如果用户描述的信息不够明确,**必须主动向用户追问**缺失的关键信息,不要自行假设:
|
|
61
|
+
- 信息不足时,列出需要确认的问题,逐项向用户提问
|
|
62
|
+
- 布局类型不明确时,询问"页面是左右布局还是上下布局?"并提供选项
|
|
63
|
+
- 查询字段不完整时,询问"还需要哪些搜索条件?"
|
|
64
|
+
- 缺少交互细节时,询问"编辑操作用弹窗还是抽屉?"并提供选项
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### 步骤3:分析设计稿(条件执行)
|
|
68
|
+
|
|
69
|
+
当用户提供了设计稿图片时:
|
|
70
|
+
- 如果**主代理已提供图片描述**(设计稿已被解读为文字),直接使用该描述,跳过此步
|
|
71
|
+
- 如果图片未被解读,调用 `analyze_design_image` 工具分析
|
|
72
|
+
- 如果该工具不可用,告知用户需要提供设计稿的文字描述
|
|
73
|
+
|
|
74
|
+
- 注意:OCR 降级模式下仅返回原始文字,需结合组件库工具自行映射
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### 步骤4:查询组件 + 依赖验证(必做)
|
|
78
|
+
|
|
79
|
+
**这一步是核心,必须实际调用 MCP 工具查询,禁止凭记忆猜测 API。**
|
|
80
|
+
|
|
81
|
+
使用 MCP 工具获取组件信息和 API:
|
|
82
|
+
|
|
83
|
+
- `get_component_library_overview` — 获取全部组件概览(含功能描述和适用场景)
|
|
84
|
+
- `search_all_components` — 按关键词搜索组件
|
|
85
|
+
- `get_other_component` / `get_cover_component` — 获取组件详情和 Props
|
|
86
|
+
- `get_accurate_component_doc` — 获取复合组件的透传机制
|
|
87
|
+
- `search_component_examples` — 搜索组件使用示例
|
|
88
|
+
- `read_file` — 读取组件源码(必要时)
|
|
89
|
+
|
|
90
|
+
**映射规则**:
|
|
91
|
+
- 页面布局 → `TemplateMode`
|
|
92
|
+
- 查询表单 → `Query`(通过 config 配置字段)
|
|
93
|
+
- 数据表格 → `TableOrList`
|
|
94
|
+
- 左侧树 → `LeftTree`
|
|
95
|
+
- 弹窗/抽屉 → `ModelOrDrawer`
|
|
96
|
+
- 返回标题 → `TitleAndBack`
|
|
97
|
+
- 按钮 → Cover 层 `Button`
|
|
98
|
+
- 输入框 → Cover 层 `Input` 或 Query 的 valueType: 'input'
|
|
99
|
+
- 下拉选择 → Cover 层 `Select` 或 Query 的 valueType: 'select'
|
|
100
|
+
- 日期选择 → Cover 层 `DatePicker` 或 Query 的 valueType: 'datePicker'
|
|
101
|
+
- 树选择 → Query 的 valueType: 'treeSelect'
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
### 步骤5:匹配组件并确认方案(必做)
|
|
105
|
+
|
|
106
|
+
根据需求描述和组件的 useCases/description 匹配最合适的组件,形成组件方案。
|
|
107
|
+
|
|
108
|
+
**交互节点**:向用户展示匹配结果,**必须确认方案后再生成代码**:
|
|
109
|
+
- 列出选用的组件及理由、页面布局结构、备选方案
|
|
110
|
+
- 询问用户是否同意此方案,或有调整需求
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
### 步骤6:生成代码(必做)
|
|
114
|
+
|
|
115
|
+
产出可直接运行的 React 代码,遵循项目规范:
|
|
116
|
+
|
|
117
|
+
- 使用 React 18 + TypeScript
|
|
118
|
+
- 从 `@szc-ft/szcd` 导入组件
|
|
119
|
+
- 遵循 antd 5.27 的 API 用法
|
|
120
|
+
- 4 空格缩进,双引号,末尾逗号
|
|
121
|
+
- ProTable/ProForm 等使用 valueType 配置列/字段类型
|
|
122
|
+
- 复合组件配合 TemplateMode 使用实现标准页面布局
|
|
123
|
+
|
|
124
|
+
**交互节点**:代码生成后,主动询问用户是否需要调整:
|
|
125
|
+
- "代码已生成,是否需要调整样式或交互细节?"
|
|
126
|
+
- "是否需要补充表单校验规则?"
|
|
127
|
+
- "是否需要添加批量操作功能?"
|
|
128
|
+
|
|
129
|
+
## 典型页面模式
|
|
130
|
+
|
|
131
|
+
### 模式1: 左右布局 + 查询 + 表格(最常见)
|
|
132
|
+
```
|
|
133
|
+
TemplateMode(templateTpye="LeftRight")
|
|
134
|
+
├── LeftContent: LeftTree 组件
|
|
135
|
+
├── Query: Query 组件 + 操作按钮
|
|
136
|
+
└── PageContent: TableOrList 组件
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 模式2: 上下布局 + 查询 + 表格
|
|
140
|
+
```
|
|
141
|
+
TemplateMode(templateTpye="TopBottom")
|
|
142
|
+
├── Query: Query 组件 + 操作按钮
|
|
143
|
+
└── PageContent: TableOrList 组件
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 模式3: 纯表格页
|
|
147
|
+
```
|
|
148
|
+
TemplateMode(templateTpye="TopBottom")
|
|
149
|
+
└── PageContent: TableOrList 组件
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 模式4: 表单页
|
|
153
|
+
```
|
|
154
|
+
ProForm / ProFormWrapper
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 关键复合组件速查
|
|
158
|
+
|
|
159
|
+
- **Query**: 查询面板,支持 antd type 和 Pro valueType 双模式
|
|
160
|
+
- **TableOrList**: 内容区,7种模式(ProTable/EditableProTable/DragSortTable/ProList 等)
|
|
161
|
+
- **LeftTree**: 左侧树,内置数据请求和搜索
|
|
162
|
+
- **FormItemOrDetailItem**: 表单/详情项动态渲染
|
|
163
|
+
- **ModelOrDrawer**: 弹窗/抽屉交互层
|
|
164
|
+
- **TemplateMode**: 模板容器,预设 LeftRight/UpDown/TreeQueryTable 等布局
|
|
165
|
+
|