@agile-team/wl-skills-kit 2.11.1 → 2.11.2

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +1 -1
  3. package/bin/wl-skills.js +27 -3
  4. package/files/.wl-skills/docs/jh-pagination.md +505 -505
  5. package/files/.wl-skills/docs/request.md +940 -940
  6. package/files/.wl-skills/guides/architecture.md +1 -1
  7. package/files/.wl-skills/skills/core/convention-audit/SKILL.md +3 -3
  8. package/files/.wl-skills/skills/core/spec-doc-parse/SKILL.md +332 -332
  9. package/files/.wl-skills/skills/core/spec-doc-parse/USAGE.md +97 -97
  10. package/files/.wl-skills/skills/sync/permission-sync/USAGE.md +107 -107
  11. package/files/.wl-skills/src/components/global/C_ParentView/index.vue +3 -3
  12. package/files/.wl-skills/src/components/global/C_RightToolbar/index.vue +157 -157
  13. package/files/.wl-skills/src/components/global/C_SvgIcon/index.vue +31 -31
  14. package/files/.wl-skills/src/components/global/C_SvgIcon/svgicon.js +10 -10
  15. package/files/.wl-skills/src/components/global/C_TagStatus/README.md +264 -264
  16. package/files/.wl-skills/src/components/global/C_TagStatus/config.ts +192 -192
  17. package/files/.wl-skills/src/components/global/C_TagStatus/index.vue +106 -106
  18. package/files/.wl-skills/src/components/global/C_TagStatus/types.ts +64 -64
  19. package/files/.wl-skills/src/components/global/C_Tree/README.md +153 -153
  20. package/files/.wl-skills/src/components/global/C_Tree/index.scss +42 -42
  21. package/files/.wl-skills/src/components/global/C_Tree/index.vue +78 -78
  22. package/files/.wl-skills/src/components/global/C_Tree/types.ts +59 -59
  23. package/files/.wl-skills/src/components/local/c_formModal/README.md +235 -235
  24. package/files/.wl-skills/src/components/local/c_formModal/data.ts +95 -95
  25. package/files/.wl-skills/src/components/local/c_formModal/index.scss +8 -8
  26. package/files/.wl-skills/src/components/local/c_formModal/index.vue +107 -107
  27. package/files/.wl-skills/src/components/local/c_formSections/data.ts +175 -175
  28. package/files/.wl-skills/src/components/local/c_formSections/index.scss +280 -280
  29. package/files/.wl-skills/src/components/local/c_formSections/index.vue +429 -429
  30. package/files/.wl-skills/src/components/local/c_listModal/data.ts +41 -41
  31. package/files/.wl-skills/src/components/local/c_listModal/index.vue +136 -136
  32. package/files/.wl-skills/src/components/local/c_spliterTitle/index.scss +25 -25
  33. package/files/.wl-skills/src/components/local/c_spliterTitle/index.vue +21 -21
  34. package/files/.wl-skills/src/components/remote/AGGrid/README.md +530 -530
  35. package/files/.wl-skills/src/components/remote/BaseForm/README.md +508 -508
  36. package/files/.wl-skills/src/components/remote/BaseQuery/README.md +865 -865
  37. package/files/.wl-skills/src/components/remote/BaseTable/README.md +941 -941
  38. package/files/.wl-skills/src/components/remote/BaseToolbar/README.md +496 -496
  39. package/files/.wl-skills/src/types/page.ts +24 -24
  40. package/files/.wl-skills/standards/04-coding-basics.md +39 -1
  41. package/files/.wl-skills/standards/09-typescript.md +26 -3
  42. package/files/.wl-skills/standards/index.md +2 -2
  43. package/files/.wl-skills/templates/README.md +44 -44
  44. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-add/api.md +54 -54
  45. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-add/data.ts +346 -346
  46. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-add/index.scss +1 -1
  47. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-add/index.vue +28 -28
  48. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-add-form/data.ts +115 -115
  49. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-add-form/index.scss +44 -44
  50. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-add-form/index.vue +43 -43
  51. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-change/data.ts +338 -338
  52. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-change/index.scss +1 -1
  53. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-change/index.vue +28 -28
  54. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-change-form/data.ts +115 -115
  55. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-change-form/index.scss +44 -44
  56. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-apply-change-form/index.vue +43 -43
  57. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-archive/api.md +88 -88
  58. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-archive/data.ts +601 -601
  59. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-archive/index.scss +1 -1
  60. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-archive/index.vue +64 -64
  61. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-detail/api.md +67 -67
  62. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-detail/data.ts +286 -286
  63. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-detail/index.scss +139 -139
  64. package/files/.wl-skills/templates/produce/aiflow/mmwr-customer-detail/index.vue +318 -318
  65. package/files/.wl-skills/templates/produce/aiflow/mmwr-temp-customer-archive/api.md +98 -98
  66. package/files/.wl-skills/templates/produce/aiflow/mmwr-temp-customer-archive/data.ts +543 -543
  67. package/files/.wl-skills/templates/produce/aiflow/mmwr-temp-customer-archive/index.scss +1 -1
  68. package/files/.wl-skills/templates/produce/aiflow/mmwr-temp-customer-archive/index.vue +52 -52
  69. package/files/.wl-skills/templates/sale/demo/add-demo/data.ts +518 -518
  70. package/files/.wl-skills/templates/sale/demo/billet-flame-cut-plan/data.ts +524 -524
  71. package/files/.wl-skills/templates/sale/demo/billet-flame-cut-plan/index.scss +154 -154
  72. package/files/.wl-skills/templates/sale/demo/billet-flame-cut-plan/index.vue +117 -117
  73. package/files/.wl-skills/templates/sale/demo/domestic-trade-order/data.ts +308 -308
  74. package/files/.wl-skills/templates/sale/demo/domestic-trade-order/index.scss +99 -99
  75. package/files/.wl-skills/templates/sale/demo/domestic-trade-order/index.vue +77 -77
  76. package/files/.wl-skills/templates/sale/demo/heat-batch-return/data.ts +367 -367
  77. package/files/.wl-skills/templates/sale/demo/heat-batch-return/index.scss +100 -100
  78. package/files/.wl-skills/templates/sale/demo/heat-batch-return/index.vue +170 -170
  79. package/files/.wl-skills/templates/sale/demo/heat-batch-return/meltDialog.vue +320 -320
  80. package/files/.wl-skills/templates/sale/demo/metallurgical-spec/data.ts +824 -824
  81. package/lib/ast-rules.js +304 -9
  82. package/mcp/config.js +46 -46
  83. package/mcp/registry.js +6 -1
  84. package/mcp/tools/projectTools.js +9 -1
  85. package/package.json +2 -2
