@agile-team/wl-skills-ui 1.7.0 → 1.8.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
@@ -4,6 +4,45 @@ All notable changes to **@agile-team/wl-skills-ui** will be documented in this f
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [1.8.0] - 2026-05-12
8
+
9
+ ### Added
10
+
11
+ - 新增 **`standards/rules.json`** R-rule 单一事实源:29 条规则按 `id / category / severity / appliesTo / autoFixable / scanner / skills` 结构化注册,所有 standards 文档、SKILL.md、scanner、MCP、未来 ESLint 插件均从此派生。
12
+ - 新增 **`standards/rules-loader.mjs`** 共享加载器:暴露 `loadRules / listRules / getRule / groupByCategory / buildRuleSummary`,scanner / MCP / check-docs / Vite 插件统一读取。
13
+ - 新增 MCP 工具 **`wl_ui_list_rules`**:按 `category / severity / autoFixable` 过滤返回规则摘要。
14
+ - 新增 MCP 工具 **`wl_ui_describe_rule`**:按 ID 返回单条 R-rule 完整定义(含 aliases 兼容旧 ID)。
15
+ - 新增 **`docs/governance-long-term.md`**:业务项目长效治理方案(基线 / 豁免 / 漂移看板 / 版本钉死 / 写作期 AI 守护五机制)。
16
+ - `scripts/check-docs.mjs` 扩展:校验 `scanner/rules/*.mjs` 中所有 `id` 必须在 `rules.json` 注册、SKILL.md 引用的 R-id 必须存在、`_registry.md` 引用的 skill 目录必须真实存在。
17
+
18
+ ### Changed (Breaking-ish)
19
+
20
+ - **修复 R011 逻辑反转 bug**:之前 scanner 检测分页"不在 #footer 报错",与 `standards/ui/05` "分页必须放内容区,不得放 #footer" 相互矛盾。v1.8.0 反转 scanner 检测语义,与 standards 对齐。
21
+ - **`scanner/rules/tag.mjs` R017/R018 重号为 R019/R020**:原 ID 与 `color.mjs` 的 R017/R018 冲突。`rules.json` 通过 `aliases: [R017_TAG_LEGACY/R018_TAG_LEGACY]` 兼容历史引用;scanner 输出 `rule` 字段改为新 ID。
22
+ - **`skills/_meta/_registry.md` 清理 12 条幽灵条目**:删除 9 个不存在的 `element/*` SKILL 引用(el-card/el-tabs/el-descriptions/el-tree/el-drawer/el-upload/el-steps/el-overlay/el-navigation/el-feedback),声明已统一归入 `element/component-family`;删除 2 个 `ops/*` 引用(route-intent / recommend-flow),声明为 MCP 工具而非独立 SKILL。
23
+ - **`skills/runtime/style-align/SKILL.md` 改为指针式引用**:不再复述 17 条 R-rule 内容,仅保留分类→编号→SKILL 映射表,规则细节统一查 `standards/rules.json` 或 `wl_ui_describe_rule`。
24
+ - `tsup.config.ts` `clean: true`:每次构建清空 `es/` 目录,避免 hash 残留。
25
+
26
+ ### Notes
27
+
28
+ - R013(Upload 嵌入 operations[])由文档约束晋升为 `rules.json` 正式条目(`severity: review`,无 scanner 实现)。
29
+ - 推荐业务项目跟进:跑一次 `npx wl-ui audit --target src --outFile .wl-baseline.json` 建立基线,配合 `--baseline` 增量门槛使用(详见 `docs/governance-long-term.md`)。
30
+
31
+ ## [1.7.1] - 2026-05-12
32
+
33
+ ### Added
34
+
35
+ - 新增 Vite 插件 `@agile-team/wl-skills-ui/vite`:消费方在 `vite.config.ts` 加一行 `wlSkillsCheck()` 即可在每次 `dev/build` 启动期自动校验 vendor 版本配对,偏离推荐组合时彩色打印警告与一键修复片段(`enforce: 'warn' | 'error' | 'silent'`)。
36
+ - 新增 `npx wl-ui doctor --print-overrides` 子命令:检测到偏离时直接输出 pnpm/npm/yarn `overrides` JSON 片段,复制即可修复。
37
+ - `skills/_meta/_compat/loader.mjs` 抽出共享 compat 加载器,统一 `evaluateVendor` / `buildOverridesSnippet` 语义,scanner、MCP、Vite、CLI 单源共用。
38
+ - `vendors.json` 的 `compat` 升级为结构化 schema(`peers / gatingPeer / conflictsWith / domAssumptions`),同时保留旧平铺字段兜底;未来新增 vendor 配对无需改读取方代码。
39
+
40
+ ### Changed
41
+
42
+ - scanner `I005` 改为遍历全部声明 `compat` 的 vendor,输出按 vendor 拆分的子检查项 `I005:<id>`,更易定位。
43
+ - MCP `wl_ui_detect_skin` 返回结构升级:`vendors[].verdict`、`fixSnippet`、`summary` 统一暴露,AI 一次拿全多 vendor 评估结果。
44
+ - `package.json` `files` 字段加入 `runtime/vite`,确保 Vite 插件随包发布。
45
+
7
46
  ## [1.7.0] - 2026-05-12
