@agile-team/wl-skills-kit 2.4.2 → 2.5.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 +44 -1
- package/README.md +68 -45
- package/bin/wl-skills.js +196 -12
- package/files/.github/copilot-instructions.md +361 -322
- package/files/.github/guides/architecture.md +1 -1
- package/files/.github/guides/usage.md +32 -10
- package/files/.github/skills/_registry.md +18 -16
- package/files/.github/skills/core/page-codegen/SKILL.md +200 -97
- package/files/.github/skills/core/page-codegen/USAGE.md +33 -12
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-DETAIL-TABS.md +80 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-FORM-ROUTE.md +183 -55
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-LIST.md +110 -21
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-MASTER-DETAIL.md +29 -9
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-RECORD-FORM.md +93 -48
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-TREE-LIST.md +49 -29
- package/files/.github/skills/core/prototype-scan/SKILL.md +16 -0
- package/files/.github/skills/sync/menu-sync/SKILL.md +27 -13
- package/mcp/server.js +279 -195
- package/mcp/tools/menuSync.js +416 -96
- package/mcp/tools/projectTools.js +336 -124
- package/package.json +5 -2
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.5.0
|
|
5
5
|
*
|
|
6
6
|
* 命令:
|
|
7
7
|
* init 全量安装(默认,向后兼容)
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* clean 构建清理(移除 AI 指令/文档/样例,保留组件和类型)
|
|
10
10
|
* check 环境预检(工具链 / MCP 配置 / manifest)
|
|
11
11
|
* diff 对比已安装文件与当前 kit 版本
|
|
12
|
-
* validate 静态检查 src/views
|
|
12
|
+
* validate 静态检查 src/views 页面文件完整性、AGGrid、skills-ui runtime、mock
|
|
13
|
+
* validate-page validate 的别名,适用于单页/目录检查
|
|
14
|
+
* doctor-ui 检查 @agile-team/wk-skills-ui 接入完整性
|
|
13
15
|
* export 导出 SYS_MENU / SYS_DICT / SYS_PERMISSION 为 xlsx
|
|
14
16
|
* --help 帮助
|
|
15
17
|
* --dry-run 预览模式(所有命令均支持)
|
|
@@ -44,7 +46,9 @@ if (showHelp) {
|
|
|
44
46
|
clean 构建清理(移除开发期 AI 文件,保留 src/components + src/types)
|
|
45
47
|
check 环境预检(Node / 工具链 / MCP 配置 / manifest)
|
|
46
48
|
diff 对比已安装文件与当前 kit 版本的差异
|
|
47
|
-
validate 静态检查 src/views
|
|
49
|
+
validate 静态检查 src/views 页面文件、AGGrid、skills-ui runtime、mock
|
|
50
|
+
validate-page validate 的别名,适用于单页/目录检查
|
|
51
|
+
doctor-ui 检查 @agile-team/wk-skills-ui 接入完整性
|
|
48
52
|
export 导出 reports/SYS_* 数据为 xlsx
|
|
49
53
|
|
|
50
54
|
选项:
|
|
@@ -60,6 +64,8 @@ if (showHelp) {
|
|
|
60
64
|
npx @agile-team/wl-skills-kit check 检查本地环境
|
|
61
65
|
npx @agile-team/wl-skills-kit diff 查看当前项目与最新 kit 差异
|
|
62
66
|
npx @agile-team/wl-skills-kit validate 检查 src/views 页面文件
|
|
67
|
+
npx @agile-team/wl-skills-kit validate-page src/views/mdata/model/demo
|
|
68
|
+
npx @agile-team/wl-skills-kit doctor-ui 检查 wk-skills-ui 接入
|
|
63
69
|
npx @agile-team/wl-skills-kit export 导出菜单/字典/权限 xlsx
|
|
64
70
|
npx @agile-team/wl-skills-kit clean 清理开发期文件
|
|
65
71
|
npx @agile-team/wl-skills-kit clean --keep-reports 保留 reports/中的菜单/字典数据
|
|
@@ -739,30 +745,53 @@ function scanPageDirs(scanRel) {
|
|
|
739
745
|
const pages = [];
|
|
740
746
|
for (const [dir, names] of dirs.entries()) {
|
|
741
747
|
if (!names.has("index.vue")) continue;
|
|
742
|
-
|
|
748
|
+
const indexPath = path.join(TARGET_DIR, dir, "index.vue");
|
|
749
|
+
const indexContent = fs.existsSync(indexPath)
|
|
750
|
+
? fs.readFileSync(indexPath, "utf8")
|
|
751
|
+
: "";
|
|
743
752
|
const dataPath = path.join(TARGET_DIR, dir, "data.ts");
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
753
|
+
const dataContent = fs.existsSync(dataPath)
|
|
754
|
+
? fs.readFileSync(dataPath, "utf8")
|
|
755
|
+
: "";
|
|
756
|
+
let apiConfigCount = 0;
|
|
757
|
+
if (dataContent)
|
|
758
|
+
apiConfigCount = (dataContent.match(/API_CONFIG/g) || []).length;
|
|
749
759
|
pages.push({
|
|
750
760
|
dir,
|
|
751
761
|
hasDataTs: names.has("data.ts"),
|
|
752
762
|
hasIndexScss: names.has("index.scss"),
|
|
753
763
|
hasApiMd: names.has("api.md"),
|
|
754
764
|
apiConfigCount,
|
|
765
|
+
baseTableCount: (indexContent.match(/<BaseTable\b/g) || []).length,
|
|
766
|
+
agGridCount: (indexContent.match(/render-type=["']agGrid["']/g) || [])
|
|
767
|
+
.length,
|
|
768
|
+
cidBindCount: (indexContent.match(/\bcid=|:cid=/g) || []).length,
|
|
769
|
+
hasDefineColumns: /defineColumns\s*\(/.test(dataContent),
|
|
770
|
+
hasRenderOps: /renderOps\s*\(/.test(dataContent),
|
|
771
|
+
hasOperationsArray: /operations\s*:/.test(dataContent),
|
|
772
|
+
hasEmptyOnClick: /onClick\s*:\s*\(\s*[^)]*\s*\)\s*=>\s*\{\s*\}/.test(
|
|
773
|
+
dataContent,
|
|
774
|
+
),
|
|
775
|
+
apiUrls: Array.from(
|
|
776
|
+
dataContent.matchAll(/:\s*["']([^"']+\/[^"']+)["']/g),
|
|
777
|
+
).map((m) => m[1]),
|
|
755
778
|
});
|
|
756
779
|
}
|
|
757
780
|
return pages.sort((a, b) => a.dir.localeCompare(b.dir));
|
|
758
781
|
}
|
|
759
782
|
|
|
783
|
+
function findMockFiles() {
|
|
784
|
+
const mockDir = path.join(TARGET_DIR, "mock");
|
|
785
|
+
if (!fs.existsSync(mockDir)) return [];
|
|
786
|
+
return walkDir(mockDir, TARGET_DIR).filter((rel) => /\.(ts|js)$/.test(rel));
|
|
787
|
+
}
|
|
788
|
+
|
|
760
789
|
function runValidate() {
|
|
761
790
|
const scanPath =
|
|
762
791
|
args.find((a) => !a.startsWith("-") && a !== command) || "src/views";
|
|
763
792
|
const pages = scanPageDirs(scanPath);
|
|
764
793
|
console.log("");
|
|
765
|
-
console.log(" wl-skills-kit v" + PKG.version + " [
|
|
794
|
+
console.log(" wl-skills-kit v" + PKG.version + " [" + command + "]");
|
|
766
795
|
console.log(" 扫描目录: " + scanPath);
|
|
767
796
|
console.log("");
|
|
768
797
|
|
|
@@ -774,6 +803,10 @@ function runValidate() {
|
|
|
774
803
|
}
|
|
775
804
|
|
|
776
805
|
const issues = [];
|
|
806
|
+
const mockFiles = findMockFiles();
|
|
807
|
+
const mockContent = mockFiles
|
|
808
|
+
.map((rel) => fs.readFileSync(path.join(TARGET_DIR, rel), "utf8"))
|
|
809
|
+
.join("\n");
|
|
777
810
|
for (const page of pages) {
|
|
778
811
|
if (!page.hasDataTs)
|
|
779
812
|
issues.push({
|
|
@@ -789,17 +822,164 @@ function runValidate() {
|
|
|
789
822
|
dir: page.dir,
|
|
790
823
|
text: "检测到 API_CONFIG 但缺 api.md",
|
|
791
824
|
});
|
|
825
|
+
if (page.baseTableCount > 0 && page.agGridCount < page.baseTableCount)
|
|
826
|
+
issues.push({
|
|
827
|
+
level: "error",
|
|
828
|
+
dir: page.dir,
|
|
829
|
+
text: 'BaseTable 必须显式 render-type="agGrid"',
|
|
830
|
+
});
|
|
831
|
+
if (page.baseTableCount > 0 && page.cidBindCount < page.baseTableCount)
|
|
832
|
+
issues.push({
|
|
833
|
+
level: "error",
|
|
834
|
+
dir: page.dir,
|
|
835
|
+
text: "BaseTable 必须配置全局唯一 cid / :cid",
|
|
836
|
+
});
|
|
837
|
+
if (page.hasDataTs && page.baseTableCount > 0 && !page.hasDefineColumns)
|
|
838
|
+
issues.push({
|
|
839
|
+
level: "error",
|
|
840
|
+
dir: page.dir,
|
|
841
|
+
text: "表格列必须使用 wk-skills-ui defineColumns()",
|
|
842
|
+
});
|
|
843
|
+
if (page.hasOperationsArray)
|
|
844
|
+
issues.push({
|
|
845
|
+
level: "error",
|
|
846
|
+
dir: page.dir,
|
|
847
|
+
text: "操作列禁止 operations 数组,必须使用 defaultSlot + renderOps()",
|
|
848
|
+
});
|
|
849
|
+
if (
|
|
850
|
+
page.hasDataTs &&
|
|
851
|
+
page.baseTableCount > 0 &&
|
|
852
|
+
!page.hasRenderOps &&
|
|
853
|
+
/操作|_action/.test(
|
|
854
|
+
fs.readFileSync(path.join(TARGET_DIR, page.dir, "data.ts"), "utf8"),
|
|
855
|
+
)
|
|
856
|
+
)
|
|
857
|
+
issues.push({
|
|
858
|
+
level: "warn",
|
|
859
|
+
dir: page.dir,
|
|
860
|
+
text: "疑似存在操作列但未使用 renderOps()",
|
|
861
|
+
});
|
|
862
|
+
if (page.hasEmptyOnClick)
|
|
863
|
+
issues.push({
|
|
864
|
+
level: "error",
|
|
865
|
+
dir: page.dir,
|
|
866
|
+
text: "存在空 onClick: () => {}",
|
|
867
|
+
});
|
|
868
|
+
if (page.apiConfigCount > 0 && mockFiles.length === 0)
|
|
869
|
+
issues.push({
|
|
870
|
+
level: "warn",
|
|
871
|
+
dir: page.dir,
|
|
872
|
+
text: "检测到 API_CONFIG 但项目 mock/ 目录无 mock 文件",
|
|
873
|
+
});
|
|
874
|
+
for (const url of page.apiUrls.filter((item) => item.startsWith("/"))) {
|
|
875
|
+
const mockUrl = `/dev-api${url}`;
|
|
876
|
+
if (mockContent && !mockContent.includes(mockUrl))
|
|
877
|
+
issues.push({
|
|
878
|
+
level: "warn",
|
|
879
|
+
dir: page.dir,
|
|
880
|
+
text: "mock 中未发现端点 " + mockUrl,
|
|
881
|
+
});
|
|
882
|
+
}
|
|
792
883
|
}
|
|
793
884
|
|
|
794
885
|
console.log(" 页面目录: " + pages.length);
|
|
795
886
|
console.log(" 提示项: " + issues.length);
|
|
796
887
|
console.log("");
|
|
888
|
+
const errors = issues.filter((issue) => issue.level === "error").length;
|
|
797
889
|
for (const issue of issues) {
|
|
798
|
-
|
|
890
|
+
const icon = issue.level === "error" ? "✖" : "⚠";
|
|
891
|
+
console.log(" " + icon + " " + issue.dir + " — " + issue.text);
|
|
799
892
|
}
|
|
800
893
|
if (issues.length === 0) console.log(" ✔ 页面文件完整性检查通过");
|
|
801
894
|
console.log("");
|
|
802
|
-
if (issues.length > 0) process.exitCode = 1;
|
|
895
|
+
if (errors > 0 || issues.length > 0) process.exitCode = 1;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function readJsonSafe(filePath) {
|
|
899
|
+
if (!fs.existsSync(filePath)) return null;
|
|
900
|
+
try {
|
|
901
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
902
|
+
} catch {
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function runDoctorUi() {
|
|
908
|
+
console.log("");
|
|
909
|
+
console.log(" wl-skills-kit v" + PKG.version + " [doctor-ui]");
|
|
910
|
+
console.log(" 目标目录: " + TARGET_DIR);
|
|
911
|
+
console.log("");
|
|
912
|
+
|
|
913
|
+
const checks = [];
|
|
914
|
+
function add(name, ok, detail) {
|
|
915
|
+
checks.push({ name, ok, detail });
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const pkg = readJsonSafe(path.join(TARGET_DIR, "package.json"));
|
|
919
|
+
const deps = pkg ? { ...pkg.dependencies, ...pkg.devDependencies } : {};
|
|
920
|
+
add(
|
|
921
|
+
"@agile-team/wk-skills-ui",
|
|
922
|
+
Boolean(deps["@agile-team/wk-skills-ui"]),
|
|
923
|
+
deps["@agile-team/wk-skills-ui"] || "未安装",
|
|
924
|
+
);
|
|
925
|
+
add(
|
|
926
|
+
"@element-plus/icons-vue",
|
|
927
|
+
Boolean(deps["@element-plus/icons-vue"]),
|
|
928
|
+
deps["@element-plus/icons-vue"] || "未安装",
|
|
929
|
+
);
|
|
930
|
+
|
|
931
|
+
const files = fs.existsSync(TARGET_DIR)
|
|
932
|
+
? walkDir(TARGET_DIR, TARGET_DIR)
|
|
933
|
+
: [];
|
|
934
|
+
const sourceFiles = files.filter(
|
|
935
|
+
(rel) =>
|
|
936
|
+
/\.(ts|vue|scss|html)$/.test(rel) && !rel.startsWith("node_modules/"),
|
|
937
|
+
);
|
|
938
|
+
const readAll = (pattern) =>
|
|
939
|
+
sourceFiles
|
|
940
|
+
.filter((rel) => pattern.test(rel))
|
|
941
|
+
.map((rel) => fs.readFileSync(path.join(TARGET_DIR, rel), "utf8"))
|
|
942
|
+
.join("\n");
|
|
943
|
+
const allSource = readAll(/.*/);
|
|
944
|
+
|
|
945
|
+
add(
|
|
946
|
+
"design tokens",
|
|
947
|
+
/@agile-team\/wk-skills-ui\/design\/tokens|dist\/tokens\.css/.test(
|
|
948
|
+
allSource,
|
|
949
|
+
),
|
|
950
|
+
"需引入 design tokens",
|
|
951
|
+
);
|
|
952
|
+
add(
|
|
953
|
+
"styles preset",
|
|
954
|
+
/@agile-team\/wk-skills-ui\/styles/.test(allSource),
|
|
955
|
+
"需引入 styles 或 skin preset",
|
|
956
|
+
);
|
|
957
|
+
add(
|
|
958
|
+
"installCommonPreset",
|
|
959
|
+
/installCommonPreset\s*\(/.test(allSource),
|
|
960
|
+
"需在入口或业务 preset 中调用",
|
|
961
|
+
);
|
|
962
|
+
add(
|
|
963
|
+
"defineColumns",
|
|
964
|
+
/defineColumns\s*\(/.test(allSource),
|
|
965
|
+
"页面列定义需使用 defineColumns",
|
|
966
|
+
);
|
|
967
|
+
add("renderOps", /renderOps\s*\(/.test(allSource), "操作列需使用 renderOps");
|
|
968
|
+
|
|
969
|
+
for (const item of checks) {
|
|
970
|
+
console.log(
|
|
971
|
+
" " + statusIcon(item.ok) + " " + item.name + " — " + item.detail,
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
const failed = checks.filter((item) => !item.ok).length;
|
|
975
|
+
console.log("");
|
|
976
|
+
console.log(
|
|
977
|
+
failed === 0
|
|
978
|
+
? " ✔ wk-skills-ui 接入检查通过"
|
|
979
|
+
: " ⚠ wk-skills-ui 接入仍有 " + failed + " 项需处理",
|
|
980
|
+
);
|
|
981
|
+
console.log("");
|
|
982
|
+
if (failed > 0) process.exitCode = 1;
|
|
803
983
|
}
|
|
804
984
|
|
|
805
985
|
function parseMarkdownTable(content) {
|
|
@@ -899,8 +1079,12 @@ switch (command) {
|
|
|
899
1079
|
runDiff();
|
|
900
1080
|
break;
|
|
901
1081
|
case "validate":
|
|
1082
|
+
case "validate-page":
|
|
902
1083
|
runValidate();
|
|
903
1084
|
break;
|
|
1085
|
+
case "doctor-ui":
|
|
1086
|
+
runDoctorUi();
|
|
1087
|
+
break;
|
|
904
1088
|
case "export":
|
|
905
1089
|
runExport();
|
|
906
1090
|
break;
|