package/lib/ast-rules.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * 依赖:@vue/compiler-sfc(解析 .vue)、@babel/parser(解析 <script> AST)
8
8
  * 这两个包在 Vue 3 + Vite 项目中天然存在,用 try-require 做优雅降级。
9
9
  *
10
- * 规则编号 R1~R12 对应 standards 02/06/07/10/12/13 中的语义约束:
10
+ * 规则编号 R1~R14 对应 standards 02/04/06/07/09/10/12/13 中的语义约束:
11
11
  * R1: index.vue <script setup> 业务逻辑行数超阈值 → warn(02)
12
12
  * R2: index.vue 含禁止 import(getAction/postAction/sessionStorage 等)→ error(02/06)
13
13
  * R3: 页面用 <el-table> 但未用 <BaseTable> → error(12/13)
@@ -20,17 +20,23 @@
20
20
  * R10: 平台组件替换检测 — el-form/el-select/el-date-picker 等应替换为平台封装 → error(13)
21
21
  * R11: data.ts 禁止 import Pinia Store → error(10)
22
22
  * R12: 硬编码 IP/URL 检测 → error/warn(07)
23
+ * R13: 单函数圈复杂度 > 10(Mcabe,与 ESLint complexity 定义一致)→ error(04)
24
+ * R14: 文件类型错误零容忍 — vue-tsc/tsc --noEmit 产物解析 → error(09)
25
+ * 注:R14 为项目级检查,体积较大,validate 默认不跑,
26
+ * 需显式 --typecheck(CLI)/ typecheck:true(MCP)触发,优雅降级为 warn
23
27
  *
24
28
  * 导出函数:
25
29
  * runAstRules(targetDir, scanRel, { stagedFiles }) → { issues, pages }
26
30
  * parseVueScript(absPath) → { content, template, source } | null
27
31
  * countEffectiveLines(scriptContent) → number
32
+ * computeFunctionComplexity(fnNode) → number
33
+ * runTypeCheck(root) → { issues, ran, errorCount }
28
34
  * hasAstAvailable() → boolean