8
47
 
9
48
  ### Added
package/README.md CHANGED
@@ -201,13 +201,36 @@ yarn add @agile-team/wl-skills-ui
201
201
  | `@jhlc/jh-ui` | **`3.1.0`** | SCSS 皮肤包,`.com-text` label 包裹 + `.has-colon` 冒号注入 |
202
202
  | `@agile-team/wl-skills-ui` | `^1.7.0` | 已对齐上述组合的 DOM 假设 |
203
203
 
204
- 新接入项目可执行 `npx wl-ui check --project .`(看 `I005`)或 MCP 工具 `wl_ui_detect_skin` 自动判断当前组合是否命中推荐。完整版本-项目实测表见 `docs/compat-matrix.md`。
204
+ 三种识别方式,任选其一:
205
+
206
+ - **启动期自动**:`vite.config.ts` 加一行 `import { wlSkillsCheck } from '@agile-team/wl-skills-ui/vite'; export default defineConfig({ plugins: [wlSkillsCheck()] })`,每次 `dev/build` 偏离推荐组合时彩色打印警告(支持 `enforce: 'error'` 阻断)。
207
+ - **手动 CLI**:`npx wl-ui check --project .` 看 `I005:<vendor>`;偏离时执行 `npx wl-ui doctor --print-overrides` 拿到可直接复制的 `pnpm.overrides` 修复片段。
208
+ - **AI 协作**:MCP 工具 `wl_ui_detect_skin` 一次返回多 vendor 评估结果(`vendors[].verdict / fixSnippet / summary`)。
209
+
210
+ 完整版本-项目实测表见 `docs/compat-matrix.md`。
205
211
 
206
212
  ---
207
213
 
208
214
  ## 版本亮点
209
215
 
