@agile-team/wl-skills-kit 2.10.0 → 2.11.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 +35 -0
- package/README.md +7 -7
- package/bin/wl-skills.js +418 -29
- package/files/.github/copilot-instructions.md +24 -369
- package/files/.wl-skills/copilot-instructions-full.md +233 -0
- package/files/{docs → .wl-skills/docs}/jh-pagination.md +2 -2
- package/files/{docs → .wl-skills/docs}/page-query-hook-best-practices.md +3 -3
- package/files/{.github → .wl-skills}/guides/README.md +1 -1
- package/files/{.github → .wl-skills}/guides/architecture.md +5 -5
- package/files/{.github → .wl-skills}/guides/mcp-setup.md +1 -1
- package/files/{.github → .wl-skills}/guides/usage.md +8 -8
- package/files/{.github → .wl-skills}/reports/SYS_MENU_INFO.md +1 -1
- package/files/{.github → .wl-skills}/reports/SYS_PERMISSION_INFO.md +1 -1
- package/files/{.github → .wl-skills}/reports//347/273/204/344/273/266/346/217/220/345/217/226/345/273/272/350/256/256.md +1 -1
- package/files/{.github → .wl-skills}/skills/_best-practices.md +20 -7
- package/files/{.github → .wl-skills}/skills/_compat/README.md +1 -1
- package/files/{.github → .wl-skills}/skills/_compat/editors.json +1 -1
- package/files/{.github → .wl-skills}/skills/_compat/headers/cursor-mdc.txt +1 -1
- package/files/{.github → .wl-skills}/skills/_compat/headers/kiro.txt +1 -1
- package/files/{.github → .wl-skills}/skills/_compat/headers/trae.txt +1 -1
- package/files/{.github → .wl-skills}/skills/_pipeline.md +23 -11
- package/files/{.github → .wl-skills}/skills/_registry.md +10 -4
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/SKILL.md +27 -27
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/USAGE.md +22 -22
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/templates/business-index.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/templates/business-open-questions.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/templates/module-dictionary.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/templates/module-field.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/templates/module-index.md +2 -2
- package/files/{.github → .wl-skills}/skills/core/business-doc-extract/templates/module-requirement.md +2 -2
- package/files/{.github → .wl-skills}/skills/core/convention-audit/SKILL.md +52 -7
- package/files/{.github → .wl-skills}/skills/core/convention-audit/USAGE.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/page-codegen/SKILL.md +33 -13
- package/files/{.github → .wl-skills}/skills/core/page-codegen/USAGE.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-DETAIL-TABS.md +7 -7
- package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-DRIVEN.md +5 -5
- package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-FORM-ROUTE.md +2 -2
- package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-RECORD-FORM.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-TREE-LIST.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/prototype-scan/SKILL.md +11 -11
- package/files/{.github → .wl-skills}/skills/core/prototype-scan/USAGE.md +3 -3
- package/files/{.github → .wl-skills}/skills/core/spec-doc-parse/SKILL.md +9 -9
- package/files/{.github → .wl-skills}/skills/core/spec-doc-parse/USAGE.md +6 -6
- package/files/{.github → .wl-skills}/skills/core/template-extract/SKILL.md +1 -1
- package/files/{.github → .wl-skills}/skills/core/template-extract/USAGE.md +1 -1
- package/files/{.github → .wl-skills}/skills/ops/code-fix/SKILL.md +135 -96
- package/files/{.github → .wl-skills}/skills/sync/_mcp-guardrail.md +3 -3
- package/files/{.github → .wl-skills}/skills/sync/dict-sync/SKILL.md +5 -5
- package/files/{.github → .wl-skills}/skills/sync/dict-sync/USAGE.md +2 -2
- package/files/{.github → .wl-skills}/skills/sync/menu-sync/SKILL.md +6 -6
- package/files/{.github → .wl-skills}/skills/sync/menu-sync/USAGE.md +3 -3
- package/files/{.github → .wl-skills}/skills/sync/menu-sync/env/guide.md +2 -2
- package/files/{.github → .wl-skills}/skills/sync/permission-sync/SKILL.md +3 -3
- package/files/{.github → .wl-skills}/skills/sync/permission-sync/USAGE.md +1 -1
- package/files/{src → .wl-skills/src}/components/global/C_Splitter/index.vue +2 -2
- package/files/{src → .wl-skills/src}/components/local/c_formModal/README.md +1 -1
- package/files/{src → .wl-skills/src}/components/local/c_formSections/README.md +2 -2
- package/files/{src → .wl-skills/src}/components/remote/BaseForm/README.md +2 -2
- package/files/{src → .wl-skills/src}/components/remote/BaseQuery/README.md +4 -4
- package/files/{src → .wl-skills/src}/components/remote/BaseTable/README.md +2 -2
- package/files/{src → .wl-skills/src}/components/remote/BaseToolbar/README.md +1 -1
- package/files/{.github → .wl-skills}/standards/02-code-structure.md +1 -1
- package/files/{.github → .wl-skills}/standards/08-git.md +1 -1
- package/files/{.github → .wl-skills}/standards/11-form-validation.md +1 -1
- package/files/{.github → .wl-skills}/standards/13-platform-components.md +15 -15
- package/files/{.github → .wl-skills}/standards/14-layout-containers.md +4 -4
- package/files/{.github → .wl-skills}/standards/index.md +1 -1
- package/files/{demo → .wl-skills/templates}/README.md +3 -3
- package/files/eslint.config.wl-skills.cjs +123 -0
- package/lib/ast-rules.js +769 -0
- package/lib/vite-plugin-wl-skills.js +97 -0
- package/mcp/tools/projectTools.js +17 -1
- package/package.json +19 -2
- /package/files/{docs → .wl-skills/docs}/jh-date-range.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-date.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-dept-picker.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-drag-row.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-file-upload.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-picker.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-select.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-text.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-textarea.md +0 -0
- /package/files/{docs → .wl-skills/docs}/jh-user-picker.md +0 -0
- /package/files/{docs → .wl-skills/docs}/mock-architecture.md +0 -0
- /package/files/{docs → .wl-skills/docs}/request.md +0 -0
- /package/files/{.github → .wl-skills}/reports/README.md +0 -0
- /package/files/{.github → .wl-skills}/reports/SYS_DICT_INFO.md +0 -0
- /package/files/{.github → .wl-skills}/reports//350/247/204/350/214/203/345/256/241/346/237/245/346/212/245/345/221/212.md" +0 -0
- /package/files/{.github → .wl-skills}/skills/_compat/headers/agents.txt +0 -0
- /package/files/{.github → .wl-skills}/skills/_compat/headers/claude-code.txt +0 -0
- /package/files/{.github → .wl-skills}/skills/_compat/headers/cline.txt +0 -0
- /package/files/{.github → .wl-skills}/skills/_compat/headers/cursor-rules.txt +0 -0
- /package/files/{.github → .wl-skills}/skills/_compat/headers/github-copilot.txt +0 -0
- /package/files/{.github → .wl-skills}/skills/_compat/headers/qoder.txt +0 -0
- /package/files/{.github → .wl-skills}/skills/_compat/headers/windsurf.txt +0 -0
- /package/files/{.github → .wl-skills}/skills/core/api-contract/SKILL.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/api-contract/USAGE.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/_index.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/domains/_CONTRIBUTING.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/domains/produce/TPL-OPERATION-STATION.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/domains/sale/README.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-CHANGE-HISTORY.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-LIST.md +0 -0
- /package/files/{.github → .wl-skills}/skills/core/page-codegen/templates/universal/TPL-MASTER-DETAIL.md +0 -0
- /package/files/{.github → .wl-skills}/skills/domain/README.md +0 -0
- /package/files/{.github → .wl-skills}/skills/ops/code-fix/USAGE.md +0 -0
- /package/files/{.github → .wl-skills}/skills/sync/env.local.json +0 -0
- /package/files/{.github → .wl-skills}/skills/sync/menu-sync/env/env.local.json +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_ParentView/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_RightToolbar/data.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_RightToolbar/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_RightToolbar/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_Splitter/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_SvgIcon/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_SvgIcon/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_SvgIcon/svgicon.js +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_TagStatus/README.md +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_TagStatus/config.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_TagStatus/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_TagStatus/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_TagStatus/types.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_Tree/README.md +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_Tree/data.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_Tree/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_Tree/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/global/C_Tree/types.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_formModal/data.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_formModal/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_formModal/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_formSections/data.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_formSections/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_formSections/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_listModal/data.ts +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_listModal/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_listModal/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_spliterTitle/index.scss +0 -0
- /package/files/{src → .wl-skills/src}/components/local/c_spliterTitle/index.vue +0 -0
- /package/files/{src → .wl-skills/src}/components/remote/AGGrid/README.md +0 -0
- /package/files/{src → .wl-skills/src}/types/page.ts +0 -0
- /package/files/{.github → .wl-skills}/standards/01-toolchain.md +0 -0
- /package/files/{.github → .wl-skills}/standards/03-comments.md +0 -0
- /package/files/{.github → .wl-skills}/standards/04-coding-basics.md +0 -0
- /package/files/{.github → .wl-skills}/standards/05-logging.md +0 -0
- /package/files/{.github → .wl-skills}/standards/06-security.md +0 -0
- /package/files/{.github → .wl-skills}/standards/07-config.md +0 -0
- /package/files/{.github → .wl-skills}/standards/09-typescript.md +0 -0
- /package/files/{.github → .wl-skills}/standards/10-pinia.md +0 -0
- /package/files/{.github → .wl-skills}/standards/12-base-table.md +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-add/api.md +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-add/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-add/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-add/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-add-form/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-add-form/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-add-form/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change-form/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change-form/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change-form/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change-history/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change-history/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-apply-change-history/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-archive/api.md +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-archive/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-archive/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-archive/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-detail/api.md +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-detail/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-detail/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-customer-detail/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-temp-customer-archive/api.md +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-temp-customer-archive/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-temp-customer-archive/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/produce/aiflow/mmwr-temp-customer-archive/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/add-demo/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/add-demo/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/add-demo/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/billet-flame-cut-plan/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/billet-flame-cut-plan/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/billet-flame-cut-plan/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/domestic-trade-order/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/domestic-trade-order/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/domestic-trade-order/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/heat-batch-return/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/heat-batch-return/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/heat-batch-return/index.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/heat-batch-return/meltDialog.vue +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/metallurgical-spec/data.ts +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/metallurgical-spec/index.scss +0 -0
- /package/files/{demo → .wl-skills/templates}/sale/demo/metallurgical-spec/index.vue +0 -0
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.
|
|
4
|
+
* wl-skills-kit CLI v2.11.0
|
|
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
|
|
@@ -107,10 +119,12 @@ if (showHelp) {
|
|
|
107
119
|
|
|
108
120
|
选项:
|
|
109
121
|
--dry-run 预览模式,不实际写入/删除任何文件
|
|
110
|
-
--keep-reports clean 命令保留 .
|
|
122
|
+
--keep-reports clean 命令保留 .wl-skills/reports/(默认一起删除)
|
|
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
|
示例:
|
|
@@ -131,7 +145,7 @@ if (showHelp) {
|
|
|
131
145
|
npx @agile-team/wl-skills-kit mock-clean --all --dry-run 预览将要清理的 mock 文件
|
|
132
146
|
|
|
133
147
|
保护路径(init / update 不覆盖已存在的):
|
|
134
|
-
.
|
|
148
|
+
.wl-skills/reports/ AI 生成报告(团队累积数据,存在则跳过)
|
|
135
149
|
|
|
136
150
|
清理保护路径(clean 不删除):
|
|
137
151
|
src/components/ 通用组件(被业务页面 import,构建必需)
|
|
@@ -232,20 +246,20 @@ function writeManifest(data) {
|
|
|
232
246
|
}
|
|
233
247
|
|
|
234
248
|
// 受保护路径(clean 不删除)
|
|
235
|
-
const PROTECTED_PREFIXES = ["src/components/", "src/types/"];
|
|
249
|
+
const PROTECTED_PREFIXES = ["src/components/", "src/types/", ".wl-skills/src/components/", ".wl-skills/src/types/"];
|
|
236
250
|
function isProtected(relPath) {
|
|
237
251
|
return PROTECTED_PREFIXES.some((p) => relPath.startsWith(p));
|
|
238
252
|
}
|
|
239
253
|
|
|
240
254
|
// reports/ 中的 AI 生成报告:init/update 遇到已存在不覆盖(团队累积数据)
|
|
241
255
|
function isReportFile(relPath) {
|
|
242
|
-
return relPath.startsWith(".github/reports/") && relPath.endsWith(".md");
|
|
256
|
+
return (relPath.startsWith(".wl-skills/reports/") || relPath.startsWith(".github/reports/")) && relPath.endsWith(".md");
|
|
243
257
|
}
|
|
244
258
|
|
|
245
259
|
// ─── 旧版遗留路径(v1.x/v2.0 → v2.1 迁移清理)───────────────────────────
|
|
246
260
|
// update 时自动检测并移除,避免旧结构与新结构并存产生歧义。
|
|
247
261
|
const LEGACY_PATHS = [
|
|
248
|
-
// Skill
|
|
262
|
+
// v2.1: Skill 目录重组 flat → core/sync/ops
|
|
249
263
|
".github/skills/prototype-scan/SKILL.md",
|
|
250
264
|
".github/skills/api-contract/SKILL.md",
|
|
251
265
|
".github/skills/page-codegen/SKILL.md",
|
|
@@ -261,34 +275,42 @@ const LEGACY_PATHS = [
|
|
|
261
275
|
".github/skills/menu-sync/SKILL.md",
|
|
262
276
|
".github/skills/menu-sync/env/env.local.json",
|
|
263
277
|
".github/skills/menu-sync/env/guide.md",
|
|
264
|
-
".github/skills/convention-extract/SKILL.md",
|
|
265
|
-
// docs/ 废弃文件:内容已迁移至 guides/ 或 reports/(v2.0)
|
|
278
|
+
".github/skills/convention-extract/SKILL.md",
|
|
266
279
|
".github/docs/menu-sync-design.md",
|
|
267
280
|
".github/docs/use-skill.md",
|
|
268
281
|
".github/docs/wl-skills-kit.md",
|
|
269
|
-
".github/docs/SYS_MENU_INFO.md",
|
|
270
|
-
// _compat/ 旧说明文件(v2.0 → v2.1 重构为可执行配置层)
|
|
282
|
+
".github/docs/SYS_MENU_INFO.md",
|
|
271
283
|
".github/skills/_compat/ai-model-matrix.md",
|
|
272
284
|
".github/skills/_compat/editor-setup.md",
|
|
273
285
|
];
|
|
274
286
|
|
|
287
|
+
// ─── v2.11 迁移:.github/ → .wl-skills/ 目录重构 ────────────────────────────
|
|
288
|
+
// update 时自动检测旧目录结构并清理
|
|
289
|
+
const LEGACY_DIR_PREFIXES = [
|
|
290
|
+
".github/skills/",
|
|
291
|
+
".github/standards/",
|
|
292
|
+
".github/guides/",
|
|
293
|
+
".github/reports/",
|
|
294
|
+
];
|
|
295
|
+
|
|
275
296
|
// ─── 编辑器配置生成(从 _compat/editors.json 读取,特化 frontmatter 注入)─────
|
|
276
297
|
|
|
277
298
|
const AUTO_HEADER_NOTE =
|
|
278
|
-
"<!-- 由 @agile-team/wl-skills-kit
|
|
299
|
+
"<!-- 由 @agile-team/wl-skills-kit 自动生成。薄壳入口 → .wl-skills/copilot-instructions-full.md -->\n" +
|
|
279
300
|
"<!-- 请勿手动编辑本文件,更新时重新执行:npx @agile-team/wl-skills-kit@latest update -->\n\n";
|
|
280
301
|
|
|
281
302
|
function getEditorConfigs(raw) {
|
|
303
|
+
// v2.11+: _compat 目录已迁移到 .wl-skills/skills/_compat/
|
|
282
304
|
const editorsJsonPath = path.join(
|
|
283
305
|
FILES_DIR,
|
|
284
|
-
".
|
|
306
|
+
".wl-skills",
|
|
285
307
|
"skills",
|
|
286
308
|
"_compat",
|
|
287
309
|
"editors.json",
|
|
288
310
|
);
|
|
289
311
|
const headersDir = path.join(
|
|
290
312
|
FILES_DIR,
|
|
291
|
-
".
|
|
313
|
+
".wl-skills",
|
|
292
314
|
"skills",
|
|
293
315
|
"_compat",
|
|
294
316
|
"headers",
|
|
@@ -341,6 +363,13 @@ function runInstall(incremental) {
|
|
|
341
363
|
|
|
342
364
|
const oldManifest = readManifest();
|
|
343
365
|
|
|
366
|
+
// ── 约束基础设施:无论版本是否相同,都确保 pre-commit hook 和 eslint 配置就绪 ──
|
|
367
|
+
// 这样即使 early-return(同版本跳过文件复制),hook 也会被创建/更新
|
|
368
|
+
if (!dryRun) {
|
|
369
|
+
ensurePreCommitHook(TARGET_DIR);
|
|
370
|
+
ensureEslintConfig(TARGET_DIR);
|
|
371
|
+
}
|
|
372
|
+
|
|
344
373
|
// ── 版本去重:同版本跳过,不同版本自动增量更新 ──────────────────────
|
|
345
374
|
if (oldManifest && !force) {
|
|
346
375
|
if (oldManifest.version === PKG.version) {
|
|
@@ -374,6 +403,9 @@ function runInstall(incremental) {
|
|
|
374
403
|
if (dryRun) console.log(" [Step 1] files/ 静态文件:\n");
|
|
375
404
|
|
|
376
405
|
for (const relPath of files) {
|
|
406
|
+
// eslint 模板由 ensureEslintConfig 单独处理,不通过 Step 1 复制
|
|
407
|
+
if (relPath === "eslint.config.wl-skills.cjs") continue;
|
|
408
|
+
|
|
377
409
|
const src = path.join(FILES_DIR, relPath);
|
|
378
410
|
const dest = path.join(TARGET_DIR, relPath);
|
|
379
411
|
const srcHash = fileMd5(src);
|
|
@@ -453,6 +485,8 @@ function runInstall(incremental) {
|
|
|
453
485
|
if (incremental) {
|
|
454
486
|
let migrated = 0;
|
|
455
487
|
if (dryRun) console.log("\n [Step 3] 旧版遗留文件检查(迁移清理):\n");
|
|
488
|
+
|
|
489
|
+
// v2.1 旧版单文件清理
|
|
456
490
|
for (const legacyRel of LEGACY_PATHS) {
|
|
457
491
|
const legacyFull = path.join(TARGET_DIR, legacyRel);
|
|
458
492
|
if (fs.existsSync(legacyFull)) {
|
|
@@ -464,6 +498,28 @@ function runInstall(incremental) {
|
|
|
464
498
|
migrated++;
|
|
465
499
|
}
|
|
466
500
|
}
|
|
501
|
+
|
|
502
|
+
// v2.11 目录重构迁移:.github/skills|standards|guides|reports/ → .wl-skills/
|
|
503
|
+
for (const prefix of LEGACY_DIR_PREFIXES) {
|
|
504
|
+
const legacyDir = path.join(TARGET_DIR, prefix);
|
|
505
|
+
if (fs.existsSync(legacyDir)) {
|
|
506
|
+
const legacyFiles = walkDir(legacyDir, legacyDir);
|
|
507
|
+
for (const f of legacyFiles) {
|
|
508
|
+
const legacyFile = path.join(legacyDir, f);
|
|
509
|
+
if (dryRun) {
|
|
510
|
+
console.log(" 迁移清理 " + prefix + f + " (v2.11 目录重构)");
|
|
511
|
+
} else {
|
|
512
|
+
removeFileAndEmptyParents(legacyFile);
|
|
513
|
+
}
|
|
514
|
+
migrated++;
|
|
515
|
+
}
|
|
516
|
+
// 删除空目录
|
|
517
|
+
if (!dryRun && fs.existsSync(legacyDir)) {
|
|
518
|
+
try { fs.rmSync(legacyDir, { recursive: true, force: true }); } catch {}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
467
523
|
if (!dryRun && migrated > 0) {
|
|
468
524
|
console.log(
|
|
469
525
|
" 迁移: " +
|
|
@@ -551,10 +607,126 @@ function runInstall(incremental) {
|
|
|
551
607
|
" ℹ 规范插件:建议执行 npx @robot-admin/git-standards init 接入代码质量与提交规范。",
|
|
552
608
|
);
|
|
553
609
|
console.log("");
|
|
610
|
+
|
|
554
611
|
}
|
|
555
612
|
|
|
556
613
|
// ─── 命令: clean ────────────────────────────────────────────────────────
|
|
557
614
|
|
|
615
|
+
/**
|
|
616
|
+
* 确保 .husky/pre-commit 包含 wl-skills validate --pre-commit
|
|
617
|
+
* — 这是让 AI 生成的代码"绕不开"规范的核心拦截点
|
|
618
|
+
*
|
|
619
|
+
* 策略:
|
|
620
|
+
* 1. 如果 .husky/pre-commit 不存在 → 创建(包含 validate 调用)
|
|
621
|
+
* 2. 如果存在但不含 wl-skills → 追加(不破坏用户已有的 hook 内容)
|
|
622
|
+
* 3. 如果存在且已含但格式过旧 → 刷新为最新格式
|
|
623
|
+
* 4. 如果存在且格式最新 → 跳过
|
|
624
|
+
*
|
|
625
|
+
* hook 使用 npx 动态解析,避免硬编码 node_modules 路径在 pnpm 下失效。
|
|
626
|
+
* 包含存在性守卫:kit 未安装时优雅跳过,不阻断提交。
|
|
627
|
+
*/
|
|
628
|
+
function ensurePreCommitHook(targetDir) {
|
|
629
|
+
const huskyDir = path.join(targetDir, ".husky");
|
|
630
|
+
const preCommitPath = path.join(huskyDir, ".husky/pre-commit");
|
|
631
|
+
|
|
632
|
+
// 只有 git 仓库才创建 husky hook
|
|
633
|
+
if (!fs.existsSync(path.join(targetDir, ".git"))) return;
|
|
634
|
+
if (!fs.existsSync(huskyDir)) return;
|
|
635
|
+
|
|
636
|
+
const VALIDATE_MARKER = "wl-skills validate --pre-commit";
|
|
637
|
+
// 最新 hook 版本标记,用于检测旧格式并刷新
|
|
638
|
+
const HOOK_VERSION_TAG = "# wl-skills-hook-v2";
|
|
639
|
+
|
|
640
|
+
const hookContent =
|
|
641
|
+
"#!/usr/bin/env sh\n" +
|
|
642
|
+
HOOK_VERSION_TAG + "\n" +
|
|
643
|
+
"# wl-skills-kit 自动管理:提交前规范检测(error 阻断提交)\n" +
|
|
644
|
+
"# 如果 node_modules 不存在或 kit 未安装,优雅跳过,不阻断提交\n" +
|
|
645
|
+
'if [ -f "node_modules/@agile-team/wl-skills-kit/bin/wl-skills.js" ]; then\n' +
|
|
646
|
+
' node node_modules/@agile-team/wl-skills-kit/bin/wl-skills.js validate --pre-commit\n' +
|
|
647
|
+
" if [ $? -ne 0 ]; then\n" +
|
|
648
|
+
' echo ""\n' +
|
|
649
|
+
' echo " ✖ 规范检测未通过,提交已阻断。修复后重新 git add + git commit"\n' +
|
|
650
|
+
" exit 1\n" +
|
|
651
|
+
" fi\n" +
|
|
652
|
+
"else\n" +
|
|
653
|
+
' echo " ⚠ wl-skills-kit 未安装(node_modules 中未找到),跳过提交前检测"\n' +
|
|
654
|
+
"fi\n";
|
|
655
|
+
|
|
656
|
+
const preCommitFile = path.join(huskyDir, "pre-commit");
|
|
657
|
+
|
|
658
|
+
if (!fs.existsSync(preCommitFile)) {
|
|
659
|
+
fs.writeFileSync(preCommitFile, hookContent, "utf8");
|
|
660
|
+
try { fs.chmodSync(preCommitFile, 0o755); } catch {}
|
|
661
|
+
console.log(" ✔ 已创建 .husky/pre-commit(提交前自动运行 wl-skills validate)");
|
|
662
|
+
console.log(" → 每次 git commit 时自动检测页面规范,error 级别阻断提交");
|
|
663
|
+
console.log(" → kit 未安装时自动跳过,不阻断提交");
|
|
664
|
+
console.log("");
|
|
665
|
+
} else {
|
|
666
|
+
const existing = fs.readFileSync(preCommitFile, "utf8");
|
|
667
|
+
|
|
668
|
+
// 已有最新版本标记 → 跳过
|
|
669
|
+
if (existing.includes(HOOK_VERSION_TAG)) return;
|
|
670
|
+
|
|
671
|
+
// 有旧 marker 但格式过旧 → 替换整段 wl-skills 块为最新格式
|
|
672
|
+
if (existing.includes(VALIDATE_MARKER)) {
|
|
673
|
+
// 删除旧的 wl-skills 块(从 VALIDATE_MARKER 前的注释行到对应的 fi)
|
|
674
|
+
const lines = existing.split("\n");
|
|
675
|
+
const filtered = [];
|
|
676
|
+
let skipMode = false;
|
|
677
|
+
for (const line of lines) {
|
|
678
|
+
if (line.includes(VALIDATE_MARKER) || line.includes("wl-skills-kit 自动")) {
|
|
679
|
+
skipMode = true;
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (skipMode && (line.includes("exit 1") || line.trim() === "fi")) {
|
|
683
|
+
skipMode = false;
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
if (!skipMode) filtered.push(line);
|
|
687
|
+
}
|
|
688
|
+
// 去尾部空行
|
|
689
|
+
while (filtered.length > 0 && filtered[filtered.length - 1].trim() === "") {
|
|
690
|
+
filtered.pop();
|
|
691
|
+
}
|
|
692
|
+
// 追加最新格式
|
|
693
|
+
const updated = filtered.join("\n").trimEnd() + "\n\n" + hookContent.replace("#!/usr/bin/env sh\n", "");
|
|
694
|
+
fs.writeFileSync(preCommitFile, updated, "utf8");
|
|
695
|
+
try { fs.chmodSync(preCommitFile, 0o755); } catch {}
|
|
696
|
+
console.log(" ✔ 已刷新 .husky/pre-commit 为最新格式(v2,含存在性守卫)");
|
|
697
|
+
console.log("");
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// 无 marker → 追加
|
|
702
|
+
const addition = "\n" + hookContent.replace("#!/usr/bin/env sh\n", "");
|
|
703
|
+
fs.writeFileSync(preCommitFile, existing.trimEnd() + "\n" + addition, "utf8");
|
|
704
|
+
try { fs.chmodSync(preCommitFile, 0o755); } catch {}
|
|
705
|
+
console.log(" ✔ 已在 .husky/pre-commit 追加 wl-skills validate(提交前规范检测)");
|
|
706
|
+
console.log(" → kit 未安装时自动跳过,不阻断提交");
|
|
707
|
+
console.log("");
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* 确保业务项目有 ESLint 配置
|
|
713
|
+
* 策略:如果项目根目录没有 eslint.config.cjs,从 kit 复制模板
|
|
714
|
+
* 如果已有,不覆盖(尊重用户自定义配置)
|
|
715
|
+
*/
|
|
716
|
+
function ensureEslintConfig(targetDir) {
|
|
717
|
+
const targetEslint = path.join(targetDir, "eslint.config.cjs");
|
|
718
|
+
if (fs.existsSync(targetEslint)) return; // 用户已有自定义配置
|
|
719
|
+
|
|
720
|
+
const templatePath = path.join(FILES_DIR, "eslint.config.wl-skills.cjs");
|
|
721
|
+
if (!fs.existsSync(templatePath)) return;
|
|
722
|
+
|
|
723
|
+
const content = fs.readFileSync(templatePath, "utf8");
|
|
724
|
+
fs.writeFileSync(targetEslint, content, "utf8");
|
|
725
|
+
console.log(" ✔ 已创建 eslint.config.cjs(wl-skills-kit 模板)");
|
|
726
|
+
console.log(" → 安装依赖后生效:pnpm add -D eslint eslint-plugin-vue vue-eslint-parser @typescript-eslint/parser @typescript-eslint/eslint-plugin");
|
|
727
|
+
console.log("");
|
|
728
|
+
}
|
|
729
|
+
|
|
558
730
|
function runClean() {
|
|
559
731
|
console.log("");
|
|
560
732
|
console.log(" wl-skills-kit v" + PKG.version + " [clean]");
|
|
@@ -573,12 +745,12 @@ function runClean() {
|
|
|
573
745
|
const allFiles = Object.keys(manifest.files);
|
|
574
746
|
const toRemove = allFiles.filter((f) => {
|
|
575
747
|
if (isProtected(f)) return false;
|
|
576
|
-
if (keepReports && f.startsWith(".github/reports/")) return false;
|
|
748
|
+
if (keepReports && (f.startsWith(".wl-skills/reports/") || f.startsWith(".github/reports/"))) return false;
|
|
577
749
|
return true;
|
|
578
750
|
});
|
|
579
751
|
const toKeep = allFiles.filter((f) => {
|
|
580
752
|
if (isProtected(f)) return true;
|
|
581
|
-
if (keepReports && f.startsWith(".github/reports/")) return true;
|
|
753
|
+
if (keepReports && (f.startsWith(".wl-skills/reports/") || f.startsWith(".github/reports/"))) return true;
|
|
582
754
|
return false;
|
|
583
755
|
});
|
|
584
756
|
|
|
@@ -614,7 +786,7 @@ function runClean() {
|
|
|
614
786
|
console.log(
|
|
615
787
|
" 保留: " +
|
|
616
788
|
toKeep.length +
|
|
617
|
-
" 个文件(src/components/ + src/types/ + .
|
|
789
|
+
" 个文件(src/components/ + src/types/ + .wl-skills/reports/)",
|
|
618
790
|
);
|
|
619
791
|
} else {
|
|
620
792
|
console.log(
|
|
@@ -633,6 +805,7 @@ function expectedManifestFiles() {
|
|
|
633
805
|
for (const relPath of files) {
|
|
634
806
|
expected[relPath] = fileMd5(path.join(FILES_DIR, relPath));
|
|
635
807
|
}
|
|
808
|
+
// v2.11+: copilot-instructions.md 是薄壳入口,完整地图在 .wl-skills/
|
|
636
809
|
const instructionsSrc = path.join(
|
|
637
810
|
FILES_DIR,
|
|
638
811
|
".github",
|
|
@@ -665,14 +838,36 @@ function runCheck() {
|
|
|
665
838
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
666
839
|
add("Node 版本", nodeMajor >= 16, process.versions.node + "(要求 >=16)");
|
|
667
840
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
841
|
+
// 工具链检测:支持多种可能的文件名
|
|
842
|
+
const prettierExists =
|
|
843
|
+
fs.existsSync(path.join(TARGET_DIR, ".prettierrc.js")) ||
|
|
844
|
+
fs.existsSync(path.join(TARGET_DIR, ".prettierrc")) ||
|
|
845
|
+
fs.existsSync(path.join(TARGET_DIR, ".prettierrc.cjs"));
|
|
846
|
+
add(".prettierrc", prettierExists, prettierExists ? "存在" : "缺失");
|
|
847
|
+
|
|
848
|
+
const eslintExists =
|
|
849
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.ts")) ||
|
|
850
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.mjs")) ||
|
|
851
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.cjs")) ||
|
|
852
|
+
fs.existsSync(path.join(TARGET_DIR, "eslint.config.js"));
|
|
853
|
+
add("eslint.config", eslintExists, eslintExists ? "存在" : "缺失");
|
|
854
|
+
|
|
855
|
+
// husky 目录检测
|
|
856
|
+
const huskyExists = fs.existsSync(path.join(TARGET_DIR, ".husky"));
|
|
857
|
+
add(".husky", huskyExists, huskyExists ? "存在" : "缺失");
|
|
858
|
+
|
|
859
|
+
// pre-commit hook 内容检测(不只检查目录存在)
|
|
860
|
+
const preCommitPath = path.join(TARGET_DIR, ".husky", "pre-commit");
|
|
861
|
+
let preCommitHasValidate = false;
|
|
862
|
+
if (fs.existsSync(preCommitPath)) {
|
|
863
|
+
const hookContent = fs.readFileSync(preCommitPath, "utf8");
|
|
864
|
+
preCommitHasValidate = hookContent.includes("wl-skills validate --pre-commit");
|
|
675
865
|
}
|
|
866
|
+
add(
|
|
867
|
+
".husky/pre-commit (wl-skills validate)",
|
|
868
|
+
preCommitHasValidate,
|
|
869
|
+
preCommitHasValidate ? "已配置规范检测" : huskyExists ? "存在但未配置 wl-skills validate" : "不存在",
|
|
870
|
+
);
|
|
676
871
|
|
|
677
872
|
const manifest = readManifest();
|
|
678
873
|
add(
|
|
@@ -859,10 +1054,37 @@ function findMockFiles() {
|
|
|
859
1054
|
function runValidate() {
|
|
860
1055
|
const scanPath =
|
|
861
1056
|
args.find((a) => !a.startsWith("-") && a !== command) || "src/views";
|
|
862
|
-
|
|
1057
|
+
|
|
1058
|
+
// --pre-commit 模式:获取 staged 文件列表,用于过滤
|
|
1059
|
+
let stagedSet = null;
|
|
1060
|
+
if (preCommit) {
|
|
1061
|
+
const staged = getStagedFiles(TARGET_DIR);
|
|
1062
|
+
if (staged.length === 0) {
|
|
1063
|
+
console.log("");
|
|
1064
|
+
console.log(" wl-skills-kit v" + PKG.version + " [validate --pre-commit]");
|
|
1065
|
+
console.log(" ⚠ 无 staged 的 .vue/.ts 文件,跳过检测");
|
|
1066
|
+
console.log("");
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
stagedSet = new Set(staged.map((f) => f.replace(/\\/g, "/")));
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
const allPages = scanPageDirs(scanPath);
|
|
1073
|
+
// 在 pre-commit 模式下,只保留包含 staged 文件的页面目录
|
|
1074
|
+
const pages = preCommit
|
|
1075
|
+
? allPages.filter((page) =>
|
|
1076
|
+
Array.from(stagedSet).some(
|
|
1077
|
+
(f) =>
|
|
1078
|
+
f.startsWith(page.dir + "/") ||
|
|
1079
|
+
f === page.dir + "/index.vue" ||
|
|
1080
|
+
f === page.dir + "/data.ts",
|
|
1081
|
+
),
|
|
1082
|
+
)
|
|
1083
|
+
: allPages;
|
|
1084
|
+
|
|
863
1085
|
console.log("");
|
|
864
|
-
console.log(" wl-skills-kit v" + PKG.version + " [" + command + "]");
|
|
865
|
-
console.log(" 扫描目录: " + scanPath);
|
|
1086
|
+
console.log(" wl-skills-kit v" + PKG.version + " [" + command + "]" + (preCommit ? " [pre-commit]" : ""));
|
|
1087
|
+
console.log(" 扫描目录: " + scanPath + (preCommit ? "(仅 staged 文件)" : ""));
|
|
866
1088
|
console.log("");
|
|
867
1089
|
|
|
868
1090
|
if (pages.length === 0) {
|
|
@@ -1021,18 +1243,185 @@ function runValidate() {
|
|
|
1021
1243
|
}
|
|
1022
1244
|
}
|
|
1023
1245
|
|
|
1024
|
-
|
|
1246
|
+
// ── AST 语义级规则检测(v2.10.1+)─────────────────────────────────
|
|
1247
|
+
// 补充正则无法覆盖的 7 条语义规则(R1~R7),与正则规则合并输出
|
|
1248
|
+
// 在 pre-commit 模式下复用上面已计算的 stagedSet
|
|
1249
|
+
const astStagedFiles = preCommit && stagedSet ? Array.from(stagedSet) : undefined;
|
|
1250
|
+
const astResult = runAstRules(TARGET_DIR, scanPath, {
|
|
1251
|
+
stagedFiles: astStagedFiles,
|
|
1252
|
+
});
|
|
1253
|
+
// 合并 AST 结果(降级和正常都 push)
|
|
1254
|
+
issues.push(...astResult.issues);
|
|
1255
|
+
|
|
1256
|
+
// ── 输出 ───────────────────────────────────────────────────────────
|
|
1257
|
+
console.log(" 页面目录: " + pages.length + (astResult.pages ? "(AST 扫描 " + astResult.pages + ")" : ""));
|
|
1025
1258
|
console.log(" 提示项: " + issues.length);
|
|
1026
1259
|
console.log("");
|
|
1027
1260
|
const errors = issues.filter((issue) => issue.level === "error").length;
|
|
1261
|
+
const warns = issues.filter(
|
|
1262
|
+
(issue) => issue.level === "warn" || issue.level === undefined,
|
|
1263
|
+
).length;
|
|
1028
1264
|
for (const issue of issues) {
|
|
1029
1265
|
const icon =
|
|
1030
1266
|
issue.level === "error" ? "✖" : issue.level === "info" ? "ℹ" : "⚠";
|
|
1031
1267
|
console.log(" " + icon + " " + issue.dir + " — " + issue.text);
|
|
1032
1268
|
}
|
|
1033
|
-
if (issues.length === 0) console.log("
|
|
1269
|
+
if (issues.length === 0) console.log(" \u2714 \u9875\u9762\u6587\u4ef6\u5b8c\u6574\u6027\u68c0\u67e5\u901a\u8fc7");
|
|
1270
|
+
console.log("");
|
|
1271
|
+
|
|
1272
|
+
// ── \u4fee\u590d\u5efa\u8bae\u8f93\u51fa\uff08P0 \u6539\u8fdb\uff1a\u963b\u65ad\u65f6\u544a\u8bc9\u5f00\u53d1\u8005\u600e\u4e48\u4fee\uff09─────────────────────
|
|
1273
|
+
const blockingIssues = issues.filter((i) => i.level === "error" || (strict && i.level === "warn"));
|
|
1274
|
+
if (blockingIssues.length > 0) {
|
|
1275
|
+
printFixSuggestions(blockingIssues);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
if (preCommit) {
|
|
1279
|
+
// pre-commit \u6a21\u5f0f\uff1aerror \u963b\u65ad\u63d0\u4ea4
|
|
1280
|
+
// --pre-commit --strict \u7ec4\u5408\uff1aerror + warn \u90fd\u963b\u65ad
|
|
1281
|
+
const failCount = strict ? errors + warns : errors;
|
|
1282
|
+
if (failCount > 0) {
|
|
1283
|
+
console.log(
|
|
1284
|
+
" \u2716 pre-commit \u68c0\u67e5\u53d1\u73b0 " +
|
|
1285
|
+
errors + " \u4e2a error" +
|
|
1286
|
+
(strict && warns > 0 ? " + " + warns + " \u4e2a warn\uff08strict \u6a21\u5f0f\uff09" : "") +
|
|
1287
|
+
"\uff0c\u63d0\u4ea4\u5df2\u963b\u65ad",
|
|
1288
|
+
);
|
|
1289
|
+
console.log(" \u2192 \u8bf7\u4fee\u590d\u540e\u91cd\u65b0 git add + git commit");
|
|
1290
|
+
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");
|
|
1291
|
+
console.log("");
|
|
1292
|
+
process.exitCode = 1;
|
|
1293
|
+
} else {
|
|
1294
|
+
console.log(" \u2714 pre-commit \u68c0\u67e5\u901a\u8fc7\uff08" + issues.length + " \u4e2a\u63d0\u793a\u9879\u4e0d\u963b\u65ad\u63d0\u4ea4\uff09");
|
|
1295
|
+
console.log("");
|
|
1296
|
+
}
|
|
1297
|
+
} else if (strict) {
|
|
1298
|
+
// --strict \u6a21\u5f0f\uff08CI \u7528\uff09\uff1aerror \u548c warn \u5bfc\u81f4\u5931\u8d25\uff0cinfo \u4e0d\u8ba1\u5165
|
|
1299
|
+
if (errors > 0 || warns > 0) {
|
|
1300
|
+
console.log(
|
|
1301
|
+
" \u2716 strict \u6a21\u5f0f\u68c0\u67e5\u53d1\u73b0 " + errors + " error / " + warns + " warn\uff0cCI \u5df2\u963b\u65ad",
|
|
1302
|
+
);
|
|
1303
|
+
console.log(" \u2192 --strict \u6a21\u5f0f\u4e0b warn \u4e5f\u4f1a\u5931\u8d25\uff0c\u8bf7\u4fee\u590d");
|
|
1304
|
+
process.exitCode = 1;
|
|
1305
|
+
} else {
|
|
1306
|
+
console.log(" \u2714 strict \u6a21\u5f0f\u68c0\u67e5\u5168\u90e8\u901a\u8fc7");
|
|
1307
|
+
}
|
|
1308
|
+
console.log("");
|
|
1309
|
+
} else {
|
|
1310
|
+
// \u666e\u901a\u6a21\u5f0f\uff1a\u53ea\u6709 error \u6216 warn \u624d exit 1\uff0cinfo \u4ec5\u63d0\u793a
|
|
1311
|
+
if (errors > 0 || warns > 0) process.exitCode = 1;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// ── \u4fee\u590d\u5efa\u8bae\u6620\u5c04\u8868\uff08P0\uff1a\u8ba9\u5f00\u53d1\u8005\u77e5\u9053\u600e\u4e48\u4fee\uff09──────────────────────────────
|
|
1316
|
+
const FIX_SUGGESTIONS = {
|
|
1317
|
+
// \u6b63\u5219\u7ea7\u68c0\u67e5
|
|
1318
|
+
'render-type="agGrid"': {
|
|
1319
|
+
fix: '<BaseTable render-type="agGrid" ...>',
|
|
1320
|
+
ref: 'standards/12-base-table.md',
|
|
1321
|
+
auto: true,
|
|
1322
|
+
},
|
|
1323
|
+
'cid / :cid': {
|
|
1324
|
+
fix: '\u7ed9 BaseTable \u52a0 cid="{\u6a21\u5757\u7f29\u5199}-{\u529f\u80fd}"\uff0c\u5168\u5c40\u552f\u4e00',
|
|
1325
|
+
ref: 'standards/12-base-table.md',
|
|
1326
|
+
auto: true,
|
|
1327
|
+
},
|
|
1328
|
+
'defineColumns()': {
|
|
1329
|
+
fix: 'import { defineColumns } from "@agile-team/wk-skills-ui/runtime" \u5e76\u7528\u4e8e\u5217\u5b9a\u4e49',
|
|
1330
|
+
ref: 'standards/12-base-table.md',
|
|
1331
|
+
auto: true,
|
|
1332
|
+
},
|
|
1333
|
+
'renderOps()': {
|
|
1334
|
+
fix: '\u64cd\u4f5c\u5217\u4f7f\u7528 defaultSlot + renderOps()\uff0c\u7981\u6b62 operations \u6570\u7ec4',
|
|
1335
|
+
ref: 'standards/12-base-table.md',
|
|
1336
|
+
auto: true,
|
|
1337
|
+
},
|
|
1338
|
+
'C_Splitter': {
|
|
1339
|
+
fix: '\u66ff\u6362\u4e3a jh-drag-col\uff08\u5de6\u53f3\uff09/ jh-drag-row\uff08\u4e0a\u4e0b\uff09',
|
|
1340
|
+
ref: 'standards/14-layout-containers.md',
|
|
1341
|
+
auto: true,
|
|
1342
|
+
},
|
|
1343
|
+
'onClick: () => {}': {
|
|
1344
|
+
fix: '\u586b\u5145\u5b9e\u9645\u4e8b\u4ef6\u5904\u7406\u903b\u8f91\uff0c\u6216\u8054\u52a8 code-fix \u81ea\u52a8\u4fee\u590d',
|
|
1345
|
+
ref: 'standards/04-coding-basics.md',
|
|
1346
|
+
auto: true,
|
|
1347
|
+
},
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
const AST_FIX_SUGGESTIONS = {
|
|
1351
|
+
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 },
|
|
1352
|
+
R2: { fix: '\u5c06 getAction/postAction/sessionStorage \u79fb\u5230 data.ts \u4e2d\u8c03\u7528', ref: 'standards/02-code-structure.md', auto: true },
|
|
1353
|
+
R3: { fix: '\u66ff\u6362 <el-table> \u4e3a <BaseTable render-type="agGrid" :cid="xxx">', ref: 'standards/12-base-table.md', auto: true },
|
|
1354
|
+
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 },
|
|
1355
|
+
R5: { fix: 'data.ts \u4e2d class extends AbstractPageQueryHook\uff0c\u5b9e\u73b0 queryDef/columnsDef', ref: 'standards/02-code-structure.md', auto: false },
|
|
1356
|
+
R6: { fix: '\u5220\u9664 import axios\uff0c\u6539\u7528 getAction/postAction', ref: 'standards/06-security.md', auto: true },
|
|
1357
|
+
R7: { fix: '\u5220\u9664 eval/new Function\uff0c\u7528\u5b89\u5168\u7684\u66ff\u4ee3\u65b9\u6848', ref: 'standards/06-security.md', auto: false },
|
|
1358
|
+
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 },
|
|
1359
|
+
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 },
|
|
1360
|
+
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 },
|
|
1361
|
+
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 },
|
|
1362
|
+
R12: { fix: '\u5c06\u786c\u7f16\u7801 IP/URL \u79fb\u81f3 .env.* \u73af\u5883\u53d8\u91cf', ref: 'standards/07-config.md', auto: true },
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
function printFixSuggestions(blockingIssues) {
|
|
1366
|
+
// \u6309\u89c4\u5219\u5206\u7ec4\u53bb\u91cd
|
|
1367
|
+
const ruleGroups = new Map();
|
|
1368
|
+
for (const issue of blockingIssues) {
|
|
1369
|
+
const key = issue.rule || guessRuleFromText(issue.text);
|
|
1370
|
+
if (!key) continue;
|
|
1371
|
+
if (!ruleGroups.has(key)) ruleGroups.set(key, []);
|
|
1372
|
+
ruleGroups.get(key).push(issue);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
if (ruleGroups.size === 0) return;
|
|
1376
|
+
|
|
1377
|
+
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");
|
|
1378
|
+
console.log(" \u2502 \ud83d\udd27 \u4fee\u590d\u5efa\u8bae \u2502");
|
|
1379
|
+
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");
|
|
1380
|
+
|
|
1381
|
+
let hasAutoFix = false;
|
|
1382
|
+
let hasUnknownIssue = false;
|
|
1383
|
+
for (const [rule, ruleIssues] of ruleGroups.entries()) {
|
|
1384
|
+
const suggestion = AST_FIX_SUGGESTIONS[rule] || findRegexSuggestion(ruleIssues[0].text);
|
|
1385
|
+
const count = ruleIssues.length;
|
|
1386
|
+
if (!suggestion) {
|
|
1387
|
+
// 免底:未知规则的阻断项也要展示,避免用户看不到任何提示
|
|
1388
|
+
hasUnknownIssue = true;
|
|
1389
|
+
console.log(" \u2502 " + rule + "\uff08" + count + " \u5904\uff09 [\u2753\u672a\u77e5\u89c4\u5219]");
|
|
1390
|
+
console.log(" \u2502 \u2192 \u8bf7\u67e5\u770b .github/standards/ \u76f8\u5173\u89c4\u8303\u6216\u89e6\u53d1\u89c4\u8303\u5ba1\u8ba1");
|
|
1391
|
+
console.log(" \u2502");
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
const autoTag = suggestion.auto ? " [\u2705\u53ef\u81ea\u52a8\u4fee]" : " [\u270b\u9700\u4eba\u5de5]";
|
|
1395
|
+
if (suggestion.auto) hasAutoFix = true;
|
|
1396
|
+
console.log(" \u2502 " + rule + "\uff08" + count + " \u5904\uff09" + autoTag);
|
|
1397
|
+
console.log(" \u2502 \u2192 " + suggestion.fix);
|
|
1398
|
+
console.log(" \u2502 \u53c2\u8003: .github/" + suggestion.ref);
|
|
1399
|
+
console.log(" \u2502");
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
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");
|
|
1403
|
+
if (hasAutoFix) {
|
|
1404
|
+
console.log(" \u2502 \ud83d\ude80 \u5feb\u901f\u4fee\u590d\uff1a\u5728 AI \u7f16\u8f91\u5668\u4e2d\u8f93\u5165\uff1a \u2502");
|
|
1405
|
+
console.log(" \u2502 \"\u89c4\u8303\u5ba1\u8ba1\" \u2192 \"\u81ea\u52a8\u4fee\u590d\" \u2192 \"\u590d\u626b\u9a8c\u8bc1\" \u2502");
|
|
1406
|
+
} else {
|
|
1407
|
+
console.log(" \u2502 \ud83d\udcdd \u8bf7\u53c2\u7167\u4e0a\u8ff0\u89c4\u8303\u6587\u6863\u624b\u52a8\u4fee\u590d \u2502");
|
|
1408
|
+
}
|
|
1409
|
+
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");
|
|
1034
1410
|
console.log("");
|
|
1035
|
-
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
function guessRuleFromText(text) {
|
|
1414
|
+
for (const key of Object.keys(FIX_SUGGESTIONS)) {
|
|
1415
|
+
if (text.includes(key)) return key;
|
|
1416
|
+
}
|
|
1417
|
+
return null;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
function findRegexSuggestion(text) {
|
|
1421
|
+
for (const [key, suggestion] of Object.entries(FIX_SUGGESTIONS)) {
|
|
1422
|
+
if (text.includes(key)) return suggestion;
|
|
1423
|
+
}
|
|
1424
|
+
return null;
|
|
1036
1425
|
}
|
|
1037
1426
|
|
|
1038
1427
|
function readJsonSafe(filePath) {
|