29
35
  */
30
36
 
31
37
  const fs = require("fs");
32
38
  const path = require("path");
33
- const { execFileSync } = require("child_process");
39
+ const { execFileSync, spawnSync } = require("child_process");
34
40
 
35
41
  // ─── AST 依赖探测(优雅降级)──────────────────────────────────────────
36
42
  //
@@ -159,6 +165,10 @@ const CONFIG = {
159
165
  ],
160
166
  FORBIDDEN_GLOBALS: ["sessionStorage", "localStorage"],
161
167
  WARN_IMPORTS: ["useRoute"],
168
+ // R13: 单函数圈复杂度上限(Mcabe),与 ESLint complexity 规则阈值一致
169
+ MAX_CYCLOMATIC_COMPLEXITY: 10,
170
+ // R14: 单页类型错误采集上限(避免输出爆炸)
171
+ TYPECHECK_ERROR_CAP: 50,
162
172
  SKIP_DIRS: ["node_modules", "dist", ".git", "demo", "template"],
163
173
  };
164
174
 
@@ -238,22 +248,30 @@ function countEffectiveLines(scriptContent) {
238
248
  }
239
249
 
240
250
  /**
241
- * 用 babel/parser 解析 script,提取 import 标识符和来源
251
+ * 用 babel/parser 解析 script AST(复用给 extractScriptInfo / 圈复杂度计算)
242
252
  */
