@agile-team/wl-skills-ui 1.16.0 → 1.17.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,11 @@ 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.17.0] - 2026-10-09
8
+
9
+ - 统一可见 notice(包版本/判定/技能或基础约束/规则/目标/runId/待检查项)、开始与收尾提示、规范与执行器版本漂移诊断;保持单包使用与宿主证据未验证口径。
10
+ - 按本包规则与实际 Profile 映射 Skill,不重复前端注释规则;保留旧入口哈希证明迁移与外来内容保护。
11
+
7
12
  ## [Unreleased]
8
13
 
9
14
  ## [1.16.0] - 2026-10-08
package/README.md CHANGED
@@ -1,12 +1,20 @@
1
1
  # @agile-team/wl-skills-ui
2
2
 
3
- **企业级 UI 一致性与 AI 精确治理工具包 v1.16.0** — 面向 Vue 3 + Element Plus 的设计令牌、能力 Profile、样式皮肤、页面骨架、运行时能力、静态扫描、确定性修复、AI Skill、MCP 与脱敏 UI 模板契约。
3
+ **企业级 UI 一致性与 AI 精确治理工具包 v1.17.0** — 面向 Vue 3 + Element Plus 的设计令牌、能力 Profile、样式皮肤、页面骨架、运行时能力、静态扫描、确定性修复、AI Skill、MCP 与脱敏 UI 模板契约。
4
4
 