210
- 当前 v1.7.0 版本聚焦“**项目集群依赖配对单一事实源 + AI/Skill 强约束识别**”:
216
+ 当前 v1.8.0 版本完成 R-rule 治理体系单一事实源化,并落地业务项目长效治理方案:
217
+
218
+ - 新增 **`standards/rules.json`** 作为 29 条 R-rule 的单一事实源;所有 standards 文档、SKILL.md、scanner、MCP、未来 ESLint 插件均从此派生
219
+ - 新增 **`standards/rules-loader.mjs`** 共享加载器;新增 MCP 工具 **`wl_ui_list_rules`** / **`wl_ui_describe_rule`**,AI 写代码前一键查规则
220
+ - 修复历史 ID 冲突与逻辑反转 bug:scanner `tag.mjs` 原 R017/R018 重号为 R019/R020(与 `color.mjs` 解耦,旧 ID 通过 `aliases` 兼容);R011 由"必须放 #footer"反转为"不得放 #footer",与 standards/ui/05 对齐
221
+ - 清理 `_registry.md` 12 条幽灵 skill 引用;`runtime/style-align/SKILL.md` 改为指针式引用 rules.json,不再复述规则细节
222
+ - `scripts/check-docs.mjs` 扩展为 R-rule / scanner / SKILL / registry 四向一致性守卫,CI 不一致直接红灯
223
+ - 新增 **`docs/governance-long-term.md`**:业务项目长效治理方案(基线 / 豁免 / 漂移看板 / 版本钉死 / 写作期 AI 守护五机制),杜绝遗留累积与升级回归
224
+
225
+ 历史亮点(v1.7.1):
226
+
227
+ - 新增 Vite 插件 `@agile-team/wl-skills-ui/vite`:消费方一行配置即可在每次启动 dev/build 时自动跑版本配对校验,偏离时彩色提示 + 修复片段
228
+ - 新增 `npx wl-ui doctor --print-overrides`:检测到偏离直接输出 pnpm/npm/yarn overrides JSON,复制粘贴即可修复
229
+ - `vendors.json` `compat` 升级为 `peers / gatingPeer / conflictsWith / domAssumptions` 结构化 schema,未来新增 vendor 配对零代码改动
230
+ - scanner `I005` 拆为 `I005:<vendor>`,MCP `wl_ui_detect_skin` 一次返回多 vendor 结果
231
+ - 抽出 `skills/_meta/_compat/loader.mjs` 共享加载器,scanner/MCP/Vite/CLI 单源共用
232
+
233
+ 历史亮点(v1.7.0 起):
211
234
 
212
235
  - 新增 `docs/compat-matrix.md` 作为项目集群推荐版本与 wl-skills-ui 的适配矩阵单一事实源;`skills/_meta/_compat/vendors.json` 在 `jh.compat` 字段钉死 `element-plus@2.2.6-prod.3` + `@jhlc/jh-ui@3.1.0`
213
236
  - scanner 接入完整性新增 `I005`:基于 `vendors.json` 校验消费方 `package.json` 是否锚定推荐组合,偏离时给出明确建议
package/bin/wl-ui.js CHANGED
@@ -207,9 +207,16 @@ if (subcommand === "clean") {
207
207
  if (subcommand === "doctor") {
208
208
  const { values } = parseArgs({
209
209
  args: rawArgs,
210
- options: { project: { type: "string", default: "." } },
210
+ options: {
211
+ project: { type: "string", default: "." },
212
+ "print-overrides": { type: "boolean", default: false },
213
+ },
211
214
  strict: false,
212
215
  });
216
+ if (values["print-overrides"]) {
217
+ await printOverrides(resolve(values.project));
218
+ process.exit(0);
219
+ }
213
220
  runDoctor(resolve(values.project));
214
221
  process.exit(0);
215
222
  }
@@ -895,6 +902,40 @@ function runClean(projectRoot, dryRun) {
895
902
  );
896
903
  }
897
904
 