243
- function extractScriptInfo(scriptContent) {
244
- if (!ensureAst() || !scriptContent) {
245
- return { specifiers: [], sources: [] };
246
- }
247
- let ast;
253
+ function parseScriptAst(scriptContent) {
254
+ if (!ensureAst() || !scriptContent) return null;
248
255
  try {
249
- ast = _babelParser.parse(scriptContent, {
256
+ return _babelParser.parse(scriptContent, {
250
257
  sourceType: "module",
251
258
  plugins: ["typescript", "jsx"],
252
259
  errorRecovery: true,
253
260
  });
254
261
  } catch {
262
+ return null;
263
+ }
264
+ }
265
+
266
+ /**
267
+ * 用 babel/parser 解析 script,提取 import 标识符和来源
268
+ */
269
+ function extractScriptInfo(scriptContent) {
270
+ if (!ensureAst() || !scriptContent) {
255
271
  return { specifiers: [], sources: [] };
256
272
  }
273
+ const ast = parseScriptAst(scriptContent);
274
+ if (!ast) return { specifiers: [], sources: [] };
257
275
  const specifiers = [];
258
276
  const sources = [];
259
277
  for (const node of ast.program.body) {
@@ -272,6 +290,114 @@ function extractScriptInfo(scriptContent) {
272
290
  return { specifiers, sources };
273
291
  }
274
292
 
293
+ // ─── R13 圈复杂度(Mcabe)──────────────────────────────────────────────
294
+ //
295
+ // 定义与 ESLint `complexity` 规则一致:复杂度 = 1 + 决策点数。
296
+ // 不依赖 @babel/traverse,自写轻量遍历,避免新增运行时依赖。
297
+
298
+ const COMPLEXITY_NODE_TYPES = new Set([
299
+ "IfStatement", // if / else if(每个 IfStatement 计 1)
300
+ "SwitchCase", // 每个 case / default
301
+ "ForStatement",
302
+ "ForInStatement",
303
+ "ForOfStatement",
304
+ "WhileStatement",
305
+ "DoWhileStatement",
306
+ "CatchClause",
307
+ "ConditionalExpression", // 三元 ?:
308
+ "LogicalExpression", // && / || / ??
309
+ ]);
310
+
311
+ const FUNCTION_NODE_TYPES = new Set([
312
+ "FunctionDeclaration",
313
+ "FunctionExpression",
314
+ "ArrowFunctionExpression",
315
+ ]);
316
+
317
+ function isFunctionNode(node) {
318
+ return Boolean(node) && FUNCTION_NODE_TYPES.has(node.type);
319
+ }
320
+
321
+ // 取节点所有子 AST 节点(跳过位置/范围元信息)
322
+ function getChildNodes(node) {
323
+ const out = [];
324
+ for (const key of Object.keys(node)) {
325
+ if (key === "loc" || key === "range" || key === "start" || key === "end") {
326
+ continue;
327
+ }
328
+ const child = node[key];
329
+ if (Array.isArray(child)) {
330
+ for (const c of child) {
331
+ if (c && typeof c.type === "string") out.push(c);
332
+ }
333
+ } else if (child && typeof child.type === "string") {
334
+ out.push(child);
335
+ }
336
+ }
337
+ return out;
338
+ }
339
+
340
+ // 尽力推断函数名(变量赋值 / 类方法 / 对象方法 / 命名函数表达式)
341
+ function resolveFnName(node, parent) {
342
+ if (node.id && node.id.name) return node.id.name;
343
+ if (!parent) return "(anonymous)";
344
+ if (parent.type === "VariableDeclarator" && parent.id && parent.id.name) {
345
+ return parent.id.name;
346
+ }
347
+ if (
348
+ (parent.type === "MethodDefinition" || parent.type === "Property") &&
349
+ parent.key
350
+ ) {
351
+ return parent.key.name || parent.key.value || "(anonymous)";
352
+ }
353
+ if (parent.type === "AssignmentExpression" && parent.left) {
354
+ const left = parent.left;
355
+ if (left.property) return left.property.name || left.property.value;
356
+ if (left.name) return left.name;
357
+ }
358
+ return "(anonymous)";
359
+ }
360
+
361
+ // 收集整棵 AST 中的所有函数节点 + 名字(含嵌套函数,各独立计 R13)
362
+ function collectFunctions(ast) {
363
+ const fns = [];
364
+ const stack = [];
365
+ function walk(node) {
366
+ if (!node || typeof node.type !== "string") return;
367
+ let pushed = false;
368
+ if (isFunctionNode(node)) {
369
+ const parent = stack.length ? stack[stack.length - 1] : null;
370
+ fns.push({ node, name: resolveFnName(node, parent) });
371
+ // 不再下钻到该函数体内部去发现"孙函数"——
372
+ // 嵌套函数会在遍历其父函数体时自然被收集
373
+ }
374
+ stack.push(node);
375
+ pushed = true;
376
+ for (const child of getChildNodes(node)) walk(child);
377
+ if (pushed) stack.pop();
378
+ }
379
+ walk(ast);
380
+ return fns;
381
+ }
382
+
383
+ /**
384
+ * 计算单个函数的圈复杂度(不下钻进嵌套函数体,嵌套函数独立计)
385
+ * @param {object} fnNode FunctionDeclaration / FunctionExpression / ArrowFunctionExpression
386
+ * @returns {number}
387
+ */
388
+ function computeFunctionComplexity(fnNode) {
389
+ if (!fnNode) return 0;
390
+ let complexity = 1;
391
+ function walk(node) {
392
+ if (!node || typeof node.type !== "string") return;
393
+ if (node !== fnNode && isFunctionNode(node)) return; // 嵌套函数边界
394
+ if (COMPLEXITY_NODE_TYPES.has(node.type)) complexity++;
395
+ for (const child of getChildNodes(node)) walk(child);
396
+ }
397
+ walk(fnNode);
398
+ return complexity;
399
+ }
400
+
275
401
  /**
276
402
  * 检测模板中是否有 el-table(非 BaseTable)
277
403
  */
@@ -419,6 +545,34 @@ function runAstRules(targetDir, scanRel, options) {
419
545
  });
420
546
  }
421
547
 
548
+ // R13: 单函数圈复杂度 ≤ MAX_CYCLOMATIC_COMPLEXITY(standard 04,Mcabe)
549
+ // 覆盖 index.vue <script> 与 data.ts 的所有函数/方法/箭头函数
550
+ if (!hasIgnoreMarker(fullSource, "R13")) {
551
+ const maxC = CONFIG.MAX_CYCLOMATIC_COMPLEXITY;
552
+ const scanComplexity = (code, label) => {
553
+ const ast = parseScriptAst(code);
554
+ if (!ast) return;
555
+ for (const fn of collectFunctions(ast)) {
556
+ const cc = computeFunctionComplexity(fn.node);
557
+ if (cc > maxC) {
558
+ issues.push({
559
+ level: "error",
560
+ dir: page.dir,
561
+ text:
562
+ label + " 函数 " + fn.name +
563
+ "() 圈复杂度 " + cc + "(阈值 " + maxC +
564
+ "),需拆分为更小函数(standard 04)",
565
+ rule: "R13",
566
+ });
567
+ }
568
+ }
569
+ };
570
+ scanComplexity(scriptContent, "index.vue");
571
+ if (fs.existsSync(dataPath)) {
572
+ scanComplexity(fs.readFileSync(dataPath, "utf8"), "data.ts");
573
+ }
574
+ }
575
+
422
576
  // R2: 禁止的 import / 全局 API
423
577
  if (scriptContent) {
424
578
  const { specifiers, sources } = extractScriptInfo(scriptContent);
@@ -754,11 +908,152 @@ function getStagedFiles(targetDir) {
754
908
  }
755
909
  }
756
910
 
911
+ // ─── R14 类型错误零容忍(项目级,vue-tsc / tsc 委托)──────────────────
912
+ //
913
+ // 体积较大,validate 默认不触发,由 CLI --typecheck / MCP typecheck:true 显式开启。
914
+ // 无 tsconfig / 无 checker → 优雅降级为 warn(与 AST 依赖降级策略一致)。
915
+ // 该函数不进入 page 粒度,整项目执行一次,结果按文件归并到 issues。
916
+
917
+ function runTypeCheck(root) {
918
+ const safeRoot = root || process.cwd();
919
+ const label = path.basename(safeRoot) || ".";
920
+ const tsconfigPath = path.join(safeRoot, "tsconfig.json");
921
+
922
+ if (!fs.existsSync(tsconfigPath)) {
923
+ return {
924
+ issues: [
925
+ {
926
+ level: "warn",
927
+ dir: label,
928
+ text: "未发现 tsconfig.json,跳过类型检查 R14",
929
+ rule: "R14",
930
+ },
931
+ ],
932
+ ran: false,
933
+ errorCount: 0,
934
+ };
935
+ }
936
+
937
+ const nmBin = path.join(safeRoot, "node_modules", ".bin");
938
+ const envPath =
939
+ nmBin + path.delimiter + (process.env.PATH || process.env.Path || "");
940
+ const env = Object.assign({}, process.env, { PATH: envPath });
941
+
942
+ // 优先 vue-tsc(.vue 项目),回退 tsc;shell:true 让 Windows 找到 .cmd
943
+ let checker = null;
944
+ for (const bin of ["vue-tsc", "tsc"]) {
945
+ try {
946
+ const probe = spawnSync(bin, ["--version"], {
947
+ shell: true,
948
+ env,
949
+ encoding: "utf8",
950
+ timeout: 20000,
951
+ });
952
+ if (probe.status === 0) {
953
+ checker = bin;
954
+ break;
955
+ }
956
+ } catch {
957
+ // ignore, try next
958
+ }
959
+ }
960
+ if (!checker) {
961
+ return {
962
+ issues: [
963
+ {
964
+ level: "warn",
965
+ dir: label,
966
+ text: "未发现 vue-tsc / tsc,跳过类型检查 R14(建议安装后纳入 CI)",
967
+ rule: "R14",
968
+ },
969
+ ],
970
+ ran: false,
971
+ errorCount: 0,
972
+ };
973
+ }
974
+
975
+ let result;
976
+ try {
977
+ result = spawnSync(checker, ["--noEmit"], {
978
+ cwd: safeRoot,
979
+ shell: true,
980
+ env,
981
+ encoding: "utf8",
982
+ timeout: 180000,
983
+ });
984
+ } catch (e) {
985
+ return {
986
+ issues: [
987
+ {
988
+ level: "warn",
989
+ dir: label,
990
+ text:
991
+ "类型检查执行异常:" + ((e && e.message) || String(e)),
992
+ rule: "R14",
993
+ },
994
+ ],
995
+ ran: false,
996
+ errorCount: 0,
997
+ };
998
+ }
999
+
1000
+ const out =
1001
+ String(result.stdout || "") + String(result.stderr || "");
1002
+ // 标准格式:path(line,col): error TS1234: message
1003
+ // 捕获组:1=file 2=line 3=col 4=code 5=msg
1004
+ const errRe = /^(.+?)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s*(.+)$/gm;
1005
+ const errors = [];
1006
+ let m;
1007
+ while ((m = errRe.exec(out)) !== null) {
1008
+ errors.push({ file: m[1], line: m[2], code: m[4], msg: m[5] });
1009
+ if (errors.length >= CONFIG.TYPECHECK_ERROR_CAP) break;
1010
+ }
1011
+
1012
+ if (errors.length === 0) {
1013
+ if (result.status === 0) {
1014
+ return { issues: [], ran: true, errorCount: 0 };
1015
+ }
1016
+ // 退出码非 0 但未解析出标准 error 行:疑似 tsconfig / 配置级错误
1017
+ return {
1018
+ issues: [
1019
+ {
1020
+ level: "error",
1021
+ dir: label,
1022
+ text:
1023
+ checker +
1024
+ " --noEmit 退出码 " +
1025
+ result.status +
1026
+ "(请检查 tsconfig / 类型配置,无标准 TS 错误输出)",
1027
+ rule: "R14",
1028
+ },
1029
+ ],
1030
+ ran: true,
1031
+ errorCount: 1,
1032
+ };
1033
+ }
1034
+
1035
+ const issues = errors.map((e) => {
1036
+ const rel = path.relative(safeRoot, e.file).replace(/\\/g, "/") || e.file;
1037
+ return {
1038
+ level: "error",
1039
+ dir: path.dirname(rel) || ".",
1040
+ text:
1041
+ e.code + " " + e.msg + " (" + path.basename(rel) + ":" + e.line + ")",
1042
+ rule: "R14",
1043
+ };
1044
+ });
1045
+ return { issues, ran: true, errorCount: errors.length };
1046
+ }
1047
+
757
1048
  module.exports = {
758
1049
  runAstRules,
759
1050
  parseVueScript,
760
1051
  countEffectiveLines,
761
1052
  extractScriptInfo,
1053
+ parseScriptAst,
1054
+ computeFunctionComplexity,
1055
+ collectFunctions,
1056
+ runTypeCheck,
762
1057
  hasAstAvailable,
763
1058
  isAstFunctionallyUsable,
764
1059
  getStagedFiles,
package/mcp/config.js CHANGED
@@ -1,47 +1,47 @@
1
- 'use strict'
2
-
3
- const fs = require('fs')
4
- const path = require('path')
5
-
6
- /**
7
- * 从项目的 .wl-skills/skills/sync/env.local.json 加载 MCP 运行配置
8
- * 项目根目录通过环境变量 WL_PROJECT_ROOT 传入(由 .cursor/mcp.json 注入)
9
- */
10
- function loadConfig() {
11
- const projectRoot = process.env.WL_PROJECT_ROOT
12
- ? path.resolve(process.env.WL_PROJECT_ROOT)
13
- : process.cwd()
14
-
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+
6
+ /**
7
+ * 从项目的 .wl-skills/skills/sync/env.local.json 加载 MCP 运行配置
8
+ * 项目根目录通过环境变量 WL_PROJECT_ROOT 传入(由 .cursor/mcp.json 注入)
9
+ */
10
+ function loadConfig() {
11
+ const projectRoot = process.env.WL_PROJECT_ROOT
12
+ ? path.resolve(process.env.WL_PROJECT_ROOT)
13
+ : process.cwd()
14
+
15
15
  const configPath = path.join(projectRoot, '.wl-skills', 'skills', 'sync', 'env.local.json')
16
-
17
- if (!fs.existsSync(configPath)) {
18
- throw new Error(
19
- `配置文件不存在: ${configPath}\n` +
20
- `请先执行 pnpm dlx @agile-team/wl-skills-kit init,然后填写 .wl-skills/skills/sync/env.local.json`
21
- )
22
- }
23
-
24
- let raw
25
- try {
26
- raw = JSON.parse(fs.readFileSync(configPath, 'utf8'))
27
- } catch (e) {
28
- throw new Error(`配置文件解析失败: ${e.message}`)
29
- }
30
-
31
- if (!raw.gatewayPath || raw.gatewayPath.includes('你的网关')) {
32
- throw new Error('请在 env.local.json 中填写真实的 gatewayPath(当前为占位值)')
33
- }
34
- if (!raw.token || raw.token.includes('Bearer Token')) {
35
- throw new Error('请在 env.local.json 中填写真实的 token(当前为占位值)')
36
- }
37
-
38
- return {
39
- gatewayPath: raw.gatewayPath.replace(/\/$/, ''), // 去掉尾部斜杠
40
- token: raw.token,
41
- sysAppNo: raw.sysAppNo || '',
42
- menu: raw.menu || {},
43
- dict: raw.dict || {},
44
- }
45
- }
46
-
47
- module.exports = { loadConfig }
16
+
17
+ if (!fs.existsSync(configPath)) {
18
+ throw new Error(
19
+ `配置文件不存在: ${configPath}\n` +
20
+ `请先执行 pnpm dlx @agile-team/wl-skills-kit init,然后填写 .wl-skills/skills/sync/env.local.json`
21
+ )
22
+ }
23
+
24
+ let raw
25
+ try {
26
+ raw = JSON.parse(fs.readFileSync(configPath, 'utf8'))
27
+ } catch (e) {
28
+ throw new Error(`配置文件解析失败: ${e.message}`)
29
+ }
30
+
31
+ if (!raw.gatewayPath || raw.gatewayPath.includes('你的网关')) {
32
+ throw new Error('请在 env.local.json 中填写真实的 gatewayPath(当前为占位值)')
33
+ }
34
+ if (!raw.token || raw.token.includes('Bearer Token')) {
35
+ throw new Error('请在 env.local.json 中填写真实的 token(当前为占位值)')
36
+ }
37
+
38
+ return {
39
+ gatewayPath: raw.gatewayPath.replace(/\/$/, ''), // 去掉尾部斜杠
40
+ token: raw.token,
41
+ sysAppNo: raw.sysAppNo || '',
42
+ menu: raw.menu || {},
43
+ dict: raw.dict || {},
44
+ }
45
+ }
46
+
47
+ module.exports = { loadConfig }
package/mcp/registry.js CHANGED
@@ -296,11 +296,16 @@ const DESCRIPTORS = [
296
296
  {
297
297
  name: "wls_validate_page",
298
298
  description:
299
- "校验页面是否符合 wl-skills-kit 最新页面规范:BaseTable+AGGrid+cid、defineColumns、renderOps、mock-first、api.md 等。",
299
+ "校验页面是否符合 wl-skills-kit 最新页面规范:BaseTable+AGGrid+cid、defineColumns、renderOps、mock-first、api.md 等。开启 typecheck 额外执行 vue-tsc/tsc 类型检查(R14)。",
300
300
  inputSchema: {
301
301
  type: "object",
302
302
  properties: {
303
303
  path: { type: "string", description: "页面或目录路径,默认 src/views" },
304
+ typecheck: {
305
+ type: "boolean",
306
+ description:
307
+ "是否额外执行 vue-tsc/tsc --noEmit 类型检查(R14,体积较大,CI 场景开启)",
308
+ },
304
309
  },
305
310
  required: [],
306
311
  },
@@ -4,7 +4,7 @@ const fs = require("fs");
4
4
  const path = require("path");
5
5
  const { execFileSync } = require("child_process");
6
6
  const https = require("https");
7
- const { runAstRules } = require("../../lib/ast-rules");
7
+ const { runAstRules, runTypeCheck } = require("../../lib/ast-rules");
8
8
  const { alignPage } = require("../../lib/page-spec");
9
9
 
10
10
  function getProjectRoot() {
@@ -213,6 +213,14 @@ async function handleValidatePage(args) {
213
213
  }
214
214
  }
215
215
 
216
+ // ── 类型检查 R14(v2.11.2+,仅当 typecheck:true 触发)─────────────────
217
+ if (args && args.typecheck) {
218
+ const tc = runTypeCheck(root);
219
+ for (const iss of tc.issues) {
220
+ issues.push([iss.dir, iss.level, `[${iss.rule}] ${iss.text}`]);
221
+ }
222
+ }
223
+
216
224
  const errors = issues.filter((item) => item[1] === "error").length;
217
225
  const lines = [
218
226
  `✅ 页面校验完成:${scanPath}`,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agile-team/wl-skills-kit",
3
- "version": "2.11.1",
4
- "description": "AI Skill 模板包 v2.11.1 — 14 条编码规范 + 11 个 AI Skill + 17 个 MCP Tool,一条命令导入 Vue 3 项目(.wl-skills/ 统一隔离架构)",
3
+ "version": "2.11.2",
4
+ "description": "AI Skill 模板包 v2.11.2 — 14 条编码规范 + 11 个 AI Skill + 17 个 MCP Tool,一条命令导入 Vue 3 项目(.wl-skills/ 统一隔离架构)",
5
5
  "main": "./bin/wl-skills.js",
6
6
  "packageManager": "pnpm@11.5.3",
7
7
  "bin": {