@qilitt-mickey/vue3-temp-skill 1.2.5 → 1.2.7

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.
@@ -1,405 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * generate-css.mjs —— 契约 → 三份 CSS 的确定性生成器
5
- *
6
- * 用法:
7
- * node generate-css.mjs --project <项目根> [--adapter vue-element-plus] [--out src/styles]
8
- *
9
- * 职责(design-apply 第 3/4/5 步的机器执行版,消灭手抄):
10
- * 1. ui-tokens.css : tokens 族.名 → --族-名 实值 + adapter.roleSupplementVars + adapter.variableBridges
11
- * 2. ui-roles.css : roles 逐条翻译 host/inner/states/variants + adapter.extraRules 原样追加
12
- * 3. ui-layout.css : layout.regions 按 adapter.layoutBridge 的挂载类与 skipGeometry 翻译
13
- *
14
- * 原则: 契约是唯一事实源;本脚本不做任何视觉判断;同一输入必得同一输出(可复现)。
15
- */
3
+ /** 退役兼容入口:不读取项目、不写文件、不启动浏览器或网络。 */
4
+ import { pathToFileURL } from "node:url";
16
5
 
17
- import fs from "node:fs";
18
- import path from "node:path";
19
- import { fileURLToPath } from "node:url";
6
+ const NOTICE = `generate-css.mjs 已退役,拒绝执行旧三 CSS 生成引擎。
7
+ visualStatus=unverified;本入口不提供业务/视觉验收结论。
8
+ 请迁移到 references/design-apply.md 和 references/visual-verification.md:
9
+ 以原稿为事实源,手写项目视图,逐区域、逐实例、逐状态进行浏览器验收。
10
+ 旧 visual-contract 可作为输入复用,但不自动选取契约、不用 adapter 覆盖原稿值、不生成或覆盖样式。`;
20
11
 