905
+ async function printOverrides(projectRoot) {
906
+ const pkgPath = join(projectRoot, "package.json");
907
+ if (!existsSync(pkgPath)) {
908
+ console.error(`[wl-ui doctor] 未找到 ${pkgPath}`);
909
+ process.exit(1);
910
+ }
911
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
912
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
913
+ const loader = await import("../skills/_meta/_compat/loader.mjs");
914
+ const vendors = loader.listCompatVendors();
915
+ const evaluations = vendors
916
+ .map((c) => loader.evaluateVendor(c, deps))
917
+ .filter((e) => e.verdict !== "not-applicable");
918
+ if (evaluations.length === 0) {
919
+ console.log(
920
+ "[wl-ui doctor] 当前项目未命中任何 vendor 适配矩阵,无需 overrides",
921
+ );
922
+ return;
923
+ }
924
+ const snippet = loader.buildOverridesSnippet(evaluations);
925
+ if (!snippet) {
926
+ console.log("[wl-ui doctor] 当前项目所有 vendor 配对已命中推荐组合 ✓");
927
+ return;
928
+ }
929
+ console.log(
930
+ "\n[wl-ui doctor --print-overrides] 检测到 vendor 版本偏离,复制以下片段到 package.json:\n",
931
+ );
932
+ console.log("// pnpm");
933
+ console.log(JSON.stringify(snippet.pnpm, null, 2));
934
+ console.log("\n// npm / yarn");
935
+ console.log(JSON.stringify(snippet.npmYarn, null, 2));
936
+ console.log("\n复制后执行:pnpm install(或对应包管理器的 install 命令)\n");
937
+ }
938
+
898
939
  function runDoctor(projectRoot) {
899
940
  const pkgPath = join(projectRoot, "package.json");
900
941
  let pkg = null;
@@ -973,8 +1014,9 @@ wl-ui — @agile-team/wl-skills-ui 统一 CLI v${PKG.version}
973
1014
  对比已安装文件与 manifest
974
1015
  wl-ui clean [--project <path>] [--dry-run]
975
1016
  清理 wl-skills-ui 安装文件
976
- wl-ui doctor [--project <path>]
977
- 检查安装状态 / MCP / 桥接 / 规范插件
1017
+ wl-ui doctor [--project <path>] [--print-overrides]
1018
+ 检查安装状态 / MCP / 桥接 / 规范插件;
1019
+ --print-overrides 时输出 vendor 版本偏离的 pnpm/npm/yarn overrides 修复片段
978
1020
  wl-ui prompts
979
1021
  打印 AI 触发提示词
980
1022
 
@@ -41,7 +41,28 @@
41
41
 
42
42
  ## 维护流程
43
43
 
44
- - 推荐版本字段统一在 `skills/_meta/_compat/vendors.json` 的 `vendors[id=jh].compat` 字段。
44
+ - 推荐版本字段统一在 `skills/_meta/_compat/vendors.json` 的 `vendors[id=jh].compat`(结构化 schema:`peers / gatingPeer / conflictsWith / domAssumptions`)。
45
+ - 共享加载器 `skills/_meta/_compat/loader.mjs` 暴露 `listCompatVendors / evaluateVendor / buildOverridesSnippet`,scanner、MCP、Vite 插件、CLI 单源共用。
45
46
  - 本文档由 `scripts/check-docs.mjs` 校验:版本号必须与 `vendors.json` 一致,避免漂移。
46
- - scanner `I005` 接入完整性检查会按本表校验消费方项目是否使用推荐组合。
47
+ - scanner `I005:<vendor>` 接入完整性检查会按本表校验消费方项目是否使用推荐组合。
47
48
  - MCP 工具 `wl_ui_detect_skin` 直接读取消费方 `package.json` 给 AI 返回结构化结果。
49
+ - Vite 插件 `@agile-team/wl-skills-ui/vite` 在启动期自动校验,无需手动调用。
50
+ - `npx wl-ui doctor --print-overrides` 在检测到偏离时输出可直接复制的 pnpm/npm/yarn `overrides` 片段。
51
+
52
+ ## 启动期自动校验(推荐)
53
+
54
+ ```ts
55
+ // vite.config.ts
56
+ import { defineConfig } from 'vite';
57
+ import { wlSkillsCheck } from '@agile-team/wl-skills-ui/vite';
58
+
59
+ export default defineConfig({
60
+ plugins: [
61
+ wlSkillsCheck({
62
+ // enforce: 'warn' | 'error' | 'silent' 默认 'warn'
63
+ // includeVendors: ['jh'] 只校验指定 vendor
64
+ // verbose: true 额外打印 match 项
65
+ }),
66
+ ],
67
+ });
68
+ ```
@@ -0,0 +1,191 @@
1
+ # 长效治理:业务项目如何避免遗留与漂移污染
2
+
3
+ > 适用:使用 `@agile-team/wl-skills-ui` 的业务项目,长期迭代过程中**不让历史违规无限累积**、**不让新代码反向污染**、**不让升级回归打穿主干**。
4
+ > 事实源:本文档结合 `standards/rules.json` 单一事实源 + `scanner/snapshot.mjs` + `scanner/exempt.mjs` 共同提供。
5
+
6
+ ## 一、问题画像
7
+
8
+ 业务项目随时间会出现三种"污染":
9
+
10
+ | 类型 | 表现 | 根因 |
11
+ | --- | --- | --- |
12
+ | **历史遗留**(legacy) | 旧页面违规但没人修,scanner 报错被忽略 | 一次性扫描门槛太高,团队选择 mute |
13
+ | **风格漂移**(drift) | 同一种场景不同写法,hex 颜色又冒出来 | 没有 PR 级别的增量校验 |
14
+ | **升级回归**(regression) | wl-skills-ui 升版后老页面视觉错乱 | vendor 版本配对没钉死、scoped 样式覆盖底层 |
15
+
16
+ 下面三条机制对应三个根因。
17
+
18
+ ---
19
+
20
+ ## 二、机制 1 · 基线快照(Baseline)
21
+
22
+ **目标**:把"历史欠债"冻结,新代码只对增量负责。
23
+
24
+ ```bash
25
+ # 在主干分支建立基线(团队第一次接入时执行一次)
26
+ npx wl-ui audit --target src --output json --outFile .wl-baseline.json
27
+ git add .wl-baseline.json && git commit -m "chore: wl-ui baseline snapshot"
28
+ ```
29
+
30
+ `.wl-baseline.json` 记录当前所有违规条目的 `(file, line, ruleId)` 指纹。后续:
31
+
32
+ ```bash
33
+ # PR 检查(仅报新增违规)
34
+ npx wl-ui scan --target src --baseline .wl-baseline.json --fail-on=new-issues
35
+ ```
36
+
37
+ - **历史条目不再阻塞构建**(团队按计划逐步消化)
38
+ - **新代码引入新违规 → 立即红灯**
39
+ - 团队修复历史条目后跑 `npx wl-ui audit --refresh-baseline` 收敛基线
40
+
41
+ > 实现入口:`scanner/snapshot.mjs` 已具备文件级快照能力,issue 级 baseline 在 v1.8.0 起以 `audit --output json` 输出为契约,业务项目可自行 diff。
42
+
43
+ ---
44
+
45
+ ## 三、机制 2 · 豁免与隔离(Quarantine)
46
+
47
+ **目标**:大屏、地图、流程设计器等强个性化页面 + 不再迭代的旧模块,明确隔离,**不参与统一风格扫描**。
48
+
49
+ `.wl-exempt.json`:
50
+
51
+ ```json
52
+ {
53
+ "exemptPaths": [
54
+ "src/views/**/big-screen/**",
55
+ "src/views/**/dashboard/**",
56
+ "src/views/legacy/**"
57
+ ],
58
+ "exemptRules": {
59
+ "R016": ["src/views/**/chart/**"]
60
+ },
61
+ "exemptCategories": ["dashboard", "big-screen", "report-designer"],
62
+ "description": "大屏与遗留模块豁免风格统一扫描;新功能不得新增豁免路径"
63
+ }
64
+ ```
65
+
66
+ 约束(团队规约):
67
+
68
+ 1. 豁免清单**只能减少**,不能增加(新增需架构组评审 PR)
69
+ 2. 豁免路径**禁止承载新业务**;新业务必须落到非豁免目录
70
+ 3. 每季度团队负责人审视豁免清单:能解开就解开
71
+
72
+ > 实现入口:`scanner/exempt.mjs::loadExemptConfig` 已支持 glob + 规则级豁免;CLI/MCP/Vite 插件统一读取。
73
+
74
+ ---
75
+
76
+ ## 四、机制 3 · 漂移看板(Drift Dashboard)
77
+
78
+ **目标**:让"逐渐变烂"可见,每次升级都能量化。
79
+
80
+ ```bash
81
+ # 每次 wl-skills-ui 升版后跑一次
82
+ npx wl-ui audit --target src --output json --outFile .wl-drift-after.json
83
+ node -e "console.log(require('@agile-team/wl-skills-ui/scanner/drift.mjs').report('.wl-baseline.json','.wl-drift-after.json'))"
84
+ ```
85
+
86
+ 输出(示意):
87
+
88
+ ```
89
+ 变化(基线 → 当前)
90
+ gained +12 新增违规(红灯)
91
+ fixed -45 消化历史(绿灯)
92
+ regressed +3 曾修过又破的(黄灯,最高优先级追查)
93
+ exempt +0 豁免清单未扩张
94
+ 按规则 Top3:R017(+8) R001(+3) R009(+1)
95
+ 按目录 Top3:src/views/quote(+9) src/views/cost(+3)
96
+ ```
97
+
98
+ > 实现入口:v1.8.0 起 `scanner/index.mjs` 已暴露 `--output json` 完整结构(含 `file/line/rule`),业务项目可基于该结构自实现 drift 报告;包内 `scanner/drift.mjs` 在 1.8.x 后续小版本提供。
99
+
100
+ ---
101
+
102
+ ## 五、机制 4 · 版本钉死(Lock & Detect)
103
+
104
+ **目标**:vendor(Element Plus / @jhlc/jh-ui)版本飘移导致老页面 DOM 不匹配的问题,**在启动期就发现**,而不是页面坏了才查。
105
+
106
+ ```ts
107
+ // vite.config.ts
108
+ import { wlSkillsCheck } from '@agile-team/wl-skills-ui/runtime/vite/check.mjs'
109
+
110
+ export default defineConfig({
111
+ plugins: [
112
+ wlSkillsCheck({
113
+ enforce: 'warn', // 'warn' | 'error'
114
+ verbose: false,
115
+ }),
116
+ ],
117
+ })
118
+ ```
119
+
120
+ 启动时输出(不匹配示意):
121
+
122
+ ```
123
+ [wl-skills-ui] 版本偏离推荐组合:
124
+ element-plus@2.6.3 ≠ 推荐 2.2.6-prod.3(jh 集群)
125
+ @jhlc/jh-ui@3.1.0 ✓
126
+ 修复片段:
127
+ "pnpm": { "overrides": { "element-plus": "2.2.6-prod.3" } }
128
+ ```
129
+
130
+ > 推荐组合 = `skills/_meta/_compat/vendors.json`(事实源)。CLI: `npx wl-ui doctor --print-overrides`。MCP: `wl_ui_detect_skin`。
131
+
132
+ ---
133
+
134
+ ## 六、机制 5 · 写作期 AI 守护(Editor Skill)
135
+
136
+ **目标**:在 AI 写出新代码的那一刻就让它**对齐 rules.json**,而不是事后扫描。
137
+
138
+ - 编辑器接入:`npx wl-ui init` 会把 `.cursor/rules` / `.windsurf/rules` 等指向 `skills/` 目录
139
+ - AI 强约束(写入 SKILL 头部):
140
+ - 任何样式相关结论必须先通过 MCP `wl_ui_describe_rule` 查 `standards/rules.json`
141
+ - **禁止**在业务 SFC 的 `<style scoped>` 中覆盖 `.el-*` / `.jh-*` 全局选择器;样式补丁全部归 wl-skills-ui 升级
142
+ - 新增组件族适配优先升级到 wl-skills-ui,而不是在业务侧封 `Base*` 二代
143
+
144
+ 效果:AI 写出的代码 day-1 即合规,不再产生历史欠债。
145
+
146
+ ---
147
+
148
+ ## 七、典型业务项目接入清单
149
+
150
+ ```bash
151
+ # 1. 安装
152
+ pnpm add -D @agile-team/wl-skills-ui
153
+
154
+ # 2. 编辑器规则 + MCP + 启动期插件
155
+ npx wl-ui init --project . --mode skin
156
+
157
+ # 3. 基线
158
+ npx wl-ui audit --target src --outFile .wl-baseline.json --output json
159
+ git add .wl-baseline.json
160
+
161
+ # 4. 豁免(如有)
162
+ cp node_modules/@agile-team/wl-skills-ui/.wl-exempt.example.json .wl-exempt.json
163
+ # 编辑为本项目实际豁免路径
164
+
165
+ # 5. CI 加 PR gate
166
+ # npx wl-ui scan --target src --baseline .wl-baseline.json --fail-on=new-issues
167
+
168
+ # 6. 每季度 / 每次升级
169
+ npx wl-ui doctor --print-overrides # 版本对齐
170
+ npx wl-ui audit --refresh-baseline # 基线收敛
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 八、wl-skills-ui 自身的承诺
176
+
177
+ 为了让业务项目"敢于"长期跟随:
178
+
179
+ 1. **R-rule 不删只迁**:v1.8.0 起 `tag.mjs` 的旧 R017/R018 → R019/R020,旧 ID 在 `rules.json.aliases` 保留,scanner 输出兼容
180
+ 2. **CSS Token 不破坏**:`design/tokens/base.css` 的变量名为长期契约,重命名走 deprecate → alias → remove 三段式
181
+ 3. **vendor 适配可选**:业务项目偏离推荐组合时插件**只警告不阻塞**,由项目方决策升级节奏
182
+ 4. **每个 minor 版本附带 compat-matrix 更新**:`docs/compat-matrix.md` 是版本与 vendor 的双向契约
183
+ 5. **`docs:check` 守护文档/规则/scanner 一致性**:包级 CI 不通过则不发版
184
+
185
+ ---
186
+
187
+ ## 九、不做的事
188
+
189
+ - ❌ 不强行重写业务 SFC 结构(除非走 ops/migrate 显式确认)
190
+ - ❌ 不在业务项目里塞自动 commit hook(团队自行选 husky/lefthook)
191
+ - ❌ 不收集业务项目数据(所有扫描结果留在本地或 CI artifact)
package/mcp/server.js CHANGED
@@ -98,6 +98,41 @@ const TOOLS = [
98
98
  required: [],
99
99
  },
100
100
  },
101
+ {
102
+ name: "wl_ui_list_rules",
103
+ description:
104
+ "列出 wl-skills-ui R-rule 全集(事实源:standards/rules.json)。可按 category / severity / autoFixable 过滤。",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ category: {
109
+ type: "string",
110
+ description:
111
+ "可选:table / button / form / dialog / tag / style / base / family / layout",
112
+ },
113
+ severity: {
114
+ type: "string",
115
+ description: "可选:error / warning / info / suggestion / review",
116
+ },
117
+ autoFixable: {
118
+ type: "boolean",
119
+ description: "可选:仅返回可自动修复的规则",
120
+ },
121
+ },
122
+ required: [],
123
+ },
124
+ },
125
+ {
126
+ name: "wl_ui_describe_rule",
127
+ description: "返回单条 R-rule 的完整定义(事实源:standards/rules.json)。",
128
+ inputSchema: {
129
+ type: "object",
130
+ properties: {
131
+ id: { type: "string", description: "规则 ID,例如 R001、R019" },
132
+ },
133
+ required: ["id"],
134
+ },
135
+ },
101
136
  {
102
137
  name: "wl_ui_recommend_flow",
103
138
  description:
@@ -128,6 +163,60 @@ function projectRoot(args = {}) {
128
163
  return resolve(args.project || process.env.WL_PROJECT_ROOT || process.cwd());
129
164
  }
130
165
 
166
+ async function detectSkin(args = {}) {
167
+ const root = projectRoot(args);
168
+ const pkgPath = join(root, "package.json");
169
+ const fs = require("node:fs");
170
+ if (!fs.existsSync(pkgPath)) {
171
+ return { ok: false, reason: `未找到 ${pkgPath}` };
172
+ }
173
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
174
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
175
+ const loader = await import("../skills/_meta/_compat/loader.mjs");
176
+ const vendors = loader.listCompatVendors();
177
+ const evaluations = vendors.map((c) => ({
178
+ compat: c,
179
+ evaluation: loader.evaluateVendor(c, deps),
180
+ }));
181
+ const applicable = evaluations.filter(
182
+ (e) => e.evaluation.verdict !== "not-applicable",
183
+ );
184
+ const overrides = loader.buildOverridesSnippet(
185
+ applicable.map((e) => e.evaluation),
186
+ );
187
+ return {
188
+ ok: true,
189
+ project: pkg.name,
190
+ vendors: evaluations.map(({ compat, evaluation }) => ({
191
+ vendorId: compat.vendorId,
192
+ vendorLabel: compat.vendorLabel,
193
+ gatingPeer: compat.gatingPeer,
194
+ verdict: evaluation.verdict,
195
+ peers: evaluation.peers,
196
+ conflictsWith: compat.conflictsWith,
197
+ domAssumptions: compat.domAssumptions,
198
+ note: compat.note,
199
+ })),
200
+ summary:
201
+ applicable.length === 0
202
+ ? "no-applicable-vendor"
203
+ : applicable.every((e) => e.evaluation.verdict === "match")
204
+ ? "all-match"
205
+ : "has-mismatch",
206
+ fixSnippet: overrides,
207
+ recommendedScss: applicable.some(
208
+ ({ compat }) => compat.vendorId === "jh" && deps["@jhlc/jh-ui"],
209
+ )
210
+ ? [
211
+ "styles/vendors/_jh-ui.scss",
212
+ "styles/vendors/_jh-tree.scss",
213
+ "styles/vendors/_jh-pagination.scss",
214
+ "styles/vendors/_jh-drag-col.scss",
215
+ ]
216
+ : ["styles/vendors/_base-components.scss"],
217
+ };
218
+ }
219
+
131
220
  function runScanner(command, args = {}) {
132
221
  const root = projectRoot(args);
133
222
  const scanner = join(PKG_ROOT, "scanner", "index.mjs");
@@ -349,13 +438,63 @@ async function dispatchTool(id, name, args) {
349
438
  return;
350
439
  }
351
440
  if (name === "wl_ui_detect_skin") {
441
+ const result = await detectSkin(args);
442
+ sendResult(id, {
443
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
444
+ });
445
+ return;
446
+ }
447
+ if (name === "wl_ui_list_rules") {
448
+ const loader = await import("../standards/rules-loader.mjs");
449
+ const rules = loader.listRules({
450
+ category: args.category,
451
+ severity: args.severity,
452
+ autoFixable: args.autoFixable,
453
+ });
454
+ const summary = rules.map((r) => ({
455
+ id: r.id,
456
+ severity: r.severity,
457
+ category: r.category,
458
+ title: r.title,
459
+ autoFixable: !!r.autoFixable,
460
+ }));
352
461
  sendResult(id, {
353
462
  content: [
354
- { type: "text", text: JSON.stringify(detectSkin(args), null, 2) },
463
+ {
464
+ type: "text",
465
+ text: JSON.stringify(
466
+ { total: summary.length, rules: summary },
467
+ null,
468
+ 2,
469
+ ),
470
+ },
355
471
  ],
356
472
  });
357
473
  return;
358
474
  }
475
+ if (name === "wl_ui_describe_rule") {
476
+ const loader = await import("../standards/rules-loader.mjs");
477
+ const rule = loader.getRule(args.id);
478
+ if (!rule) {
479
+ sendResult(id, {
480
+ content: [
481
+ {
482
+ type: "text",
483
+ text: JSON.stringify(
484
+ { ok: false, reason: `规则 ${args.id} 未注册` },
485
+ null,
486
+ 2,
487
+ ),
488
+ },
489
+ ],
490
+ });
491
+ return;
492
+ }
493
+ sendResult(id, {
494
+ content: [{ type: "text", text: JSON.stringify(rule, null, 2) }],
495
+ });
496
+ return;
497
+ }
359
498
  if (name === "wl_ui_recommend_flow") {
360
499
  sendResult(id, {
361
500
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-ui",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "企业级 UI 风格对齐框架 — Vue + Element Plus 项目通用化妆/原生双模式(tokens / element / vendors / layouts / runtime / scanner / fixer / skills)",
5
5
  "type": "module",
6
6
  "main": "./es/index.js",
@@ -19,6 +19,7 @@
19
19
  "types": "./es/presets/security.d.ts"
20
20
  },
21
21
  "./runtime/presets/*": "./runtime/presets/*",
22
+ "./vite": "./runtime/vite/check.mjs",
22
23
  "./styles": "./styles/index.scss",
23
24
  "./styles/*": "./styles/*",
24
25
  "./design/tokens": "./design/tokens/index.css",
@@ -52,6 +53,7 @@
52
53
  "files": [
53
54
  "dist",
54
55
  "es",
56
+ "runtime/vite",
55
57
  "scanner",
56
58
  "standards",
57
59
  "styles",