5
5
  它把“视觉统一、问题识别、机械修复、项目适配和样例沉淀”拆成可验证、可组合的工程能力。脚本、规则和 MCP 先完成确定性工作,AI 只处理业务语义与人工决策,从而减少重复读源码、上下文 token 和误修改。
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@agile-team/wl-skills-ui.svg)](https://www.npmjs.com/package/@agile-team/wl-skills-ui)
8
8
  [![Node](https://img.shields.io/badge/node-%3E%3D22-green.svg)]()
9
9
 
10
+ ## 如何确认本包正在起作用
11
+
12
+ 每次适用任务先运行项目本地 `wl-ui task "修改目标文件" --target src/Foo.vue --json`。编辑前展示真实 `notice`:包名与版本、判定、选中 Skill 或基础约束、具体规则、目标、runId 和尚未执行的检查。普通修改也需要基础约束提醒;相关但未覆盖的意图显示 gap 与建议;没有目标依据显示 needs-context,职责外显示 not-applicable,不强行匹配。
13
+
14
+ 执行实际检查时复用 `--run-id`,结束读取 `wl-ui status --run-id <id> --json`,分别报告执行和验证、实际检查文件、过期证据与未执行项。`notice.displayEvidence=unverified` 表示工具回执不能证明聊天界面展示;安装、路由或模型自报不能证明宿主加载/模型读取。
15
+
16
+ `wl-ui doctor-host --json` 对比已分发规范、本地执行器和正在运行的版本,漂移会显式报告。规范更新不会替代依赖升级:同步本包依赖、锁文件和受管入口;未使用的兄弟包无需安装。重开/刷新宿主加载后仍需观察真实任务调用,不能宣称所有 AI 自动触发。
17
+
10
18
  ## 你能获得什么
11
19
 
12
20
  | 目标 | 包提供的能力 | 带来的效果 |
@@ -500,7 +508,7 @@ npx wl-ui doctor --project . --print-overrides
500
508
 
501
509
  ## 版本与进一步阅读
502
510
 
503
- 当前 v1.16.0:公开集成协议 describe/request 统一信封(能力清单与路由语料经真实打包产物验证)、输入校验收口与 MCP 目录对齐真实 tools/list。v1.15.0:任务判定、真实执行/验证回执、实际检查范围和过期检测;Codex 原生薄入口按需加载 canonical Skill。确定性路由覆盖已发布目录与显式缺口,不保证识别所有自然语言。v1.14.0:明确独立能力边界,安装、更新、清理按本包贡献保护用户及其他包内容;共享文件采用单文件原子替换,支持历史路由与 `.clinerules` 目录迁移。v1.13.2:R044 审查多列表单超宽标签,R040 识别自定义 `stepper`,均供人工复核。v1.13.0:新增 `native-jh-ag` Profile、显式 Profile 兼容承诺与联邦 AG 依赖识别。
511
+ 当前 v1.17.0:公开集成协议 describe/request 统一信封(能力清单与路由语料经真实打包产物验证)、输入校验收口与 MCP 目录对齐真实 tools/list。v1.15.0:任务判定、真实执行/验证回执、实际检查范围和过期检测;Codex 原生薄入口按需加载 canonical Skill。确定性路由覆盖已发布目录与显式缺口,不保证识别所有自然语言。v1.14.0:明确独立能力边界,安装、更新、清理按本包贡献保护用户及其他包内容;共享文件采用单文件原子替换,支持历史路由与 `.clinerules` 目录迁移。v1.13.2:R044 审查多列表单超宽标签,R040 识别自定义 `stepper`,均供人工复核。v1.13.0:新增 `native-jh-ag` Profile、显式 Profile 兼容承诺与联邦 AG 依赖识别。
504
512
 
505
513
  - [版本摘要](docs/version-history.md)
506
514
  - [完整 Changelog](CHANGELOG.md)
@@ -9,7 +9,9 @@ description: Vue and Element Plus UI consistency, design tokens, styles, compone
9
9
  # wl-skills-ui task gateway
10
10
  For one user task reuse the same \`--run-id\` or \`WL_TASK_RUN_ID\` across installed applicable packages. Do not install unused sibling packages or create unrelated IDs.
11
11
 
12
- 1. Start each task with \`node node_modules/@agile-team/wl-skills-ui/bin/wl-ui.js task "<task>" --target <path>\`. Retain the run ID and briefly explain the decision, applicable rules and unverified items. A routing decision does not execute checks.
12
+ 多项目工作区先沿目标路径向上找到本包安装清单与项目 AGENTS,切到该项目根再调用工具;不要以聚合工作区根代替 projectRoot,也不要在聚合根安装。不同项目分别保存项目身份,同一用户任务复用 runId。
13
+
14
+ 1. Start each relevant task from its project root with \`node node_modules/@agile-team/wl-skills-ui/bin/wl-ui.js task "<task>" --target <path> --json\`. Before editing, visibly show the returned \`notice\`: actual package/version, decision, Skill or baseline, rule IDs/names, target, runId and unverified checks. Include baseline and gaps. Report missing/unsupported local commands and version drift explicitly. A routing decision does not execute checks.
13
15
  2. Read \`node_modules/@agile-team/wl-skills-ui/SKILL.md\`, then only the selected canonical Skill under \`node_modules/@agile-team/wl-skills-ui/skills\`. Resolve references relative to that Skill. Honor the actual project Profile; do not infer a table migration from a Skill match.
14
16
  3. Run the actual scan/check/fix tools with the same \`--run-id\`. For a code gate use \`wl-ui scan --target <path> --fail-on-error --run-id <id>\`. An ordinary Vue edit still receives UI baseline checks; tasks outside the domain are not applicable.
15
17
  4. End with \`wl-ui status --run-id <id>\` and report execution separately from validation. Zero files, skipped checks, dependency-only checks and unresolved review items are not proof of all UI constraints passing.
@@ -41,7 +41,7 @@ export function taskCatalog() {
41
41
  return catalogFiles(join(root, "skills")).map((file) => {
42
42
  const id = relative(join(root, "skills"), file).replace(/\\/g, "/").replace(/\/SKILL\.md$/, "");
43
43
  const content = readFileSync(file, "utf8");
44
- return { id, path: `node_modules/${pkg.name}/skills/${id}/SKILL.md`, description: content.match(/^# (.+)$/m)?.[1] || id, triggers: TRIGGERS[id] || [], rules: POLICY.baselineRules, checks: ["scan"], status: "enabled" };
44
+ return { id, path: `node_modules/${pkg.name}/skills/${id}/SKILL.md`, description: content.match(/^# (.+)$/m)?.[1] || id, triggers: TRIGGERS[id] || [], rules: getRules().filter((rule) => rule.skills?.includes(id)).map((rule) => rule.id), checks: ["scan"], status: "enabled" };
45
45
  });
46
46
  }
47
47
 
@@ -75,10 +75,15 @@ export function runTaskAction(action, input = {}) {
75
75
  if (action === "doctor-host") return hostDiagnostic({ ...opts, host: input.host || "unknown" });
76
76
  const profile = resolveProjectProfile({ projectRoot: opts.projectRoot, profile: input.profile }).profile;
77
77
  const routed = decideTask({ task: input.task || "", targets: opts.targets, skill: input.skill });
78
- const decision = { ...routed, profile: profile.id, applicableConstraints: routed.applicable === true ? getRules({ profile: profile.id }).map((rule) => rule.id) : [] };
78
+ const applicable = getRules({ profile: profile.id });
79
+ const enabled = new Set(applicable.map((rule) => rule.id));
80
+ const decision = { ...routed, profile: profile.id, applicableConstraints: routed.applicable === true ? [...enabled] : [] };
81
+ decision.baselineRules = decision.baselineRules.filter((id) => enabled.has(id));
82
+ decision.requiredRules = decision.requiredRules.filter((id) => enabled.has(id));
83
+ decision.ruleDetails = applicable.filter((rule) => decision.requiredRules.includes(rule.id)).map((rule) => ({ id: rule.id, name: rule.title, source: `node_modules/${pkg.name}/standards/rules.json` }));
79
84
  assetReadiness(decision, opts.projectRoot);
80
- if (action !== "task") return decision;
81
- return { ...decision, ...core.startTask({ ...opts, task: input.task || "", decision }) };
85
+ if (action !== "task") return core.attachNotice(decision, opts);
86
+ return core.attachNotice({ ...decision, ...core.startTask({ ...opts, task: input.task || "", decision }) }, opts);
82
87
  }
83
88
 
84
89
  function hostDiagnostic(opts) {
@@ -265,6 +265,7 @@ function startTask(options) {
265
265
  const record = { ...baseRecord(metadata, "task-decision"), task: safeText(options.task), decision: selected, targets: strings(options.targets), inputSnapshot: captureSnapshot(options),
266
266
  ruleSnapshot: captureReadEvidence(options, [...strings(options.ruleFiles), ...strings(selected.requiredFiles)]),
267
267
  configSnapshot: contextSnapshot(options, "configFiles"), executionStatus: "not-executed", validationStatus: "unverified" };
268
+ attachNotice(record, options);
268
269
  record.recordPath = appendRecord(options, record);
269
270
  return record;
270
271
  }
@@ -406,12 +407,12 @@ function statusForRecords(records, options) {
406
407
  const started = records.filter((record) => record.kind === "tool-started");
407
408
  const current = validationContext(plan, tools, options);
408
409
  const failed = tools.some((record) => record.executionStatus === "failed");
409
- return {
410
+ return attachNotice({
410
411
  recordVersion: RECORD_VERSION, packageName: options.packageName, ...lastRecordContext(records.at(-1), options),
411
412
  ...planContext(plan), records, tools,
412
413
  executionStatus: executionState(failed, started, history),
413
414
  ...current, stages: { selected: plan ? plan.decision.status : "unverified", hostDiscovered: "unverified", contentLoaded: "unverified", toolsExecuted: history.length },
414
- };
415
+ }, options);
415
416
  }
416
417
 
417
418
  function validationContext(plan, tools, options) {
@@ -439,7 +440,7 @@ function uncoveredScope(plan, tools, options) {
439
440
 
440
441
  function planContext(plan) {
441
442
  if (!plan) return { task: null, decision: null, gaps: [] };
442
- return { task: plan.task, decision: plan.decision, gaps: plan.decision.gaps || [] };
443
+ return { task: plan.task, decision: plan.decision, targets: plan.targets, gaps: plan.decision.gaps || [] };
443
444
  }
444
445
 
445
446
  function lastRecordContext(latest, options) {
@@ -505,18 +506,91 @@ function doctorHost(options) {
505
506
  const skills = strings(options.skillPaths).map((relative) => inspectHostFile(root, relative, Infinity));
506
507
  const gateway = options.gatewayPath ? inspectHostFile(root, options.gatewayPath, Infinity) : null;
507
508
  const warnings = entries.filter((entry) => entry.oversized).map((entry) => `${entry.path} 超过 ${maximum} 字节参考预算;请核对宿主实际配置与内容加载范围`);
508
- const ready = gateway?.status === "present" && skills.length > 0 && skills.every((skill) => skill.status === "present") && entries.every((entry) => entry.status === "present" && !entry.oversized);
509
- return { recordVersion: RECORD_VERSION, packageName: options.packageName, host: options.host || "unspecified", projectRoot: root, entries, skills, gateway, entryReadiness: ready ? "ready" : "incomplete", hostDiscovery: "unverified", mcpConnection: "unverified", contentLoaded: "unverified", warnings, instructionBudgetSource: "reference-default-not-observed-host-config" };
509
+ const runtime = inspectRuntime(root, options);
510
+ warnings.push(...runtime.warnings);
511
+ const ready = hostReady(gateway, skills, entries, runtime);
512
+ return { recordVersion: RECORD_VERSION, packageName: options.packageName, packageVersion: options.packageVersion, host: options.host || "unspecified", projectRoot: root, entries, skills, gateway, runtime, entryReadiness: ready ? "ready" : "incomplete", hostDiscovery: "unverified", mcpConnection: "unverified", contentLoaded: "unverified", warnings, instructionBudgetSource: "reference-default-not-observed-host-config" };
513
+ }
514
+
515
+ function hostReady(gateway, skills, entries, runtime) {
516
+ return gateway?.status === "present" && skills.length > 0 && skills.every((skill) => skill.status === "present") && entries.every((entry) => entry.status === "present" && !entry.oversized) && ["aligned", "unverified"].includes(runtime.status);
517
+ }
518
+ function runtimeVersion(root, relative, diagnostics) {
519
+ try { const value = JSON.parse(fs.readFileSync(readEvidencePath(root, relative), "utf8")).version; if (typeof value !== "string" || !value.trim()) throw new Error("version 必须是非空字符串"); return value; }
520
+ catch (error) { if (error.code !== "ENOENT") diagnostics.push(`${relative}: ${error.message}`); return null; }
521
+ }
522
+ function declaredDependency(root, packageName) {
523
+ try { const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")); return pkg.dependencies?.[packageName] || pkg.devDependencies?.[packageName] || null; }
524
+ catch { return null; }
525
+ }
526
+ function runtimeMismatch(versions, declared, local) {
527
+ return strings(versions).length > 1 || Boolean(local && /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?$/.test(declared || "") && declared !== local);
528
+ }
529
+ function inspectRuntime(root, options) {
530
+ root = fs.realpathSync(path.resolve(root));
531
+ const key = (options.packageName || "").replace("@agile-team/wl-skills-", "");
532
+ const manifestPath = { kit: ".wl-skills-manifest.json", design: ".wl-skills-design/state.json", test: ".wl-skills-test/manifest.json" }[key] || `.wl-skills-${key}-manifest.json`;
533
+ const diagnostics = [];
534
+ const distributedVersion = runtimeVersion(root, manifestPath, diagnostics);
535
+ const localVersion = runtimeVersion(root, `node_modules/${options.packageName}/package.json`, diagnostics);
536
+ const declaredVersion = declaredDependency(root, options.packageName);
537
+ const mismatch = runtimeMismatch([distributedVersion, localVersion, options.packageVersion], declaredVersion, localVersion);
538
+ const state = distributedVersion ? "aligned" : "unverified";
539
+ return { status: mismatch ? "mismatch" : diagnostics.length ? "invalid" : state, distributedVersion, localVersion, runningVersion: options.packageVersion || null, declaredVersion, diagnostics,
540
+ warnings: mismatch ? [`${options.packageName} 规范/本地执行器/当前执行器版本不一致:${distributedVersion} / ${localVersion} / ${options.packageVersion};声明依赖=${declaredVersion};同步本包依赖、锁文件与受管入口后复验`] : diagnostics, hostInvocation: "unverified" };
541
+ }
542
+
543
+ /** A notice describes the receipt; it does not claim the host displayed it or read rules. */
544
+ function noticeProjectRoot(record, options) {
545
+ return options.projectRoot ? fs.realpathSync(path.resolve(options.projectRoot)) : record.notice?.projectRoot || null;
546
+ }
547
+ function noticeRules(item) {
548
+ const details = new Map((item.ruleDetails || []).map((rule) => [rule.id, rule]));
549
+ const ids = strings([...strings(item.baselineRules), ...strings(item.requiredRules), ...strings(item.constraints)]);
550
+ return ids.map((id) => ({ id, name: details.get(id)?.name || id, source: details.get(id)?.source || null }));
551
+ }
552
+ function noticeScope(record, options, item) {
553
+ return { targets: strings(options.originalTargets || record.targets || options.targets),
554
+ executionStatus: record.executionStatus || "not-executed", validationStatus: record.validationStatus || "unverified",
555
+ unverified: strings(item.unverified || ["host-discovery", "model-read-canonical-files", "planned-actions-not-executed"]) };
556
+ }
557
+ function noticeReason(item) { return safeText(item.reason || strings(item.reasons).join(";")); }
558
+ function attachNotice(record, options = {}) {
559
+ const item = record.decision || record;
560
+ record.packageName ||= options.packageName;
561
+ record.packageVersion ||= options.packageVersion;
562
+ record.notice = {
563
+ schemaVersion: 1, packageName: record.packageName || null, packageVersion: record.packageVersion || null,
564
+ projectRoot: noticeProjectRoot(record, options), runId: record.runId || null, decision: item.status || "unverified",
565
+ skills: strings(item.selectedSkills), requiredChecks: strings(item.requiredChecks), baselineRules: strings(item.baselineRules), rules: noticeRules(item),
566
+ reason: noticeReason(item), missingInputs: strings(item.missingInputs),
567
+ gaps: (item.gaps || []).map((gap) => ({ reason: safeText(gap.reason), suggestion: safeText(gap.suggestion) })),
568
+ ...noticeScope(record, options, item), displayEvidence: "unverified",
569
+ };
570
+ return record;
571
+ }
572
+ function noticeRuleText(notice) {
573
+ const rules = notice.rules.slice(0, 8).map((rule) => rule.name === rule.id ? rule.id : `${rule.id}(${rule.name})`).join("、");
574
+ const remaining = notice.rules.length > 8 ? `等 ${notice.rules.length} 条,完整清单见 JSON 回执` : "";
575
+ return `${rules}${remaining || (rules ? "" : "尚无确认规则")}`;
576
+ }
577
+ function noticePlanText(notice) {
578
+ return `${notice.requiredChecks.length ? `\n待执行检查:${notice.requiredChecks.join("、")}` : ""}` + `${notice.runId ? `\nrunId=${notice.runId}` : "\n只读判定,尚无任务记录"};执行=${notice.executionStatus};验证=${notice.validationStatus}`;
579
+ }
580
+ function noticeGapText(notice) {
581
+ return `${notice.missingInputs.length ? `\n缺少:${notice.missingInputs.join("、")}` : ""}` + `${notice.gaps.length ? `\n缺口:${notice.gaps.map((gap) => `${gap.reason};建议:${gap.suggestion}`).join("\n")}` : ""}`;
510
582
  }
511
-
512
583
  function formatDecision(record) {
513
- const item = record.decision || record;
514
- const label = { baseline: "基础约束", ambiguous: "候选需确认", gap: "规则缺口", "not-applicable": "不适用", "needs-context": "缺少上下文" }[item.status] || "待判定";
515
- return `[WL ${item.status}] ${strings(item.selectedSkills).join("、") || label};${safeText(item.reason || strings(item.reasons).join(";"), 300)}${record.runId ? `;runId=${record.runId}` : ""}`;
584
+ const notice = record.notice || attachNotice({ ...record }).notice;
585
+ const label = { baseline: "基础约束", ambiguous: "候选需确认", gap: "规则缺口", "not-applicable": "不适用", "needs-context": "缺少上下文" }[notice.decision] || "待判定";
586
+ const identity = notice.packageName ? `${notice.packageName.replace("@agile-team/wl-skills-", "")}@${notice.packageVersion || "未知"};` : "";
587
+ return `[WL ${notice.decision}] ${identity}${notice.skills.join("、") || label};${notice.reason}` + `\n适用规则:${noticeRuleText(notice)};范围:${notice.targets.slice(0, 3).join("、") || "未指定目标,检查范围待确认"}` + noticePlanText(notice) + noticeGapText(notice);
516
588
  }
517
589
 
518
590
  function formatStatus(status) {
519
- return `[WL 执行=${status.executionStatus} 验证=${status.validationStatus}] runId=${status.runId || "尚无记录"};工具=${status.tools.length};待验证=${status.pendingChecks.length};范围缺口=${(status.scopeGaps || []).length};规则缺口=${(status.gaps || []).length};宿主发现=${status.stages.hostDiscovered}`;
591
+ const identity = `${status.packageName?.replace("@agile-team/wl-skills-", "") || "未知包"}@${status.packageVersion || "未知"}`;
592
+ const checked = [...new Set(status.tools.flatMap((tool) => (tool.checkedFiles || []).map((file) => file.path)))];
593
+ return `[WL 执行=${status.executionStatus} 验证=${status.validationStatus}] ${identity};runId=${status.runId || "尚无记录"};工具=${status.tools.length};实际检查文件=${checked.length};待验证=${status.pendingChecks.length};范围缺口=${(status.scopeGaps || []).length};规则缺口=${(status.gaps || []).length};证据过期=${Boolean(status.stale)};宿主发现=${status.stages.hostDiscovered}`;
520
594
  }
521
595
 
522
596
  function aggregateStatus(statuses) {
@@ -525,4 +599,4 @@ function aggregateStatus(statuses) {
525
599
  return { recordVersion: RECORD_VERSION, runId: runIds.length === 1 ? runIds[0] : null, correlated: runIds.length === 1, packages: valid, mixedRuns: runIds.length > 1, hasFailure: valid.some((status) => status.validationStatus === "failed"), gaps: valid.flatMap((status) => status.gaps || []) };
526
600
  }
527
601
 
528
- module.exports = { RECORD_VERSION, STORAGE, evaluateTask, startTask, beginExecution, finishExecution, withExecution, captureSnapshot, readStatus, listGaps, doctorHost, formatDecision, formatStatus, aggregateStatus };
602
+ module.exports = { RECORD_VERSION, STORAGE, evaluateTask, startTask, beginExecution, finishExecution, withExecution, captureSnapshot, readStatus, listGaps, doctorHost, inspectRuntime, attachNotice, formatDecision, formatStatus, aggregateStatus };
@@ -34,7 +34,8 @@
34
34
  "artifacts": { "type": "array", "items": { "$ref": "#/definitions/file" } },
35
35
  "changedDuringCheck": { "type": "boolean" },
36
36
  "errorCode": { "type": "string" },
37
- "recordPath": { "type": ["string", "null"] }
37
+ "recordPath": { "type": ["string", "null"] },
38
+ "notice": { "$ref": "#/definitions/notice" }
38
39
  },
39
40
  "allOf": [
40
41
  { "if": { "properties": { "kind": { "const": "task-decision" } } }, "then": { "required": ["task", "decision"], "properties": { "executionStatus": { "const": "not-executed" }, "validationStatus": { "const": "unverified" } } } },
@@ -42,6 +43,100 @@
42
43
  { "if": { "properties": { "kind": { "const": "tool-finished" } } }, "then": { "required": ["tool", "executionId", "executionStartedAt", "exitCode", "durationMs", "checks", "artifacts", "inputBefore", "changedDuringCheck"], "properties": { "executionStatus": { "enum": ["completed", "failed"] } } } }
43
44
  ],
44
45
  "definitions": {
46
+ "notice": {
47
+ "type": "object",
48
+ "required": [
49
+ "schemaVersion",
50
+ "packageName",
51
+ "packageVersion",
52
+ "runId",
53
+ "decision",
54
+ "skills",
55
+ "rules",
56
+ "requiredChecks",
57
+ "executionStatus",
58
+ "validationStatus",
59
+ "displayEvidence"
60
+ ],
61
+ "properties": {
62
+ "schemaVersion": {
63
+ "const": 1
64
+ },
65
+ "packageName": {
66
+ "type": [
67
+ "string",
68
+ "null"
69
+ ]
70
+ },
71
+ "packageVersion": {
72
+ "type": [
73
+ "string",
74
+ "null"
75
+ ]
76
+ },
77
+ "runId": {
78
+ "type": [
79
+ "string",
80
+ "null"
81
+ ]
82
+ },
83
+ "projectRoot": {
84
+ "type": [
85
+ "string",
86
+ "null"
87
+ ]
88
+ },
89
+ "decision": {
90
+ "type": "string"
91
+ },
92
+ "skills": {
93
+ "type": "array",
94
+ "items": {
95
+ "type": "string"
96
+ }
97
+ },
98
+ "requiredChecks": {
99
+ "type": "array",
100
+ "items": {
101
+ "type": "string"
102
+ }
103
+ },
104
+ "rules": {
105
+ "type": "array",
106
+ "items": {
107
+ "type": "object",
108
+ "required": [
109
+ "id",
110
+ "name",
111
+ "source"
112
+ ],
113
+ "properties": {
114
+ "id": {
115
+ "type": "string"
116
+ },
117
+ "name": {
118
+ "type": "string"
119
+ },
120
+ "source": {
121
+ "type": [
122
+ "string",
123
+ "null"
124
+ ]
125
+ }
126
+ }
127
+ }
128
+ },
129
+ "executionStatus": {
130
+ "type": "string"
131
+ },
132
+ "validationStatus": {
133
+ "type": "string"
134
+ },
135
+ "displayEvidence": {
136
+ "const": "unverified"
137
+ }
138
+ }
139
+ },
45
140
  "file": {
46
141
  "type": "object", "required": ["path", "sha256", "bytes"],
47
142
  "properties": { "path": { "type": "string" }, "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "bytes": { "type": "integer", "minimum": 0 } }
package/bin/wl-ui.js CHANGED
@@ -574,7 +574,7 @@ function transformForEditor(content, editor) {
574
574
  ?.slice(2) || "Skill",
575
575
  );
576
576
 
577
- return header + "\nStart each task with local `wl-ui task \"<task>\" --target <path>`. Read only the selected Skill. Finish actual tools with the same `--run-id` and report `wl-ui status`; model declarations do not prove verification.\n\n" + body;
577
+ return header + "\nStart each task with local `wl-ui task \"<task>\" --target <path>`. Before editing visibly show the returned notice: actual package/version, decision, Skill or baseline, rule IDs/names, targets, runId and unexecuted checks. Report local command failures/version drift. Read only the selected Skill. Finish actual tools with the same `--run-id` and report `wl-ui status`; model declarations do not prove verification.\n\n" + body;
578
578
  }
579
579
 
580
580
  function selectedSkills(mode, profile) {
@@ -664,7 +664,7 @@ function buildSingleFileRouter(skills, profile) {
664
664
  `> Active profile: \`${profile?.id || "native-element"}\`.`,
665
665
  "",
666
666
  "Use the package router first, then open only the relevant Skill:",
667
- "Start each task with local `wl-ui task \"<task>\" --target <path>`; read only the selected canonical Skill. Finish actual checks with the same `--run-id`, then report `wl-ui status --run-id <id>`. Routing/model declarations do not prove execution or validation. Native discovery: `.agents/skills/wl-skills-ui/SKILL.md`.",
667
+ "Start each task with local `wl-ui task \"<task>\" --target <path>`; before editing visibly show notice (actual package/version, decision, Skill or baseline, rule IDs/names, targets, runId, unexecuted checks), including gaps and local command/version failures; read only the selected canonical Skill. Finish actual checks with the same `--run-id`, then report `wl-ui status --run-id <id>`. Routing/model declarations do not prove execution or validation. Native discovery: `.agents/skills/wl-skills-ui/SKILL.md`.",
668
668
  "Reuse one `--run-id` / `WL_TASK_RUN_ID` across installed applicable packages for this user task; do not install unused sibling packages.",
669
669
  "",
670
670
  "- `node_modules/@agile-team/wl-skills-ui/SKILL.md`",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-ui",
3
- "version": "1.16.0",
3
+ "version": "1.17.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",