@agile-team/wl-skills-kit 1.1.6 → 1.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0] - 2026-04-10
4
+
5
+ ### 新增:CLI v2.0 — update 增量更新 + clean 构建清理
6
+
7
+ - **`update` 命令**:基于 MD5 增量更新,仅覆盖有变化的文件
8
+ - 对比 `.wl-skills-manifest.json` 清单中的 MD5 哈希
9
+ - 输出新增/更新/未变文件数 + 版本变化提示
10
+ - 支持 `--dry-run` 预览
11
+ - **`clean` 命令**:构建前清理 AI 开发辅助文件
12
+ - 删除 `.github/`、`docs/`、`demo/`、编辑器配置等非组件文件
13
+ - **保护路径**:`src/components/` + `src/types/` 永远不会被清理
14
+ - 删除空父目录(自动清理目录树)
15
+ - 清除 `.wl-skills-manifest.json` 自身
16
+ - 支持 `--dry-run` 预览
17
+ - **Manifest 清单系统**(`.wl-skills-manifest.json`):
18
+ - `init` / `update` 执行后自动生成,记录版本 + 文件路径 → MD5 映射
19
+ - 供 `update` 做增量比对、`clean` 做精准删除
20
+ - 已加入 `.gitignore`
21
+ - CLI 重构为 3 个子命令:`init`(默认,向后兼容)、`update`、`clean`
22
+ - README.md 新增「CLI 命令」章节,更新「快速开始」「更新策略」「安装行为说明」
23
+
24
+ ## [1.1.7] - 2026-04-10
25
+
26
+ ### 修复:menu-sync SKILL.md 漏同步
27
+
28
+ - `files/.github/skills/menu-sync/SKILL.md` 未随 v1.1.6 同步源仓库版本
29
+ - 修复内容:sysAppNo 字段补齐(配置模板/表格/示例)、`{appNo}` → `{sysAppNo}` 变量统一、响应码说明补充
30
+ - 全量审计确认 19 个同步文件 + 9 个 TPL 模板零差异
31
+
3
32
  ## [1.1.6] - 2026-04-10
4
33
 
5
34
  ### 修复:menu-config.md 残留引用 + env 模板字段缺失
package/README.md CHANGED
@@ -15,11 +15,14 @@ npx @agile-team/wl-skills-kit
15
15
  # 预览将写入哪些文件(不实际写入)
16
16
  npx @agile-team/wl-skills-kit --dry-run
17
17
 