21
- const SKILL_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
22
-
23
- // ────────────────────────────── 参数
24
-
25
- function parseArgs(argv) {
26
- const out = { project: process.cwd(), adapter: null, out: null };
27
- for (let i = 0; i < argv.length; i++) {
28
- const a = argv[i];
29
- if (a === "--project" || a === "-p") out.project = path.resolve(argv[++i]);
30
- else if (a === "--adapter") out.adapter = argv[++i];
31
- else if (a === "--out") out.out = argv[++i];
32
- }
33
- return out;
34
- }
35
-
36
- // ────────────────────────────── 名称转换(与 check-landing.mjs 的 expectedClassName 完全一致)
37
-
38
- function expectedClassName(roleName) {
39
- return (
40
- "role-" +
41
- roleName
42
- .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
43
- .replace(/\./g, "-")
44
- .replace(/_/g, "-")
45
- .toLowerCase()
46
- );
47
- }
48
-
49
- function variantClassName(variantName) {
50
- return variantName
51
- .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
52
- .replace(/_/g, "-")
53
- .toLowerCase();
54
- }
55
-
56
- function tokenVar(tokenRef) {
57
- // "color.brand" → "--color-brand";"space.6" → "--space-6"
58
- return "--" + tokenRef.replace(/\./g, "-").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
59
- }
60
-
61
- /** 展开值中的 {族.名} 令牌引用 → var(--族-名);字面量原样(色值统一小写,过 prettier 且保证确定性输出) */
62
- function expandValue(value, tokenIndex) {
63
- return String(value)
64
- .replace(/\{([a-zA-Z0-9.]+)\}/g, (m, ref) => {
65
- const v = tokenVar(ref);
66
- if (!tokenIndex.has(ref)) {
67
- // 引用未定义令牌:仍生成 var() 引用(浏览器解析为空时可见),但记入警告
68
- warnings.push(`令牌引用未在 tokens 定义: {${ref}} → ${v}(请检查契约或补 roleSupplementVars)`);
69
- }
70
- return `var(${v})`;
71
- })
72
- .replace(/#[0-9a-fA-F]{3,8}\b/g, (c) => c.toLowerCase());
73
- }
74
-
75
- /** camelCase 键 → kebab CSS 属性 */
76
- function cssProp(key) {
77
- if (key.includes("-")) return key;
78
- return key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
79
- }
80
-
81
- const warnings = [];
82
-
83
- // ────────────────────────────── 读取
84
-
85
- function loadInputs(args) {
86
- const designDir = path.join(args.project, ".design");
87
- const names = fs.readdirSync(designDir).filter((n) => /^visual-contract.*\.json$/.test(n));
88
- if (!names.length) throw new Error(".design/ 下没有 visual-contract-*.json");
89
- const contractPath = path.join(designDir, names[0]);
90
- const contract = JSON.parse(fs.readFileSync(contractPath, "utf-8"));
91
-
92
- let adapterName = args.adapter;
93
- if (!adapterName) {
94
- // 从 mapping/<scene>.json 读取
95
- const mapPath = path.join(SKILL_DIR, "mapping", `${contract.meta.scene}.json`);
96
- if (fs.existsSync(mapPath)) {
97
- adapterName = JSON.parse(fs.readFileSync(mapPath, "utf-8")).adapter;
98
- }
99
- }
100
- if (!adapterName) throw new Error("无法确定 adapter(契约场景无 mapping 且未传 --adapter)");
101
-
102
- const adapterPath = path.join(SKILL_DIR, "adapters", `${adapterName}.json`);
103
- if (!fs.existsSync(adapterPath)) throw new Error(`adapter JSON 不存在: ${adapterPath}`);
104
- const adapter = JSON.parse(fs.readFileSync(adapterPath, "utf-8"));
105
-
106
- return { contract, adapter, contractPath, adapterName };
107
- }
108
-
109
- // ────────────────────────────── ui-tokens.css
110
-
111
- function genTokens(contract, adapter) {
112
- const L = [];
113
- L.push("/**");
114
- L.push(" * ui-tokens.css —— 视觉契约令牌层(由 generate-css.mjs 生成,禁止手改)");
115
- L.push(` * 事实源: .design/visual-contract 令牌 + adapter(${adapter.adapter}) 桥接段`);
116
- L.push(" */");
117
- L.push("");
118
- L.push(":root {");
119
-
120
- const tokenIndex = new Set();
121
- for (const [family, names] of Object.entries(contract.tokens || {})) {
122
- L.push(` /* ===== ${family} 族 ===== */`);
123
- for (const [name, def] of Object.entries(names)) {
124
- const v = tokenVar(`${family}.${name}`);
125
- tokenIndex.add(`${family}.${name}`);
126
- L.push(` ${v}: ${String(def.value).replace(/#[0-9a-fA-F]{3,8}\b/g, (c) => c.toLowerCase())};`);
127
- }
128
- L.push("");
129
- }
130
-
131
- // 辅助变量(契约 role 声明用到但 tokens 未定义的)
132
- if (adapter.roleSupplementVars) {
133
- L.push(" /* ===== 辅助变量(adapter.roleSupplementVars) ===== */");
134
- for (const [k, v] of Object.entries(adapter.roleSupplementVars)) {
135
- if (k.startsWith("$")) continue;
136
- tokenIndex.add(k.replace(/^--/, "").replace(/-/g, ".")); // 宽松
137
- L.push(` ${k}: ${String(v).replace(/#[0-9a-fA-F]{3,8}\b/g, (c) => c.toLowerCase())};`);
138
- }
139
- L.push("");
140
- }
141
-
142
- // 桥接段
143
- L.push(" /* ==========================================================");
144
- L.push(" * 桥接段(adapter.variableBridges):契约值 → 项目既有变量消费链");
145
- L.push(" * ========================================================== */");
146
- const regions = (contract.layout && contract.layout.regions) || {};
147
- const bridge = adapter.variableBridges || {};
148
- for (const group of Object.keys(bridge)) {
149
- if (group.startsWith("$")) continue;
150
- L.push("");
151
- L.push(` /* -- ${group} -- */`);
152
- for (const b of bridge[group]) {
153
- let value;
154
- if (b.from && b.from.startsWith("layout:")) {
155
- // "layout:header.height" → regions.header.height(展开令牌引用)
156
- const [region, key] = b.from.slice(7).split(".");
157
- const raw = regions[region] && regions[region][key];
158
- if (raw == null) {
159
- warnings.push(`桥接 layout:${region}.${key} 在契约 layout.regions 中不存在,跳过`);
160
- continue;
161
- }
162
- value = expandValue(raw, tokenIndex);
163
- } else if (b.from && b.from.startsWith("--")) {
164
- value = `var(${b.from})`;
165
- } else {
166
- value = expandValue(b.value, tokenIndex);
167
- }
168
- L.push(` ${b.var}: ${value}${b.important ? " !important" : ""};`);
169
- }
170
- }
171
-
172
- L.push("}");
173
- L.push("");
174
- return { css: L.join("\n"), tokenIndex };
175
- }
176
-
177
- // ────────────────────────────── ui-roles.css
178
-
179
- function declBlock(decls, indent, tokenIndex) {
180
- const out = [];
181
- for (const d of decls) {
182
- out.push(`${indent}${cssProp(d.prop)}: ${expandValue(d.value, tokenIndex)};`);
183
- }
184
- return out;
185
- }
186
-
187
- function genRoles(contract, adapter, tokenIndex) {
188
- const L = [];
189
- L.push("/**");
190
- L.push(" * ui-roles.css —— 视觉契约角色层(由 generate-css.mjs 生成,禁止手改)");
191
- L.push(` * 事实源: .design/visual-contract roles + adapter(${adapter.adapter}) 结构等价转换`);
192
- L.push(" * 全局文件:禁止 :deep();inner 穿透用普通后代选择器(adapter.innerTargets)");
193
- L.push(" */");
194
- L.push("");
195
-
196
- const roles = contract.roles || {};
197
- const innerTargets = adapter.innerTargets || {};
198
- const stateChannels = adapter.stateChannels || {};
199
- const extraRules = adapter.extraRules || {};
200
-
201
- // adapter 映射表的键 = 类名去掉 role- 前缀
202
- const bare = (roleName) => expectedClassName(roleName).replace(/^role-/, "");
203
- const innerTargetOf = (roleName) => innerTargets[bare(roleName)] || "default";
204
-
205
- for (const [roleName, role] of Object.entries(roles)) {
206
- if (roleName.startsWith("$")) continue;
207
- const cls = "." + expectedClassName(roleName);
208
- L.push(`/* ===== 角色 ${roleName}${role.label ? " · " + role.label : ""} ===== */`);
209
-
210
- // host
211
- if (Array.isArray(role.host) && role.host.length) {
212
- L.push(`${cls} {`);
213
- L.push(...declBlock(role.host, " ", tokenIndex));
214
- L.push("}");
215
- }
216
-
217
- // inner
218
- if (Array.isArray(role.inner) && role.inner.length) {
219
- const target = innerTargetOf(roleName);
220
- const sel = target === "default" ? cls : `${cls} ${target}`;
221
- L.push(`${sel} {`);
222
- L.push(...declBlock(role.inner, " ", tokenIndex));
223
- L.push("}");
224
- }
225
-
226
- // 结构键 box/flow/align/span/sticky
227
- const structDecls = [];
228
- if (role.box) for (const [k, v] of Object.entries(role.box)) structDecls.push({ prop: k, value: v });
229
- if (role.flow) for (const [k, v] of Object.entries(role.flow)) structDecls.push({ prop: k, value: v });
230
- if (role.align) for (const [k, v] of Object.entries(role.align)) structDecls.push({ prop: k, value: v });
231
- if (role.span) for (const [k, v] of Object.entries(role.span)) structDecls.push({ prop: k, value: v });
232
- if (role.sticky && role.sticky.position) {
233
- structDecls.push({ prop: "position", value: role.sticky.position });
234
- if (role.sticky.side) structDecls.push({ prop: role.sticky.side, value: "0" });
235
- }
236
- if (structDecls.length) {
237
- L.push(`${cls} {`);
238
- L.push(...declBlock(structDecls, " ", tokenIndex));
239
- L.push("}");
240
- }
241
-
242
- // states
243
- if (role.states) {
244
- for (const [state, decl] of Object.entries(role.states)) {
245
- const channel = stateChannels[state] || `:${state}`;
246
- // 多通道(逗号分隔):作为选择器附加段整体展开(如 ".is-disabled, :disabled")
247
- for (const ch of channel.split(",").map((c) => c.trim())) {
248
- const hostSel = ch.startsWith(":") ? `${cls}${ch}` : `${cls}${ch}`;
249
- if (Array.isArray(decl.host) && decl.host.length) {
250
- L.push(`${hostSel} {`);
251
- L.push(...declBlock(decl.host, " ", tokenIndex));
252
- L.push("}");
253
- }
254
- if (Array.isArray(decl.inner) && decl.inner.length) {
255
- const target = innerTargetOf(roleName);
256
- const innerSel = target === "default" ? hostSel : `${hostSel} ${target}`;
257
- L.push(`${innerSel} {`);
258
- L.push(...declBlock(decl.inner, " ", tokenIndex));
259
- L.push("}");
260
- }
261
- }
262
- }
263
- }
264
-
265
- // variants(整份替换:variant host 覆盖基础 host,inner 同理)
266
- if (Array.isArray(role.variants)) {
267
- for (const v of role.variants) {
268
- const vcls = `${cls}--${variantClassName(v.name)}`;
269
- const vhost = [];
270
- if (Array.isArray(role.host)) for (const d of role.host) vhost.push(d);
271
- if (Array.isArray(v.host)) {
272
- for (const d of v.host) {
273
- const i = vhost.findIndex((x) => cssProp(x.prop) === cssProp(d.prop));
274
- if (i >= 0) vhost[i] = d;
275
- else vhost.push(d);
276
- }
277
- }
278
- if (vhost.length) {
279
- L.push(`${vcls} {`);
280
- L.push(...declBlock(vhost, " ", tokenIndex));
281
- L.push("}");
282
- }
283
- if (Array.isArray(v.inner) && v.inner.length) {
284
- const target = innerTargetOf(roleName);
285
- const sel = target === "default" ? vcls : `${vcls} ${target}`;
286
- L.push(`${sel} {`);
287
- L.push(...declBlock(v.inner, " ", tokenIndex));
288
- L.push("}");
289
- }
290
- }
291
- }
292
-
293
- // extraRules(adapter 的结构等价转换 / 提权段,原样追加——后置覆盖基础翻译)
294
- const extras = extraRules[bare(roleName)] || extraRules[expectedClassName(roleName)];
295
- if (extras) {
296
- L.push(`/* -- adapter 结构等价转换(${adapter.adapter}) -- */`);
297
- for (const r of extras) {
298
- const emits = Array.isArray(r.emit) ? r.emit : [r.emit];
299
- for (const e of emits) L.push(e.trim());
300
- }
301
- }
302
- L.push("");
303
- }
304
-
305
- return L.join("\n");
306
- }
307
-
308
- // ────────────────────────────── ui-layout.css
309
-
310
- function genLayout(contract, adapter, tokenIndex) {
311
- const L = [];
312
- L.push("/**");
313
- L.push(" * ui-layout.css —— 视觉契约布局层(由 generate-css.mjs 生成,禁止手改)");
314
- L.push(` * 事实源: .design/visual-contract layout.regions + adapter(${adapter.adapter}) layoutBridge`);
315
- L.push(" */");
316
- L.push("");
317
-
318
- const bridge = adapter.layoutBridge || {};
319
- const regions = (contract.layout && contract.layout.regions) || {};
320
- const areas = new Set((contract.layout?.areas || []).map((a) => a.name));
321
- const skipGeometry = bridge.skipGeometry || {};
322
- const regionClasses = bridge.regionClasses || {};
323
-
324
- // 壳层
325
- const shell = contract.roles?.["shell.shell"];
326
- if (shell && bridge.shellClass) {
327
- L.push(`/* ===== 内容壳(shell.shell) ===== */`);
328
- L.push(`${bridge.shellClass.join(",\n")} {`);
329
- L.push(...declBlock(shell.host || [], " ", tokenIndex));
330
- L.push("}");
331
- L.push("");
332
- }
333
-
334
- for (const [regionName, def] of Object.entries(regions)) {
335
- if (!areas.has(regionName)) continue; // 未写入 areas 的区域不生成
336
- const sels = regionClasses[regionName] || [`ui-region-${regionName}`].map((c) => `.${c}`);
337
- // skipGeometry 用 CSS 属性名;契约键是 camelCase(collapsedWidth)——统一按 cssProp 归一后匹配
338
- const skip = new Set((skipGeometry[regionName] || []).map((k) => cssProp(k)));
339
- L.push(`/* ===== 区域 ${regionName}(skip: ${[...skip].join(", ") || "无"}) ===== */`);
340
- const decls = [];
341
- const push = (prop, value) => {
342
- if (skip.has(cssProp(prop))) return;
343
- decls.push({ prop: cssProp(prop), value: expandValue(value, tokenIndex) });
344
- };
345
- if (def.width) push("width", def.width);
346
- if (def.height) push("height", def.height);
347
- if (def.minHeight) push("min-height", def.minHeight);
348
- if (def.padding) push("padding", def.padding);
349
- if (def.gap) push("gap", def.gap);
350
- if (def.bg) push("background-color", def.bg);
351
- if (def.display) push("display", def.display);
352
- if (def.direction) push("flex-direction", def.direction);
353
- if (def.alignItems) push("align-items", def.alignItems);
354
- if (def.justifyContent) push("justify-content", def.justifyContent);
355
- if (def.position) push("position", def.position);
356
- if (def.zIndex) push("z-index", def.zIndex);
357
- // 区域角色同名视觉(如 shell.header 的 border-bottom)从契约 roles 合并(同受 skip 过滤)
358
- const roleForRegion = contract.roles?.[`shell.${regionName}`];
359
- if (roleForRegion && Array.isArray(roleForRegion.host)) {
360
- for (const d of roleForRegion.host) {
361
- if (skip.has(cssProp(d.prop))) continue;
362
- if (!decls.some((x) => x.prop === cssProp(d.prop))) {
363
- decls.push({ prop: cssProp(d.prop), value: expandValue(d.value, tokenIndex) });
364
- }
365
- }
366
- }
367
- L.push(`${sels.join(",\n")} {`);
368
- for (const d of decls) L.push(` ${d.prop}: ${d.value};`);
369
- L.push("}");
370
- L.push("");
12
+ export function main(argv = process.argv.slice(2)) {
13
+ if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h")) {
14
+ console.log(`用法: node generate-css.mjs --help
15
+ ${NOTICE}
16
+ 仅帮助退出 0;其他调用(包括旧参数或未知参数)均退出 2。`);
17
+ return 0;
371
18
  }
372
-
373
- return L.join("\n");
19
+ console.error(NOTICE);
20
+ return 2;
374
21
  }
375
22
 
376
- // ────────────────────────────── 主流程
377
-
378
- function main() {
379
- const args = parseArgs(process.argv.slice(2));
380
- const { contract, adapter, contractPath } = loadInputs(args);
381
-
382
- const outDir = path.join(args.project, args.out || adapter.output?.dir || "src/styles");
383
- fs.mkdirSync(outDir, { recursive: true });
384
-
385
- const { css: tokensCss, tokenIndex } = genTokens(contract, adapter);
386
- const rolesCss = genRoles(contract, adapter, tokenIndex);
387
- const layoutCss = genLayout(contract, adapter, tokenIndex);
388
-
389
- fs.writeFileSync(path.join(outDir, "ui-tokens.css"), tokensCss);
390
- fs.writeFileSync(path.join(outDir, "ui-roles.css"), rolesCss);
391
- fs.writeFileSync(path.join(outDir, "ui-layout.css"), layoutCss);
392
-
393
- console.log(`契约: ${path.relative(args.project, contractPath)}`);
394
- console.log(`adapter: ${adapter.adapter}`);
395
- console.log(`生成: ${path.relative(args.project, outDir)}/ui-tokens.css ui-roles.css ui-layout.css`);
396
- const roleCount = Object.keys(contract.roles || {}).filter((k) => !k.startsWith("$")).length;
397
- console.log(`角色 ${roleCount} 个全部翻译;令牌族 ${Object.keys(contract.tokens || {}).join("/")}`);
398
- if (warnings.length) {
399
- console.log("");
400
- console.log("警告(不阻塞,但需回查):");
401
- for (const w of [...new Set(warnings)]) console.log(` - ${w}`);
402
- }
23
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
24
+ process.exitCode = main();
403
25
  }
404
-
405
- main();
package/scripts/match.py CHANGED
@@ -96,22 +96,41 @@ def print_result(result):
96
96
 
97
97
 
98
98
  def batch():
99
- """Regression scenarios — ASCII labels only (Windows console safe)."""
99
+ """Assert routing and resource availability; fail on a regression."""
100
100
  scenarios = [
101
- ("customer-crud-detail", "客户管理列表,按名称和状态查询,跳转详情"),
102
- ("dashboard-echarts", "仪表盘展示 ECharts 趋势图"),
103
- ("workflow-bpmn", "审批流程 BPMN 页面"),
104
- ("mobile-h5-list", "移动端客户 H5 列表"),
105
- ("file-upload-export", "附件上传、预览和导出"),
106
- ("edit-data-compare", "编辑页增加数据变更对比"),
101
+ ("customer-crud-detail", "客户管理列表,按名称和状态查询,跳转详情", {"page-crud", "page-detail"}, {"design-apply"}),
102
+ ("dashboard-echarts", "仪表盘展示 ECharts 趋势图", {"visualization"}, set()),
103
+ ("workflow-bpmn", "审批流程 BPMN 页面", {"workflow"}, set()),
104
+ ("mobile-h5-list", "移动端客户 H5 列表", {"realtime-and-mobile", "page-crud"}, set()),
105
+ ("file-upload-export", "附件上传、预览和导出", {"files-and-editors"}, set()),
106
+ ("edit-data-compare", "编辑页增加数据变更对比", {"data-compare", "page-detail"}, set()),
107
+ ("two-skills", "/project-ui-design 按照设计效果进行还原落地到项目中\n/vue3-temp-skill 代码规范按照此要求", {"design-apply"}, set()),
108
+ ("pixel-restore", "按图逐像素还原此页", {"design-apply"}, set()),
109
+ ("raw-mockup", "按这个效果图做,保持原有业务", {"design-apply"}, set()),
110
+ ("new-handoff", "落地 ui.design-handoff/1.0", {"design-apply"}, set()),
111
+ ("old-contract", "复用 visual-contract 设计文件", {"design-apply"}, set()),
112
+ ("api-contract", "更新 API 契约和响应类型", {"data-service"}, {"design-apply"}),
113
+ ("git-restore", "git 还原这个文件", set(), {"design-apply"}),
107
114
  ]
108
115
  manifest = load_manifest()
116
+ root = SCRIPT_DIR.parent
117
+ for group in manifest["modules"].values():
118
+ for module_id in group["load"]:
119
+ if not (root / "references" / f"{module_id}.md").is_file():
120
+ raise AssertionError(f"Missing reference: {module_id}")
109
121
  print(f"match batch: {len(scenarios)} scenarios")
110
- for label, text in scenarios:
122
+ for label, text, required, forbidden in scenarios:
111
123
  result = run(text, manifest)
112
- groups = ", ".join(x["id"] for x in result["matched"]) or "core-kernel"
113
- print(f" [{label}] -> {groups}")
114
- print(f"OK {len(scenarios)}/{len(scenarios)} scenarios")
124
+ groups = {item["id"] for item in result["matched"]}
125
+ if not required.issubset(groups) or forbidden.intersection(groups):
126
+ raise AssertionError(f"{label}: expected {required}, excluded {forbidden}, got {groups}")
127
+ files = result["load_files"]
128
+ if files[0] != "references/core-kernel.md" or "references/code-quality.md" not in files or len(files) != len(set(files)):
129
+ raise AssertionError(f"{label}: invalid reference order or duplicates")
130
+ if "design-apply" in groups and "references/visual-verification.md" not in files:
131
+ raise AssertionError(f"{label}: missing visual verification")
132
+ print(f" [{label}] -> {', '.join(sorted(groups)) or 'core-kernel'}")
133
+ print(f"OK {len(scenarios)}/{len(scenarios)} scenarios; all references exist")
115
134
 
116
135
 
117
136
  def main():
@@ -100,9 +100,9 @@
100
100
  "packages": []
101
101
  },
102
102
  "design-apply": {
103
- "description": "视觉契约落地:读契约 → 选 adapter → 生成令牌/角色/布局三份 CSS → 挂载目视对照",
104
- "load": ["design-apply", "code-quality"],
105
- "keywords": ["视觉契约", "契约", "visual-contract", "设计落地", "设计稿", "效果图还原", "UI还原", "换肤", "改主题", "全系统风格", "设计文件", "adapter", "角色样式"],
103
+ "description": "设计还原:固定原稿 → 保留项目业务 → 调整视图与样式 → 逐区域视觉及功能验证",
104
+ "load": ["design-apply", "visual-verification", "code-quality"],
105
+ "keywords": ["project-ui-design", "design-handoff", "视觉契约", "visual-contract", "设计还原", "视觉还原", "设计落地", "设计稿", "设计效果", "效果图", "按图", "逐像素", "UI还原", "UI改版", "外观改版", "换肤", "改主题", "全系统风格", "设计文件"],
106
106
  "packages": []
107
107
  }
108
108
  }