frontend-guardian-core 3.14.0 → 3.20.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.
Files changed (131) hide show
  1. package/bin/fg-core.js +349 -16
  2. package/bin/fg-lsp.js +1 -1
  3. package/bin/fg-server.js +2 -2
  4. package/bin/watch-mode.js +155 -9
  5. package/dist/engine/rule-engine.d.ts +17 -1
  6. package/dist/engine/rule-engine.d.ts.map +1 -1
  7. package/dist/engine/rule-engine.js +158 -3
  8. package/dist/engine/rule-engine.js.map +1 -1
  9. package/dist/index.d.ts +21 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +55 -4
  12. package/dist/index.js.map +1 -1
  13. package/dist/integrations/cypress.d.ts +9 -0
  14. package/dist/integrations/cypress.d.ts.map +1 -0
  15. package/dist/integrations/cypress.js +104 -0
  16. package/dist/integrations/cypress.js.map +1 -0
  17. package/dist/integrations/index.d.ts +6 -0
  18. package/dist/integrations/index.d.ts.map +1 -1
  19. package/dist/integrations/index.js +8 -1
  20. package/dist/integrations/index.js.map +1 -1
  21. package/dist/integrations/katalon.d.ts +9 -0
  22. package/dist/integrations/katalon.d.ts.map +1 -0
  23. package/dist/integrations/katalon.js +102 -0
  24. package/dist/integrations/katalon.js.map +1 -0
  25. package/dist/integrations/page-health.d.ts +15 -0
  26. package/dist/integrations/page-health.d.ts.map +1 -1
  27. package/dist/integrations/page-health.js +80 -12
  28. package/dist/integrations/page-health.js.map +1 -1
  29. package/dist/integrations/selenium.d.ts +9 -0
  30. package/dist/integrations/selenium.d.ts.map +1 -0
  31. package/dist/integrations/selenium.js +143 -0
  32. package/dist/integrations/selenium.js.map +1 -0
  33. package/dist/mcp/agent-preferences.d.ts +50 -0
  34. package/dist/mcp/agent-preferences.d.ts.map +1 -0
  35. package/dist/mcp/agent-preferences.js +149 -0
  36. package/dist/mcp/agent-preferences.js.map +1 -0
  37. package/dist/mcp/agent-registry.d.ts +8 -0
  38. package/dist/mcp/agent-registry.d.ts.map +1 -1
  39. package/dist/mcp/agent-registry.js +25 -1
  40. package/dist/mcp/agent-registry.js.map +1 -1
  41. package/dist/mcp/guidance.d.ts +24 -0
  42. package/dist/mcp/guidance.d.ts.map +1 -0
  43. package/dist/mcp/guidance.js +86 -0
  44. package/dist/mcp/guidance.js.map +1 -0
  45. package/dist/mcp/mcp-server.d.ts.map +1 -1
  46. package/dist/mcp/mcp-server.js +27 -1
  47. package/dist/mcp/mcp-server.js.map +1 -1
  48. package/dist/mcp/tools.d.ts.map +1 -1
  49. package/dist/mcp/tools.js +320 -18
  50. package/dist/mcp/tools.js.map +1 -1
  51. package/dist/mcp/types.d.ts +33 -2
  52. package/dist/mcp/types.d.ts.map +1 -1
  53. package/dist/rules/registry.d.ts +12 -1
  54. package/dist/rules/registry.d.ts.map +1 -1
  55. package/dist/rules/registry.js +26 -0
  56. package/dist/rules/registry.js.map +1 -1
  57. package/dist/scanners/backend-scanner.d.ts +9 -0
  58. package/dist/scanners/backend-scanner.d.ts.map +1 -0
  59. package/dist/scanners/backend-scanner.js +414 -0
  60. package/dist/scanners/backend-scanner.js.map +1 -0
  61. package/dist/scanners/css-scanner.d.ts +8 -0
  62. package/dist/scanners/css-scanner.d.ts.map +1 -0
  63. package/dist/scanners/css-scanner.js +185 -0
  64. package/dist/scanners/css-scanner.js.map +1 -0
  65. package/dist/scanners/data-scanner.d.ts +8 -0
  66. package/dist/scanners/data-scanner.d.ts.map +1 -0
  67. package/dist/scanners/data-scanner.js +377 -0
  68. package/dist/scanners/data-scanner.js.map +1 -0
  69. package/dist/types.d.ts +146 -2
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/utils/ai-vision.d.ts +33 -0
  72. package/dist/utils/ai-vision.d.ts.map +1 -0
  73. package/dist/utils/ai-vision.js +172 -0
  74. package/dist/utils/ai-vision.js.map +1 -0
  75. package/dist/utils/baseline.d.ts +1 -1
  76. package/dist/utils/baseline.d.ts.map +1 -1
  77. package/dist/utils/baseline.js +14 -2
  78. package/dist/utils/baseline.js.map +1 -1
  79. package/dist/utils/config-loader.d.ts.map +1 -1
  80. package/dist/utils/config-loader.js +14 -0
  81. package/dist/utils/config-loader.js.map +1 -1
  82. package/dist/utils/config-recommender.d.ts +38 -0
  83. package/dist/utils/config-recommender.d.ts.map +1 -0
  84. package/dist/utils/config-recommender.js +183 -0
  85. package/dist/utils/config-recommender.js.map +1 -0
  86. package/dist/utils/e2e-gap-detector.d.ts +2 -0
  87. package/dist/utils/e2e-gap-detector.d.ts.map +1 -1
  88. package/dist/utils/e2e-gap-detector.js +62 -16
  89. package/dist/utils/e2e-gap-detector.js.map +1 -1
  90. package/dist/utils/impact-graph.d.ts +70 -0
  91. package/dist/utils/impact-graph.d.ts.map +1 -0
  92. package/dist/utils/impact-graph.js +230 -0
  93. package/dist/utils/impact-graph.js.map +1 -0
  94. package/dist/utils/init-config.js +3 -1
  95. package/dist/utils/init-config.js.map +1 -1
  96. package/dist/utils/market-index.d.ts +27 -0
  97. package/dist/utils/market-index.d.ts.map +1 -0
  98. package/dist/utils/market-index.js +151 -0
  99. package/dist/utils/market-index.js.map +1 -0
  100. package/dist/utils/project-detector.js +4 -0
  101. package/dist/utils/project-detector.js.map +1 -1
  102. package/dist/utils/rule-compatibility.d.ts +15 -0
  103. package/dist/utils/rule-compatibility.d.ts.map +1 -0
  104. package/dist/utils/rule-compatibility.js +129 -0
  105. package/dist/utils/rule-compatibility.js.map +1 -0
  106. package/dist/utils/rule-doc-generator.d.ts +19 -0
  107. package/dist/utils/rule-doc-generator.d.ts.map +1 -0
  108. package/dist/utils/rule-doc-generator.js +143 -0
  109. package/dist/utils/rule-doc-generator.js.map +1 -0
  110. package/dist/utils/rule-hot-reload.d.ts +22 -0
  111. package/dist/utils/rule-hot-reload.d.ts.map +1 -0
  112. package/dist/utils/rule-hot-reload.js +59 -0
  113. package/dist/utils/rule-hot-reload.js.map +1 -0
  114. package/dist/utils/rule-scoring.d.ts +18 -0
  115. package/dist/utils/rule-scoring.d.ts.map +1 -0
  116. package/dist/utils/rule-scoring.js +173 -0
  117. package/dist/utils/rule-scoring.js.map +1 -0
  118. package/dist/utils/rule-template-generator.d.ts +16 -0
  119. package/dist/utils/rule-template-generator.d.ts.map +1 -0
  120. package/dist/utils/rule-template-generator.js +146 -0
  121. package/dist/utils/rule-template-generator.js.map +1 -0
  122. package/dist/utils/test-recommender.d.ts +5 -0
  123. package/dist/utils/test-recommender.d.ts.map +1 -1
  124. package/dist/utils/test-recommender.js +20 -1
  125. package/dist/utils/test-recommender.js.map +1 -1
  126. package/dist/utils/visual-regression.d.ts +2 -0
  127. package/dist/utils/visual-regression.d.ts.map +1 -1
  128. package/dist/utils/visual-regression.js.map +1 -1
  129. package/market/README.md +47 -0
  130. package/market/index.json +50 -0
  131. package/package.json +2 -1