18
- # 更新到最新版
19
- npx @agile-team/wl-skills-kit@latest
18
+ # 增量更新(仅覆盖有变化的文件)
19
+ npx @agile-team/wl-skills-kit@latest update
20
+
21
+ # 构建前清理 AI 开发辅助文件(保留组件代码)
22
+ npx @agile-team/wl-skills-kit clean
20
23
  ```
21
24
 
22
- 就这样。108 个文件按原始路径导入到你的项目,**无其他副作用**。
25
+ 117 个文件按原始路径导入到你的项目,**无其他副作用**。
23
26
 
24
27
  ---
25
28
 
@@ -240,6 +243,61 @@ npx @agile-team/wl-skills-kit
240
243
 
241
244
  ---
242
245
 
246
+ ## CLI 命令
247
+
248
+ ### `init`(默认)
249
+
250
+ 首次安装或完整重装 — 全量写入所有文件。
251
+
252
+ ```bash
253
+ npx @agile-team/wl-skills-kit # 等同于 init
254
+ npx @agile-team/wl-skills-kit init
255
+ npx @agile-team/wl-skills-kit --dry-run # 预览模式
256
+ ```
257
+
258
+ ### `update`
259
+
260
+ 增量更新 — 基于 MD5 比对,仅覆盖有变化的文件,跳过未变化的文件。
261
+
262
+ ```bash
263
+ npx @agile-team/wl-skills-kit@latest update
264
+ npx @agile-team/wl-skills-kit update --dry-run
265
+ ```
266
+
267
+ 输出示例:
268
+ ```
269
+ ✔ 完成!
270
+ 新增: 3 个文件 ← 新版本新增的文件
271
+ 更新: 5 个文件 ← 内容有变化,已覆盖
272
+ 未变: 109 个文件 ← MD5 一致,未动
273
+ ```
274
+
275
+ > 需要先执行过 `init` 才能使用 `update`(依赖 `.wl-skills-manifest.json` 清单文件)。
276
+
277
+ ### `clean`
278
+
279
+ 构建前清理 — 移除 AI 开发辅助文件(Skill、文档、样例、编辑器配置),**保留组件代码**。
280
+
281
+ ```bash
282
+ npx @agile-team/wl-skills-kit clean
283
+ npx @agile-team/wl-skills-kit clean --dry-run
284
+ ```
285
+
286
+ **保护路径**(永远不会被清理):
287
+ - `src/components/` — 全局/局部/远程组件
288
+ - `src/types/` — 类型桶文件
289
+
290
+ **清理范围**:
291
+ - `.github/` — Skill 文件 + 设计文档
292
+ - `docs/` — 组件 API 文档
293
+ - `demo/` — 领域样例
294
+ - 编辑器配置 — `.cursorrules` / `AGENTS.md` / `CLAUDE.md` 等 8 个文件
295
+ - `.wl-skills-manifest.json` — 清单文件自身
296
+
297
+ > 适用于 CI/CD 构建流程:`npx @agile-team/wl-skills-kit clean && npm run build`
298
+
299
+ ---
300
+
243
301
  ## 安装行为说明
244
302
 
245
303
  | ✅ 会做 | ❌ 不会做 |
@@ -247,7 +305,7 @@ npx @agile-team/wl-skills-kit
247
305
  | 写入 `.github/` `docs/` `src/` `demo/` | 不修改 `package.json` |
248
306
  | 已存在的同名文件会被覆盖 | 不修改 `node_modules/` |
249
307
  | 自动创建不存在的目录 | 不执行 postinstall |
250
- | | 不删除任何文件 |
308
+ | 生成 `.wl-skills-manifest.json` 清单 | 不删除任何文件(init/update) |
251
309
 
252
310
  ### 安全路径(不会被覆盖)
253
311
 
@@ -310,13 +368,25 @@ import { AbstractPageQueryHook, BaseQueryItemDesc, ActionButtonDesc, TableColumn
310
368
  ### 5. 更新策略
311
369
 
312
370
  ```bash
313
- # 拉取最新模板(会覆盖内置文件,不动安全路径)
371
+ # 方式一:增量更新(推荐 — 仅覆盖变化的文件,速度快)
372
+ npx @agile-team/wl-skills-kit@latest update
373
+
374
+ # 方式二:全量重装(适合版本跨度大、或清单文件丢失的情况)
314
375
  npx @agile-team/wl-skills-kit@latest
315
376
  ```
316
377
 
317
378
  - **通用改进** → 提 PR 到 wl-skills-kit 仓库,合并后所有项目受益
318
379
  - **领域专有** → 放在安全路径下,永远不会被覆盖
319
380
 
381
+ ### 6. Manifest 清单文件
382
+
383
+ `init` 和 `update` 执行后会在项目根目录生成 `.wl-skills-manifest.json`,记录所有写入的文件路径及 MD5 哈希。该文件用于:
384
+
385
+ - `update` 命令的增量比对(跳过未变化的文件)
386
+ - `clean` 命令的精准删除(只删除 Skill 体系写入的文件)
387
+
388
+ > 建议将 `.wl-skills-manifest.json` 加入 `.gitignore`(每台开发机独立维护即可)。
389
+
320
390
  ---
321
391
 
322
392
  ## 技术栈适配
package/bin/wl-skills.js CHANGED
@@ -1,52 +1,61 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * wl-skills-kit CLI v1.1
4
+ * wl-skills-kit CLI v2.0
5
5
  *
6
- * 功能:
7
- * 1. 将 files/ 目录下的所有文件按原始路径拷贝到当前工作目录
8
- * 2. copilot-instructions.md 动态生成多编辑器配置文件
9
- * 覆盖 Cursor / Windsurf / Kiro / Trae / Claude Code / Roo/Cline / AGENTS.md
10
- *
11
- * 用法:npx @agile-team/wl-skills-kit [--dry-run]
6
+ * 命令:
7
+ * init 全量安装(默认,向后兼容)
8
+ * update 增量更新(仅覆盖有变化的文件,展示 diff 摘要)
9
+ * clean 构建清理(移除 AI 指令/文档/样例,保留组件和类型)
10
+ * --help 帮助
11
+ * --dry-run 预览模式(所有命令均支持)
12
12
  */
13
13
 
14
14
  const fs = require("fs");
15
15
  const path = require("path");
16
+ const crypto = require("crypto");
16
17
 
17
18
  const FILES_DIR = path.resolve(__dirname, "..", "files");
18
19
  const TARGET_DIR = process.cwd();
20
+ const MANIFEST_NAME = ".wl-skills-manifest.json";
21
+ const MANIFEST_PATH = path.join(TARGET_DIR, MANIFEST_NAME);
22
+ const PKG = require("../package.json");
19
23
  const args = process.argv.slice(2);
20
24
  const dryRun = args.includes("--dry-run");
21
25
  const showHelp = args.includes("--help") || args.includes("-h");
26
+ const command = args.find((a) => !a.startsWith("-")) || "init";
22
27
 
23
28
  if (showHelp) {
24
29
  console.log(`
