@wot-ui/cli 0.0.1-beta.4 → 0.0.1-beta.6
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/LICENSE.md +1 -1
- package/README.md +133 -35
- package/dist/index.mjs +49 -44
- package/dist/{scanner-BFHnD5iE.mjs → scanner-D0vLA-5K.mjs} +16 -12
- package/dist/{server-HjZltXBO.mjs → server-BWgY0XHJ.mjs} +18 -18
- package/package.json +38 -6
- package/README.zh-CN.md +0 -74
- package/skills/wot-ui/SKILL.md +0 -18
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026-PRESENT Wot UI
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
#
|
|
1
|
+
# open-wot
|
|
2
2
|
|
|
3
|
-
`@wot-ui/cli
|
|
3
|
+
open-wot 是 wot-ui 的 AI 工具链仓库,当前对外发布的核心包为 `@wot-ui/cli`。它提供命令行工具、MCP Server、离线组件知识库与数据提取脚本,用于把 wot-ui v2 的组件知识接入编辑器、AI Agent 和本地工程分析流程。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 仓库定位
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- 面向 wot-ui v2 的组件知识查询工具
|
|
8
|
+
- 面向本地项目的组件使用分析与 lint 工具
|
|
9
|
+
- 面向 AI 客户端的 MCP stdio 服务
|
|
10
|
+
- 面向仓库维护者的数据提取与同步工作流
|
|
11
|
+
|
|
12
|
+
## 核心能力
|
|
13
|
+
|
|
14
|
+
- 组件知识查询:`list`、`info`、`doc`、`demo`、`token`、`changelog`
|
|
8
15
|
- 项目分析:`doctor`、`usage`、`lint`
|
|
9
|
-
- MCP
|
|
10
|
-
-
|
|
16
|
+
- MCP Server:`wot mcp`
|
|
17
|
+
- 元数据提取:从 `wot-ui/wot-ui` 源码生成本地 `v2.json`
|
|
11
18
|
|
|
12
19
|
## 安装
|
|
13
20
|
|
|
@@ -15,37 +22,49 @@
|
|
|
15
22
|
npm install -g @wot-ui/cli
|
|
16
23
|
```
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
安装完成后可直接使用 `wot` 命令。
|
|
26
|
+
|
|
27
|
+
如果你在仓库内本地调试,推荐直接运行源码入口,而不是依赖全局命令:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm exec tsx src/index.ts list
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 快速开始
|
|
19
34
|
|
|
20
35
|
```bash
|
|
21
36
|
wot list
|
|
22
37
|
wot info Button
|
|
23
|
-
wot doc Button
|
|
24
38
|
wot demo Button basic
|
|
39
|
+
wot doc Button
|
|
25
40
|
wot token Button
|
|
26
41
|
wot changelog
|
|
27
|
-
wot doctor ./my-
|
|
28
|
-
wot usage ./my-
|
|
29
|
-
wot lint ./my-
|
|
42
|
+
wot doctor ./my-project
|
|
43
|
+
wot usage ./my-project
|
|
44
|
+
wot lint ./my-project
|
|
30
45
|
wot mcp
|
|
31
46
|
```
|
|
32
47
|
|
|
33
48
|
## 命令说明
|
|
34
49
|
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
### 组件知识
|
|
51
|
+
|
|
52
|
+
- `wot list`:列出可用的 wot-ui 组件
|
|
53
|
+
- `wot info <Component>`:查看组件 props、events、slots、CSS 变量
|
|
37
54
|
- `wot doc <Component>`:输出组件 markdown 文档
|
|
38
|
-
- `wot demo <Component> [name]
|
|
39
|
-
- `wot token [Component]
|
|
55
|
+
- `wot demo <Component> [name]`:查看 demo 列表或指定 demo 源码
|
|
56
|
+
- `wot token [Component]`:查看组件 CSS 变量与默认值
|
|
40
57
|
- `wot changelog [version] [component]`:查看版本更新记录
|
|
41
|
-
- `wot doctor [dir]`:检查工程环境与依赖
|
|
42
|
-
- `wot usage [dir]`:统计 `.vue` 中的 `wd-*` 使用情况
|
|
43
|
-
- `wot lint [dir]`:运行 wot-ui 相关规则检查
|
|
44
|
-
- `wot mcp`:启动 MCP stdio Server
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
### 项目分析
|
|
60
|
+
|
|
61
|
+
- `wot doctor [dir]`:检查项目依赖、运行环境与基础集成情况
|
|
62
|
+
- `wot usage [dir]`:统计 `.vue` 文件中的 `wd-*` 使用情况
|
|
63
|
+
- `wot lint [dir]`:检查未知组件、空按钮等规则
|
|
47
64
|
|
|
48
|
-
|
|
65
|
+
### 通用参数
|
|
66
|
+
|
|
67
|
+
多数查询命令支持以下参数:
|
|
49
68
|
|
|
50
69
|
- `--format text`
|
|
51
70
|
- `--format json`
|
|
@@ -53,41 +72,120 @@ wot mcp
|
|
|
53
72
|
- `--lang en`
|
|
54
73
|
- `--version v2`
|
|
55
74
|
|
|
56
|
-
##
|
|
75
|
+
## MCP 集成
|
|
76
|
+
|
|
77
|
+
将以下配置加入支持 MCP 的客户端:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"wot-ui": {
|
|
83
|
+
"command": "wot",
|
|
84
|
+
"args": ["mcp"]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
当前 MCP Server 提供以下 tools:
|
|
91
|
+
|
|
92
|
+
- `wot_list`
|
|
93
|
+
- `wot_info`
|
|
94
|
+
- `wot_doc`
|
|
95
|
+
- `wot_demo`
|
|
96
|
+
- `wot_token`
|
|
97
|
+
- `wot_changelog`
|
|
98
|
+
- `wot_lint`
|
|
99
|
+
|
|
100
|
+
## 数据来源
|
|
101
|
+
|
|
102
|
+
当前版本聚焦 `wot-ui v2`。仓库内的离线数据来自 `wot-ui/wot-ui` 源码,主要提取自:
|
|
103
|
+
|
|
104
|
+
- `docs/component/*.md`
|
|
105
|
+
- `docs/guide/changelog.md`
|
|
106
|
+
- `src/uni_modules/wot-ui/components/*/index.scss`
|
|
107
|
+
|
|
108
|
+
重新生成本地数据有两种方式。
|
|
57
109
|
|
|
58
110
|
使用本地已有的 wot-ui 仓库:
|
|
59
111
|
|
|
60
112
|
```bash
|
|
61
|
-
pnpm extract --wot-dir ../wot-ui --output
|
|
113
|
+
pnpm extract:cli --wot-dir ../wot-ui --output data/v2.json
|
|
62
114
|
```
|
|
63
115
|
|
|
64
|
-
|
|
116
|
+
直接克隆最新的 wot-ui 仓库并提取:
|
|
65
117
|
|
|
66
118
|
```bash
|
|
67
119
|
pnpm extract:clone
|
|
68
120
|
```
|
|
69
121
|
|
|
70
|
-
|
|
122
|
+
## 开发本仓库
|
|
123
|
+
|
|
124
|
+
当前根目录就是主发布包,核心源码位于 `src`,离线数据位于 `data`,提取脚本位于 `scripts`。
|
|
71
125
|
|
|
72
|
-
|
|
126
|
+
### 环境要求
|
|
73
127
|
|
|
74
|
-
|
|
128
|
+
- Node.js `>= 20`
|
|
129
|
+
- pnpm `10.x`
|
|
130
|
+
|
|
131
|
+
### 安装依赖
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pnpm install
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 常用开发命令
|
|
75
138
|
|
|
76
139
|
```bash
|
|
77
|
-
pnpm
|
|
78
|
-
pnpm
|
|
140
|
+
pnpm lint
|
|
141
|
+
pnpm test:all
|
|
142
|
+
pnpm build:all
|
|
143
|
+
pnpm typecheck:all
|
|
144
|
+
pnpm build:cli
|
|
145
|
+
pnpm test:cli
|
|
146
|
+
pnpm typecheck:cli
|
|
79
147
|
```
|
|
80
148
|
|
|
81
|
-
|
|
149
|
+
### 本地调试 CLI
|
|
150
|
+
|
|
151
|
+
直接运行源码入口最方便:
|
|
82
152
|
|
|
83
153
|
```bash
|
|
84
|
-
pnpm
|
|
85
|
-
pnpm
|
|
86
|
-
pnpm --filter @wot-ui/cli typecheck
|
|
154
|
+
pnpm exec tsx src/index.ts list
|
|
155
|
+
pnpm exec tsx src/index.ts info Button
|
|
87
156
|
```
|
|
88
157
|
|
|
158
|
+
如果要调试构建产物:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pnpm build:cli
|
|
162
|
+
node dist/index.mjs list
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 本地调试 MCP
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
pnpm exec tsx src/index.ts mcp
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
MCP 走 stdio,终端无交互输出属于正常现象。若要查看 tools 与 prompts 的调用过程,建议配合 MCP Inspector 或编辑器内置 MCP 客户端调试。
|
|
172
|
+
|
|
173
|
+
## 自动化流程
|
|
174
|
+
|
|
175
|
+
- `.github/workflows/ci.yml`:执行主包的 lint、typecheck、build、test
|
|
176
|
+
- `.github/workflows/release.yml`:在 `v*` tag 上通过 reusable workflow 发布 `@wot-ui/cli`
|
|
177
|
+
- `.github/workflows/sync.yml`:拉取上游 `wot-ui/wot-ui`,提取最新元数据,并自动创建同步 PR
|
|
178
|
+
|
|
89
179
|
## 当前边界
|
|
90
180
|
|
|
91
181
|
- 当前仅支持 `wot-ui v2`
|
|
92
|
-
- `usage`
|
|
93
|
-
- CSS
|
|
182
|
+
- `usage` 与 `lint` 当前聚焦 `.vue` 文件中的 `<wd-*>` 标签及相关 import
|
|
183
|
+
- 提取脚本优先从 SCSS 源码解析 CSS 变量,并在必要时回退到 markdown 表格
|
|
184
|
+
|
|
185
|
+
## 相关文档
|
|
186
|
+
|
|
187
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md):贡献与开发流程
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
[MIT](./LICENSE) License © wot-ui
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as resolveVersion, i as listComponents, n as lintProject, o as loadMetadataFile, r as findComponent, t as analyzeUsage } from "./scanner-
|
|
2
|
+
import { a as resolveVersion, i as listComponents, n as lintProject, o as loadMetadataFile, r as findComponent, s as version, t as analyzeUsage } from "./scanner-D0vLA-5K.mjs";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
6
|
import { join, resolve } from "node:path";
|
|
7
7
|
|
|
8
|
+
//#region src/utils/output.ts
|
|
9
|
+
function writeStdout(message) {
|
|
10
|
+
process.stdout.write(message);
|
|
11
|
+
}
|
|
12
|
+
function writeLine(message = "") {
|
|
13
|
+
writeStdout(`${message}\n`);
|
|
14
|
+
}
|
|
15
|
+
function writeJson(value) {
|
|
16
|
+
writeLine(JSON.stringify(value, null, 2));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
8
20
|
//#region src/commands/shared.ts
|
|
9
21
|
function addQueryOptions(command) {
|
|
10
22
|
return command.option("--format <format>", "output format: text, json, markdown", "text").option("--lang <lang>", "output language: zh, en", "zh").option("--version <version>", "target wot-ui version");
|
|
@@ -17,10 +29,10 @@ function normalizeQueryOptions(options) {
|
|
|
17
29
|
};
|
|
18
30
|
}
|
|
19
31
|
function printError(message, format) {
|
|
20
|
-
if (format === "json")
|
|
32
|
+
if (format === "json") writeJson({
|
|
21
33
|
error: true,
|
|
22
34
|
message
|
|
23
|
-
}
|
|
35
|
+
});
|
|
24
36
|
else console.error(message);
|
|
25
37
|
}
|
|
26
38
|
function getComponentLabel(component, lang) {
|
|
@@ -55,16 +67,15 @@ function registerChangelogCommand(program) {
|
|
|
55
67
|
return versionMatches && componentMatches;
|
|
56
68
|
});
|
|
57
69
|
if (query.format === "json") {
|
|
58
|
-
|
|
70
|
+
writeJson({ entries });
|
|
59
71
|
return;
|
|
60
72
|
}
|
|
61
|
-
|
|
73
|
+
writeLine(entries.flatMap((entry) => [
|
|
62
74
|
`${entry.version}${entry.date ? ` (${entry.date})` : ""}`,
|
|
63
75
|
entry.summary,
|
|
64
76
|
...entry.highlights.map((item) => `- ${item}`),
|
|
65
77
|
""
|
|
66
|
-
]);
|
|
67
|
-
console.log(lines.join("\n").trim());
|
|
78
|
+
]).join("\n").trim());
|
|
68
79
|
} catch (error) {
|
|
69
80
|
printError(error instanceof Error ? error.message : "Failed to load changelog", query.format);
|
|
70
81
|
process.exitCode = 1;
|
|
@@ -87,13 +98,13 @@ function registerDemoCommand(program) {
|
|
|
87
98
|
const demos = component.demos ?? [];
|
|
88
99
|
if (!demoName) {
|
|
89
100
|
if (query.format === "json") {
|
|
90
|
-
|
|
101
|
+
writeJson({
|
|
91
102
|
component: component.name,
|
|
92
103
|
demos
|
|
93
|
-
}
|
|
104
|
+
});
|
|
94
105
|
return;
|
|
95
106
|
}
|
|
96
|
-
|
|
107
|
+
writeLine(demos.map((demo$1) => `- ${demo$1.name}: ${demo$1.title}`).join("\n"));
|
|
97
108
|
return;
|
|
98
109
|
}
|
|
99
110
|
const demo = demos.find((item) => item.name.toLowerCase() === demoName.toLowerCase());
|
|
@@ -103,13 +114,13 @@ function registerDemoCommand(program) {
|
|
|
103
114
|
return;
|
|
104
115
|
}
|
|
105
116
|
if (query.format === "json") {
|
|
106
|
-
|
|
117
|
+
writeJson({
|
|
107
118
|
component: component.name,
|
|
108
119
|
demo
|
|
109
|
-
}
|
|
120
|
+
});
|
|
110
121
|
return;
|
|
111
122
|
}
|
|
112
|
-
|
|
123
|
+
writeLine(demo.code);
|
|
113
124
|
} catch (error) {
|
|
114
125
|
printError(error instanceof Error ? error.message : "Failed to load demo", query.format);
|
|
115
126
|
process.exitCode = 1;
|
|
@@ -130,13 +141,13 @@ function registerDocCommand(program) {
|
|
|
130
141
|
return;
|
|
131
142
|
}
|
|
132
143
|
if (query.format === "json") {
|
|
133
|
-
|
|
144
|
+
writeJson({
|
|
134
145
|
name: component.name,
|
|
135
146
|
doc: component.doc
|
|
136
|
-
}
|
|
147
|
+
});
|
|
137
148
|
return;
|
|
138
149
|
}
|
|
139
|
-
|
|
150
|
+
writeLine(component.doc);
|
|
140
151
|
} catch (error) {
|
|
141
152
|
printError(error instanceof Error ? error.message : "Failed to load documentation", query.format);
|
|
142
153
|
process.exitCode = 1;
|
|
@@ -156,8 +167,8 @@ function getDependencyVersion(pkg, name) {
|
|
|
156
167
|
}
|
|
157
168
|
function detectWotDependency(pkg) {
|
|
158
169
|
for (const name of ["wot-design-uni", "wot-ui"]) {
|
|
159
|
-
const version = getDependencyVersion(pkg, name);
|
|
160
|
-
if (version) return `${name}@${version}`;
|
|
170
|
+
const version$1 = getDependencyVersion(pkg, name);
|
|
171
|
+
if (version$1) return `${name}@${version$1}`;
|
|
161
172
|
}
|
|
162
173
|
if (pkg?.dependencies) {
|
|
163
174
|
const scoped = Object.keys(pkg.dependencies).find((name) => name.includes("wot"));
|
|
@@ -218,11 +229,10 @@ function registerDoctorCommand(program) {
|
|
|
218
229
|
try {
|
|
219
230
|
const report = diagnoseProject(resolve(dir));
|
|
220
231
|
if (query.format === "json") {
|
|
221
|
-
|
|
232
|
+
writeJson(report);
|
|
222
233
|
return;
|
|
223
234
|
}
|
|
224
|
-
|
|
225
|
-
console.log(lines.join("\n"));
|
|
235
|
+
writeLine([`Directory: ${report.dir}`, ...report.checks.map((check) => `${check.status.toUpperCase()} ${check.name}: ${check.message}${check.suggestion ? ` | ${check.suggestion}` : ""}`)].join("\n"));
|
|
226
236
|
} catch (error) {
|
|
227
237
|
printError(error instanceof Error ? error.message : "Failed to diagnose project", query.format);
|
|
228
238
|
process.exitCode = 1;
|
|
@@ -243,10 +253,10 @@ function registerInfoCommand(program) {
|
|
|
243
253
|
return;
|
|
244
254
|
}
|
|
245
255
|
if (query.format === "json") {
|
|
246
|
-
|
|
256
|
+
writeJson(component);
|
|
247
257
|
return;
|
|
248
258
|
}
|
|
249
|
-
|
|
259
|
+
writeLine([
|
|
250
260
|
`${getComponentLabel(component, query.lang)} (${component.tag})`,
|
|
251
261
|
getComponentDescription(component, query.lang),
|
|
252
262
|
"",
|
|
@@ -264,8 +274,7 @@ function registerInfoCommand(program) {
|
|
|
264
274
|
const defaultValue = cssVar.defaultValue ?? cssVar.token;
|
|
265
275
|
return `- ${cssVar.name}${defaultValue ? ` = ${defaultValue}` : ""}: ${cssVar.description}`;
|
|
266
276
|
})
|
|
267
|
-
];
|
|
268
|
-
console.log(lines.join("\n"));
|
|
277
|
+
].join("\n"));
|
|
269
278
|
} catch (error) {
|
|
270
279
|
printError(error instanceof Error ? error.message : "Failed to load component info", query.format);
|
|
271
280
|
process.exitCode = 1;
|
|
@@ -281,15 +290,14 @@ function registerLintCommand(program) {
|
|
|
281
290
|
try {
|
|
282
291
|
const report = lintProject(resolve(dir), query.version);
|
|
283
292
|
if (query.format === "json") {
|
|
284
|
-
|
|
293
|
+
writeJson(report);
|
|
285
294
|
return;
|
|
286
295
|
}
|
|
287
296
|
if (!report.issues.length) {
|
|
288
|
-
|
|
297
|
+
writeLine(`No lint issues found. Scanned files: ${report.scannedFiles}`);
|
|
289
298
|
return;
|
|
290
299
|
}
|
|
291
|
-
|
|
292
|
-
console.log(lines.join("\n"));
|
|
300
|
+
writeLine([`Scanned files: ${report.scannedFiles}`, ...report.issues.map((issue) => `${issue.severity.toUpperCase()} ${issue.file}:${issue.line} [${issue.rule}] ${issue.message}`)].join("\n"));
|
|
293
301
|
} catch (error) {
|
|
294
302
|
printError(error instanceof Error ? error.message : "Failed to lint project", query.format);
|
|
295
303
|
process.exitCode = 1;
|
|
@@ -305,11 +313,10 @@ function registerListCommand(program) {
|
|
|
305
313
|
try {
|
|
306
314
|
const components = listComponents(query.version);
|
|
307
315
|
if (query.format === "json") {
|
|
308
|
-
|
|
316
|
+
writeJson({ components });
|
|
309
317
|
return;
|
|
310
318
|
}
|
|
311
|
-
|
|
312
|
-
console.log(lines.join("\n"));
|
|
319
|
+
writeLine(components.map((component) => `- ${getComponentLabel(component, query.lang)} (${component.tag}): ${getComponentDescription(component, query.lang)}`).join("\n"));
|
|
313
320
|
} catch (error) {
|
|
314
321
|
printError(error instanceof Error ? error.message : "Failed to list components", query.format);
|
|
315
322
|
process.exitCode = 1;
|
|
@@ -321,7 +328,7 @@ function registerListCommand(program) {
|
|
|
321
328
|
//#region src/commands/mcp.ts
|
|
322
329
|
function registerMcpCommand(program) {
|
|
323
330
|
program.command("mcp").description("Start the wot-ui MCP server").action(async () => {
|
|
324
|
-
const { startMcpServer } = await import("./server-
|
|
331
|
+
const { startMcpServer } = await import("./server-BWgY0XHJ.mjs");
|
|
325
332
|
await startMcpServer();
|
|
326
333
|
});
|
|
327
334
|
}
|
|
@@ -333,11 +340,10 @@ function registerTokenCommand(program) {
|
|
|
333
340
|
const query = normalizeQueryOptions(options);
|
|
334
341
|
try {
|
|
335
342
|
if (!componentName) {
|
|
336
|
-
|
|
343
|
+
writeJson(listComponents(query.version).flatMap((component$1) => component$1.cssVars.map((cssVar) => ({
|
|
337
344
|
component: component$1.name,
|
|
338
345
|
...cssVar
|
|
339
|
-
})));
|
|
340
|
-
console.log(JSON.stringify(tokens, null, 2));
|
|
346
|
+
}))));
|
|
341
347
|
return;
|
|
342
348
|
}
|
|
343
349
|
const component = findComponent(componentName, query.version);
|
|
@@ -347,17 +353,16 @@ function registerTokenCommand(program) {
|
|
|
347
353
|
return;
|
|
348
354
|
}
|
|
349
355
|
if (query.format === "json") {
|
|
350
|
-
|
|
356
|
+
writeJson({
|
|
351
357
|
name: component.name,
|
|
352
358
|
cssVars: formatCssVars(component.cssVars)
|
|
353
|
-
}
|
|
359
|
+
});
|
|
354
360
|
return;
|
|
355
361
|
}
|
|
356
|
-
|
|
362
|
+
writeLine([component.name, ...component.cssVars.map((cssVar) => {
|
|
357
363
|
const defaultValue = cssVar.defaultValue ?? cssVar.token;
|
|
358
364
|
return `- ${cssVar.name}${defaultValue ? ` = ${defaultValue}` : ""}: ${cssVar.description}`;
|
|
359
|
-
})];
|
|
360
|
-
console.log(lines.join("\n"));
|
|
365
|
+
})].join("\n"));
|
|
361
366
|
} catch (error) {
|
|
362
367
|
printError(error instanceof Error ? error.message : "Failed to query CSS variables", query.format);
|
|
363
368
|
process.exitCode = 1;
|
|
@@ -373,7 +378,7 @@ function registerUsageCommand(program) {
|
|
|
373
378
|
try {
|
|
374
379
|
const report = analyzeUsage(resolve(dir), query.version);
|
|
375
380
|
if (query.format === "json") {
|
|
376
|
-
|
|
381
|
+
writeJson(report);
|
|
377
382
|
return;
|
|
378
383
|
}
|
|
379
384
|
const lines = [
|
|
@@ -382,7 +387,7 @@ function registerUsageCommand(program) {
|
|
|
382
387
|
...report.components.map((component) => `- ${component.name} (${component.tag}): ${component.count} in ${component.files.join(", ")}`)
|
|
383
388
|
];
|
|
384
389
|
if (report.imports.length) lines.push("", "Imports:", ...report.imports.map((item) => `- ${item}`));
|
|
385
|
-
|
|
390
|
+
writeLine(lines.join("\n"));
|
|
386
391
|
} catch (error) {
|
|
387
392
|
printError(error instanceof Error ? error.message : "Failed to analyze project usage", query.format);
|
|
388
393
|
process.exitCode = 1;
|
|
@@ -394,7 +399,7 @@ function registerUsageCommand(program) {
|
|
|
394
399
|
//#region src/app.ts
|
|
395
400
|
function createCliProgram() {
|
|
396
401
|
const program = new Command();
|
|
397
|
-
program.name("wot").description("wot-ui AI toolkit CLI").version(
|
|
402
|
+
program.name("wot").description("wot-ui AI toolkit CLI").version(version);
|
|
398
403
|
registerListCommand(program);
|
|
399
404
|
registerInfoCommand(program);
|
|
400
405
|
registerDocCommand(program);
|
|
@@ -4,6 +4,10 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { gunzipSync } from "node:zlib";
|
|
5
5
|
import { parse } from "@vue/compiler-sfc";
|
|
6
6
|
|
|
7
|
+
//#region package.json
|
|
8
|
+
var version = "0.0.1-beta.6";
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
7
11
|
//#region src/data/loader.ts
|
|
8
12
|
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
9
13
|
function resolveDataDir() {
|
|
@@ -48,15 +52,15 @@ function resolveVersion(requested) {
|
|
|
48
52
|
|
|
49
53
|
//#endregion
|
|
50
54
|
//#region src/data/metadata.ts
|
|
51
|
-
function loadResolvedMetadata(version) {
|
|
52
|
-
return loadMetadataFile(resolveVersion(version));
|
|
55
|
+
function loadResolvedMetadata(version$1) {
|
|
56
|
+
return loadMetadataFile(resolveVersion(version$1));
|
|
53
57
|
}
|
|
54
|
-
function listComponents(version) {
|
|
55
|
-
return loadResolvedMetadata(version).components;
|
|
58
|
+
function listComponents(version$1) {
|
|
59
|
+
return loadResolvedMetadata(version$1).components;
|
|
56
60
|
}
|
|
57
|
-
function findComponent(name, version) {
|
|
61
|
+
function findComponent(name, version$1) {
|
|
58
62
|
const normalized = name.trim().toLowerCase();
|
|
59
|
-
return listComponents(version).find((component) => component.name.toLowerCase() === normalized || component.tag.toLowerCase() === normalized);
|
|
63
|
+
return listComponents(version$1).find((component) => component.name.toLowerCase() === normalized || component.tag.toLowerCase() === normalized);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
//#endregion
|
|
@@ -114,10 +118,10 @@ function collectImports(scriptContent) {
|
|
|
114
118
|
for (const match of scriptContent.matchAll(IMPORT_RE)) if (match[1]) imports.add(match[1]);
|
|
115
119
|
return [...imports];
|
|
116
120
|
}
|
|
117
|
-
function analyzeUsage(targetDir, version) {
|
|
121
|
+
function analyzeUsage(targetDir, version$1) {
|
|
118
122
|
const dir = resolve(targetDir);
|
|
119
123
|
const files = walkFiles(dir, [".vue"]);
|
|
120
|
-
const knownByTag = new Map(listComponents(version).map((component) => [component.tag.toLowerCase(), component]));
|
|
124
|
+
const knownByTag = new Map(listComponents(version$1).map((component) => [component.tag.toLowerCase(), component]));
|
|
121
125
|
const usageMap = /* @__PURE__ */ new Map();
|
|
122
126
|
const imports = /* @__PURE__ */ new Set();
|
|
123
127
|
for (const file of files) {
|
|
@@ -148,7 +152,7 @@ function analyzeUsage(targetDir, version) {
|
|
|
148
152
|
imports: [...imports].sort()
|
|
149
153
|
};
|
|
150
154
|
}
|
|
151
|
-
function lintProject(targetDir, version) {
|
|
155
|
+
function lintProject(targetDir, version$1) {
|
|
152
156
|
const dir = resolve(targetDir);
|
|
153
157
|
const files = walkFiles(dir, [".vue"]);
|
|
154
158
|
const issues = [];
|
|
@@ -157,7 +161,7 @@ function lintProject(targetDir, version) {
|
|
|
157
161
|
for (const match of template.matchAll(TAG_RE)) {
|
|
158
162
|
const tag = match[1]?.toLowerCase();
|
|
159
163
|
if (!tag) continue;
|
|
160
|
-
if (!findComponent(tag, version)) issues.push({
|
|
164
|
+
if (!findComponent(tag, version$1)) issues.push({
|
|
161
165
|
file: safeRelative(dir, file),
|
|
162
166
|
line: getLineNumber(template, match.index ?? 0),
|
|
163
167
|
rule: "unknown-component",
|
|
@@ -175,7 +179,7 @@ function lintProject(targetDir, version) {
|
|
|
175
179
|
severity: "warning",
|
|
176
180
|
message: "wd-button should include visible text content or an icon attribute."
|
|
177
181
|
});
|
|
178
|
-
const component = findComponent("wd-button", version);
|
|
182
|
+
const component = findComponent("wd-button", version$1);
|
|
179
183
|
for (const prop of component?.props ?? []) {
|
|
180
184
|
if (!prop.deprecated) continue;
|
|
181
185
|
if (!(/* @__PURE__ */ new RegExp(`\\b${prop.name}\\b`)).test(attrs)) continue;
|
|
@@ -196,4 +200,4 @@ function lintProject(targetDir, version) {
|
|
|
196
200
|
}
|
|
197
201
|
|
|
198
202
|
//#endregion
|
|
199
|
-
export { resolveVersion as a, listComponents as i, lintProject as n, loadMetadataFile as o, findComponent as r, analyzeUsage as t };
|
|
203
|
+
export { resolveVersion as a, listComponents as i, lintProject as n, loadMetadataFile as o, findComponent as r, version as s, analyzeUsage as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resolveVersion, i as listComponents, n as lintProject, o as loadMetadataFile, r as findComponent } from "./scanner-
|
|
1
|
+
import { a as resolveVersion, i as listComponents, n as lintProject, o as loadMetadataFile, r as findComponent, s as version } from "./scanner-D0vLA-5K.mjs";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";
|
|
4
4
|
import * as z from "zod/v4";
|
|
@@ -31,10 +31,10 @@ function registerMcpTools(server) {
|
|
|
31
31
|
idempotentHint: true,
|
|
32
32
|
openWorldHint: false
|
|
33
33
|
}
|
|
34
|
-
}, async ({ version }) => {
|
|
34
|
+
}, async ({ version: version$1 }) => {
|
|
35
35
|
return { content: [{
|
|
36
36
|
type: "text",
|
|
37
|
-
text: jsonText({ components: listComponents(version) })
|
|
37
|
+
text: jsonText({ components: listComponents(version$1) })
|
|
38
38
|
}] };
|
|
39
39
|
});
|
|
40
40
|
server.registerTool("wot_info", {
|
|
@@ -49,8 +49,8 @@ function registerMcpTools(server) {
|
|
|
49
49
|
idempotentHint: true,
|
|
50
50
|
openWorldHint: false
|
|
51
51
|
}
|
|
52
|
-
}, async ({ component, version }) => {
|
|
53
|
-
const result = findComponent(component, version);
|
|
52
|
+
}, async ({ component, version: version$1 }) => {
|
|
53
|
+
const result = findComponent(component, version$1);
|
|
54
54
|
if (!result) return {
|
|
55
55
|
isError: true,
|
|
56
56
|
content: [{
|
|
@@ -75,8 +75,8 @@ function registerMcpTools(server) {
|
|
|
75
75
|
idempotentHint: true,
|
|
76
76
|
openWorldHint: false
|
|
77
77
|
}
|
|
78
|
-
}, async ({ component, version }) => {
|
|
79
|
-
const result = findComponent(component, version);
|
|
78
|
+
}, async ({ component, version: version$1 }) => {
|
|
79
|
+
const result = findComponent(component, version$1);
|
|
80
80
|
if (!result?.doc) return {
|
|
81
81
|
isError: true,
|
|
82
82
|
content: [{
|
|
@@ -102,8 +102,8 @@ function registerMcpTools(server) {
|
|
|
102
102
|
idempotentHint: true,
|
|
103
103
|
openWorldHint: false
|
|
104
104
|
}
|
|
105
|
-
}, async ({ component, demo, version }) => {
|
|
106
|
-
const result = findComponent(component, version);
|
|
105
|
+
}, async ({ component, demo, version: version$1 }) => {
|
|
106
|
+
const result = findComponent(component, version$1);
|
|
107
107
|
if (!result) return {
|
|
108
108
|
isError: true,
|
|
109
109
|
content: [{
|
|
@@ -140,15 +140,15 @@ function registerMcpTools(server) {
|
|
|
140
140
|
idempotentHint: true,
|
|
141
141
|
openWorldHint: false
|
|
142
142
|
}
|
|
143
|
-
}, async ({ component, version }) => {
|
|
143
|
+
}, async ({ component, version: version$1 }) => {
|
|
144
144
|
if (!component) return { content: [{
|
|
145
145
|
type: "text",
|
|
146
|
-
text: jsonText({ components: listComponents(version).map((item) => ({
|
|
146
|
+
text: jsonText({ components: listComponents(version$1).map((item) => ({
|
|
147
147
|
name: item.name,
|
|
148
148
|
cssVars: item.cssVars
|
|
149
149
|
})) })
|
|
150
150
|
}] };
|
|
151
|
-
const result = findComponent(component, version);
|
|
151
|
+
const result = findComponent(component, version$1);
|
|
152
152
|
if (!result) return {
|
|
153
153
|
isError: true,
|
|
154
154
|
content: [{
|
|
@@ -176,11 +176,11 @@ function registerMcpTools(server) {
|
|
|
176
176
|
idempotentHint: true,
|
|
177
177
|
openWorldHint: false
|
|
178
178
|
}
|
|
179
|
-
}, async ({ version, component }) => {
|
|
179
|
+
}, async ({ version: version$1, component }) => {
|
|
180
180
|
return { content: [{
|
|
181
181
|
type: "text",
|
|
182
|
-
text: jsonText({ entries: (loadMetadataFile(resolveVersion(version)).changelog ?? []).filter((entry) => {
|
|
183
|
-
const versionMatches = version ? entry.version === version || `v${entry.version}` === version : true;
|
|
182
|
+
text: jsonText({ entries: (loadMetadataFile(resolveVersion(version$1)).changelog ?? []).filter((entry) => {
|
|
183
|
+
const versionMatches = version$1 ? entry.version === version$1 || `v${entry.version}` === version$1 : true;
|
|
184
184
|
const componentMatches = component ? (entry.components ?? []).some((item) => item.toLowerCase() === component.toLowerCase()) : true;
|
|
185
185
|
return versionMatches && componentMatches;
|
|
186
186
|
}) })
|
|
@@ -198,10 +198,10 @@ function registerMcpTools(server) {
|
|
|
198
198
|
idempotentHint: true,
|
|
199
199
|
openWorldHint: true
|
|
200
200
|
}
|
|
201
|
-
}, async ({ dir, version }) => {
|
|
201
|
+
}, async ({ dir, version: version$1 }) => {
|
|
202
202
|
return { content: [{
|
|
203
203
|
type: "text",
|
|
204
|
-
text: jsonText(lintProject(dir ?? process.cwd(), version))
|
|
204
|
+
text: jsonText(lintProject(dir ?? process.cwd(), version$1))
|
|
205
205
|
}] };
|
|
206
206
|
});
|
|
207
207
|
}
|
|
@@ -211,7 +211,7 @@ function registerMcpTools(server) {
|
|
|
211
211
|
async function startMcpServer() {
|
|
212
212
|
const server = new McpServer({
|
|
213
213
|
name: "wot-ui",
|
|
214
|
-
version
|
|
214
|
+
version
|
|
215
215
|
}, {
|
|
216
216
|
instructions: "Use wot-ui component tools before generating UI code. Only wot-ui v2 metadata is available in this server.",
|
|
217
217
|
capabilities: { logging: {} }
|
package/package.json
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wot-ui/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "0.0.1-beta.6",
|
|
5
|
+
"description": "面向 wot-ui 的 CLI、MCP 与数据提取工具集",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"funding": "https://github.com/sponsors/antfu",
|
|
7
8
|
"homepage": "https://github.com/wot-ui/open-wot#readme",
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
10
11
|
"url": "git+https://github.com/wot-ui/open-wot.git"
|
|
11
12
|
},
|
|
12
13
|
"bugs": "https://github.com/wot-ui/open-wot/issues",
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.mjs",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
13
20
|
"main": "./dist/index.mjs",
|
|
14
21
|
"module": "./dist/index.mjs",
|
|
15
22
|
"types": "./dist/index.d.mts",
|
|
@@ -18,8 +25,7 @@
|
|
|
18
25
|
},
|
|
19
26
|
"files": [
|
|
20
27
|
"data",
|
|
21
|
-
"dist"
|
|
22
|
-
"skills"
|
|
28
|
+
"dist"
|
|
23
29
|
],
|
|
24
30
|
"engines": {
|
|
25
31
|
"node": ">=20.0.0"
|
|
@@ -32,18 +38,44 @@
|
|
|
32
38
|
"zod": "^4.1.12"
|
|
33
39
|
},
|
|
34
40
|
"devDependencies": {
|
|
41
|
+
"@antfu/eslint-config": "^6.6.1",
|
|
42
|
+
"@antfu/ni": "^28.0.0",
|
|
43
|
+
"@antfu/utils": "^9.3.0",
|
|
35
44
|
"@types/node": "^25.0.1",
|
|
45
|
+
"bumpp": "^10.3.2",
|
|
46
|
+
"eslint": "^9.39.2",
|
|
47
|
+
"lint-staged": "^16.2.7",
|
|
48
|
+
"publint": "^0.3.16",
|
|
49
|
+
"simple-git-hooks": "^2.13.1",
|
|
50
|
+
"tinyexec": "^1.0.2",
|
|
36
51
|
"tsdown": "^0.17.3",
|
|
37
52
|
"tsx": "^4.21.0",
|
|
38
53
|
"typescript": "^5.9.3",
|
|
39
|
-
"vitest": "^4.0.15"
|
|
54
|
+
"vitest": "^4.0.15",
|
|
55
|
+
"vitest-package-exports": "^0.1.1",
|
|
56
|
+
"yaml": "^2.8.2"
|
|
57
|
+
},
|
|
58
|
+
"simple-git-hooks": {
|
|
59
|
+
"pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && npx lint-staged"
|
|
60
|
+
},
|
|
61
|
+
"lint-staged": {
|
|
62
|
+
"*": "eslint --fix"
|
|
40
63
|
},
|
|
41
64
|
"scripts": {
|
|
42
65
|
"build": "tsdown",
|
|
43
66
|
"dev": "tsdown --watch",
|
|
44
67
|
"extract": "tsx scripts/extract.ts",
|
|
68
|
+
"extract:cli": "pnpm extract",
|
|
69
|
+
"extract:clone": "rm -rf /tmp/open-wot-wot-ui && git clone --depth 1 https://github.com/wot-ui/wot-ui.git /tmp/open-wot-wot-ui && pnpm extract --wot-dir /tmp/open-wot-wot-ui --output data/v2.json",
|
|
70
|
+
"lint": "eslint",
|
|
71
|
+
"release": "bumpp",
|
|
72
|
+
"start": "tsx src/index.ts",
|
|
45
73
|
"test": "vitest run",
|
|
74
|
+
"test:all": "pnpm test",
|
|
75
|
+
"test:cli": "pnpm test",
|
|
46
76
|
"test:watch": "vitest",
|
|
47
|
-
"typecheck": "tsc --noEmit"
|
|
77
|
+
"typecheck": "tsc --noEmit",
|
|
78
|
+
"typecheck:all": "pnpm typecheck",
|
|
79
|
+
"typecheck:cli": "pnpm typecheck"
|
|
48
80
|
}
|
|
49
81
|
}
|
package/README.zh-CN.md
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# @wot-ui/cli
|
|
2
|
-
|
|
3
|
-
面向 wot-ui 的 CLI、MCP 与 Skills 工具集。
|
|
4
|
-
|
|
5
|
-
## 能力概览
|
|
6
|
-
|
|
7
|
-
- 组件知识查询:`list`、`info`、`doc`、`demo`、`token`、`changelog`
|
|
8
|
-
- 本地项目分析:`doctor`、`usage`、`lint`
|
|
9
|
-
- 通过 `wot mcp` 启动 MCP stdio 服务
|
|
10
|
-
- 通过提取脚本从 wot-ui 源码生成离线元数据
|
|
11
|
-
|
|
12
|
-
## 安装
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install -g @wot-ui/cli
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## 命令示例
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
wot list
|
|
22
|
-
wot info Button
|
|
23
|
-
wot doc Button
|
|
24
|
-
wot demo Button basic
|
|
25
|
-
wot token Button
|
|
26
|
-
wot changelog
|
|
27
|
-
wot doctor ./my-app
|
|
28
|
-
wot usage ./my-app
|
|
29
|
-
wot lint ./my-app
|
|
30
|
-
wot mcp
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## 命令说明
|
|
34
|
-
|
|
35
|
-
- `wot list`:列出可用组件
|
|
36
|
-
- `wot info <Component>`:查看组件详情
|
|
37
|
-
- `wot doc <Component>`:输出组件 markdown 文档
|
|
38
|
-
- `wot demo <Component> [name]`:查看 demo 列表或示例源码
|
|
39
|
-
- `wot token [Component]`:查看 CSS 变量与默认值
|
|
40
|
-
- `wot changelog [version] [component]`:查看更新记录
|
|
41
|
-
- `wot doctor [dir]`:检查工程依赖与环境
|
|
42
|
-
- `wot usage [dir]`:统计 `.vue` 中的 `wd-*` 使用情况
|
|
43
|
-
- `wot lint [dir]`:执行 wot-ui 相关规则检查
|
|
44
|
-
- `wot mcp`:启动 MCP stdio Server
|
|
45
|
-
|
|
46
|
-
## 提取命令
|
|
47
|
-
|
|
48
|
-
使用本地已有的 wot-ui 仓库:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
pnpm extract --wot-dir ../wot-ui --output ./data/v2.json
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
直接克隆上游仓库并提取:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
pnpm extract:clone
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## 开发调试
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
pnpm --filter @wot-ui/cli exec tsx src/index.ts list
|
|
64
|
-
pnpm --filter @wot-ui/cli exec tsx src/index.ts mcp
|
|
65
|
-
pnpm --filter @wot-ui/cli build
|
|
66
|
-
pnpm --filter @wot-ui/cli test
|
|
67
|
-
pnpm --filter @wot-ui/cli typecheck
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## 当前边界
|
|
71
|
-
|
|
72
|
-
- 当前只支持 wot-ui v2
|
|
73
|
-
- `usage` 和 `lint` 当前只聚焦 `.vue` 文件
|
|
74
|
-
- CSS 变量优先从组件 SCSS 源码提取
|
package/skills/wot-ui/SKILL.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: wot-ui
|
|
3
|
-
description: Query wot-ui component knowledge before generating or refactoring UI code.
|
|
4
|
-
summary: Query wot-ui component knowledge before generating code.
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
Use the local `wot` CLI before generating or refactoring wot-ui based code.
|
|
8
|
-
|
|
9
|
-
Recommended workflow:
|
|
10
|
-
1. Run `wot list` to find the target component.
|
|
11
|
-
2. Run `wot info <Component>` to inspect props, events, slots, and CSS variables.
|
|
12
|
-
3. Run `wot doc <Component>` when you need the markdown documentation.
|
|
13
|
-
4. Run `wot token <Component>` when theme customization is involved.
|
|
14
|
-
|
|
15
|
-
Current limitations:
|
|
16
|
-
- Only wot-ui v2 is supported.
|
|
17
|
-
- Bundled data is still a seed dataset until `pnpm extract` is run against the full wot-ui source repository.
|
|
18
|
-
- `usage` and `lint` currently focus on `.vue` files.
|