package/bin/fg-core.js CHANGED
@@ -10,15 +10,21 @@ import pc from "picocolors";
10
10
  import {
11
11
  AIFixSuggester,
12
12
  a11yRules,
13
+ backendRules,
13
14
  BaselineManager,
14
15
  buildNotificationPayload,
16
+ checkRuleCompatibility,
15
17
  CodeownersParser,
16
18
  compareHistoryReports,
17
19
  complianceReportToMarkdown,
18
20
  componentRules,
21
+ computeRuleScores,
19
22
  createEngine,
20
23
  createPublisher,
21
24
  crossFileRules,
25
+ cssRules,
26
+ cypressIntegration,
27
+ dataRules,
22
28
  detectAIConfig,
23
29
  detectCIProvider,
24
30
  detectE2EGaps,
@@ -31,36 +37,53 @@ import {
31
37
  e2eRules,
32
38
  FileWatcher,
33
39
  formatAllAnnotations,
40
+ formatCompatibilityReport,
41
+ formatCompatibilityReportJson,
34
42
  formatE2EGapJson,
35
43
  formatE2EGapReport,
44
+ formatGeneratedDocs,
36
45
  formatHistoryCompare,
37
46
  formatHistoryCompareJson,
47
+ formatImpactGraph,
48
+ formatMarketIndex,
49
+ formatMarketIndexJson,
38
50
  formatMiniProgramJson,
51
+ formatScanProfile,
39
52
  formatMiniProgramReport,
40
53
  formatPageHealthJson,
54
+ formatRecommendedConfig,
41
55
  formatPageHealthReport,
56
+ formatRuleScores,
57
+ formatRuleScoresJson,
42
58
  formatWorkspaceJson,
43
59
  formatWorkspaceReport,
44
60
  generateCIConfig,
45
61
  generateComplianceReport,
46
62
  generateDashboard,
47
63
  generatePRComment,
64
+ generateRuleDocs,
65
+ generateRuleTemplate,
48
66
  generateSarif,
49
67
  HistoryReport,
50
68
  hooksRules,
51
69
  i18nRules,
52
70
  initConfig,
53
71
  installGitHooks,
72
+ isCompatibilityReportClean,
54
73
  isGitHubActions,
55
74
  isPlaywrightAvailable,
75
+ katalonIntegration,
76
+ loadMarketIndex,
56
77
  namingRules,
57
78
  ProjectIndexer,
58
79
  performanceRules,
59
80
  platformRules,
81
+ recommendConfig,
60
82
  playwrightIntegration,
61
83
  runFixBot,
62
84
  runMiniProgramTest,
63
85
  runPageHealthCheck,
86
+ seleniumIntegration,
64
87
  SmartCache,
65
88
  saveComplianceReport,
66
89
  scanWorkspace,
@@ -87,6 +110,9 @@ const MODULES = [
87
110
  "platform",
88
111
  "svelte",
89
112
  "e2e",
113
+ "css",
114
+ "data",
115
+ "backend",
90
116
  ];
91
117
 
92
118
  const MODULE_RULES = {
@@ -101,18 +127,21 @@ const MODULE_RULES = {
101
127
  platform: platformRules,
102
128
  svelte: svelteRules,
103
129
  e2e: e2eRules,
130
+ css: cssRules,
131
+ data: dataRules,
132
+ backend: backendRules,
104
133
  };
105
134
 
106
135
  function showHelp() {
107
136
  console.log(`
108
- Frontend Guardian Core v3.14.0
137
+ Frontend Guardian Core v3.20.0
109
138
 
110
139
  Usage:
111
140
  fg-core <project-dir> [options]
112
141
 
113
142
  Options:
114
143
  --scan 全量扫描(等价于 --module all)
115
- --module <name> 扫描模块: i18n | performance | a11y | security | naming | cross-file | component | hooks | platform | svelte | e2e | all
144
+ --module <name> 扫描模块: i18n | performance | a11y | security | naming | cross-file | component | hooks | platform | svelte | e2e | css | data | backend | all
116
145
  --severity <level> 最低严重级别: critical | warning | suggestion (默认: suggestion)
117
146
  --files <pattern> 仅扫描匹配的文件
118
147
  --exclude <pattern> 排除匹配的文件
@@ -129,6 +158,9 @@ Options:
129
158
  --output <file> 将扫描报告写入指定文件(Markdown 格式)
130
159
  --external 运行外部工具集成 (ESLint / TypeScript / Stylelint)
131
160
  --watch Watch 模式:文件变更自动增量扫描
161
+ --no-progress 禁用扫描进度条(v3.15.0)
162
+ --profile 输出扫描耗时分析:规则/文件耗时排名(v3.15.0)
163
+ --recommend-config 根据项目规模与框架自动推荐最优配置(v3.15.0)
132
164
  --no-cache 禁用智能缓存
133
165
  --config <file> 指定配置文件
134
166
  --install-hooks 安装 Git hook(默认 pre-commit,可用 --install-hooks-type 指定)
@@ -164,7 +196,8 @@ Options:
164
196
  --server <url> 扫描后上报到治理看板服务器
165
197
  --serve 扫描前启动本地看板服务(扫描完成后停止)
166
198
  --e2e-detect-gaps 检测 E2E 测试覆盖缺口(页面 + 接口)
167
- --e2e-run 运行 Playwright E2E 测试并输出治理报告
199
+ --e2e-run 运行 E2E 测试并输出治理报告
200
+ --e2e-tool <tool> E2E 工具: auto | playwright | cypress | selenium | katalon (默认: auto)(v3.16.0)
168
201
  --page-health 页面健康检查:遍历路由验证渲染、控制台错误、白屏
169
202
  --serve <cmd> 自动启动 dev server(配合 --page-health,如 "npm run dev")
170
203
  --port <n> dev server 端口(默认 5173,配合 --serve)
@@ -186,6 +219,10 @@ Options:
186
219
  --device <name> 页面健康检查模拟设备(如 "iPhone 14 Pro",配合 --page-health)
187
220
  --viewport <WxH> 页面健康检查自定义视口(如 390x844,配合 --page-health)
188
221
  --viewport-mobile 页面健康检查使用移动端预设视口(配合 --page-health)
222
+ --page-health-ai-vision 启用 LLM Vision 判断截图差异是否为噪声(配合 --page-health,需 FG_AI_API_KEY)
223
+ --page-health-ai-vision-strict AI 判断为噪声时仍上报 visual regression issue(配合 --page-health-ai-vision)
224
+ --page-health-record-video 页面健康检查时录制视频,失败时保留回放(配合 --page-health)
225
+ --page-health-video-dir <dir> 视频保存目录(默认 .frontend-guardian/videos/)
189
226
  --mini-program [p] 小程序自动化测试: wechat | alipay | douyin | auto | all | wechat,alipay,... (默认 auto)
190
227
  --miniprogram-screenshot 小程序测试时截取首页截图(配合 --mini-program)
191
228
  --miniprogram-update-baseline 更新小程序截图基线(配合 --mini-program)
@@ -207,6 +244,22 @@ Options:
207
244
  --watch-index 监听文件变更并自动同步索引
208
245
  --index-status 查看项目索引状态
209
246
  --recommend-tests 智能测试推荐:基于变更影响分析推荐需要运行的测试(可配合 --staged/--diff/--auto-scope/--files)
247
+ --impact-graph 配合 --recommend-tests 输出变更影响图(默认 mermaid 格式)(v3.16.0)
248
+ --impact-graph-format <format> 影响图格式: json | mermaid | dot (默认: mermaid)(v3.16.0)
249
+ --create-rule <id> 生成规则模板文件(默认输出到 ./rules)(v3.17.0)
250
+ --create-rule-dir <dir> 规则模板输出目录(默认 ./rules)(v3.17.0)
251
+ --create-rule-category <cat> 规则分类(默认 security)(v3.17.0)
252
+ --create-rule-severity <sev> 严重级别: critical | warning | suggestion (默认: warning)(v3.17.0)
253
+ --create-rule-fix 生成的规则模板包含 fix 示例(v3.17.0)
254
+ --create-rule-lang <js|ts> 规则模板语言(默认 js)(v3.17.0)
255
+ --market-index 打印规则市场索引(v3.17.0)
256
+ --market-index-json 以 JSON 格式输出规则市场索引(v3.17.0)
257
+ --market-index-url <url> 覆盖默认市场索引 URL(v3.17.0)
258
+ --rule-scores 打印规则评分(基于本地扫描历史)(v3.17.0)
259
+ --rule-scores-json 以 JSON 格式输出规则评分(v3.17.0)
260
+ --generate-rule-docs 为所有规则生成 Markdown 文档(默认输出 ./docs/rules)(v3.18.0)
261
+ --generate-rule-docs-dir <dir> 指定规则文档输出目录(v3.18.0)
262
+ --check-rule-compat 检查当前启用规则集的兼容性/冲突(v3.18.0)
210
263
  --flaky-threshold-failure-rate <n> flaky 测试失败率阈值(默认 0.2)
211
264
  --flaky-threshold-flip-rate <n> flaky 测试状态翻转率阈值(默认 0.15)
212
265
  --flaky-min-runs <n> 参与 flaky 计算的最少历史运行次数(默认 3)
@@ -215,6 +268,8 @@ Options:
215
268
 
216
269
  Examples:
217
270
  fg-core ./my-project --module all
271
+ fg-core ./my-project --module backend
272
+ fg-core ./my-project --module backend --files "server/**/*.go"
218
273
  fg-core ./my-project --module i18n
219
274
  fg-core ./my-project --module i18n --severity warning --json
220
275
  fg-core ./my-project --module performance --files "src/**/*.tsx"
@@ -318,6 +373,11 @@ async function main() {
318
373
  device: undefined,
319
374
  viewport: undefined,
320
375
  viewportMobile: false,
376
+ // v3.14.1
377
+ pageHealthAIVision: false,
378
+ pageHealthAIVisionStrict: false,
379
+ pageHealthRecordVideo: false,
380
+ pageHealthVideoDir: undefined,
321
381
  // v3.11.0
322
382
  miniProgram: undefined,
323
383
  miniProgramPlatforms: undefined,
@@ -340,6 +400,30 @@ async function main() {
340
400
  flakyThresholdFailureRate: undefined,
341
401
  flakyThresholdFlipRate: undefined,
342
402
  flakyMinRuns: undefined,
403
+ // v3.16.0
404
+ impactGraph: false,
405
+ impactGraphFormat: "mermaid",
406
+ e2eTool: "auto",
407
+ // v3.17.0
408
+ createRule: undefined,
409
+ createRuleDir: "./rules",
410
+ createRuleCategory: "security",
411
+ createRuleSeverity: "warning",
412
+ createRuleFix: false,
413
+ createRuleLang: "js",
414
+ marketIndex: false,
415
+ marketIndexJson: false,
416
+ marketIndexUrl: undefined,
417
+ ruleScores: false,
418
+ ruleScoresJson: false,
419
+ // v3.18.0
420
+ generateRuleDocs: false,
421
+ generateRuleDocsDir: "./docs/rules",
422
+ checkRuleCompat: false,
423
+ // v3.15.0
424
+ profile: false,
425
+ showProgress: true,
426
+ recommendConfig: false,
343
427
  mcp: false,
344
428
  };
345
429
 
@@ -582,6 +666,19 @@ async function main() {
582
666
  case "--viewport-mobile":
583
667
  options.viewportMobile = true;
584
668
  break;
669
+ // v3.14.1
670
+ case "--page-health-ai-vision":
671
+ options.pageHealthAIVision = true;
672
+ break;
673
+ case "--page-health-ai-vision-strict":
674
+ options.pageHealthAIVisionStrict = true;
675
+ break;
676
+ case "--page-health-record-video":
677
+ options.pageHealthRecordVideo = true;
678
+ break;
679
+ case "--page-health-video-dir":
680
+ options.pageHealthVideoDir = args[++i];
681
+ break;
585
682
  // v3.11.0 小程序自动化测试
586
683
  case "--mini-program": {
587
684
  const next = args[i + 1];
@@ -652,6 +749,65 @@ async function main() {
652
749
  case "--recommend-tests":
653
750
  options.recommendTests = true;
654
751
  break;
752
+ case "--impact-graph":
753
+ options.impactGraph = true;
754
+ break;
755
+ case "--impact-graph-format":
756
+ options.impactGraphFormat = args[++i];
757
+ break;
758
+ case "--e2e-tool":
759
+ options.e2eTool = args[++i];
760
+ break;
761
+ // v3.17.0
762
+ case "--create-rule": {
763
+ const next = args[i + 1];
764
+ if (next && !next.startsWith("-")) {
765
+ options.createRule = args[++i];
766
+ } else {
767
+ options.createRule = true;
768
+ }
769
+ break;
770
+ }
771
+ case "--create-rule-dir":
772
+ options.createRuleDir = args[++i];
773
+ break;
774
+ case "--create-rule-category":
775
+ options.createRuleCategory = args[++i];
776
+ break;
777
+ case "--create-rule-severity":
778
+ options.createRuleSeverity = args[++i];
779
+ break;
780
+ case "--create-rule-fix":
781
+ options.createRuleFix = true;
782
+ break;
783
+ case "--create-rule-lang":
784
+ options.createRuleLang = args[++i];
785
+ break;
786
+ case "--market-index":
787
+ options.marketIndex = true;
788
+ break;
789
+ case "--market-index-json":
790
+ options.marketIndexJson = true;
791
+ break;
792
+ case "--market-index-url":
793
+ options.marketIndexUrl = args[++i];
794
+ break;
795
+ case "--rule-scores":
796
+ options.ruleScores = true;
797
+ break;
798
+ case "--rule-scores-json":
799
+ options.ruleScoresJson = true;
800
+ break;
801
+ // v3.18.0
802
+ case "--generate-rule-docs":
803
+ options.generateRuleDocs = true;
804
+ break;
805
+ case "--generate-rule-docs-dir":
806
+ options.generateRuleDocsDir = args[++i];
807
+ break;
808
+ case "--check-rule-compat":
809
+ options.checkRuleCompat = true;
810
+ break;
655
811
  case "--flaky-threshold-failure-rate":
656
812
  options.flakyThresholdFailureRate = parseFloat(args[++i]);
657
813
  break;
@@ -664,6 +820,16 @@ async function main() {
664
820
  case "--mcp":
665
821
  options.mcp = true;
666
822
  break;
823
+ // v3.15.0
824
+ case "--profile":
825
+ options.profile = true;
826
+ break;
827
+ case "--no-progress":
828
+ options.showProgress = false;
829
+ break;
830
+ case "--recommend-config":
831
+ options.recommendConfig = true;
832
+ break;
667
833
  case "--help":
668
834
  case "-h":
669
835
  showHelp();
@@ -682,6 +848,102 @@ async function main() {
682
848
  return;
683
849
  }
684
850
 
851
+ // v3.15.0: 配置推荐
852
+ if (options.recommendConfig) {
853
+ const rec = recommendConfig(options.projectDir);
854
+ if (options.json) {
855
+ console.log(JSON.stringify(rec, null, 2));
856
+ } else {
857
+ console.log(formatRecommendedConfig(rec));
858
+ }
859
+ process.exit(0);
860
+ }
861
+
862
+ // v3.17.0: 生成规则模板
863
+ if (options.createRule) {
864
+ const ruleId = typeof options.createRule === "string" ? options.createRule : undefined;
865
+ if (!ruleId) {
866
+ console.log(pc.yellow("⚠️ 请提供规则 ID,例如: --create-rule no-console-log"));
867
+ process.exit(1);
868
+ }
869
+ try {
870
+ const result = generateRuleTemplate({
871
+ targetDir: resolve(options.projectDir, options.createRuleDir),
872
+ ruleId,
873
+ category: options.createRuleCategory,
874
+ severity: options.createRuleSeverity,
875
+ includeFix: options.createRuleFix,
876
+ language: options.createRuleLang,
877
+ });
878
+ console.log(pc.cyan("📝 规则模板已生成"));
879
+ console.log(pc.green(` ✅ 规则文件: ${result.rulePath}`));
880
+ console.log(pc.green(` ✅ 测试文件: ${result.testPath}`));
881
+ process.exit(0);
882
+ } catch (err) {
883
+ console.error(pc.red("❌ 生成规则模板失败:"), err instanceof Error ? err.message : String(err));
884
+ process.exit(1);
885
+ }
886
+ }
887
+
888
+ // v3.17.0: 规则市场索引
889
+ if (options.marketIndex || options.marketIndexJson) {
890
+ const market = await loadMarketIndex({
891
+ url: options.marketIndexUrl,
892
+ projectDir: options.projectDir,
893
+ });
894
+ if (options.marketIndexJson) {
895
+ console.log(formatMarketIndexJson(market));
896
+ } else {
897
+ console.log(formatMarketIndex(market));
898
+ }
899
+ process.exit(0);
900
+ }
901
+
902
+ // v3.17.0: 规则评分
903
+ if (options.ruleScores || options.ruleScoresJson) {
904
+ const summary = computeRuleScores(options.projectDir);
905
+ if (options.ruleScoresJson) {
906
+ console.log(formatRuleScoresJson(summary));
907
+ } else {
908
+ console.log(formatRuleScores(summary));
909
+ }
910
+ process.exit(0);
911
+ }
912
+
913
+ // v3.18.0: 规则文档生成
914
+ if (options.generateRuleDocs) {
915
+ try {
916
+ const engine = createEngine({ projectDir: options.projectDir, configFile: options.configFile });
917
+ const rules = engine.getRules ? engine.getRules() : Object.values(MODULE_RULES).flat();
918
+ const result = generateRuleDocs({
919
+ outputDir: resolve(options.projectDir, options.generateRuleDocsDir),
920
+ rules,
921
+ });
922
+ console.log(pc.cyan(formatGeneratedDocs(result)));
923
+ process.exit(0);
924
+ } catch (err) {
925
+ console.error(pc.red("❌ 生成规则文档失败:"), err instanceof Error ? err.message : String(err));
926
+ process.exit(1);
927
+ }
928
+ }
929
+
930
+ // v3.18.0: 规则兼容性检查
931
+ if (options.checkRuleCompat) {
932
+ try {
933
+ const engine = createEngine({ projectDir: options.projectDir, configFile: options.configFile });
934
+ const report = engine.checkRuleCompatibility();
935
+ if (options.json) {
936
+ console.log(formatCompatibilityReportJson(report));
937
+ } else {
938
+ console.log(formatCompatibilityReport(report));
939
+ }
940
+ process.exit(isCompatibilityReportClean(report) ? 0 : 2);
941
+ } catch (err) {
942
+ console.error(pc.red("❌ 规则兼容性检查失败:"), err instanceof Error ? err.message : String(err));
943
+ process.exit(1);
944
+ }
945
+ }
946
+
685
947
  // v3.6.0: E2E 测试覆盖缺口检测
686
948
  if (options.e2eDetectGaps) {
687
949
  const gapResult = detectE2EGaps({ projectDir: options.projectDir });
@@ -693,28 +955,49 @@ async function main() {
693
955
  process.exit(gapResult.uncoveredPages.length + gapResult.uncoveredApis.length > 0 ? 1 : 0);
694
956
  }
695
957
 
696
- // v3.6.1: 运行 Playwright E2E 测试
958
+ // v3.6.1 / v3.16.0: 运行 E2E 测试(支持 Playwright / Cypress / Selenium / Katalon)
697
959
  if (options.e2eRun) {
698
- if (!playwrightIntegration.isAvailable(options.projectDir)) {
699
- console.log(pc.yellow("⚠️ 未检测到 Playwright 配置(playwright.config.ts/js)或 playwright 包未安装"));
700
- console.log(pc.gray(" 请先安装 Playwright: npm install -D @playwright/test"));
701
- console.log(pc.gray(" 或初始化配置: npx playwright init"));
960
+ const tools = [
961
+ { name: "Playwright", integration: playwrightIntegration },
962
+ { name: "Cypress", integration: cypressIntegration },
963
+ { name: "Selenium", integration: seleniumIntegration },
964
+ { name: "Katalon", integration: katalonIntegration },
965
+ ];
966
+
967
+ let selected;
968
+ if (options.e2eTool === "auto") {
969
+ selected = tools.find((t) => t.integration.isAvailable(options.projectDir));
970
+ } else {
971
+ selected = tools.find((t) => t.name.toLowerCase() === options.e2eTool.toLowerCase());
972
+ if (selected && !selected.integration.isAvailable(options.projectDir)) {
973
+ console.log(pc.yellow(`⚠️ 未检测到 ${selected.name} 配置或依赖`));
974
+ process.exit(1);
975
+ }
976
+ }
977
+
978
+ if (!selected) {
979
+ if (options.e2eTool === "auto") {
980
+ console.log(pc.yellow("⚠️ 未检测到任何支持的 E2E 工具(Playwright / Cypress / Selenium / Katalon)"));
981
+ } else {
982
+ console.log(pc.yellow(`⚠️ 未知的 E2E 工具: ${options.e2eTool}`));
983
+ }
984
+ console.log(pc.gray(" 支持的工具: auto | playwright | cypress | selenium | katalon"));
702
985
  process.exit(1);
703
986
  }
704
987
 
705
- console.log(pc.cyan("🎭 正在运行 Playwright E2E 测试..."));
988
+ console.log(pc.cyan(`🎭 正在运行 ${selected.name} E2E 测试...`));
706
989
  console.log(pc.gray(" 这可能需要几分钟(取决于测试数量和浏览器启动时间)"));
707
990
  console.log("");
708
991
 
709
992
  const start = Date.now();
710
- const issues = playwrightIntegration.run(options.projectDir);
993
+ const issues = selected.integration.run(options.projectDir);
711
994
  const duration = Date.now() - start;
712
995
 
713
996
  if (options.json) {
714
997
  console.log(
715
998
  JSON.stringify(
716
999
  {
717
- tool: "Playwright",
1000
+ tool: selected.name,
718
1001
  total: issues.length,
719
1002
  duration,
720
1003
  issues,
@@ -724,12 +1007,12 @@ async function main() {
724
1007
  )
725
1008
  );
726
1009
  } else {
727
- console.log(pc.cyan(`📊 Playwright 测试结果`));
1010
+ console.log(pc.cyan(`📊 ${selected.name} 测试结果`));
728
1011
  console.log(pc.gray(` 耗时: ${duration}ms`));
729
1012
  if (issues.length === 0) {
730
1013
  console.log(pc.green(` ✅ 所有测试通过`));
731
1014
  } else {
732
- console.log(pc.red(` ❌ ${issues.length} 个测试失败`));
1015
+ console.log(pc.red(` ❌ ${issues.length} 个测试失败/异常`));
733
1016
  for (const issue of issues) {
734
1017
  const severityColor = issue.severity === "critical" ? pc.red : pc.yellow;
735
1018
  console.log(severityColor(` [${issue.severity.toUpperCase()}] ${issue.title}`));
@@ -796,6 +1079,11 @@ async function main() {
796
1079
  device: options.device,
797
1080
  viewport: options.viewport,
798
1081
  viewportMobile: options.viewportMobile,
1082
+ // v3.14.1
1083
+ aiVision: options.pageHealthAIVision,
1084
+ aiVisionStrict: options.pageHealthAIVisionStrict,
1085
+ recordVideo: options.pageHealthRecordVideo,
1086
+ videoDir: options.pageHealthVideoDir,
799
1087
  });
800
1088
 
801
1089
  if (options.json) {
@@ -951,7 +1239,7 @@ async function main() {
951
1239
  process.exit(0);
952
1240
  }
953
1241
 
954
- // v3.9.0: 智能测试推荐
1242
+ // v3.9.0 / v3.16.0: 智能测试推荐
955
1243
  if (options.recommendTests) {
956
1244
  const { recommendTests, formatRecommendations, formatRecommendationsJson } = await import("../dist/index.js");
957
1245
  const changedFiles = options.files?.map((f) => resolve(options.projectDir, f));
@@ -968,12 +1256,18 @@ async function main() {
968
1256
  flipRate: options.flakyThresholdFlipRate,
969
1257
  minRuns: options.flakyMinRuns,
970
1258
  },
1259
+ includeImpactGraph: options.impactGraph,
971
1260
  });
972
1261
 
973
1262
  if (options.json) {
974
1263
  console.log(JSON.stringify(formatRecommendationsJson(result), null, 2));
975
1264
  } else {
976
1265
  console.log(formatRecommendations(result));
1266
+ if (options.impactGraph && result.impactGraph) {
1267
+ console.log("");
1268
+ console.log(pc.cyan("🕸️ 变更影响图"));
1269
+ console.log(formatImpactGraph(result.impactGraph, options.impactGraphFormat));
1270
+ }
977
1271
  }
978
1272
 
979
1273
  process.exit(result.uncoveredChanges.length > 0 ? 1 : 0);
@@ -1277,7 +1571,30 @@ async function main() {
1277
1571
  const scanFn = options.module === "all" ? runAllModules : runSingleModule;
1278
1572
  // v2.6.0: Watch 模式复用 SmartCache,实现缓存预热
1279
1573
  const cacheInstance = options.cache !== false ? new SmartCache(options.projectDir) : undefined;
1280
- await runWatchMode(options, scanFn, cacheInstance);
1574
+ // v3.17.0: 需要先创建 engine,以便 watch 模式热重载自定义规则
1575
+ const engine = createEngine({
1576
+ projectDir: options.projectDir,
1577
+ minSeverity: options.minSeverity,
1578
+ files: options.files,
1579
+ exclude: options.exclude,
1580
+ configFile: options.configFile,
1581
+ staged: options.staged,
1582
+ diffRange: options.diffRange,
1583
+ autoScope: options.autoScope,
1584
+ external: options.external,
1585
+ cache: options.cache,
1586
+ cacheInstance,
1587
+ dryRun: options.dryRun,
1588
+ interactive: options.interactive,
1589
+ skipLargeFilesThreshold: options.skipLargeFilesThreshold,
1590
+ strategy: options.strategy,
1591
+ profile: options.profile,
1592
+ showProgress: options.showProgress,
1593
+ });
1594
+ for (const rules of Object.values(MODULE_RULES)) {
1595
+ engine.registerAll(rules);
1596
+ }
1597
+ await runWatchMode(options, scanFn, cacheInstance, runSingleModule, engine);
1281
1598
  } else if (options.module === "all") {
1282
1599
  await runAllModules(options);
1283
1600
  } else {
@@ -1288,7 +1605,7 @@ async function main() {
1288
1605
  async function runAllModules(options, cacheInstance) {
1289
1606
  console.log(pc.cyan("🛡️ Frontend Guardian Core"));
1290
1607
  console.log(pc.gray(` Project: ${options.projectDir}`));
1291
- console.log(pc.gray(` Module: all (9 modules)`));
1608
+ console.log(pc.gray(` Module: all (${MODULES.length} modules)`));
1292
1609
  if (options.staged) {
1293
1610
  console.log(pc.gray(` Mode: staged (git cached only)`));
1294
1611
  } else if (options.diffRange) {
@@ -1314,6 +1631,9 @@ async function runAllModules(options, cacheInstance) {
1314
1631
  interactive: options.interactive,
1315
1632
  skipLargeFilesThreshold: options.skipLargeFilesThreshold,
1316
1633
  strategy: options.strategy,
1634
+ // v3.15.0
1635
+ profile: options.profile,
1636
+ showProgress: options.showProgress,
1317
1637
  });
1318
1638
 
1319
1639
  // 注册所有模块的规则
@@ -1366,6 +1686,11 @@ async function runAllModules(options, cacheInstance) {
1366
1686
  }
1367
1687
 
1368
1688
  allResults[mod] = result;
1689
+ // v3.15.0: 输出扫描耗时分析
1690
+ if (options.profile && !options.json && result.profile) {
1691
+ console.log(formatScanProfile(result.profile));
1692
+ console.log("");
1693
+ }
1369
1694
  totalCritical += result.issues.critical.length;
1370
1695
  totalWarning += result.issues.warning.length;
1371
1696
  totalSuggestion += result.issues.suggestion.length;
@@ -1920,6 +2245,9 @@ async function runSingleModule(options, cacheInstance) {
1920
2245
  interactive: options.interactive,
1921
2246
  skipLargeFilesThreshold: options.skipLargeFilesThreshold,
1922
2247
  strategy: options.strategy,
2248
+ // v3.15.0
2249
+ profile: options.profile,
2250
+ showProgress: options.showProgress,
1923
2251
  });
1924
2252
 
1925
2253
  // 注册规则
@@ -1951,6 +2279,11 @@ async function runSingleModule(options, cacheInstance) {
1951
2279
  }
1952
2280
 
1953
2281
  const { issues } = result;
2282
+ // v3.15.0: 输出扫描耗时分析
2283
+ if (options.profile && !options.json && result.profile) {
2284
+ console.log(formatScanProfile(result.profile));
2285
+ console.log("");
2286
+ }
1954
2287
  let totalCritical = issues.critical.length;
1955
2288
  let totalWarning = issues.warning.length;
1956
2289
  let totalSuggestion = issues.suggestion.length;
package/bin/fg-lsp.js CHANGED
@@ -27,7 +27,7 @@ for (let i = 0; i < args.length; i++) {
27
27
  } else if (arg === "--severity" || arg === "-s") {
28
28
  minSeverity = args[++i] ?? "suggestion";
29
29
  } else if (arg === "--help" || arg === "-h") {
30
- console.log(`Frontend Guardian LSP Server v3.14.0
30
+ console.log(`Frontend Guardian LSP Server v3.20.0
31
31
 
32
32
  Usage: fg-lsp [options]
33
33
 
package/bin/fg-server.js CHANGED
@@ -10,7 +10,7 @@ import { DashboardServer } from "../dist/index.js";
10
10
 
11
11
  function showHelp() {
12
12
  console.log(`
13
- Frontend Guardian Dashboard Server v3.14.0
13
+ Frontend Guardian Dashboard Server v3.20.0
14
14
 
15
15
  Usage:
16
16
  fg-server [options]
@@ -62,7 +62,7 @@ async function main() {
62
62
  }
63
63
 
64
64
  console.log(pc.cyan("Frontend Guardian Dashboard Server"));
65
- console.log(pc.gray(` Version: 3.14.0`));
65
+ console.log(pc.gray(` Version: 3.20.0`));
66
66
  console.log("");
67
67
 
68
68
  const server = new DashboardServer({