25
- wl-skills-kit — AI Skill 模板包
30
+ wl-skills-kit v${PKG.version} — AI Skill 模板包
26
31
 
27
32
  用法:
28
- npx @agile-team/wl-skills-kit 安装/更新模板文件到当前项目
29
- npx @agile-team/wl-skills-kit --dry-run 预览将要写入的文件(不实际写入)
30
-
31
- 安装后写入:
32
- .github/ AI 指令 + Skills + 文档(GitHub Copilot)
33
- docs/ 组件 API 文档
34
- src/ 通用组件 + 类型定义
35
- demo/ 领域样例(AI 学习参考)
36
- AGENTS.md 通用 AI 编辑器兜底(Linux Foundation 标准)
37
- CLAUDE.md Claude Code / Claude CLI
38
- .clinerules Roo Code / Cline
39
- .cursorrules Cursor(legacy)
40
- .cursor/rules/ Cursor(新格式)
41
- .windsurfrules Windsurf (Cascade)
42
- .kiro/steering/ Kiro
43
- .trae/rules/ Trae
33
+ npx @agile-team/wl-skills-kit [命令] [选项]
34
+
35
+ 命令:
36
+ init 全量安装模板文件到当前项目(默认)
37
+ update 增量更新(仅覆盖有变化的文件,展示变更摘要)
38
+ clean 构建清理(移除开发期 AI 文件,保留 src/components + src/types)
39
+
40
+ 选项:
41
+ --dry-run 预览模式,不实际写入/删除任何文件
42
+ --help 显示帮助
43
+
44
+ 示例:
45
+ npx @agile-team/wl-skills-kit 安装全量文件
46
+ npx @agile-team/wl-skills-kit update 仅更新有变化的文件
47
+ npx @agile-team/wl-skills-kit clean 清理开发期文件
48
+ npx @agile-team/wl-skills-kit clean --dry-run 预览将要清理哪些文件
49
+
50
+ 清理保护路径(clean 不删除):
51
+ src/components/ 通用组件(被业务页面 import,构建必需)
52
+ src/types/ 类型桶文件(构建必需)
44
53
  `);
45
54
  process.exit(0);
46
55
  }
47
56
 
48
57
  /**
49
- * 递归遍历目录,返回所有文件的相对路径
58
+ * 递归遍历目录,返回所有文件的相对路径(正斜杠)
50
59
  */
51
60
  function walkDir(dir, baseDir, fileList) {
52
61
  fileList = fileList || [];
@@ -56,12 +65,22 @@ function walkDir(dir, baseDir, fileList) {
56
65
  if (entry.isDirectory()) {
57
66
  walkDir(fullPath, baseDir, fileList);
58
67
  } else {
59
- fileList.push(path.relative(baseDir, fullPath));
68
+ fileList.push(path.relative(baseDir, fullPath).replace(/\\/g, "/"));
60
69
  }
61
70
  }
62
71
  return fileList;
63
72
  }
64
73
 
74
+ /** 计算文件 md5 */
75
+ function fileMd5(fp) {
76
+ return crypto.createHash("md5").update(fs.readFileSync(fp)).digest("hex");
77
+ }
78
+
79
+ /** 计算字符串内容 md5 */
80
+ function contentMd5(c) {
81
+ return crypto.createHash("md5").update(c, "utf8").digest("hex");
82
+ }
83
+
65
84
  /**
66
85
  * 写入文件(自动创建目录)
67
86
  * @returns {"created"|"updated"}
@@ -78,121 +97,245 @@ function writeFile(destPath, content) {
78
97
  return exists ? "updated" : "created";
79
98
  }
80
99
 
81
- // ─── 主逻辑 ────────────────────────────────────────────────────────────────
82
-
83
- console.log("");
84
- console.log(" wl-skills-kit v" + require("../package.json").version);
85
- console.log(" 目标目录: " + TARGET_DIR);
86
- console.log("");
87
-
88
- if (!fs.existsSync(FILES_DIR)) {
89
- console.error(" ✖ files/ 目录不存在,包可能已损坏");
90
- process.exit(1);
100
+ /** 复制文件(自动创建目录) */
101
+ function copyFileSafe(srcPath, destPath) {
102
+ const destDir = path.dirname(destPath);
103
+ if (!fs.existsSync(destDir)) {
104
+ fs.mkdirSync(destDir, { recursive: true });
105
+ }
106
+ const exists = fs.existsSync(destPath);
107
+ if (!dryRun) {
108
+ fs.copyFileSync(srcPath, destPath);
109
+ }
110
+ return exists ? "updated" : "created";
91
111
  }
92
112
 
93
- let created = 0;
94
- let updated = 0;
95
-
96
- // ── Step 1: 复制 files/ 静态文件 ─────────────────────────────────────────
113
+ /** 删除文件,并向上清理空父目录 */
114
+ function removeFileAndEmptyParents(filePath) {
115
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
116
+ let dir = path.dirname(filePath);
117
+ while (dir !== TARGET_DIR && dir.length > TARGET_DIR.length) {
118
+ try {
119
+ if (fs.readdirSync(dir).length === 0) {
120
+ fs.rmdirSync(dir);
121
+ dir = path.dirname(dir);
122
+ } else { break; }
123
+ } catch (e) { break; }
124
+ }
125
+ }
97
126
 
98
- const files = walkDir(FILES_DIR, FILES_DIR);
127
+ /** 读取 manifest */
128
+ function readManifest() {
129
+ if (fs.existsSync(MANIFEST_PATH)) {
130
+ try { return JSON.parse(fs.readFileSync(MANIFEST_PATH, "utf8")); }
131
+ catch (e) { return null; }
132
+ }
133
+ return null;
134
+ }
99
135
 
100
- if (dryRun) {
101
- console.log(" [Step 1] files/ 静态文件:\n");
136
+ /** 写入 manifest */
137
+ function writeManifest(data) {
138
+ fs.writeFileSync(MANIFEST_PATH, JSON.stringify(data, null, 2), "utf8");
102
139
  }
103
140
 
104
- for (const file of files) {
105
- const src = path.join(FILES_DIR, file);
106
- const dest = path.join(TARGET_DIR, file);
107
- const destDir = path.dirname(dest);
141
+ // 受保护路径(clean 不删除)
142
+ const PROTECTED_PREFIXES = ["src/components/", "src/types/"];
143
+ function isProtected(relPath) {
144
+ return PROTECTED_PREFIXES.some((p) => relPath.startsWith(p));
145
+ }
108
146
 
109
- if (!fs.existsSync(destDir)) {
110
- fs.mkdirSync(destDir, { recursive: true });
111
- }
147
+ // ─── 编辑器配置生成 ─────────────────────────────────────────────────────
112
148
 
113
- const exists = fs.existsSync(dest);
114
- if (!dryRun) {
115
- fs.copyFileSync(src, dest);
116
- }
149
+ const AUTO_HEADER =
150
+ "<!-- @agile-team/wl-skills-kit 自动生成。源文件:.github/copilot-instructions.md -->\n" +
151
+ "<!-- 请勿手动编辑本文件,更新时重新执行:npx @agile-team/wl-skills-kit@latest -->\n\n";
117
152
 
118
- if (dryRun) {
119
- console.log(" " + (exists ? "覆盖" : "新增") + " " + file);
120
- } else {
121
- exists ? updated++ : created++;
122
- }
123
- }
153
+ const CURSOR_MDC_HEADER =
154
+ "---\n" +
155
+ 'description: "项目编码规范(由 wl-skills-kit 自动生成)"\n' +
156
+ "alwaysApply: true\n" +
157
+ "---\n\n" +
158
+ AUTO_HEADER;
124
159
 
125
- // ── Step 2: 动态生成多编辑器配置文件 ────────────────────────────────────
126
-
127
- // 读取 copilot-instructions.md(来自 files/ 包内,保证内容与安装文件一致)
128
- const INSTRUCTIONS_SRC = path.join(
129
- FILES_DIR,
130
- ".github",
131
- "copilot-instructions.md"
132
- );
133
-
134
- if (fs.existsSync(INSTRUCTIONS_SRC)) {
135
- const raw = fs.readFileSync(INSTRUCTIONS_SRC, "utf8");
136
- const AUTO_HEADER =
137
- "<!-- 由 @agile-team/wl-skills-kit 自动生成。源文件:.github/copilot-instructions.md -->\n" +
138
- "<!-- 请勿手动编辑本文件,更新时重新执行:npx @agile-team/wl-skills-kit@latest -->\n\n";
139
-
140
- // Cursor 新格式 .mdc 需要 YAML frontmatter
141
- const CURSOR_MDC_HEADER =
142
- "---\n" +
143
- 'description: "项目编码规范(由 wl-skills-kit 自动生成)"\n' +
144
- "alwaysApply: true\n" +
145
- "---\n\n" +
146
- AUTO_HEADER;
147
-
148
- // [目标相对路径, 文件内容]
149
- const editorConfigs = [
150
- // 通用兜底(Linux Foundation 标准,绝大多数 AI 工具均支持)
160
+ function getEditorConfigs(raw) {
161
+ return [
151
162
  ["AGENTS.md", AUTO_HEADER + raw],
152
- // Claude Code / Claude CLI
153
163
  ["CLAUDE.md", AUTO_HEADER + raw],
154
- // Roo Code / Cline
155
164
  [".clinerules", AUTO_HEADER + raw],
156
- // Cursor — legacy(根目录,仍被新版支持)
157
165
  [".cursorrules", AUTO_HEADER + raw],
158
- // Cursor — 新格式(带 frontmatter 的 .mdc)
159
166
  [".cursor/rules/conventions.mdc", CURSOR_MDC_HEADER + raw],
160
- // Windsurf (Cascade)
161
167
  [".windsurfrules", AUTO_HEADER + raw],
162
- // Kiro
163
168
  [".kiro/steering/conventions.md", AUTO_HEADER + raw],
164
- // Trae
165
- [".trae/rules/conventions.md", AUTO_HEADER + raw]
169
+ [".trae/rules/conventions.md", AUTO_HEADER + raw],
166
170
  ];
171
+ }
167
172
 
168
- if (dryRun) {
169
- console.log("\n [Step 2] 多编辑器配置文件(从 copilot-instructions.md 生成):\n");
173
+ // ─── 命令: init / update ────────────────────────────────────────────────
174
+
175
+ function runInstall(incremental) {
176
+ const label = incremental ? "update" : "init";
177
+ console.log("");
178
+ console.log(" wl-skills-kit v" + PKG.version + " [" + label + "]");
179
+ console.log(" 目标目录: " + TARGET_DIR);
180
+ if (dryRun) console.log(" 模式: --dry-run(预览)");
181
+ console.log("");
182
+
183
+ if (!fs.existsSync(FILES_DIR)) {
184
+ console.error(" ✖ files/ 目录不存在,包可能已损坏");
185
+ process.exit(1);
170
186
  }
171
187
 
172
- for (const [relPath, content] of editorConfigs) {
188
+ const oldManifest = readManifest();
189
+ const newManifest = { version: PKG.version, files: {} };
190
+ let created = 0, updated = 0, unchanged = 0;
191
+
192
+ // ── Step 1: 复制 files/ 静态文件 ───────────────────────────────────
193
+
194
+ const files = walkDir(FILES_DIR, FILES_DIR);
195
+ if (dryRun) console.log(" [Step 1] files/ 静态文件:\n");
196
+
197
+ for (const relPath of files) {
198
+ const src = path.join(FILES_DIR, relPath);
173
199
  const dest = path.join(TARGET_DIR, relPath);
174
- const exists = fs.existsSync(dest);
200
+ const srcHash = fileMd5(src);
201
+ newManifest.files[relPath] = srcHash;
202
+
203
+ // update 模式: 跳过内容相同的文件
204
+ if (incremental && fs.existsSync(dest)) {
205
+ if (srcHash === fileMd5(dest)) { unchanged++; continue; }
206
+ }
207
+
208
+ if (dryRun) {
209
+ const exists = fs.existsSync(dest);
210
+ console.log(" " + (exists ? "覆盖" : "新增") + " " + relPath);
211
+ exists ? updated++ : created++;
212
+ } else {
213
+ copyFileSafe(src, dest) === "created" ? created++ : updated++;
214
+ }
215
+ }
216
+
217
+ // ── Step 2: 动态生成编辑器配置文件 ────────────────────────────────
218
+
219
+ const INSTRUCTIONS_SRC = path.join(FILES_DIR, ".github", "copilot-instructions.md");
220
+ if (fs.existsSync(INSTRUCTIONS_SRC)) {
221
+ const raw = fs.readFileSync(INSTRUCTIONS_SRC, "utf8");
222
+ const editorConfigs = getEditorConfigs(raw);
223
+
175
224
  if (dryRun) {
176
- console.log(" " + (exists ? "覆盖" : "新增") + " [编辑器] " + relPath);
225
+ console.log("\n [Step 2] 编辑器配置文件(从 copilot-instructions.md 生成):\n");
226
+ }
227
+
228
+ for (const [ecPath, ecContent] of editorConfigs) {
229
+ const ecDest = path.join(TARGET_DIR, ecPath);
230
+ const ecHash = contentMd5(ecContent);
231
+ newManifest.files[ecPath] = ecHash;
232
+
233
+ if (incremental && fs.existsSync(ecDest)) {
234
+ if (ecHash === fileMd5(ecDest)) { unchanged++; continue; }
235
+ }
236
+
237
+ if (dryRun) {
238
+ const ecExists = fs.existsSync(ecDest);
239
+ console.log(" " + (ecExists ? "覆盖" : "新增") + " [编辑器] " + ecPath);
240
+ ecExists ? updated++ : created++;
241
+ } else {
242
+ writeFile(ecDest, ecContent) === "created" ? created++ : updated++;
243
+ }
244
+ }
245
+ }
246
+
247
+ // ── Step 3: 写 manifest ────────────────────────────────────────────
248
+
249
+ if (!dryRun) writeManifest(newManifest);
250
+
251
+ // ── 输出统计 ──────────────────────────────────────────────────────
252
+
253
+ const total = created + updated + unchanged;
254
+ if (dryRun) {
255
+ console.log("");
256
+ if (incremental) {
257
+ console.log(" 共 " + total + " 个文件(新增 " + created + ",变更 " + updated + ",未变 " + unchanged + ")(未实际写入)");
258
+ } else {
259
+ console.log(" 共 " + total + " 个文件(未实际写入)");
260
+ }
261
+ } else {
262
+ console.log(" ✔ 完成!");
263
+ if (incremental) {
264
+ console.log(" 新增: " + created + " 个文件");
265
+ console.log(" 更新: " + updated + " 个文件");
266
+ console.log(" 未变: " + unchanged + " 个文件");
267
+ if (oldManifest && oldManifest.version !== PKG.version) {
268
+ console.log(" 版本: " + oldManifest.version + " → " + PKG.version);
269
+ }
177
270
  } else {
178
- const result = writeFile(dest, content);
179
- result === "created" ? created++ : updated++;
271
+ console.log(" 新增: " + created + " 个文件");
272
+ console.log(" 覆盖: " + updated + " 个文件");
273
+ console.log(" 总计: " + (created + updated) + " 个文件");
180
274
  }
181
275
  }
276
+ console.log("");
182
277
  }
183
278
 
184
- if (dryRun) {
185
- const editorCount = fs.existsSync(INSTRUCTIONS_SRC) ? 8 : 0;
186
- console.log(
187
- "\n " +
188
- (files.length + editorCount) +
189
- " 个文件(未实际写入)"
190
- );
191
- process.exit(0);
279
+ // ─── 命令: clean ────────────────────────────────────────────────────────
280
+
281
+ function runClean() {
282
+ console.log("");
283
+ console.log(" wl-skills-kit v" + PKG.version + " [clean]");
284
+ console.log(" 目标目录: " + TARGET_DIR);
285
+ if (dryRun) console.log(" 模式: --dry-run(预览)");
286
+ console.log("");
287
+
288
+ const manifest = readManifest();
289
+ if (!manifest) {
290
+ console.log(" ⚠ 未找到 " + MANIFEST_NAME);
291
+ console.log(" 请先执行 npx @agile-team/wl-skills-kit init 安装一次。");
292
+ console.log("");
293
+ process.exit(1);
294
+ }
295
+
296
+ const allFiles = Object.keys(manifest.files);
297
+ const toRemove = allFiles.filter((f) => !isProtected(f));
298
+ const toKeep = allFiles.filter((f) => isProtected(f));
299
+
300
+ if (dryRun) {
301
+ console.log(" 将要删除(" + toRemove.length + " 个文件):\n");
302
+ for (const f of toRemove) {
303
+ const exists = fs.existsSync(path.join(TARGET_DIR, f));
304
+ console.log(" " + (exists ? "删除" : "跳过(不存在)") + " " + f);
305
+ }
306
+ console.log("\n 保留(" + toKeep.length + " 个文件):\n");
307
+ for (const f of toKeep) {
308
+ console.log(" 保留 " + f);
309
+ }
310
+ } else {
311
+ let removed = 0, skipped = 0;
312
+ for (const f of toRemove) {
313
+ const fullPath = path.join(TARGET_DIR, f);
314
+ if (fs.existsSync(fullPath)) {
315
+ removeFileAndEmptyParents(fullPath);
316
+ removed++;
317
+ } else {
318
+ skipped++;
319
+ }
320
+ }
321
+ // 删除 manifest 自身
322
+ if (fs.existsSync(MANIFEST_PATH)) fs.unlinkSync(MANIFEST_PATH);
323
+
324
+ console.log(" ✔ 清理完成!");
325
+ console.log(" 删除: " + removed + " 个文件");
326
+ if (skipped > 0) console.log(" 跳过: " + skipped + " 个(已不存在)");
327
+ console.log(" 保留: " + toKeep.length + " 个文件(src/components/ + src/types/)");
328
+ }
329
+ console.log("");
192
330
  }
193
331
 
194
- console.log(" ✔ 完成!");
195
- console.log(" 新增: " + created + " 个文件");
196
- console.log(" 覆盖: " + updated + " 个文件");
197
- console.log(" 总计: " + (created + updated) + " 个文件");
198
- console.log("");
332
+ // ─── 主路由 ─────────────────────────────────────────────────────────────
333
+
334
+ switch (command) {
335
+ case "init": runInstall(false); break;
336
+ case "update": runInstall(true); break;
337
+ case "clean": runClean(); break;
338
+ default:
339
+ console.error(' ✖ 未知命令: "' + command + '",请使用 --help 查看可用命令');
340
+ process.exit(1);
341
+ }
@@ -21,9 +21,9 @@ description: "Use when: creating system menus for newly generated pages, batch r
21
21
  | ------------------------------------------------ | ------------------ | --------------------------------------- |
22
22
  | 菜单名称、路径、组件、权限、隐藏、排序、应用编码 | `SYS_MENU_INFO.md` | 原型/详设阶段自动生成,AI 直接读取 |
23
23
  | `parentMenuNameCode` | API 自动查询 | AI 调 children 接口获取,无需手填 |
24
- | **gatewayPath、parentMenuId、token** | `env.local.json` | 每套环境不同,唯一需要手动维护的 3 个值 |
24
+ | **gatewayPath、parentMenuId、sysAppNo、token** | `env.local.json` | 每套环境不同,唯一需要手动维护的 4 个值 |
25
25
 
26
- ### 配置文件(只需填 3 个字段)
26
+ ### 配置文件(只需填 4 个字段)
27
27
 
28
28
  `.github/skills/menu-sync/env/env.local.json`(已加入 `.gitignore`,本地维护,不提交)
29
29
 
@@ -31,6 +31,7 @@ description: "Use when: creating system menus for newly generated pages, batch r
31
31
  {
32
32
  "gatewayPath": "http://网关地址:端口",
33
33
  "parentMenuId": "父级菜单ID",
34
+ "sysAppNo": "应用编码(从已有菜单的sysAppNo字段获取,非明文)",
34
35
  "token": "Bearer Token(不含bearer前缀)"
35
36
  }
36
37
  ```
