@agile-team/wl-skills-ui 1.6.2
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/CHANGELOG.md +207 -0
- package/README.md +623 -0
- package/SKILL.md +632 -0
- package/bin/wl-ui.js +794 -0
- package/design/spec/color.md +41 -0
- package/design/spec/spacing.md +28 -0
- package/design/spec/typography.md +29 -0
- package/design/tokens/base.css +144 -0
- package/design/tokens/index.css +8 -0
- package/dist/ag-grid-override.scss +1 -0
- package/dist/element.scss +1 -0
- package/dist/index.scss +1 -0
- package/dist/portal.scss +1 -0
- package/dist/style-override.scss +1 -0
- package/dist/tokens.css +144 -0
- package/es/chunk-GBB7LDKQ.js +261 -0
- package/es/chunk-MDI2SCUP.js +281 -0
- package/es/chunk-QDTNMM7W.js +286 -0
- package/es/chunk-YLNAFAWE.js +233 -0
- package/es/common-preset.d.ts +38 -0
- package/es/common-preset.js +157 -0
- package/es/index.d.ts +32 -0
- package/es/index.js +1 -0
- package/es/presets/security.d.ts +16 -0
- package/es/presets/security.js +99 -0
- package/es/renderers-BFRSp2BH.d.ts +51 -0
- package/es/renderers-DMZbi_Gs.d.ts +51 -0
- package/es/renderers-DWhUlI2y.d.ts +47 -0
- package/es/types-04qLKHMt.d.ts +62 -0
- package/es/types-BrCPjAH6.d.ts +62 -0
- package/es/types-QZ1gy9KX.d.ts +63 -0
- package/examples/migration-operations-to-renderOps.md +107 -0
- package/examples/wk-exempt.example.json +30 -0
- package/mcp/server.js +394 -0
- package/package.json +130 -0
- package/reference/SelectPopupCom.vue +634 -0
- package/reference/ag-cell-renders.ts +644 -0
- package/reference/define-columns.ts +185 -0
- package/reference/jhlc.d.ts +1 -0
- package/scanner/coverage.mjs +287 -0
- package/scanner/exempt.mjs +127 -0
- package/scanner/fix.mjs +216 -0
- package/scanner/index.mjs +403 -0
- package/scanner/integration.mjs +155 -0
- package/scanner/report.mjs +459 -0
- package/scanner/rules/_shared.mjs +134 -0
- package/scanner/rules/button.mjs +97 -0
- package/scanner/rules/color.mjs +118 -0
- package/scanner/rules/componentFamily.mjs +184 -0
- package/scanner/rules/dialog.mjs +42 -0
- package/scanner/rules/form.mjs +86 -0
- package/scanner/rules/index.mjs +58 -0
- package/scanner/rules/table.mjs +170 -0
- package/scanner/rules/tag.mjs +245 -0
- package/scanner/snapshot.mjs +198 -0
- package/skills/_flows/full-audit.md +65 -0
- package/skills/_flows/legacy-skin-align.md +71 -0
- package/skills/_flows/new-project-init.md +80 -0
- package/skills/_flows/progressive-migrate.md +49 -0
- package/skills/_meta/_compat/README.md +47 -0
- package/skills/_meta/_compat/editors.json +76 -0
- package/skills/_meta/_compat/headers/agents.txt +7 -0
- package/skills/_meta/_compat/headers/claude-code.txt +7 -0
- package/skills/_meta/_compat/headers/cline.txt +7 -0
- package/skills/_meta/_compat/headers/cursor-mdc.txt +5 -0
- package/skills/_meta/_compat/headers/github-copilot.txt +5 -0
- package/skills/_meta/_compat/headers/kiro.txt +4 -0
- package/skills/_meta/_compat/headers/qoder.txt +4 -0
- package/skills/_meta/_compat/headers/trae.txt +3 -0
- package/skills/_meta/_compat/headers/windsurf.txt +5 -0
- package/skills/_meta/_detection.md +81 -0
- package/skills/_meta/_registry.md +96 -0
- package/skills/element/component-family/SKILL.md +71 -0
- package/skills/element/el-dialog/SKILL.md +124 -0
- package/skills/element/el-form/SKILL.md +84 -0
- package/skills/element/el-table/SKILL.md +86 -0
- package/skills/element/el-tag/SKILL.md +127 -0
- package/skills/layouts/detail-page/SKILL.md +99 -0
- package/skills/layouts/form-dialog/SKILL.md +128 -0
- package/skills/layouts/list-page/SKILL.md +111 -0
- package/skills/layouts/tree-list/SKILL.md +108 -0
- package/skills/ops/audit/SKILL.md +109 -0
- package/skills/ops/fix/SKILL.md +52 -0
- package/skills/ops/fix/USAGE.md +24 -0
- package/skills/ops/migrate/SKILL.md +157 -0
- package/skills/ops/scan/SKILL.md +71 -0
- package/skills/ops/scan/USAGE.md +16 -0
- package/skills/runtime/design-tokens/SKILL.md +88 -0
- package/skills/runtime/design-tokens/USAGE.md +19 -0
- package/skills/runtime/migration/SKILL.md +106 -0
- package/skills/runtime/migration/USAGE.md +34 -0
- package/skills/runtime/style-align/SKILL.md +124 -0
- package/skills/runtime/style-align/USAGE.md +52 -0
- package/skills/vendors/ag-grid/SKILL.md +94 -0
- package/skills/vendors/base-table/SKILL.md +57 -0
- package/skills/vendors/c-components/SKILL.md +36 -0
- package/skills/vendors/custom-wrappers/SKILL.md +45 -0
- package/skills/vendors/jh-components/SKILL.md +42 -0
- package/skills/vendors/unknown-wrapper/SKILL.md +44 -0
- package/standards/engineering/01-import-order.md +46 -0
- package/standards/engineering/02-naming.md +54 -0
- package/standards/engineering/03-scss-structure.md +59 -0
- package/standards/ui/01-table.md +111 -0
- package/standards/ui/02-button.md +83 -0
- package/standards/ui/03-form.md +118 -0
- package/standards/ui/04-tag-status.md +110 -0
- package/standards/ui/05-dialog-pagination.md +118 -0
- package/styles/element/_card.scss +26 -0
- package/styles/element/_descriptions.scss +22 -0
- package/styles/element/_dialog.scss +43 -0
- package/styles/element/_drawer.scss +22 -0
- package/styles/element/_feedback.scss +52 -0
- package/styles/element/_form.scss +22 -0
- package/styles/element/_navigation.scss +36 -0
- package/styles/element/_overlay.scss +32 -0
- package/styles/element/_pagination.scss +16 -0
- package/styles/element/_steps.scss +32 -0
- package/styles/element/_table.scss +13 -0
- package/styles/element/_tabs.scss +40 -0
- package/styles/element/_tree.scss +27 -0
- package/styles/element/_upload.scss +34 -0
- package/styles/element/index.scss +17 -0
- package/styles/index.scss +21 -0
- package/styles/layouts/_detail-page.scss +27 -0
- package/styles/layouts/_form-dialog.scss +19 -0
- package/styles/layouts/_list-page.scss +47 -0
- package/styles/layouts/_tree-list.scss +33 -0
- package/styles/layouts/index.scss +6 -0
- package/styles/presets/element-only.scss +6 -0
- package/styles/presets/full.scss +12 -0
- package/styles/presets/security.scss +37 -0
- package/styles/presets/skin.scss +14 -0
- package/styles/presets/tokens-only.scss +7 -0
- package/styles/tokens/index.scss +79 -0
- package/styles/vendors/_ag-grid.scss +273 -0
- package/styles/vendors/_base-components.scss +480 -0
- package/styles/vendors/_base-query-toolbar.scss +253 -0
- package/styles/vendors/_base-table.scss +41 -0
- package/styles/vendors/_c-components.scss +34 -0
- package/styles/vendors/_custom-wrappers.scss +30 -0
- package/styles/vendors/_jh-drag-col.scss +63 -0
- package/styles/vendors/_jh-pagination.scss +81 -0
- package/styles/vendors/_jh-tree.scss +196 -0
- package/styles/vendors/_portal.scss +114 -0
- package/styles/vendors/index.scss +17 -0
- package/templates/ag-grid-page/TPL-AG-GRID.md +76 -0
- package/templates/form-dialog/TPL-FORM-DIALOG.md +165 -0
- package/templates/list-page/TPL-LIST.md +139 -0
- package/templates/tree-list/TPL-TREE-LIST.md +176 -0
- package/templates/ui-optimization-report/TPL-UI-OPTIM-REPORT.md +343 -0
package/bin/wl-ui.js
ADDED
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* wl-ui — wl-skills-ui 统一 CLI
|
|
4
|
+
*
|
|
5
|
+
* 子命令:
|
|
6
|
+
* wl-ui init [--project <path>] [--editor copilot|cursor|windsurf|kiro|trae] [--dry-run]
|
|
7
|
+
* 把 skills/ 写入目标项目的 AI 编辑器规则目录
|
|
8
|
+
* wl-ui scan → 委托给 scanner/index.mjs
|
|
9
|
+
* wl-ui check → 委托给 scanner/index.mjs
|
|
10
|
+
* wl-ui fix → 委托给 scanner/index.mjs
|
|
11
|
+
* wl-ui all → 委托给 scanner/index.mjs
|
|
12
|
+
*
|
|
13
|
+
* 向后兼容:wl-scan 仍可用(直接调用 scanner/index.mjs)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
existsSync,
|
|
18
|
+
mkdirSync,
|
|
19
|
+
readdirSync,
|
|
20
|
+
readFileSync,
|
|
21
|
+
writeFileSync,
|
|
22
|
+
rmSync,
|
|
23
|
+
unlinkSync,
|
|
24
|
+
} from "node:fs";
|
|
25
|
+
import { join, resolve, dirname, relative } from "node:path";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
import { parseArgs } from "node:util";
|
|
28
|
+
import { createRequire } from "node:module";
|
|
29
|
+
import { execFileSync } from "node:child_process";
|
|
30
|
+
import { createHash } from "node:crypto";
|
|
31
|
+
|
|
32
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
33
|
+
const __dirname = dirname(__filename);
|
|
34
|
+
const PKG_ROOT = resolve(__dirname, "..");
|
|
35
|
+
const require = createRequire(import.meta.url);
|
|
36
|
+
const PKG = require("../package.json");
|
|
37
|
+
const MANIFEST_NAME = ".wl-skills-ui-manifest.json";
|
|
38
|
+
|
|
39
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
40
|
+
// 常量
|
|
41
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
42
|
+
|
|
43
|
+
/** 编辑器 → 安装目录映射 */
|
|
44
|
+
const EDITOR_TARGETS = {
|
|
45
|
+
"github-copilot": {
|
|
46
|
+
dir: ".github/instructions/wk-skills",
|
|
47
|
+
ext: ".instructions.md",
|
|
48
|
+
},
|
|
49
|
+
cursor: { dir: ".cursor/rules", ext: ".mdc" },
|
|
50
|
+
windsurf: { dir: ".windsurf/rules", ext: ".md" },
|
|
51
|
+
kiro: { dir: ".kiro/steering", ext: ".md" },
|
|
52
|
+
trae: { dir: ".trae/rules", ext: ".md" },
|
|
53
|
+
"claude-code": { dir: ".", ext: ".md", singleFile: "CLAUDE.md" },
|
|
54
|
+
cline: { dir: ".", ext: ".md", singleFile: ".clinerules" },
|
|
55
|
+
"agents-generic": { dir: ".", ext: ".md", singleFile: "AGENTS.md" },
|
|
56
|
+
qoder: { dir: ".qoder/rules", ext: ".md" },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// ── 参数解析 ─────────────────────────────────────────────────────────────────
|
|
60
|
+
const rawArgs = process.argv.slice(2);
|
|
61
|
+
const SUBCOMMANDS = new Set([
|
|
62
|
+
"init",
|
|
63
|
+
"update",
|
|
64
|
+
"diff",
|
|
65
|
+
"clean",
|
|
66
|
+
"doctor",
|
|
67
|
+
"prompts",
|
|
68
|
+
"scan",
|
|
69
|
+
"check",
|
|
70
|
+
"fix",
|
|
71
|
+
"all",
|
|
72
|
+
"add-preset",
|
|
73
|
+
"snapshot",
|
|
74
|
+
]);
|
|
75
|
+
let subcommand = "help";
|
|
76
|
+
|
|
77
|
+
if (rawArgs.length > 0 && SUBCOMMANDS.has(rawArgs[0])) {
|
|
78
|
+
subcommand = rawArgs.shift();
|
|
79
|
+
} else if (rawArgs.length === 0) {
|
|
80
|
+
subcommand = "help";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 非 init 子命令:直接委托给 scanner/index.mjs
|
|
84
|
+
const SCANNER_CMDS = new Set(["scan", "check", "fix", "all", "snapshot"]);
|
|
85
|
+
if (SCANNER_CMDS.has(subcommand)) {
|
|
86
|
+
const scannerBin = join(PKG_ROOT, "scanner", "index.mjs");
|
|
87
|
+
try {
|
|
88
|
+
execFileSync(process.execPath, [scannerBin, subcommand, ...rawArgs], {
|
|
89
|
+
stdio: "inherit",
|
|
90
|
+
});
|
|
91
|
+
} catch (e) {
|
|
92
|
+
process.exit(e.status ?? 1);
|
|
93
|
+
}
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── help ──────────────────────────────────────────────────────────────────────
|
|
98
|
+
if (
|
|
99
|
+
subcommand === "help" ||
|
|
100
|
+
rawArgs.includes("--help") ||
|
|
101
|
+
rawArgs.includes("-h")
|
|
102
|
+
) {
|
|
103
|
+
printHelp();
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── init ──────────────────────────────────────────────────────────────────────
|
|
108
|
+
if (subcommand === "init" || subcommand === "update") {
|
|
109
|
+
const { values } = parseArgs({
|
|
110
|
+
args: rawArgs,
|
|
111
|
+
options: {
|
|
112
|
+
project: { type: "string", default: "." },
|
|
113
|
+
editor: { type: "string", default: "" },
|
|
114
|
+
mode: { type: "string", default: "native" }, // native | skin
|
|
115
|
+
"dry-run": { type: "boolean", default: false },
|
|
116
|
+
"skills-only": { type: "boolean", default: false },
|
|
117
|
+
force: { type: "boolean", default: false },
|
|
118
|
+
},
|
|
119
|
+
strict: false,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const projectRoot = resolve(values.project);
|
|
123
|
+
const dryRun = values["dry-run"];
|
|
124
|
+
const skillsOnly = values["skills-only"];
|
|
125
|
+
const mode = values.mode === "skin" ? "skin" : "native";
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
subcommand === "update" &&
|
|
129
|
+
readManifest(projectRoot)?.version === PKG.version &&
|
|
130
|
+
!values.force
|
|
131
|
+
) {
|
|
132
|
+
console.log(
|
|
133
|
+
`\n[wl-ui update] 当前项目已安装 v${PKG.version},无需重复操作。`,
|
|
134
|
+
);
|
|
135
|
+
console.log("如需强制更新:npx wl-ui update --force\n");
|
|
136
|
+
process.exit(0);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log(`\n[wl-ui ${subcommand}] 目标项目:${projectRoot}`);
|
|
140
|
+
console.log(
|
|
141
|
+
`[wl-ui ${subcommand}] 模式:${mode === "skin" ? "化妆 (skin)" : "原生 (native)"}`,
|
|
142
|
+
);
|
|
143
|
+
if (dryRun)
|
|
144
|
+
console.log(`[wl-ui ${subcommand}] DRY-RUN 模式:不实际写入文件\n`);
|
|
145
|
+
|
|
146
|
+
// 1. 检测编辑器
|
|
147
|
+
const editor = values.editor || detectEditor(projectRoot);
|
|
148
|
+
console.log(`[wl-ui ${subcommand}] 检测到编辑器:${editor}\n`);
|
|
149
|
+
|
|
150
|
+
// 2. 安装 skills(按 mode 过滤)
|
|
151
|
+
const installedFiles = installSkills({ projectRoot, editor, mode, dryRun });
|
|
152
|
+
installedFiles.push(...installSupportFiles({ projectRoot, dryRun }));
|
|
153
|
+
|
|
154
|
+
// 3. 安装接入配置(非 --skills-only 时)
|
|
155
|
+
if (!skillsOnly) {
|
|
156
|
+
installStyleSetup({ projectRoot, mode, dryRun });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!dryRun) {
|
|
160
|
+
writeManifest(projectRoot, {
|
|
161
|
+
version: PKG.version,
|
|
162
|
+
editor,
|
|
163
|
+
mode,
|
|
164
|
+
installedAt: new Date().toISOString(),
|
|
165
|
+
files: Object.fromEntries(
|
|
166
|
+
installedFiles.map((f) => [f, fileHash(join(projectRoot, f))]),
|
|
167
|
+
),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log(`\n✅ wl-ui ${subcommand} 完成!\n`);
|
|
172
|
+
printInstallSummary({ projectRoot, mode, editor });
|
|
173
|
+
console.log("下一步:");
|
|
174
|
+
if (mode === "skin") {
|
|
175
|
+
console.log(" npx wl-ui scan --target src --mode skin # 仅化妆层审计");
|
|
176
|
+
} else {
|
|
177
|
+
console.log(" npx wl-ui check --project . # 验证接入完整性");
|
|
178
|
+
console.log(" npx wl-ui all --project . # 完整扫描报告");
|
|
179
|
+
}
|
|
180
|
+
console.log("");
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (subcommand === "diff") {
|
|
185
|
+
const { values } = parseArgs({
|
|
186
|
+
args: rawArgs,
|
|
187
|
+
options: { project: { type: "string", default: "." } },
|
|
188
|
+
strict: false,
|
|
189
|
+
});
|
|
190
|
+
runDiff(resolve(values.project));
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (subcommand === "clean") {
|
|
195
|
+
const { values } = parseArgs({
|
|
196
|
+
args: rawArgs,
|
|
197
|
+
options: {
|
|
198
|
+
project: { type: "string", default: "." },
|
|
199
|
+
"dry-run": { type: "boolean", default: false },
|
|
200
|
+
},
|
|
201
|
+
strict: false,
|
|
202
|
+
});
|
|
203
|
+
runClean(resolve(values.project), values["dry-run"]);
|
|
204
|
+
process.exit(0);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (subcommand === "doctor") {
|
|
208
|
+
const { values } = parseArgs({
|
|
209
|
+
args: rawArgs,
|
|
210
|
+
options: { project: { type: "string", default: "." } },
|
|
211
|
+
strict: false,
|
|
212
|
+
});
|
|
213
|
+
runDoctor(resolve(values.project));
|
|
214
|
+
process.exit(0);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (subcommand === "prompts") {
|
|
218
|
+
console.log(triggerPrompts());
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── add-preset ─────────────────────────────────────────────────────────────────
|
|
223
|
+
if (subcommand === "add-preset") {
|
|
224
|
+
const presetName = rawArgs[0];
|
|
225
|
+
if (!presetName) {
|
|
226
|
+
console.error(
|
|
227
|
+
"[wl-ui add-preset] 请提供预设名称,例如:wl-ui add-preset my-biz",
|
|
228
|
+
);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
scaffoldPreset(presetName);
|
|
232
|
+
process.exit(0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
236
|
+
// 工具函数
|
|
237
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
238
|
+
|
|
239
|
+
/** 工具函数 */
|
|
240
|
+
|
|
241
|
+
/** 检测目标项目使用哪个 AI 编辑器 */
|
|
242
|
+
function detectEditor(projectRoot) {
|
|
243
|
+
if (existsSync(join(projectRoot, ".cursor"))) return "cursor";
|
|
244
|
+
if (existsSync(join(projectRoot, ".windsurf"))) return "windsurf";
|
|
245
|
+
if (existsSync(join(projectRoot, ".kiro"))) return "kiro";
|
|
246
|
+
if (existsSync(join(projectRoot, ".trae"))) return "trae";
|
|
247
|
+
if (existsSync(join(projectRoot, "CLAUDE.md"))) return "claude-code";
|
|
248
|
+
if (existsSync(join(projectRoot, ".clinerules"))) return "cline";
|
|
249
|
+
if (existsSync(join(projectRoot, "AGENTS.md"))) return "agents-generic";
|
|
250
|
+
if (existsSync(join(projectRoot, ".qoder"))) return "qoder";
|
|
251
|
+
// 默认 Copilot(最常见)
|
|
252
|
+
return "github-copilot";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** 读取 skills/ 目录下所有 SKILL.md 文件 */
|
|
256
|
+
function collectSkills(skillsDir) {
|
|
257
|
+
const skills = [];
|
|
258
|
+
function walk(dir) {
|
|
259
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
260
|
+
const full = join(dir, entry.name);
|
|
261
|
+
if (entry.isDirectory() && !entry.name.startsWith("_")) {
|
|
262
|
+
walk(full);
|
|
263
|
+
} else if (entry.name === "SKILL.md") {
|
|
264
|
+
const rel = relative(skillsDir, dir).replace(/\\/g, "/");
|
|
265
|
+
const content = readFileSync(full, "utf8");
|
|
266
|
+
skills.push({ path: rel, content, full });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
walk(skillsDir);
|
|
271
|
+
return skills;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** 读取编辑器 frontmatter 模板 */
|
|
275
|
+
function getHeaderTemplate(editor) {
|
|
276
|
+
const headerPath = join(
|
|
277
|
+
PKG_ROOT,
|
|
278
|
+
"skills",
|
|
279
|
+
"_meta",
|
|
280
|
+
"_compat",
|
|
281
|
+
"headers",
|
|
282
|
+
`${editorHeaderName(editor)}.txt`,
|
|
283
|
+
);
|
|
284
|
+
if (existsSync(headerPath)) return readFileSync(headerPath, "utf8");
|
|
285
|
+
return "";
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** 从 SKILL.md 内容中提取 frontmatter 字段 */
|
|
289
|
+
function parseSkillFrontmatter(content) {
|
|
290
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
291
|
+
if (!match) return { description: "", applyTo: "**/*.vue" };
|
|
292
|
+
const fm = match[1];
|
|
293
|
+
const descMatch = fm.match(/description:\s*\|\n([\s\S]*?)(?=\n\w|$)/);
|
|
294
|
+
const applyToMatch = fm.match(/applyTo:\s*"?([^"\n]+)"?/);
|
|
295
|
+
return {
|
|
296
|
+
description: descMatch ? descMatch[1].replace(/^ {2}/gm, "").trim() : "",
|
|
297
|
+
applyTo: applyToMatch ? applyToMatch[1].trim() : "**/*.vue",
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** 把 SKILL.md 内容(去掉原有 frontmatter)转为目标编辑器格式 */
|
|
302
|
+
function transformForEditor(content, editor) {
|
|
303
|
+
// 去掉原有 frontmatter
|
|
304
|
+
const body = content.replace(/^---\n[\s\S]*?\n---\n\n?/, "");
|
|
305
|
+
const { description, applyTo } = parseSkillFrontmatter(content);
|
|
306
|
+
const headerTemplate = getHeaderTemplate(editor);
|
|
307
|
+
|
|
308
|
+
if (!headerTemplate) return body;
|
|
309
|
+
|
|
310
|
+
const header = headerTemplate
|
|
311
|
+
.replace("{SKILL_DESCRIPTION}", description.split("\n")[0])
|
|
312
|
+
.replace("{APPLY_GLOB}", applyTo)
|
|
313
|
+
.replace(
|
|
314
|
+
"{SKILL_NAME}",
|
|
315
|
+
body
|
|
316
|
+
.split("\n")
|
|
317
|
+
.find((l) => l.startsWith("# "))
|
|
318
|
+
?.slice(2) || "Skill",
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
return header + "\n" + body;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** 安装 skills 到目标项目(按 mode 过滤)*/
|
|
325
|
+
function installSkills({ projectRoot, editor, mode = "native", dryRun }) {
|
|
326
|
+
const target = EDITOR_TARGETS[editor] || EDITOR_TARGETS["github-copilot"];
|
|
327
|
+
const targetDir = join(projectRoot, target.dir);
|
|
328
|
+
const skillsDir = join(PKG_ROOT, "skills");
|
|
329
|
+
|
|
330
|
+
console.log(`[wl-ui init] 安装 Skills → ${target.dir}`);
|
|
331
|
+
|
|
332
|
+
let skills = collectSkills(skillsDir);
|
|
333
|
+
|
|
334
|
+
// skin 模式:过滤掉 runtime/ 和 layouts/ (避免干扰老项目布局)
|
|
335
|
+
if (mode === "skin") {
|
|
336
|
+
skills = skills.filter(
|
|
337
|
+
(s) => !s.path.startsWith("runtime/") && !s.path.startsWith("layouts/"),
|
|
338
|
+
);
|
|
339
|
+
console.log(" [skin mode] 已过滤掉 runtime/ 和 layouts/ 类 skill");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
let count = 0;
|
|
343
|
+
const installedFiles = [];
|
|
344
|
+
if (target.singleFile) {
|
|
345
|
+
const outPath = join(projectRoot, target.singleFile);
|
|
346
|
+
const transformed = skills
|
|
347
|
+
.map((skill) => transformForEditor(skill.content, editor))
|
|
348
|
+
.join("\n\n---\n\n");
|
|
349
|
+
|
|
350
|
+
if (dryRun) {
|
|
351
|
+
console.log(` [dry-run] 写入 ${relative(projectRoot, outPath)}`);
|
|
352
|
+
} else {
|
|
353
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
354
|
+
writeFileSync(outPath, transformed, "utf8");
|
|
355
|
+
console.log(` ✔ 写入 ${relative(projectRoot, outPath)}`);
|
|
356
|
+
}
|
|
357
|
+
installedFiles.push(relative(projectRoot, outPath).replace(/\\/g, "/"));
|
|
358
|
+
count = skills.length;
|
|
359
|
+
} else {
|
|
360
|
+
for (const skill of skills) {
|
|
361
|
+
const fileName = skill.path.replace(/\//g, "-") + target.ext;
|
|
362
|
+
const outPath = join(targetDir, fileName);
|
|
363
|
+
const transformed = transformForEditor(skill.content, editor);
|
|
364
|
+
|
|
365
|
+
if (dryRun) {
|
|
366
|
+
console.log(` [dry-run] 写入 ${relative(projectRoot, outPath)}`);
|
|
367
|
+
} else {
|
|
368
|
+
mkdirSync(targetDir, { recursive: true });
|
|
369
|
+
writeFileSync(outPath, transformed, "utf8");
|
|
370
|
+
console.log(` ✔ 写入 ${relative(projectRoot, outPath)}`);
|
|
371
|
+
}
|
|
372
|
+
installedFiles.push(relative(projectRoot, outPath).replace(/\\/g, "/"));
|
|
373
|
+
count++;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
console.log(`\n 共安装 ${count} 个 Skill 文件\n`);
|
|
378
|
+
return installedFiles;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** 接入配置引导(tokens.css + styles import)按 mode 推荐不同 presets */
|
|
382
|
+
function installStyleSetup({ projectRoot, mode = "native", dryRun }) {
|
|
383
|
+
console.log("[wl-ui init] 检查样式接入配置...\n");
|
|
384
|
+
|
|
385
|
+
const tokensHref =
|
|
386
|
+
"/node_modules/@agile-team/wl-skills-ui/design/tokens/base.css";
|
|
387
|
+
const stylesEntry =
|
|
388
|
+
mode === "skin"
|
|
389
|
+
? "@agile-team/wl-skills-ui/styles/presets/skin"
|
|
390
|
+
: "@agile-team/wl-skills-ui/styles";
|
|
391
|
+
|
|
392
|
+
// 检查 index.html
|
|
393
|
+
const htmlFiles = [
|
|
394
|
+
join(projectRoot, "index.html"),
|
|
395
|
+
join(projectRoot, "public", "index.html"),
|
|
396
|
+
].filter((f) => existsSync(f));
|
|
397
|
+
|
|
398
|
+
for (const htmlFile of htmlFiles) {
|
|
399
|
+
const content = readFileSync(htmlFile, "utf8");
|
|
400
|
+
if (!content.includes("tokens") && !content.includes("wl-skills-ui")) {
|
|
401
|
+
const tokensLink = `\n <link rel="stylesheet" href="${tokensHref}" />`;
|
|
402
|
+
const updated = content.replace("</head>", tokensLink + "\n </head>");
|
|
403
|
+
if (dryRun) {
|
|
404
|
+
console.log(
|
|
405
|
+
` [dry-run] 追加 tokens link → ${relative(projectRoot, htmlFile)}`,
|
|
406
|
+
);
|
|
407
|
+
} else {
|
|
408
|
+
writeFileSync(htmlFile, updated, "utf8");
|
|
409
|
+
console.log(
|
|
410
|
+
` ✔ 追加 tokens link → ${relative(projectRoot, htmlFile)}`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
console.log(` ✅ tokens 已存在 → ${relative(projectRoot, htmlFile)}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// 提示 SCSS 接入(不自动修改,避免破坏现有样式顺序)
|
|
419
|
+
console.log(`
|
|
420
|
+
⚠️ 请手动在全局 SCSS 入口(main.scss 或 index.scss)添加:
|
|
421
|
+
@use '${stylesEntry}' as *;
|
|
422
|
+
${
|
|
423
|
+
mode === "native"
|
|
424
|
+
? `
|
|
425
|
+
⚠️ 请手动在 src/main.ts 添加:
|
|
426
|
+
import { installCommonPreset } from '@agile-team/wl-skills-ui/runtime/common-preset';
|
|
427
|
+
installCommonPreset();
|
|
428
|
+
`
|
|
429
|
+
: `
|
|
430
|
+
ℹ️ skin 模式不需要引入 runtime,页面布局 / 业务代码均保持现状。
|
|
431
|
+
`
|
|
432
|
+
}`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** 脚手架新预设文件 */
|
|
436
|
+
function scaffoldPreset(name) {
|
|
437
|
+
const outPath = join(PKG_ROOT, "runtime", "presets", `${name}.ts`);
|
|
438
|
+
if (existsSync(outPath)) {
|
|
439
|
+
console.error(`[wl-ui add-preset] 文件已存在:${outPath}`);
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
const template = `/**
|
|
443
|
+
* runtime/presets/${name}.ts — ${name} 业务预设
|
|
444
|
+
* 使用:import { install${capitalize(name)}Preset } from '@agile-team/wl-skills-ui/runtime/presets/${name}';
|
|
445
|
+
*/
|
|
446
|
+
import type { TagMapItem } from '../core/types';
|
|
447
|
+
import { renderTagNode } from '../core/renderers';
|
|
448
|
+
import { registerColumnAutoMaps } from '../core/registry';
|
|
449
|
+
|
|
450
|
+
// ── 状态映射 ──────────────────────────────────────────────────────────────────
|
|
451
|
+
export const MY_STATUS_MAP: Record<string | number, TagMapItem> = {
|
|
452
|
+
"0": { label: "待处理", type: "info" },
|
|
453
|
+
"1": { label: "处理中", type: "primary" },
|
|
454
|
+
"2": { label: "已完成", type: "success" },
|
|
455
|
+
"3": { label: "已驳回", type: "danger" },
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
export const renderMyStatus = (v: string | number | null | undefined) =>
|
|
459
|
+
renderTagNode(v, MY_STATUS_MAP);
|
|
460
|
+
|
|
461
|
+
// ── 安装 ──────────────────────────────────────────────────────────────────────
|
|
462
|
+
export function install${capitalize(name)}Preset(): void {
|
|
463
|
+
registerColumnAutoMaps({
|
|
464
|
+
myStatus: {
|
|
465
|
+
width: 90,
|
|
466
|
+
fixed: "right",
|
|
467
|
+
defaultNode: ({ row }) => renderMyStatus(row.myStatus),
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
`;
|
|
472
|
+
writeFileSync(outPath, template, "utf8");
|
|
473
|
+
console.log(`[wl-ui add-preset] ✔ 创建预设文件:${outPath}`);
|
|
474
|
+
console.log(` 在 main.ts 中引入:install${capitalize(name)}Preset()`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function capitalize(s) {
|
|
478
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function editorHeaderName(editor) {
|
|
482
|
+
return (
|
|
483
|
+
{
|
|
484
|
+
cursor: "cursor-mdc",
|
|
485
|
+
"github-copilot": "github-copilot",
|
|
486
|
+
windsurf: "windsurf",
|
|
487
|
+
kiro: "kiro",
|
|
488
|
+
trae: "trae",
|
|
489
|
+
"claude-code": "claude-code",
|
|
490
|
+
cline: "cline",
|
|
491
|
+
"agents-generic": "agents",
|
|
492
|
+
qoder: "qoder",
|
|
493
|
+
}[editor] || editor
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function installSupportFiles({ projectRoot, dryRun }) {
|
|
498
|
+
const files = [
|
|
499
|
+
{
|
|
500
|
+
rel: ".github/wl-skills-ui/TRIGGER_PROMPTS.md",
|
|
501
|
+
content: triggerPrompts(),
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
rel: ".github/wl-skills-ui/README.md",
|
|
505
|
+
content: installReadme(),
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
rel: ".mcp.json",
|
|
509
|
+
content: mergeMcpConfig(projectRoot),
|
|
510
|
+
},
|
|
511
|
+
];
|
|
512
|
+
const installed = [];
|
|
513
|
+
for (const f of files) {
|
|
514
|
+
const outPath = join(projectRoot, f.rel);
|
|
515
|
+
if (dryRun) {
|
|
516
|
+
console.log(` [dry-run] 写入 ${f.rel}`);
|
|
517
|
+
} else {
|
|
518
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
519
|
+
writeFileSync(outPath, f.content, "utf8");
|
|
520
|
+
console.log(` ✔ 写入 ${f.rel}`);
|
|
521
|
+
}
|
|
522
|
+
installed.push(f.rel);
|
|
523
|
+
}
|
|
524
|
+
return installed;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function triggerPrompts() {
|
|
528
|
+
return `# wl-skills-ui Skill 触发提示
|
|
529
|
+
|
|
530
|
+
## 核心原则
|
|
531
|
+
|
|
532
|
+
- wl-skills-ui 优先保证样式绝对管控,覆盖纯 Element Plus、老项目封装、Base*/jh*/C_* 以及 wl-skills-kit 最佳写法
|
|
533
|
+
- 不管是否使用 wl-skills-kit,wl-skills-ui 都要先保证视觉统一,再按需引导规范化重构
|
|
534
|
+
|
|
535
|
+
## 组合流程
|
|
536
|
+
|
|
537
|
+
- 新项目:用 wl-ui 的 new-project-init 流程接入统一 UI 风格
|
|
538
|
+
- 老项目:用 wl-ui 的 legacy-skin-align 流程做老项目化妆对齐
|
|
539
|
+
- 全量审计:用 wl-ui 的 full-audit 流程扫描当前项目,不修改代码
|
|
540
|
+
- 渐进迁移:用 wl-ui 的 progressive-migrate 流程从 skin 迁移到 runtime
|
|
541
|
+
|
|
542
|
+
## 智能触发
|
|
543
|
+
|
|
544
|
+
- 用户说"样式乱 / 不统一 / 老项目化妆":先调用 wks_ui_route_intent,再调用 wks_ui_scan --mode skin
|
|
545
|
+
- 用户说"卡片 / Tab / 详情 / 树 / 抽屉 / 上传 / 步骤条 / 更多操作":触发对应 Element Plus 组件族 skill
|
|
546
|
+
- 扫描 JSON 返回后:调用 wks_ui_recommend_flow 判断 recommendedFlows、nextActions 和 kitBridge
|
|
547
|
+
|
|
548
|
+
## 单点触发
|
|
549
|
+
|
|
550
|
+
- 用 wl-ui 的 vendors/base-table skill 检查当前文件
|
|
551
|
+
- 用 wl-ui 的 vendors/jh-components skill 检查当前文件
|
|
552
|
+
- 用 wl-ui 的 element/el-table skill 检查当前文件
|
|
553
|
+
- 用 wl-ui 的 element 组件族 skill 检查 card/tabs/descriptions/tree/drawer/upload/steps/overlay/navigation/feedback
|
|
554
|
+
- 用 wl-ui 的 runtime/design-tokens skill 检查硬编码颜色
|
|
555
|
+
|
|
556
|
+
## 分工边界
|
|
557
|
+
|
|
558
|
+
- wl-skills-ui:视觉一致性、化妆层、设计令牌、Runtime 渲染、UI 扫描修复
|
|
559
|
+
- wl-skills-kit:编码规范、页面生成、菜单/字典/权限同步、通用 Agent Pipeline
|
|
560
|
+
|
|
561
|
+
## 执行约束
|
|
562
|
+
|
|
563
|
+
- 扫描只读,修复前必须等待用户确认
|
|
564
|
+
- skin 模式只处理 L0/L1/L2,不改业务布局和 runtime
|
|
565
|
+
- fix 前建议先 dry-run 或通过 MCP 调用 wks_ui_fix_dry_run
|
|
566
|
+
- 涉及 BaseTable render-type/cid、renderOps 或页面结构规范时,视觉统一后再桥接 wl-skills-kit validate-page / doctor-ui
|
|
567
|
+
`;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function installReadme() {
|
|
571
|
+
return `# wl-skills-ui 已安装
|
|
572
|
+
|
|
573
|
+
- 触发提示:.github/wl-skills-ui/TRIGGER_PROMPTS.md
|
|
574
|
+
- MCP Server:wl-skills-ui
|
|
575
|
+
- 更新命令:npx wl-ui update
|
|
576
|
+
|
|
577
|
+
wl-skills-kit 可选安装,两者分工独立、不强耦合。
|
|
578
|
+
`;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function mergeMcpConfig(projectRoot) {
|
|
582
|
+
const mcpPath = join(projectRoot, ".mcp.json");
|
|
583
|
+
let config = { mcpServers: {} };
|
|
584
|
+
if (existsSync(mcpPath)) {
|
|
585
|
+
try {
|
|
586
|
+
config = JSON.parse(readFileSync(mcpPath, "utf8"));
|
|
587
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
588
|
+
} catch {
|
|
589
|
+
config = { mcpServers: {} };
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
config.mcpServers["wl-skills-ui"] = {
|
|
593
|
+
command: "node",
|
|
594
|
+
args: ["node_modules/@agile-team/wl-skills-ui/mcp/server.js"],
|
|
595
|
+
};
|
|
596
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function readManifest(projectRoot) {
|
|
600
|
+
const manifestPath = join(projectRoot, MANIFEST_NAME);
|
|
601
|
+
if (!existsSync(manifestPath)) return null;
|
|
602
|
+
try {
|
|
603
|
+
return JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
604
|
+
} catch {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function writeManifest(projectRoot, data) {
|
|
610
|
+
writeFileSync(
|
|
611
|
+
join(projectRoot, MANIFEST_NAME),
|
|
612
|
+
`${JSON.stringify(data, null, 2)}\n`,
|
|
613
|
+
"utf8",
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function fileHash(filePath) {
|
|
618
|
+
if (!existsSync(filePath)) return "";
|
|
619
|
+
return createHash("sha256").update(readFileSync(filePath)).digest("hex");
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function runDiff(projectRoot) {
|
|
623
|
+
const manifest = readManifest(projectRoot);
|
|
624
|
+
if (!manifest) {
|
|
625
|
+
console.log(
|
|
626
|
+
`\n[wl-ui diff] 未找到 ${MANIFEST_NAME},请先执行 wl-ui init。\n`,
|
|
627
|
+
);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const changed = [];
|
|
631
|
+
const missing = [];
|
|
632
|
+
const same = [];
|
|
633
|
+
for (const [rel, hash] of Object.entries(manifest.files || {})) {
|
|
634
|
+
const full = join(projectRoot, rel);
|
|
635
|
+
if (!existsSync(full)) missing.push(rel);
|
|
636
|
+
else if (fileHash(full) !== hash) changed.push(rel);
|
|
637
|
+
else same.push(rel);
|
|
638
|
+
}
|
|
639
|
+
console.log(`\n[wl-ui diff] manifest: v${manifest.version}`);
|
|
640
|
+
console.log(`缺失: ${missing.length}`);
|
|
641
|
+
console.log(`内容不同: ${changed.length}`);
|
|
642
|
+
console.log(`相同: ${same.length}\n`);
|
|
643
|
+
printList("缺失文件", missing);
|
|
644
|
+
printList("内容不同", changed);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function runClean(projectRoot, dryRun) {
|
|
648
|
+
const manifest = readManifest(projectRoot);
|
|
649
|
+
if (!manifest) {
|
|
650
|
+
console.log(`\n[wl-ui clean] 未找到 ${MANIFEST_NAME},无需清理。\n`);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const files = Object.keys(manifest.files || {});
|
|
654
|
+
for (const rel of files) {
|
|
655
|
+
const full = join(projectRoot, rel);
|
|
656
|
+
if (!existsSync(full)) continue;
|
|
657
|
+
if (dryRun) console.log(` 删除 ${rel}`);
|
|
658
|
+
else rmSync(full, { force: true });
|
|
659
|
+
}
|
|
660
|
+
if (!dryRun && existsSync(join(projectRoot, MANIFEST_NAME)))
|
|
661
|
+
unlinkSync(join(projectRoot, MANIFEST_NAME));
|
|
662
|
+
console.log(
|
|
663
|
+
dryRun
|
|
664
|
+
? "\n[DRY-RUN] 未实际删除。\n"
|
|
665
|
+
: `\n✅ 已清理 ${files.length} 个安装文件。\n`,
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function runDoctor(projectRoot) {
|
|
670
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
671
|
+
let pkg = null;
|
|
672
|
+
if (existsSync(pkgPath)) {
|
|
673
|
+
try {
|
|
674
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
675
|
+
} catch {
|
|
676
|
+
pkg = null;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const deps = pkg ? { ...pkg.dependencies, ...pkg.devDependencies } : {};
|
|
680
|
+
console.log("\n[wl-ui doctor]\n");
|
|
681
|
+
console.log(`${pkg ? "✔" : "⚠"} package.json — ${pkg ? "存在" : "缺失"}`);
|
|
682
|
+
console.log(
|
|
683
|
+
`${readManifest(projectRoot) ? "✔" : "⚠"} wl-skills-ui manifest — ${readManifest(projectRoot)?.version || "未安装"}`,
|
|
684
|
+
);
|
|
685
|
+
console.log(
|
|
686
|
+
`${existsSync(join(projectRoot, ".mcp.json")) ? "✔" : "⚠"} MCP config — .mcp.json`,
|
|
687
|
+
);
|
|
688
|
+
console.log(
|
|
689
|
+
`${deps["@agile-team/wl-skills-kit"] || existsSync(join(projectRoot, ".wl-skills-manifest.json")) ? "✔" : "ℹ"} wl-skills-kit bridge — 可选,不强耦合`,
|
|
690
|
+
);
|
|
691
|
+
console.log(
|
|
692
|
+
`${hasGitStandards(projectRoot, pkg) ? "✔" : "⚠"} git-standards — 建议接入 @robot-admin/git-standards\n`,
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function hasGitStandards(projectRoot, pkg) {
|
|
697
|
+
return Boolean(
|
|
698
|
+
existsSync(join(projectRoot, "eslint.config.ts")) ||
|
|
699
|
+
existsSync(join(projectRoot, ".prettierrc.js")) ||
|
|
700
|
+
existsSync(join(projectRoot, ".husky")) ||
|
|
701
|
+
pkg?.scripts?.["standards:init"],
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function printInstallSummary({ projectRoot, mode, editor }) {
|
|
706
|
+
console.log("已安装能力:");
|
|
707
|
+
console.log(` - 编辑器规则:${editor}`);
|
|
708
|
+
console.log(
|
|
709
|
+
` - UI Skill:${mode === "skin" ? "skin 模式" : "native 全量模式"}`,
|
|
710
|
+
);
|
|
711
|
+
console.log(" - MCP:wl-skills-ui");
|
|
712
|
+
console.log(" - 触发提示:.github/wl-skills-ui/TRIGGER_PROMPTS.md");
|
|
713
|
+
if (existsSync(join(projectRoot, ".wl-skills-manifest.json"))) {
|
|
714
|
+
console.log(" - 桥接提醒:检测到 wl-skills-kit,两包独立分工,可组合使用");
|
|
715
|
+
} else {
|
|
716
|
+
console.log(
|
|
717
|
+
" - 桥接提醒:如需编码规范/页面生成/菜单字典权限,可选安装 wl-skills-kit",
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
if (!hasGitStandards(projectRoot, null)) {
|
|
721
|
+
console.log(" - 规范插件:建议执行 npx @robot-admin/git-standards init");
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function printList(title, list) {
|
|
726
|
+
if (!list.length) return;
|
|
727
|
+
console.log(`${title}:`);
|
|
728
|
+
for (const item of list) console.log(` - ${item}`);
|
|
729
|
+
console.log("");
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// ── 帮助信息 ──────────────────────────────────────────────────────────────────
|
|
733
|
+
function printHelp() {
|
|
734
|
+
console.log(`
|
|
735
|
+
wl-ui — @agile-team/wl-skills-ui 统一 CLI v${PKG.version}
|
|
736
|
+
|
|
737
|
+
用法:
|
|
738
|
+
wl-ui init [--project <path>] [--editor <editor>] [--mode native|skin]
|
|
739
|
+
[--dry-run] [--skills-only]
|
|
740
|
+
把 skills/ 写入目标项目的 AI 编辑器规则目录
|
|
741
|
+
wl-ui update [--project <path>] [--force] [--dry-run]
|
|
742
|
+
增量更新 skills / MCP / 触发提示
|
|
743
|
+
wl-ui diff [--project <path>]
|
|
744
|
+
对比已安装文件与 manifest
|
|
745
|
+
wl-ui clean [--project <path>] [--dry-run]
|
|
746
|
+
清理 wl-skills-ui 安装文件
|
|
747
|
+
wl-ui doctor [--project <path>]
|
|
748
|
+
检查安装状态 / MCP / 桥接 / 规范插件
|
|
749
|
+
wl-ui prompts
|
|
750
|
+
打印 AI 触发提示词
|
|
751
|
+
|
|
752
|
+
wl-ui scan --target <src> [--layer L0,L1,L2] [--vendor base-table,jh]
|
|
753
|
+
[--mode skin|native] [--outFile report.md]
|
|
754
|
+
[--exempt <config.json>]
|
|
755
|
+
wl-ui check --project <项目根目录>
|
|
756
|
+
wl-ui fix --target <src目录> [--dry-run] [--no-snapshot]
|
|
757
|
+
wl-ui all --project <项目根目录> [--outFile report.md]
|
|
758
|
+
|
|
759
|
+
wl-ui snapshot list [--project .] 列出所有快照
|
|
760
|
+
wl-ui snapshot rollback [--id <id>] [--dry-run] 回退到快照(默认最新)
|
|
761
|
+
wl-ui snapshot diff [--id <id>] 查看快照与当前差异
|
|
762
|
+
wl-ui snapshot clean [--keep <N>] 清理旧快照
|
|
763
|
+
|
|
764
|
+
wl-ui add-preset <name> 脚手架一个新的业务预设文件
|
|
765
|
+
|
|
766
|
+
参数:
|
|
767
|
+
--project 项目根目录(默认 .)
|
|
768
|
+
--editor 指定编辑器:github-copilot | cursor | windsurf | kiro | trae | claude-code | cline | agents-generic | qoder
|
|
769
|
+
--mode init: native(默认,完整接入) | skin(化妆,老项目)
|
|
770
|
+
scan: skin(只看L0/L1/L2) | native(全量)
|
|
771
|
+
--layer scan 过滤:L0/L1/L2/L3/L4(逗号分隔)
|
|
772
|
+
--vendor scan 过滤:element/base-table/jh-components/...(逗号分隔)
|
|
773
|
+
--exempt 豁免配置文件路径(默认 .wk-exempt.json)
|
|
774
|
+
--dry-run 预览模式,不实际写入文件
|
|
775
|
+
--no-snapshot fix 时跳过快照创建
|
|
776
|
+
--skills-only 仅安装 skill 文件,不处理 index.html
|
|
777
|
+
--force update 时强制覆盖同版本安装
|
|
778
|
+
|
|
779
|
+
示例:
|
|
780
|
+
npx wl-ui init
|
|
781
|
+
npx wl-ui update --force
|
|
782
|
+
npx wl-ui doctor
|
|
783
|
+
npx wl-ui prompts
|
|
784
|
+
npx wl-ui init --mode skin --project /path/to/legacy-project
|
|
785
|
+
npx wl-ui scan --target src --mode skin --outFile report.md
|
|
786
|
+
npx wl-ui scan --target src --layer L0,L1
|
|
787
|
+
npx wl-ui fix --target src # 自动创建快照 + 修复
|
|
788
|
+
npx wl-ui snapshot rollback # 一键回退最近修复
|
|
789
|
+
npx wl-ui add-preset my-biz
|
|
790
|
+
|
|
791
|
+
规范插件:
|
|
792
|
+
npx @robot-admin/git-standards init
|
|
793
|
+
`);
|
|
794
|
+
}
|