@agile-team/wl-skills-kit 2.10.0 → 2.10.1
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 +17 -0
- package/README.md +1 -1
- package/bin/wl-skills.js +370 -14
- package/files/.github/copilot-instructions.md +97 -0
- package/files/.github/guides/architecture.md +1 -1
- package/files/.github/skills/_best-practices.md +15 -2
- package/files/.github/skills/_compat/headers/cursor-mdc.txt +1 -1
- package/files/.github/skills/_compat/headers/kiro.txt +1 -1
- package/files/.github/skills/_compat/headers/trae.txt +1 -1
- package/files/.github/skills/_pipeline.md +14 -2
- package/files/.github/skills/_registry.md +7 -1
- package/files/.github/skills/core/convention-audit/SKILL.md +46 -1
- package/files/.github/skills/core/page-codegen/SKILL.md +23 -3
- package/files/.github/skills/ops/code-fix/SKILL.md +135 -96
- package/files/.github/standards/index.md +1 -1
- package/files/eslint.config.wl-skills.cjs +123 -0
- package/lib/ast-rules.js +769 -0
- package/mcp/tools/projectTools.js +17 -1
- package/package.json +19 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.10.1] - 2026-06-14
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **pre-commit 阻断修复建议输出**:`wl-skills validate` 阻断时自动输出格式化修复建议框,含 AST R1~R12 规则映射和正则检查建议,标注可自动修复项,引导触发 `规范审计 → 自动修复 → 复扫验证` 流程
|
|
8
|
+
- **code-fix 强制复扫验证**:新增步骤[6]闭环关键步骤,修复完成后必须自动执行 `wl-skills validate` 复扫,用户不可跳过
|
|
9
|
+
- **convention-audit --quick 复扫模式**:仅复查上次报告中的 🔴🟡 项,token 消耗约全量 10%,适用于 code-fix 后轻量级闭环验证
|
|
10
|
+
- **_pipeline.md 闭环强化**:code-fix→validate 从"建议"升级为"强制"约定,新增强制执行 vs 建议执行对照表
|
|
11
|
+
- **_registry.md 加固调度规则 10-12**:闭环强制约定、高风险 Skill 确认机制(page-codegen/sync 类/code-fix)、误触发防护
|
|
12
|
+
- **_best-practices.md 场景 5 闭环补全**:完整闭环流程含强制复扫步骤和触发话术
|
|
13
|
+
- **copilot-instructions.md AI 执行护栏**:新增 5 条强制约定(闭环强制、确认机制、误触发防护、Pre-flight 声明、修复建议输出规范)
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- 修复 `printFixSuggestions` 对未知规则静默跳过的问题,新增兜底输出
|
|
18
|
+
- 修复 copilot-instructions.md 高风险 Skill 列表与 _registry.md 不一致的问题
|
|
19
|
+
|
|
3
20
|
## [2.10.0] - 2026-05-31
|
|
4
21
|
|
|
5
22
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @agile-team/wl-skills-kit
|
|
2
2
|
|
|
3
|
-
**AI Skill 模板包 v2.10.
|
|
3
|
+
**AI Skill 模板包 v2.10.1** — 一键将 14 条规范、11 个 AI Skill、17 个 MCP Tool、编辑器 MCP 配置、文档导入 Vue 3 项目。
|
|
4
4
|
|
|
5
5
|
让 AI 编辑器(Copilot / Cursor / Windsurf / Claude Code / Cline / Kiro / Trae / Qoder / 通用 Agents)**真正理解项目规范**,从原型/详设到完整页面代码全流程自动化。
|
|
6
6
|
|
package/bin/wl-skills.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* wl-skills-kit CLI v2.10.
|
|
4
|
+
* wl-skills-kit CLI v2.10.1
|
|
5
5
|
*
|
|
6
6
|
* 命令:
|
|
7
7
|
* init 全量安装(默认,向后兼容)
|
|
@@ -22,6 +22,13 @@ const fs = require("fs");
|
|
|
22
22
|
const path = require("path");
|
|
23
23
|
const crypto = require("crypto");
|
|
24
24
|
|
|
25
|
+
// ─── AST 规则引擎(v2.10.1+,语义级约束检测)──────────────────────────
|
|
26
|
+
const {
|
|
27
|
+
runAstRules,
|
|
28
|
+
getStagedFiles,
|
|
29
|
+
hasAstAvailable,
|
|
30
|
+
} = require("../lib/ast-rules");
|
|
31
|
+
|
|
25
32
|
const FILES_DIR = path.resolve(__dirname, "..", "files");
|
|
26
33
|
const TARGET_DIR = process.cwd();
|
|
27
34
|
const MANIFEST_NAME = ".wl-skills-manifest.json";
|
|
@@ -50,12 +57,16 @@ const KNOWN_FLAGS = new Set([
|
|
|
50
57
|
"-h",
|
|
51
58
|
"--domain",
|
|
52
59
|
"--all",
|
|
60
|
+
"--pre-commit",
|
|
61
|
+
"--strict",
|
|
53
62
|
]);
|
|
54
63
|
|
|
55
64
|
const dryRun = args.includes("--dry-run");
|
|
56
65
|
const showHelp = args.includes("--help") || args.includes("-h");
|
|
57
66
|
const keepReports = args.includes("--keep-reports");
|
|
58
67
|
const force = args.includes("--force");
|
|
68
|
+
const preCommit = args.includes("--pre-commit");
|
|
69
|
+
const strict = args.includes("--strict");
|
|
59
70
|
|
|
60
71
|
// 校验所有 flag 是否已知(--help 优先,跳过校验直接显示帮助)
|
|
61
72
|
if (!showHelp) {
|
|
@@ -100,6 +111,7 @@ if (showHelp) {
|
|
|
100
111
|
check 环境预检(Node / 工具链 / MCP 配置 / manifest)
|
|
101
112
|
diff 对比已安装文件与当前 kit 版本的差异
|
|
102
113
|
validate 静态检查 src/views 页面文件、AGGrid、skills-ui runtime、mock
|
|
114
|
+
v2.10.1+ 集成 AST 语义级检测(R1~R7),覆盖正则无法检测的规则
|
|
103
115
|
validate-page validate 的别名,适用于单页/目录检查
|
|
104
116
|
doctor-ui 检查 @agile-team/wk-skills-ui 接入完整性
|
|
105
117
|
export 导出 reports/SYS_* 数据为 xlsx
|
|
@@ -111,6 +123,8 @@ if (showHelp) {
|
|
|
111
123
|
--force 强制执行,跳过同版本检测(忽略已安装状态)
|
|
112
124
|
--domain <name> mock-clean 指定要清理的业务域(如 sale、mdata)
|
|
113
125
|
--all mock-clean 清理全部 mock(保留 _utils.ts)
|
|
126
|
+
--pre-commit validate 仅检测 git staged 文件,error 阻断提交,warn 仅提示
|
|
127
|
+
--strict validate 的 error 和 warn 都导致退出码 1(CI 用)
|
|
114
128
|
--help 显示帮助
|
|
115
129
|
|
|
116
130
|
示例:
|
|
@@ -341,6 +355,13 @@ function runInstall(incremental) {
|
|
|
341
355
|
|
|
342
356
|
const oldManifest = readManifest();
|
|
343
357
|
|
|
358
|
+
// ── 约束基础设施:无论版本是否相同,都确保 pre-commit hook 和 eslint 配置就绪 ──
|
|
359
|
+
// 这样即使 early-return(同版本跳过文件复制),hook 也会被创建/更新
|
|
360
|
+
if (!dryRun) {
|
|
361
|
+
ensurePreCommitHook(TARGET_DIR);
|
|
362
|
+
ensureEslintConfig(TARGET_DIR);
|
|
363
|
+
}
|
|
364
|
+
|
|
344
365
|
// ── 版本去重:同版本跳过,不同版本自动增量更新 ──────────────────────
|
|
345
366
|
if (oldManifest && !force) {
|
|
346
367
|
if (oldManifest.version === PKG.version) {
|
|
@@ -374,6 +395,9 @@ function runInstall(incremental) {
|
|
|
374
395
|
if (dryRun) console.log(" [Step 1] files/ 静态文件:\n");
|
|
375
396
|
|
|
376
397
|
for (const relPath of files) {
|
|
398
|
+
// eslint 模板由 ensureEslintConfig 单独处理,不通过 Step 1 复制
|
|
399
|
+
if (relPath === "eslint.config.wl-skills.cjs") continue;
|
|
400
|
+
|
|
377
401
|
const src = path.join(FILES_DIR, relPath);
|
|
378
402
|
const dest = path.join(TARGET_DIR, relPath);
|
|
379
403
|
const srcHash = fileMd5(src);
|
|
@@ -551,10 +575,126 @@ function runInstall(incremental) {
|
|
|
551
575
|
" ℹ 规范插件:建议执行 npx @robot-admin/git-standards init 接入代码质量与提交规范。",
|
|
552
576
|
);
|
|
553
577
|
console.log("");
|
|
578
|
+
|
|
554
579
|
}
|
|
555
580
|
|
|
556
581
|
// ─── 命令: clean ────────────────────────────────────────────────────────
|
|
557
582
|
|
|
583
|
+
/**
|
|
584
|
+
* 确保 .husky/pre-commit 包含 wl-skills validate --pre-commit
|
|
585
|
+
* — 这是让 AI 生成的代码"绕不开"规范的核心拦截点
|
|
586
|
+
*
|
|
587
|
+
* 策略:
|
|
588
|
+
* 1. 如果 .husky/pre-commit 不存在 → 创建(包含 validate 调用)
|
|
589
|
+
* 2. 如果存在但不含 wl-skills → 追加(不破坏用户已有的 hook 内容)
|
|
590
|
+
* 3. 如果存在且已含但格式过旧 → 刷新为最新格式
|
|
591
|
+
* 4. 如果存在且格式最新 → 跳过
|
|
592
|
+
*
|
|
593
|
+
* hook 使用 npx 动态解析,避免硬编码 node_modules 路径在 pnpm 下失效。
|
|
594
|
+
* 包含存在性守卫:kit 未安装时优雅跳过,不阻断提交。
|
|
595
|
+
*/
|
|
596
|
+
function ensurePreCommitHook(targetDir) {
|
|
597
|
+
const huskyDir = path.join(targetDir, ".husky");
|
|
598
|
+
const preCommitPath = path.join(huskyDir, ".husky/pre-commit");
|
|
599
|
+
|
|
600
|
+
// 只有 git 仓库才创建 husky hook
|
|
601
|
+
if (!fs.existsSync(path.join(targetDir, ".git"))) return;
|
|
602
|
+
if (!fs.existsSync(huskyDir)) return;
|
|
603
|
+
|
|
604
|
+
const VALIDATE_MARKER = "wl-skills validate --pre-commit";
|
|
605
|
+
// 最新 hook 版本标记,用于检测旧格式并刷新
|
|
606
|
+
const HOOK_VERSION_TAG = "# wl-skills-hook-v2";
|
|
607
|
+
|
|
608
|
+
const hookContent =
|
|
609
|
+
"#!/usr/bin/env sh\n" +
|
|
610
|
+
HOOK_VERSION_TAG + "\n" +
|
|
611
|
+
"# wl-skills-kit 自动管理:提交前规范检测(error 阻断提交)\n" +
|
|
612
|
+
"# 如果 node_modules 不存在或 kit 未安装,优雅跳过,不阻断提交\n" +
|
|
613
|
+
'if [ -f "node_modules/@agile-team/wl-skills-kit/bin/wl-skills.js" ]; then\n' +
|
|
614
|
+
' node node_modules/@agile-team/wl-skills-kit/bin/wl-skills.js validate --pre-commit\n' +
|
|
615
|
+
" if [ $? -ne 0 ]; then\n" +
|
|
616
|
+
' echo ""\n' +
|
|
617
|
+
' echo " ✖ 规范检测未通过,提交已阻断。修复后重新 git add + git commit"\n' +
|
|
618
|
+
" exit 1\n" +
|
|
619
|
+
" fi\n" +
|
|
620
|
+
"else\n" +
|
|
621
|
+
' echo " ⚠ wl-skills-kit 未安装(node_modules 中未找到),跳过提交前检测"\n' +
|
|
622
|
+
"fi\n";
|
|
623
|
+
|
|
624
|
+
const preCommitFile = path.join(huskyDir, "pre-commit");
|
|
625
|
+
|
|
626
|
+
if (!fs.existsSync(preCommitFile)) {
|
|
627
|
+
fs.writeFileSync(preCommitFile, hookContent, "utf8");
|
|
628
|
+
try { fs.chmodSync(preCommitFile, 0o755); } catch {}
|
|
629
|
+
console.log(" ✔ 已创建 .husky/pre-commit(提交前自动运行 wl-skills validate)");
|
|
630
|
+
console.log(" → 每次 git commit 时自动检测页面规范,error 级别阻断提交");
|
|
631
|
+
console.log(" → kit 未安装时自动跳过,不阻断提交");
|
|
632
|
+
console.log("");
|
|
633
|
+
} else {
|
|
634
|
+
const existing = fs.readFileSync(preCommitFile, "utf8");
|
|
635
|
+
|
|
636
|
+
// 已有最新版本标记 → 跳过
|
|
637
|
+
if (existing.includes(HOOK_VERSION_TAG)) return;
|
|
638
|
+
|
|
639
|
+
// 有旧 marker 但格式过旧 → 替换整段 wl-skills 块为最新格式
|
|
640
|
+
if (existing.includes(VALIDATE_MARKER)) {
|
|
641
|
+
// 删除旧的 wl-skills 块(从 VALIDATE_MARKER 前的注释行到对应的 fi)
|
|
642
|
+
const lines = existing.split("\n");
|
|
643
|
+
const filtered = [];
|
|
644
|
+
let skipMode = false;
|
|
645
|
+
for (const line of lines) {
|
|
646
|
+
if (line.includes(VALIDATE_MARKER) || line.includes("wl-skills-kit 自动")) {
|
|
647
|
+
skipMode = true;
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (skipMode && (line.includes("exit 1") || line.trim() === "fi")) {
|
|
651
|
+
skipMode = false;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
if (!skipMode) filtered.push(line);
|
|
655
|
+
}
|
|
656
|
+
// 去尾部空行
|
|
657
|
+
while (filtered.length > 0 && filtered[filtered.length - 1].trim() === "") {
|
|
658
|
+
filtered.pop();
|
|
659
|
+
}
|
|
660
|
+
// 追加最新格式
|
|
661
|
+
const updated = filtered.join("\n").trimEnd() + "\n\n" + hookContent.replace("#!/usr/bin/env sh\n", "");
|
|
662
|
+
fs.writeFileSync(preCommitFile, updated, "utf8");
|
|
663
|
+
try { fs.chmodSync(preCommitFile, 0o755); } catch {}
|
|
664
|
+
console.log(" ✔ 已刷新 .husky/pre-commit 为最新格式(v2,含存在性守卫)");
|
|
665
|
+
console.log("");
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// 无 marker → 追加
|
|
670
|
+
const addition = "\n" + hookContent.replace("#!/usr/bin/env sh\n", "");
|
|
671
|
+
fs.writeFileSync(preCommitFile, existing.trimEnd() + "\n" + addition, "utf8");
|
|
672
|
+
try { fs.chmodSync(preCommitFile, 0o755); } catch {}
|
|
673
|
+
console.log(" ✔ 已在 .husky/pre-commit 追加 wl-skills validate(提交前规范检测)");
|
|
674
|
+
console.log(" → kit 未安装时自动跳过,不阻断提交");
|
|
675
|
+
console.log("");
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* 确保业务项目有 ESLint 配置
|
|
681
|
+
* 策略:如果项目根目录没有 eslint.config.cjs,从 kit 复制模板
|
|
682
|
+
* 如果已有,不覆盖(尊重用户自定义配置)
|
|
683
|
+
*/
|
|
684
|
+
function ensureEslintConfig(targetDir) {
|
|
685
|
+
const targetEslint = path.join(targetDir, "eslint.config.cjs");
|
|
686
|
+
if (fs.existsSync(targetEslint)) return; // 用户已有自定义配置
|
|
687
|
+
|
|
688
|
+
const templatePath = path.join(FILES_DIR, "eslint.config.wl-skills.cjs");
|
|
689
|
+
if (!fs.existsSync(templatePath)) return;
|
|
690
|
+
|
|
691
|
+
const content = fs.readFileSync(templatePath, "utf8");
|
|
692
|
+
fs.writeFileSync(targetEslint, content, "utf8");
|
|
693
|
+
console.log(" ✔ 已创建 eslint.config.cjs(wl-skills-kit 模板)");
|
|
694
|
+
console.log(" → 安装依赖后生效:pnpm add -D eslint eslint-plugin-vue vue-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin");
|
|
695
|
+
console.log("");
|
|
696
|
+
}
|
|
697
|
+
|
|
558
698
|
function runClean() {
|
|
559
699
|
console.log("");
|
|
560
700
|
console.log(" wl-skills-kit v" + PKG.version + " [clean]");
|
|
@@ -665,14 +805,36 @@ function runCheck() {
|
|
|
665
805
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
666
806
|
add("Node 版本", nodeMajor >= 16, process.versions.node + "(要求 >=16)");
|
|
667
807
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
808
|
+
// 工具链检测:支持多种可能的文件名
|
|
809
|
+
const prettierExists =
|
|
810
|
+
fs.existsSync(path.join(TARGET_DIR, ".prettierrc.js")) ||
|
|
811
|
+
fs.existsSync(path.join(TARGET_DIR, ".prettierrc")) ||
|
|
812
|
+
fs.existsSync(path.join(TARGET_DIR, ".prettierrc.cjs"));
|
|
813
|
+
add(".prettierrc", prettierExists, prettierExists ? "存在" : "缺失");
|
|
814
|
+
|
|
815
|
+
const eslintExists =
|
|
816
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.ts")) ||
|
|
817
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.mjs")) ||
|
|
818
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.cjs")) ||
|
|
819
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.js"));
|
|
820
|
+
add("eslint.config", eslintExists, eslintExists ? "存在" : "缺失");
|
|
821
|
+
|
|
822
|
+
// husky 目录检测
|
|
823
|
+
const huskyExists = fs.existsSync(path.join(TARGET_DIR, ".husky"));
|
|
824
|
+
add(".husky", huskyExists, huskyExists ? "存在" : "缺失");
|
|
825
|
+
|
|
826
|
+
// pre-commit hook 内容检测(不只检查目录存在)
|
|
827
|
+
const preCommitPath = path.join(TARGET_DIR, ".husky", "pre-commit");
|
|
828
|
+
let preCommitHasValidate = false;
|
|
829
|
+
if (fs.existsSync(preCommitPath)) {
|
|
830
|
+
const hookContent = fs.readFileSync(preCommitPath, "utf8");
|
|
831
|
+
preCommitHasValidate = hookContent.includes("wl-skills validate --pre-commit");
|
|
675
832
|
}
|
|
833
|
+
add(
|
|
834
|
+
".husky/pre-commit (wl-skills validate)",
|
|
835
|
+
preCommitHasValidate,
|
|
836
|
+
preCommitHasValidate ? "已配置规范检测" : huskyExists ? "存在但未配置 wl-skills validate" : "不存在",
|
|
837
|
+
);
|
|
676
838
|
|
|
677
839
|
const manifest = readManifest();
|
|
678
840
|
add(
|
|
@@ -859,10 +1021,37 @@ function findMockFiles() {
|
|
|
859
1021
|
function runValidate() {
|
|
860
1022
|
const scanPath =
|
|
861
1023
|
args.find((a) => !a.startsWith("-") && a !== command) || "src/views";
|
|
862
|
-
|
|
1024
|
+
|
|
1025
|
+
// --pre-commit 模式:获取 staged 文件列表,用于过滤
|
|
1026
|
+
let stagedSet = null;
|
|
1027
|
+
if (preCommit) {
|
|
1028
|
+
const staged = getStagedFiles(TARGET_DIR);
|
|
1029
|
+
if (staged.length === 0) {
|
|
1030
|
+
console.log("");
|
|
1031
|
+
console.log(" wl-skills-kit v" + PKG.version + " [validate --pre-commit]");
|
|
1032
|
+
console.log(" ⚠ 无 staged 的 .vue/.ts 文件,跳过检测");
|
|
1033
|
+
console.log("");
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
stagedSet = new Set(staged.map((f) => f.replace(/\\/g, "/")));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const allPages = scanPageDirs(scanPath);
|
|
1040
|
+
// 在 pre-commit 模式下,只保留包含 staged 文件的页面目录
|
|
1041
|
+
const pages = preCommit
|
|
1042
|
+
? allPages.filter((page) =>
|
|
1043
|
+
Array.from(stagedSet).some(
|
|
1044
|
+
(f) =>
|
|
1045
|
+
f.startsWith(page.dir + "/") ||
|
|
1046
|
+
f === page.dir + "/index.vue" ||
|
|
1047
|
+
f === page.dir + "/data.ts",
|
|
1048
|
+
),
|
|
1049
|
+
)
|
|
1050
|
+
: allPages;
|
|
1051
|
+
|
|
863
1052
|
console.log("");
|
|
864
|
-
console.log(" wl-skills-kit v" + PKG.version + " [" + command + "]");
|
|
865
|
-
console.log(" 扫描目录: " + scanPath);
|
|
1053
|
+
console.log(" wl-skills-kit v" + PKG.version + " [" + command + "]" + (preCommit ? " [pre-commit]" : ""));
|
|
1054
|
+
console.log(" 扫描目录: " + scanPath + (preCommit ? "(仅 staged 文件)" : ""));
|
|
866
1055
|
console.log("");
|
|
867
1056
|
|
|
868
1057
|
if (pages.length === 0) {
|
|
@@ -1021,18 +1210,185 @@ function runValidate() {
|
|
|
1021
1210
|
}
|
|
1022
1211
|
}
|
|
1023
1212
|
|
|
1024
|
-
|
|
1213
|
+
// ── AST 语义级规则检测(v2.10.1+)─────────────────────────────────
|
|
1214
|
+
// 补充正则无法覆盖的 7 条语义规则(R1~R7),与正则规则合并输出
|
|
1215
|
+
// 在 pre-commit 模式下复用上面已计算的 stagedSet
|
|
1216
|
+
const astStagedFiles = preCommit && stagedSet ? Array.from(stagedSet) : undefined;
|
|
1217
|
+
const astResult = runAstRules(TARGET_DIR, scanPath, {
|
|
1218
|
+
stagedFiles: astStagedFiles,
|
|
1219
|
+
});
|
|
1220
|
+
// 合并 AST 结果(降级和正常都 push)
|
|
1221
|
+
issues.push(...astResult.issues);
|
|
1222
|
+
|
|
1223
|
+
// ── 输出 ───────────────────────────────────────────────────────────
|
|
1224
|
+
console.log(" 页面目录: " + pages.length + (astResult.pages ? "(AST 扫描 " + astResult.pages + ")" : ""));
|
|
1025
1225
|
console.log(" 提示项: " + issues.length);
|
|
1026
1226
|
console.log("");
|
|
1027
1227
|
const errors = issues.filter((issue) => issue.level === "error").length;
|
|
1228
|
+
const warns = issues.filter(
|
|
1229
|
+
(issue) => issue.level === "warn" || issue.level === undefined,
|
|
1230
|
+
).length;
|
|
1028
1231
|
for (const issue of issues) {
|
|
1029
1232
|
const icon =
|
|
1030
1233
|
issue.level === "error" ? "✖" : issue.level === "info" ? "ℹ" : "⚠";
|
|
1031
1234
|
console.log(" " + icon + " " + issue.dir + " — " + issue.text);
|
|
1032
1235
|
}
|
|
1033
|
-
if (issues.length === 0) console.log("
|
|
1236
|
+
if (issues.length === 0) console.log(" \u2714 \u9875\u9762\u6587\u4ef6\u5b8c\u6574\u6027\u68c0\u67e5\u901a\u8fc7");
|
|
1034
1237
|
console.log("");
|
|
1035
|
-
|
|
1238
|
+
|
|
1239
|
+
// ── \u4fee\u590d\u5efa\u8bae\u8f93\u51fa\uff08P0 \u6539\u8fdb\uff1a\u963b\u65ad\u65f6\u544a\u8bc9\u5f00\u53d1\u8005\u600e\u4e48\u4fee\uff09─────────────────────
|
|
1240
|
+
const blockingIssues = issues.filter((i) => i.level === "error" || (strict && i.level === "warn"));
|
|
1241
|
+
if (blockingIssues.length > 0) {
|
|
1242
|
+
printFixSuggestions(blockingIssues);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
if (preCommit) {
|
|
1246
|
+
// pre-commit \u6a21\u5f0f\uff1aerror \u963b\u65ad\u63d0\u4ea4
|
|
1247
|
+
// --pre-commit --strict \u7ec4\u5408\uff1aerror + warn \u90fd\u963b\u65ad
|
|
1248
|
+
const failCount = strict ? errors + warns : errors;
|
|
1249
|
+
if (failCount > 0) {
|
|
1250
|
+
console.log(
|
|
1251
|
+
" \u2716 pre-commit \u68c0\u67e5\u53d1\u73b0 " +
|
|
1252
|
+
errors + " \u4e2a error" +
|
|
1253
|
+
(strict && warns > 0 ? " + " + warns + " \u4e2a warn\uff08strict \u6a21\u5f0f\uff09" : "") +
|
|
1254
|
+
"\uff0c\u63d0\u4ea4\u5df2\u963b\u65ad",
|
|
1255
|
+
);
|
|
1256
|
+
console.log(" \u2192 \u8bf7\u4fee\u590d\u540e\u91cd\u65b0 git add + git commit");
|
|
1257
|
+
console.log(" \u2192 \u5982\u9700 AI \u8f85\u52a9\u4fee\u590d\uff0c\u8bf7\u89e6\u53d1\uff1a\u89c4\u8303\u5ba1\u8ba1 \u2192 \u81ea\u52a8\u4fee\u590d \u2192 \u590d\u626b\u9a8c\u8bc1");
|
|
1258
|
+
console.log("");
|
|
1259
|
+
process.exitCode = 1;
|
|
1260
|
+
} else {
|
|
1261
|
+
console.log(" \u2714 pre-commit \u68c0\u67e5\u901a\u8fc7\uff08" + issues.length + " \u4e2a\u63d0\u793a\u9879\u4e0d\u963b\u65ad\u63d0\u4ea4\uff09");
|
|
1262
|
+
console.log("");
|
|
1263
|
+
}
|
|
1264
|
+
} else if (strict) {
|
|
1265
|
+
// --strict \u6a21\u5f0f\uff08CI \u7528\uff09\uff1aerror \u548c warn \u5bfc\u81f4\u5931\u8d25\uff0cinfo \u4e0d\u8ba1\u5165
|
|
1266
|
+
if (errors > 0 || warns > 0) {
|
|
1267
|
+
console.log(
|
|
1268
|
+
" \u2716 strict \u6a21\u5f0f\u68c0\u67e5\u53d1\u73b0 " + errors + " error / " + warns + " warn\uff0cCI \u5df2\u963b\u65ad",
|
|
1269
|
+
);
|
|
1270
|
+
console.log(" \u2192 --strict \u6a21\u5f0f\u4e0b warn \u4e5f\u4f1a\u5931\u8d25\uff0c\u8bf7\u4fee\u590d");
|
|
1271
|
+
process.exitCode = 1;
|
|
1272
|
+
} else {
|
|
1273
|
+
console.log(" \u2714 strict \u6a21\u5f0f\u68c0\u67e5\u5168\u90e8\u901a\u8fc7");
|
|
1274
|
+
}
|
|
1275
|
+
console.log("");
|
|
1276
|
+
} else {
|
|
1277
|
+
// \u666e\u901a\u6a21\u5f0f\uff1a\u53ea\u6709 error \u6216 warn \u624d exit 1\uff0cinfo \u4ec5\u63d0\u793a
|
|
1278
|
+
if (errors > 0 || warns > 0) process.exitCode = 1;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// ── \u4fee\u590d\u5efa\u8bae\u6620\u5c04\u8868\uff08P0\uff1a\u8ba9\u5f00\u53d1\u8005\u77e5\u9053\u600e\u4e48\u4fee\uff09──────────────────────────────
|
|
1283
|
+
const FIX_SUGGESTIONS = {
|
|
1284
|
+
// \u6b63\u5219\u7ea7\u68c0\u67e5
|
|
1285
|
+
'render-type="agGrid"': {
|
|
1286
|
+
fix: '<BaseTable render-type="agGrid" ...>',
|
|
1287
|
+
ref: 'standards/12-base-table.md',
|
|
1288
|
+
auto: true,
|
|
1289
|
+
},
|
|
1290
|
+
'cid / :cid': {
|
|
1291
|
+
fix: '\u7ed9 BaseTable \u52a0 cid="{\u6a21\u5757\u7f29\u5199}-{\u529f\u80fd}"\uff0c\u5168\u5c40\u552f\u4e00',
|
|
1292
|
+
ref: 'standards/12-base-table.md',
|
|
1293
|
+
auto: true,
|
|
1294
|
+
},
|
|
1295
|
+
'defineColumns()': {
|
|
1296
|
+
fix: 'import { defineColumns } from "@agile-team/wk-skills-ui/runtime" \u5e76\u7528\u4e8e\u5217\u5b9a\u4e49',
|
|
1297
|
+
ref: 'standards/12-base-table.md',
|
|
1298
|
+
auto: true,
|
|
1299
|
+
},
|
|
1300
|
+
'renderOps()': {
|
|
1301
|
+
fix: '\u64cd\u4f5c\u5217\u4f7f\u7528 defaultSlot + renderOps()\uff0c\u7981\u6b62 operations \u6570\u7ec4',
|
|
1302
|
+
ref: 'standards/12-base-table.md',
|
|
1303
|
+
auto: true,
|
|
1304
|
+
},
|
|
1305
|
+
'C_Splitter': {
|
|
1306
|
+
fix: '\u66ff\u6362\u4e3a jh-drag-col\uff08\u5de6\u53f3\uff09/ jh-drag-row\uff08\u4e0a\u4e0b\uff09',
|
|
1307
|
+
ref: 'standards/14-layout-containers.md',
|
|
1308
|
+
auto: true,
|
|
1309
|
+
},
|
|
1310
|
+
'onClick: () => {}': {
|
|
1311
|
+
fix: '\u586b\u5145\u5b9e\u9645\u4e8b\u4ef6\u5904\u7406\u903b\u8f91\uff0c\u6216\u8054\u52a8 code-fix \u81ea\u52a8\u4fee\u590d',
|
|
1312
|
+
ref: 'standards/04-coding-basics.md',
|
|
1313
|
+
auto: true,
|
|
1314
|
+
},
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
const AST_FIX_SUGGESTIONS = {
|
|
1318
|
+
R1: { fix: '\u5c06\u4e1a\u52a1\u903b\u8f91\u8fc1\u79fb\u5230 data.ts\uff0cindex.vue \u53ea\u4fdd\u7559\u6a21\u677f+\u89e3\u6784', ref: 'standards/02-code-structure.md', auto: false },
|
|
1319
|
+
R2: { fix: '\u5c06 getAction/postAction/sessionStorage \u79fb\u5230 data.ts \u4e2d\u8c03\u7528', ref: 'standards/02-code-structure.md', auto: true },
|
|
1320
|
+
R3: { fix: '\u66ff\u6362 <el-table> \u4e3a <BaseTable render-type="agGrid" :cid="xxx">', ref: 'standards/12-base-table.md', auto: true },
|
|
1321
|
+
R4: { fix: '\u4fee\u6539\u91cd\u590d cid \u4e3a\u5168\u5c40\u552f\u4e00\u503c\uff08\u683c\u5f0f: {\u6a21\u5757\u7f29\u5199}-{\u529f\u80fd}\uff09', ref: 'standards/12-base-table.md', auto: true },
|
|
1322
|
+
R5: { fix: 'data.ts \u4e2d class extends AbstractPageQueryHook\uff0c\u5b9e\u73b0 queryDef/columnsDef', ref: 'standards/02-code-structure.md', auto: false },
|
|
1323
|
+
R6: { fix: '\u5220\u9664 import axios\uff0c\u6539\u7528 getAction/postAction', ref: 'standards/06-security.md', auto: true },
|
|
1324
|
+
R7: { fix: '\u5220\u9664 eval/new Function\uff0c\u7528\u5b89\u5168\u7684\u66ff\u4ee3\u65b9\u6848', ref: 'standards/06-security.md', auto: false },
|
|
1325
|
+
R8: { fix: '\u521b\u5efa data.ts\uff0c\u5c06\u63a5\u53e3\u8c03\u7528\u548c\u4e1a\u52a1\u903b\u8f91\u79fb\u5165\uff1b\u786e\u4fdd index.vue \u65e0 API \u8c03\u7528', ref: 'standards/02-code-structure.md', auto: true },
|
|
1326
|
+
R9: { fix: '\u66f4\u65b0 api.md\uff0c\u786e\u4fdd URL \u4e0e data.ts API_CONFIG \u4e00\u81f4', ref: 'standards/02-code-structure.md', auto: true },
|
|
1327
|
+
R10: { fix: '\u66ff\u6362\u539f\u751f el-* \u7ec4\u4ef6\u4e3a\u5e73\u53f0\u5c01\u88c5\uff08jh-select/jh-date/jh-pagination \u7b49\uff09', ref: 'standards/13-platform-components.md', auto: true },
|
|
1328
|
+
R11: { fix: '\u4ece data.ts \u4e2d\u79fb\u9664 Pinia Store import\uff0cStore \u5e94\u5728\u7ec4\u4ef6\u5c42\u4f7f\u7528', ref: 'standards/10-pinia.md', auto: true },
|
|
1329
|
+
R12: { fix: '\u5c06\u786c\u7f16\u7801 IP/URL \u79fb\u81f3 .env.* \u73af\u5883\u53d8\u91cf', ref: 'standards/07-config.md', auto: true },
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
function printFixSuggestions(blockingIssues) {
|
|
1333
|
+
// \u6309\u89c4\u5219\u5206\u7ec4\u53bb\u91cd
|
|
1334
|
+
const ruleGroups = new Map();
|
|
1335
|
+
for (const issue of blockingIssues) {
|
|
1336
|
+
const key = issue.rule || guessRuleFromText(issue.text);
|
|
1337
|
+
if (!key) continue;
|
|
1338
|
+
if (!ruleGroups.has(key)) ruleGroups.set(key, []);
|
|
1339
|
+
ruleGroups.get(key).push(issue);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (ruleGroups.size === 0) return;
|
|
1343
|
+
|
|
1344
|
+
console.log(" \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
1345
|
+
console.log(" \u2502 \ud83d\udd27 \u4fee\u590d\u5efa\u8bae \u2502");
|
|
1346
|
+
console.log(" \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
1347
|
+
|
|
1348
|
+
let hasAutoFix = false;
|
|
1349
|
+
let hasUnknownIssue = false;
|
|
1350
|
+
for (const [rule, ruleIssues] of ruleGroups.entries()) {
|
|
1351
|
+
const suggestion = AST_FIX_SUGGESTIONS[rule] || findRegexSuggestion(ruleIssues[0].text);
|
|
1352
|
+
const count = ruleIssues.length;
|
|
1353
|
+
if (!suggestion) {
|
|
1354
|
+
// 免底:未知规则的阻断项也要展示,避免用户看不到任何提示
|
|
1355
|
+
hasUnknownIssue = true;
|
|
1356
|
+
console.log(" \u2502 " + rule + "\uff08" + count + " \u5904\uff09 [\u2753\u672a\u77e5\u89c4\u5219]");
|
|
1357
|
+
console.log(" \u2502 \u2192 \u8bf7\u67e5\u770b .github/standards/ \u76f8\u5173\u89c4\u8303\u6216\u89e6\u53d1\u89c4\u8303\u5ba1\u8ba1");
|
|
1358
|
+
console.log(" \u2502");
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
const autoTag = suggestion.auto ? " [\u2705\u53ef\u81ea\u52a8\u4fee]" : " [\u270b\u9700\u4eba\u5de5]";
|
|
1362
|
+
if (suggestion.auto) hasAutoFix = true;
|
|
1363
|
+
console.log(" \u2502 " + rule + "\uff08" + count + " \u5904\uff09" + autoTag);
|
|
1364
|
+
console.log(" \u2502 \u2192 " + suggestion.fix);
|
|
1365
|
+
console.log(" \u2502 \u53c2\u8003: .github/" + suggestion.ref);
|
|
1366
|
+
console.log(" \u2502");
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
console.log(" \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
1370
|
+
if (hasAutoFix) {
|
|
1371
|
+
console.log(" \u2502 \ud83d\ude80 \u5feb\u901f\u4fee\u590d\uff1a\u5728 AI \u7f16\u8f91\u5668\u4e2d\u8f93\u5165\uff1a \u2502");
|
|
1372
|
+
console.log(" \u2502 \"\u89c4\u8303\u5ba1\u8ba1\" \u2192 \"\u81ea\u52a8\u4fee\u590d\" \u2192 \"\u590d\u626b\u9a8c\u8bc1\" \u2502");
|
|
1373
|
+
} else {
|
|
1374
|
+
console.log(" \u2502 \ud83d\udcdd \u8bf7\u53c2\u7167\u4e0a\u8ff0\u89c4\u8303\u6587\u6863\u624b\u52a8\u4fee\u590d \u2502");
|
|
1375
|
+
}
|
|
1376
|
+
console.log(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
1377
|
+
console.log("");
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
function guessRuleFromText(text) {
|
|
1381
|
+
for (const key of Object.keys(FIX_SUGGESTIONS)) {
|
|
1382
|
+
if (text.includes(key)) return key;
|
|
1383
|
+
}
|
|
1384
|
+
return null;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
function findRegexSuggestion(text) {
|
|
1388
|
+
for (const [key, suggestion] of Object.entries(FIX_SUGGESTIONS)) {
|
|
1389
|
+
if (text.includes(key)) return suggestion;
|
|
1390
|
+
}
|
|
1391
|
+
return null;
|
|
1036
1392
|
}
|
|
1037
1393
|
|
|
1038
1394
|
function readJsonSafe(filePath) {
|
|
@@ -373,6 +373,103 @@ AI 生成的所有报告类文件统一写入 `.github/reports/`,**全部追
|
|
|
373
373
|
|
|
374
374
|
---
|
|
375
375
|
|
|
376
|
+
## 小修改 / 零散变更的约束(同样不可绕过)
|
|
377
|
+
|
|
378
|
+
> 即使不是"生成新页面",仅做小修改(加字段、改样式、修 bug、加按钮)也必须遵守:
|
|
379
|
+
|
|
380
|
+
| 规则 | 说明 |
|
|
381
|
+
|------|------|
|
|
382
|
+
| ❌ 不在 index.vue 写业务逻辑 | 新加的方法/状态必须放在 data.ts |
|
|
383
|
+
| ❌ 不直接用 axios / getAction / sessionStorage | 这些只能出现在 data.ts |
|
|
384
|
+
| ❌ 不用 el-table / el-form 替代平台组件 | 用 BaseTable / BaseForm |
|
|
385
|
+
| ❌ 不引入新依赖不经团队确认 | 需在 PR 中说明理由 |
|
|
386
|
+
| ✅ 改完即验证 | 触发 `wls_validate_page` 或 `wl-skills validate` |
|
|
387
|
+
| ✅ git cz 提交 | pre-commit 会自动检测规范,error 级别阻断提交 |
|
|
388
|
+
|
|
389
|
+
> **提交即验证**:`.husky/pre-commit` 会自动运行 `wl-skills validate --pre-commit`,检测 staged 文件中的规范偏差。error 阻断提交,warn 仅提示。这是**机器强制**,不是建议。
|
|
390
|
+
>
|
|
391
|
+
> **CI 兜底**:即使 `git commit --no-verify` 绕过 pre-commit,CI 流水线中的 `wl-skills validate --strict`(正则 + AST 全量检测)会再次拦截,error 和 warn 都导致 CI 失败、阻断 PR 合并。这是不可绕开的最后一道防线。
|
|
392
|
+
|
|
393
|
+
### 豁免标记(特殊场景)
|
|
394
|
+
|
|
395
|
+
极少数场景下确实需要绕过某条规则(如弹窗内确实需要原生 `<el-table>`)时,在文件中添加精确豁免标记:
|
|
396
|
+
|
|
397
|
+
```vue
|
|
398
|
+
<!-- wl-skills:ignore R3 -->
|
|
399
|
+
<el-table :data="dialogData">...</el-table>
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
或在 `<script>` 中:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// wl-skills:ignore R2
|
|
406
|
+
const data = sessionStorage.getItem("key");
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**规则**:
|
|
410
|
+
- 必须带规则编号(R1~R9),精确豁免,不支持全局豁免
|
|
411
|
+
- 标记放在被豁免的代码附近,便于 review
|
|
412
|
+
- CI `--strict` 模式下豁免标记仍然生效(豁免不是违规)
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## AI 执行护栏(强制约定)
|
|
417
|
+
|
|
418
|
+
以下规则对所有 AI 助手(Copilot / Claude / GPT 等)在本项目中执行任务时**强制生效**,不可被用户口头覆盖。
|
|
419
|
+
|
|
420
|
+
### 1. 闭环强制约定
|
|
421
|
+
|
|
422
|
+
| 触发场景 | 强制动作 | 不可跳过原因 |
|
|
423
|
+
|----------|----------|-------------|
|
|
424
|
+
| code-fix 执行完毕 | 自动执行 `wl-skills validate` 复扫 | 确保修复未引入新偏差 |
|
|
425
|
+
| 复扫发现新问题 | 继续修复 → 再次复扫,直到通过 | 闭环不允许断开 |
|
|
426
|
+
| 用户说“不用验证了” | **仍然执行**复扫,只是不再追问 | 规范高于口头指令 |
|
|
427
|
+
|
|
428
|
+
### 2. 高风险 Skill 确认机制
|
|
429
|
+
|
|
430
|
+
以下 Skill 触发前必须向用户**二次确认**,不可静默执行:
|
|
431
|
+
|
|
432
|
+
- `page-codegen`(生成整页代码,不可逆)
|
|
433
|
+
- `menu-sync` / `dict-sync` / `permission-sync`(跨系统同步,影响后端数据)
|
|
434
|
+
- `code-fix`(批量修改源码文件,需确认范围)
|
|
435
|
+
|
|
436
|
+
确认话术模板:
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
即将执行 [Skill名称],影响范围:[文件列表 / 后端数据类型]
|
|
440
|
+
是否继续?(Y/n)
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### 3. 误触发防护
|
|
444
|
+
|
|
445
|
+
| 情况 | 处理方式 |
|
|
446
|
+
|------|----------|
|
|
447
|
+
| 用户意图匹配 2+ 个 Skill | 必须列出候选并询问用户意图 |
|
|
448
|
+
| 用户意图模糊无法映射到 Skill | 询问澄清,不猜测执行 |
|
|
449
|
+
| 仅匹配 1 个 Skill 且置信度高 | 直接执行,无需确认 |
|
|
450
|
+
|
|
451
|
+
### 4. Pre-flight 声明完整性
|
|
452
|
+
|
|
453
|
+
AI 在执行任何 Skill 前必须输出 Pre-flight 声明:
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
📋 Pre-flight:
|
|
457
|
+
- Skill: [skill-name]
|
|
458
|
+
- 触发依据: [用户原话 / 管道上游输出]
|
|
459
|
+
- 影响文件: [文件列表]
|
|
460
|
+
- 预期结果: [一句话描述]
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### 5. 修复建议输出规范
|
|
464
|
+
|
|
465
|
+
当 `wl-skills validate` 阻断时,AI 必须:
|
|
466
|
+
1. 完整展示阻断项(error + strict 模式下的 warn)
|
|
467
|
+
2. 对每项给出**具体修复建议**(而非泛泛的“请修复”)
|
|
468
|
+
3. 标注是否可自动修复(auto: true/false)
|
|
469
|
+
4. 引导用户触发修复流程:`规范审计 → 自动修复 → 复扫验证`
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
376
473
|
> 📚 完整指南:`.github/guides/usage.md`
|
|
377
474
|
> 🏗️ 架构设计:`.github/guides/architecture.md`
|
|
378
475
|
> 🔧 维护者文档:`kit-internal/`(仓库内,不安装到业务项目)
|
|
@@ -125,15 +125,28 @@ wls_menu_sync_from_report ← MCP 工具,自动读报告 + 查菜单树 + 一
|
|
|
125
125
|
- "接手新项目"
|
|
126
126
|
- "项目体检"
|
|
127
127
|
- "规范审计"
|
|
128
|
+
- "确认修复效果" / "复扫验证"
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
**推荐流程(完整闭环)**:
|
|
130
131
|
|
|
131
132
|
```
|
|
132
133
|
wls_code_scan ← 概览:页面目录、API_CONFIG、文件完整性
|
|
133
134
|
→ convention-audit ← 14 条规范全量扫描,产出 AUDIT_*.md
|
|
134
|
-
→ code-fix
|
|
135
|
+
→ code-fix ← 自动修复可整改项(用户确认 diff 后写入)
|
|
136
|
+
→ wl-skills validate ← ★ 强制复扫(自动执行,不等确认)
|
|
137
|
+
→ 闭环确认 ← 0 error = 可提交;有残余 = 继续处理
|
|
135
138
|
```
|
|
136
139
|
|
|
140
|
+
**闭环强制规则**:
|
|
141
|
+
- code-fix 完成后必须自动执行 `wl-skills validate` 复扫
|
|
142
|
+
- 复扫未通过时,提示用户继续修复或标记人工处理
|
|
143
|
+
- 大规模修复后可另触发 `convention-audit --quick` 进行轻量级复扫
|
|
144
|
+
|
|
145
|
+
**关键检查点**:
|
|
146
|
+
- convention-audit 报告是否包含所有页面
|
|
147
|
+
- code-fix 仅修 🟡🟢,🔴 必须人工或 page-codegen
|
|
148
|
+
- 复扫通过后才可 git commit
|
|
149
|
+
|
|
137
150
|
---
|
|
138
151
|
|
|
139
152
|
## 6. 场景:仅 mock 跑通 / 后端没好先能跑
|