@@ -39,7 +40,7 @@ description: "Use when: creating system menus for newly generated pages, batch r
39
40
 
40
41
  ### 使用步骤
41
42
 
42
- 1. **首次**:按 `env/guide.md` 填写 `env.local.json` 的 3 个字段
43
+ 1. **首次**:按 `env/guide.md` 填写 `env.local.json` 的 4 个字段
43
44
  2. **之后**:直接对 AI 说「帮我创建菜单」/「同步菜单」/「补菜单」
44
45
  3. AI 自动执行:读 `SYS_MENU_INFO.md` → 读 `env.local.json` → 查父级已有子节点 → 逐条对比去重 → 调 `/system/menu/save` → 输出 created/skipped 结果表
45
46
  4. **全程无需手动执行任何命令**
@@ -90,18 +91,20 @@ description: "Use when: creating system menus for newly generated pages, batch r
90
91
  GET {gatewayPath}/system/menu/children?current=1&size=100&menuId={parentMenuId}
91
92
  Headers:
92
93
  authorization: bearer {token}
93
- Sysappno: {appNo}
94
+ Sysappno: {sysAppNo}
94
95
  ```
95
96
 
96
97
  #### Step 2: 逐条创建菜单
97
98
 
98
99
  对于每条待创建的菜单,先检查是否与已有菜单重名(`menuName` 或 `path` 相同),重复则跳过。
99
100
 
101
+ > **响应码说明**:后端成功响应为 `code: 2000`(非标准 HTTP 200),判断成功应检查 `response.body.code === 2000` 或 `message` 包含"成功"。
102
+
100
103
  ```
101
104
  POST {gatewayPath}/system/menu/save
102
105
  Headers:
103
106
  authorization: bearer {token}
104
- Sysappno: {appNo}
107
+ Sysappno: {sysAppNo}
105
108
  Content-Type: application/json
106
109
 
107
110
  Body:
@@ -112,12 +115,12 @@ Body:
112
115
  "hidden": false,
113
116
  "type": "C",
114
117
  "parentId": "{parentMenuId}",
115
- "sysAppNo": "{appNo}",
118
+ "sysAppNo": "{sysAppNo}",
116
119
  "orderNum": {nextOrder},
117
120
  "menuName": "客户档案",
118
121
  "menuNameCode": "{parentMenuNameCode}:{pinyinName}",
119
122
  "path": "mmwrCustomerArchive",
120
- "permission": "produce:mmwrCustomerArchive:list",
123
+ "permission": "mmwrCustomerArchive",
121
124
  "component": "produce/production-mmwr/aiflow/mmwr-customer-archive/index.vue"
122
125
  }
123
126
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-kit",
3
- "version": "1.1.6",
3
+ "version": "1.2.0",
4
4
  "description": "AI Skill 模板包 — 一键导入 AI 指令 + 组件文档 + 通用组件 + 领域样例,覆盖 Copilot/Cursor/Windsurf/Kiro 等主流 AI 编辑器",
5
5
  "bin": {
6
6
  "wl-skills-kit": "./bin/wl-skills.js"