pdd-skills 3.0.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 (261) hide show
  1. package/README.md +1478 -0
  2. package/bin/pdd.js +354 -0
  3. package/config/bpmn-rules.yaml +166 -0
  4. package/config/checkstyle.xml +105 -0
  5. package/config/eslint.config.js +48 -0
  6. package/config/pmd.xml +91 -0
  7. package/config/prd-rules.yaml +113 -0
  8. package/config/ruff.toml +45 -0
  9. package/config/sqlfluff.cfg +82 -0
  10. package/hooks/hook-executor.js +332 -0
  11. package/index.js +43 -0
  12. package/lib/api-routes.js +750 -0
  13. package/lib/api-server.js +408 -0
  14. package/lib/cache/cache-config.js +209 -0
  15. package/lib/cache/system-cache.js +852 -0
  16. package/lib/config-manager.js +373 -0
  17. package/lib/generate.js +528 -0
  18. package/lib/grpc/grpc-routes.js +1134 -0
  19. package/lib/grpc/grpc-server.js +912 -0
  20. package/lib/grpc/proto-definitions.js +1033 -0
  21. package/lib/init.js +172 -0
  22. package/lib/iteration/auto-fixer.js +1025 -0
  23. package/lib/iteration/auto-reviewer.js +923 -0
  24. package/lib/iteration/controller.js +577 -0
  25. package/lib/list.js +130 -0
  26. package/lib/mcp-server.js +548 -0
  27. package/lib/openclaw/api-integration.js +535 -0
  28. package/lib/openclaw/cli-integration.js +567 -0
  29. package/lib/openclaw/data-sync.js +845 -0
  30. package/lib/openclaw/openclaw-adapter.js +783 -0
  31. package/lib/plugin/example-plugins/code-stats/index.js +332 -0
  32. package/lib/plugin/example-plugins/code-stats/plugin.json +1 -0
  33. package/lib/plugin/example-plugins/custom-linter/index.js +472 -0
  34. package/lib/plugin/example-plugins/custom-linter/plugin.json +1 -0
  35. package/lib/plugin/example-plugins/hello-world/index.js +86 -0
  36. package/lib/plugin/example-plugins/hello-world/plugin.json +1 -0
  37. package/lib/plugin/plugin-manager.js +655 -0
  38. package/lib/plugin/plugin-sdk.js +565 -0
  39. package/lib/plugin/sandbox.js +627 -0
  40. package/lib/quality/rules/maintainability.js +418 -0
  41. package/lib/quality/rules/performance.js +498 -0
  42. package/lib/quality/rules/readability.js +441 -0
  43. package/lib/quality/rules/robustness.js +504 -0
  44. package/lib/quality/rules/security.js +444 -0
  45. package/lib/quality/scorer.js +576 -0
  46. package/lib/report.js +669 -0
  47. package/lib/sdk-base.js +301 -0
  48. package/lib/sdk-js.js +446 -0
  49. package/lib/sdk-python/README.md +546 -0
  50. package/lib/sdk-python/examples/basic_usage.py +450 -0
  51. package/lib/sdk-python/pdd_sdk/__init__.py +180 -0
  52. package/lib/sdk-python/pdd_sdk/client.py +1170 -0
  53. package/lib/sdk-python/pdd_sdk/events.py +423 -0
  54. package/lib/sdk-python/pdd_sdk/exceptions.py +158 -0
  55. package/lib/sdk-python/pdd_sdk/models.py +518 -0
  56. package/lib/sdk-python/pdd_sdk/utils.py +759 -0
  57. package/lib/token/budget-alert.js +367 -0
  58. package/lib/token/budget-manager.js +485 -0
  59. package/lib/update.js +54 -0
  60. package/lib/utils/logger.js +88 -0
  61. package/lib/verify.js +741 -0
  62. package/lib/version.js +52 -0
  63. package/lib/vm/README.md +102 -0
  64. package/lib/vm/dashboard/api-routes.js +669 -0
  65. package/lib/vm/dashboard/server.js +391 -0
  66. package/lib/vm/dashboard/sse.js +358 -0
  67. package/lib/vm/dashboard/static/css/dashboard.css +1378 -0
  68. package/lib/vm/dashboard/static/index.html +118 -0
  69. package/lib/vm/dashboard/static/js/app.js +949 -0
  70. package/lib/vm/dashboard/static/js/charts.js +913 -0
  71. package/lib/vm/dashboard/static/js/kanban-view.js +1053 -0
  72. package/lib/vm/dashboard/static/js/pipeline-view.js +463 -0
  73. package/lib/vm/dashboard/static/js/quality-view.js +598 -0
  74. package/lib/vm/dashboard/static/js/system-view.js +1021 -0
  75. package/lib/vm/data-provider.js +1191 -0
  76. package/lib/vm/event-bus.js +402 -0
  77. package/lib/vm/hooks/extract-hook.js +307 -0
  78. package/lib/vm/hooks/generate-hook.js +374 -0
  79. package/lib/vm/hooks/hook-interface.js +458 -0
  80. package/lib/vm/hooks/report-hook.js +331 -0
  81. package/lib/vm/hooks/verify-hook.js +454 -0
  82. package/lib/vm/models.js +1003 -0
  83. package/lib/vm/reconciler.js +855 -0
  84. package/lib/vm/scanner.js +988 -0
  85. package/lib/vm/state-schema.js +955 -0
  86. package/lib/vm/state-store.js +733 -0
  87. package/lib/vm/tui/components/card.js +339 -0
  88. package/lib/vm/tui/components/progress-bar.js +368 -0
  89. package/lib/vm/tui/components/sparkline.js +327 -0
  90. package/lib/vm/tui/components/status-light.js +294 -0
  91. package/lib/vm/tui/components/table.js +370 -0
  92. package/lib/vm/tui/input.js +335 -0
  93. package/lib/vm/tui/renderer.js +548 -0
  94. package/lib/vm/tui/screens/kanban-screen.js +397 -0
  95. package/lib/vm/tui/screens/overview-screen.js +357 -0
  96. package/lib/vm/tui/screens/quality-screen.js +336 -0
  97. package/lib/vm/tui/screens/system-screen.js +379 -0
  98. package/lib/vm/tui/tui.js +805 -0
  99. package/package.json +1 -0
  100. package/scripts/cso-analyzer.js +198 -0
  101. package/scripts/eval-runner.js +359 -0
  102. package/scripts/i18n-checker.js +109 -0
  103. package/scripts/linter/activiti-linter.js +272 -0
  104. package/scripts/linter/prd-linter.js +162 -0
  105. package/scripts/linter/report-generator.js +207 -0
  106. package/scripts/linter/run-linters.js +285 -0
  107. package/scripts/linter/sql-linter.js +166 -0
  108. package/scripts/token-analyzer.js +162 -0
  109. package/scripts/vm-test.js +180 -0
  110. package/skills/core/official-doc-writer/LICENSE +21 -0
  111. package/skills/core/official-doc-writer/README.md +232 -0
  112. package/skills/core/official-doc-writer/SKILL.md +475 -0
  113. package/skills/core/official-doc-writer/_meta.json +1 -0
  114. package/skills/core/official-doc-writer/document_generator.py +580 -0
  115. package/skills/core/official-doc-writer/evals/default-evals.json +1 -0
  116. package/skills/core/official-doc-writer/examples.md +150 -0
  117. package/skills/core/official-doc-writer/fonts/FONTS_LIST.md +45 -0
  118. package/skills/core/official-doc-writer/fonts/README.md +141 -0
  119. package/skills/core/official-doc-writer/fonts/SIMFANG.TTF +0 -0
  120. package/skills/core/official-doc-writer/fonts/SIMHEI.TTF +0 -0
  121. package/skills/core/official-doc-writer/fonts/SIMKAI.TTF +0 -0
  122. package/skills/core/official-doc-writer/fonts/SIMSUN.TTC +0 -0
  123. package/skills/core/official-doc-writer/fonts//346/226/271/346/255/243/345/260/217/346/240/207/345/256/213GBK.TTF +0 -0
  124. package/skills/core/official-doc-writer/references/GBT_9704-2012_/345/205/232/346/224/277/346/234/272/345/205/263/345/205/254/346/226/207/346/240/274/345/274/217.md +422 -0
  125. package/skills/core/official-doc-writer/scripts/__pycache__/generate_official_doc.cpython-313.pyc +0 -0
  126. package/skills/core/official-doc-writer/scripts/dialog_manager.py +564 -0
  127. package/skills/core/official-doc-writer/scripts/generate_official_doc.py +252 -0
  128. package/skills/core/official-doc-writer/scripts/install_fonts.py +390 -0
  129. package/skills/core/official-doc-writer/scripts/smart_prompts.py +363 -0
  130. package/skills/core/pdd-ba/SKILL.md +305 -0
  131. package/skills/core/pdd-ba/_meta.json +1 -0
  132. package/skills/core/pdd-ba/evals/default-evals.json +1 -0
  133. package/skills/core/pdd-code-reviewer/SKILL.md +378 -0
  134. package/skills/core/pdd-code-reviewer/_meta.json +1 -0
  135. package/skills/core/pdd-code-reviewer/evals/default-evals.json +1 -0
  136. package/skills/core/pdd-doc-change/SKILL.md +350 -0
  137. package/skills/core/pdd-doc-change/_meta.json +1 -0
  138. package/skills/core/pdd-doc-change/evals/default-evals.json +1 -0
  139. package/skills/core/pdd-doc-gardener/SKILL.md +248 -0
  140. package/skills/core/pdd-doc-gardener/_meta.json +1 -0
  141. package/skills/core/pdd-doc-gardener/evals/default-evals.json +1 -0
  142. package/skills/core/pdd-entropy-reduction/SKILL.md +360 -0
  143. package/skills/core/pdd-entropy-reduction/_meta.json +1 -0
  144. package/skills/core/pdd-entropy-reduction/evals/default-evals.json +1 -0
  145. package/skills/core/pdd-entropy-reduction/references/entropy-report-template.md +287 -0
  146. package/skills/core/pdd-entropy-reduction/references/golden-principles.md +573 -0
  147. package/skills/core/pdd-entropy-reduction/scripts/entropy_scan.py +712 -0
  148. package/skills/core/pdd-extract-features/SKILL.md +320 -0
  149. package/skills/core/pdd-extract-features/_meta.json +1 -0
  150. package/skills/core/pdd-extract-features/evals/default-evals.json +1 -0
  151. package/skills/core/pdd-generate-spec/SKILL.md +418 -0
  152. package/skills/core/pdd-generate-spec/_meta.json +1 -0
  153. package/skills/core/pdd-generate-spec/evals/default-evals.json +1 -0
  154. package/skills/core/pdd-implement-feature/SKILL.md +332 -0
  155. package/skills/core/pdd-implement-feature/_meta.json +1 -0
  156. package/skills/core/pdd-implement-feature/evals/default-evals.json +1 -0
  157. package/skills/core/pdd-main/SKILL.md +540 -0
  158. package/skills/core/pdd-main/_meta.json +1 -0
  159. package/skills/core/pdd-main/evals/default-evals.json +1 -0
  160. package/skills/core/pdd-main/evals/evals.json +215 -0
  161. package/skills/core/pdd-verify-feature/SKILL.md +474 -0
  162. package/skills/core/pdd-verify-feature/_meta.json +1 -0
  163. package/skills/core/pdd-verify-feature/evals/default-evals.json +1 -0
  164. package/skills/core/pdd-vm/evals/default-evals.json +1 -0
  165. package/skills/core/traffic-accident-assessor/LICENSE +29 -0
  166. package/skills/core/traffic-accident-assessor/SKILL.md +439 -0
  167. package/skills/core/traffic-accident-assessor/evals/evals.json +1 -0
  168. package/skills/core/traffic-accident-assessor/references/accident-types.md +369 -0
  169. package/skills/core/traffic-accident-assessor/references/liability-rules.md +287 -0
  170. package/skills/core/traffic-accident-assessor/references/traffic-laws.md +226 -0
  171. package/skills/core/traffic-accident-assessor/references//351/253/230/345/260/224/345/244/253/350/257/264/346/230/216/344/271/246.pdf +32576 -106
  172. package/skills/core/traffic-accident-assessor/scripts/generate_official_statement.py +588 -0
  173. package/skills/core/traffic-accident-assessor/scripts/generate_report.py +495 -0
  174. package/skills/core/traffic-accident-assessor/scripts/generate_statement.py +528 -0
  175. package/skills/core/traffic-accident-assessor.zip +0 -0
  176. package/skills/entropy/expert-arch-enforcer/SKILL.md +292 -0
  177. package/skills/entropy/expert-arch-enforcer/_meta.json +1 -0
  178. package/skills/entropy/expert-arch-enforcer/evals/default-evals.json +1 -0
  179. package/skills/entropy/expert-auto-refactor/SKILL.md +327 -0
  180. package/skills/entropy/expert-auto-refactor/_meta.json +1 -0
  181. package/skills/entropy/expert-auto-refactor/evals/default-evals.json +1 -0
  182. package/skills/entropy/expert-code-quality/SKILL.md +468 -0
  183. package/skills/entropy/expert-code-quality/_meta.json +1 -0
  184. package/skills/entropy/expert-code-quality/evals/default-evals.json +1 -0
  185. package/skills/entropy/expert-code-quality/evals/evals.json +109 -0
  186. package/skills/entropy/expert-code-quality/references/code-smells.md +605 -0
  187. package/skills/entropy/expert-code-quality/references/design-patterns.md +1111 -0
  188. package/skills/entropy/expert-code-quality/references/refactoring-catalog.md +1281 -0
  189. package/skills/entropy/expert-code-quality/references/solid-principles.md +524 -0
  190. package/skills/entropy/expert-entropy-auditor/SKILL.md +276 -0
  191. package/skills/entropy/expert-entropy-auditor/_meta.json +1 -0
  192. package/skills/entropy/expert-entropy-auditor/evals/default-evals.json +1 -0
  193. package/skills/expert/expert-activiti/SKILL.md +497 -0
  194. package/skills/expert/expert-activiti/_meta.json +1 -0
  195. package/skills/expert/expert-mysql/SKILL.md +832 -0
  196. package/skills/expert/expert-mysql/_meta.json +1 -0
  197. package/skills/expert/expert-performance/SKILL.md +379 -0
  198. package/skills/expert/expert-performance/_meta.json +1 -0
  199. package/skills/expert/expert-performance/evals/default-evals.json +1 -0
  200. package/skills/expert/expert-ruoyi/SKILL.md +472 -0
  201. package/skills/expert/expert-ruoyi/_meta.json +1 -0
  202. package/skills/expert/expert-security/SKILL.md +1341 -0
  203. package/skills/expert/expert-security/_meta.json +1 -0
  204. package/skills/expert/expert-security/evals/default-evals.json +1 -0
  205. package/skills/expert/software-architect/SKILL.md +350 -0
  206. package/skills/expert/software-architect/_meta.json +1 -0
  207. package/skills/expert/software-engineer/SKILL.md +437 -0
  208. package/skills/expert/software-engineer/_meta.json +1 -0
  209. package/skills/expert/software-engineer/architecture.md +130 -0
  210. package/skills/expert/software-engineer/patterns.md +151 -0
  211. package/skills/expert/software-engineer/testing.md +135 -0
  212. package/skills/expert/system-architect/SKILL.md +628 -0
  213. package/skills/expert/system-architect/_meta.json +1 -0
  214. package/skills/expert/system-architect/assets/templates/ARCHITECTURE.md +25 -0
  215. package/skills/expert/system-architect/assets/templates/README.md +44 -0
  216. package/skills/expert/system-architect/references/js-ts-standards.md +18 -0
  217. package/skills/expert/system-architect/references/python-standards.md +19 -0
  218. package/skills/expert/system-architect/references/scaffolding.md +61 -0
  219. package/skills/expert/system-architect/references/security-checklist.md +21 -0
  220. package/skills/openspec/openspec-apply-change/SKILL.md +156 -0
  221. package/skills/openspec/openspec-apply-change/_meta.json +1 -0
  222. package/skills/openspec/openspec-archive-change/SKILL.md +114 -0
  223. package/skills/openspec/openspec-archive-change/_meta.json +1 -0
  224. package/skills/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
  225. package/skills/openspec/openspec-bulk-archive-change/_meta.json +1 -0
  226. package/skills/openspec/openspec-continue-change/SKILL.md +118 -0
  227. package/skills/openspec/openspec-continue-change/_meta.json +1 -0
  228. package/skills/openspec/openspec-explore/SKILL.md +288 -0
  229. package/skills/openspec/openspec-explore/_meta.json +1 -0
  230. package/skills/openspec/openspec-ff-change/SKILL.md +101 -0
  231. package/skills/openspec/openspec-ff-change/_meta.json +1 -0
  232. package/skills/openspec/openspec-new-change/SKILL.md +74 -0
  233. package/skills/openspec/openspec-new-change/_meta.json +1 -0
  234. package/skills/openspec/openspec-onboard/SKILL.md +554 -0
  235. package/skills/openspec/openspec-onboard/_meta.json +1 -0
  236. package/skills/openspec/openspec-sync-specs/SKILL.md +138 -0
  237. package/skills/openspec/openspec-sync-specs/_meta.json +1 -0
  238. package/skills/openspec/openspec-verify-change/SKILL.md +168 -0
  239. package/skills/openspec/openspec-verify-change/_meta.json +1 -0
  240. package/skills/pr/pdd-multi-review/SKILL.md +534 -0
  241. package/skills/pr/pdd-multi-review/_meta.json +1 -0
  242. package/skills/pr/pdd-pr-batch/SKILL.md +303 -0
  243. package/skills/pr/pdd-pr-batch/_meta.json +1 -0
  244. package/skills/pr/pdd-pr-create/SKILL.md +344 -0
  245. package/skills/pr/pdd-pr-create/_meta.json +1 -0
  246. package/skills/pr/pdd-pr-merge/SKILL.md +286 -0
  247. package/skills/pr/pdd-pr-merge/_meta.json +1 -0
  248. package/skills/pr/pdd-pr-review/SKILL.md +217 -0
  249. package/skills/pr/pdd-pr-review/_meta.json +1 -0
  250. package/skills/pr/pdd-task-manager/SKILL.md +636 -0
  251. package/skills/pr/pdd-task-manager/_meta.json +1 -0
  252. package/skills/pr/pdd-template-engine/SKILL.md +306 -0
  253. package/skills/pr/pdd-template-engine/_meta.json +1 -0
  254. package/templates/behavior-shaping/iron-law-template.md +87 -0
  255. package/templates/behavior-shaping/rationalization-template.md +62 -0
  256. package/templates/behavior-shaping/red-flags-template.md +70 -0
  257. package/templates/bilingual-template.md +139 -0
  258. package/templates/config/default.yaml +47 -0
  259. package/templates/project/default/README.md +31 -0
  260. package/templates/project/frontend/README.md +46 -0
  261. package/templates/project/java/README.md +48 -0
@@ -0,0 +1,418 @@
1
+ // lib/quality/rules/maintainability.js - 可维护性规则集
2
+ // 评估代码可维护性: 重复代码、模块化、耦合度、内聚性等
3
+
4
+ /**
5
+ * 可维护性规则集
6
+ *
7
+ * 规则列表:
8
+ * - duplicateCode: 重复代码检测
9
+ * - moduleCohesion: 模块内聚性
10
+ * - coupling: 耦合度检测
11
+ * - singleResponsibility: 单一职责原则
12
+ * - deadCode: 死代码检测
13
+ * - dependencyDirection: 依赖方向检查
14
+ */
15
+
16
+ const rules = [
17
+ {
18
+ name: 'duplicateCode',
19
+ description: '重复代码: 避免复制粘贴的代码块(相似度>70%视为重复)',
20
+ maxScore: 25,
21
+ check(code, ext) {
22
+ const lines = code.split('\n');
23
+ const normalizedLines = lines.map(l => l.trim()).filter(l => l.length > 5);
24
+ const duplicates = [];
25
+
26
+ // 使用滑动窗口检测重复代码块(至少3行)
27
+ const minBlockSize = 3;
28
+ const similarityThreshold = 0.7;
29
+
30
+ for (let i = 0; i < normalizedLines.length - minBlockSize; i++) {
31
+ for (let j = i + minBlockSize; j < normalizedLines.length - minBlockSize; j++) {
32
+ let matchCount = 0;
33
+ let totalLen = 0;
34
+
35
+ for (let k = 0; k < minBlockSize && i + k < normalizedLines.length && j + k < normalizedLines.length; k++) {
36
+ const lineA = normalizedLines[i + k];
37
+ const lineB = normalizedLines[j + k];
38
+ totalLen += Math.max(lineA.length, lineB.length);
39
+
40
+ if (this._similarity(lineA, lineB) >= similarityThreshold) {
41
+ matchCount++;
42
+ }
43
+ }
44
+
45
+ if (matchCount >= minBlockSize) {
46
+ const similarity = matchCount / minBlockSize;
47
+ if (similarity >= similarityThreshold) {
48
+ // 检查是否已经记录过这个重复对
49
+ const key = `${Math.min(i, j)}-${Math.max(i, j)}`;
50
+ if (!duplicates.find(d => d.key === key)) {
51
+ duplicates.push({
52
+ key,
53
+ startLine1: i + 1,
54
+ startLine2: j + 1,
55
+ blockSize: matchCount,
56
+ similarity: Math.round(similarity * 100)
57
+ });
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ return {
65
+ passed: duplicates.length <= 1,
66
+ deduction: Math.min(duplicates.length * 8, 25),
67
+ message: duplicates.length > 0
68
+ ? `发现${duplicates.length}处代码重复(>=${minBlockSize}行, 相似度>=${Math.round(similarityThreshold * 100)}%)`
69
+ : '未发现明显代码重复',
70
+ suggestion: '将重复代码提取为公共函数或工具方法,遵循DRY原则',
71
+ line: duplicates.length > 0 ? duplicates[0].startLine1 : null
72
+ };
73
+ },
74
+
75
+ _similarity(strA, strB) {
76
+ if (strA === strB) return 1.0;
77
+ if (!strA || !strB) return 0;
78
+
79
+ // 简化的相似度计算:基于共同子序列比例
80
+ const lenA = strA.length;
81
+ const lenB = strB.length;
82
+ const maxLen = Math.max(lenA, lenB);
83
+
84
+ if (maxLen === 0) return 1.0;
85
+
86
+ // 计算编辑距离的近似值
87
+ let matches = 0;
88
+ for (let i = 0; i < Math.min(lenA, lenB); i++) {
89
+ if (strA[i] === strB[i]) matches++;
90
+ }
91
+
92
+ return matches / maxLen;
93
+ }
94
+ },
95
+
96
+ {
97
+ name: 'moduleCohesion',
98
+ description: '模块内聚性: 单个文件应专注于单一功能领域',
99
+ maxScore: 15,
100
+ check(code, ext) {
101
+ const lines = code.split('\n');
102
+ const concerns = new Set();
103
+
104
+ // 通过关键词识别关注点
105
+ const concernPatterns = [
106
+ { pattern: /\b(database|db|query|sql|mysql|postgres|mongodb|redis)\b/i, name: '数据访问' },
107
+ { pattern: /\b(http|request|response|express|koa|router|api|rest|graphql)\b/i, name: 'HTTP/路由' },
108
+ { pattern: /\b(auth|token|jwt|session|login|password|hash|bcrypt|crypto)\b/i, name: '认证/安全' },
109
+ { pattern: /\b(validate|schema|joi|yup|zod|type-check|assert)\b/i, name: '数据校验' },
110
+ { pattern: /\b(log|logger|winston|pino|debug|console\.(log|error))\b/i, name: '日志' },
111
+ { pattern: /\b(cache|memoize|lru|ttl|expire)\b/i, name: '缓存' },
112
+ { pattern: /\b(config|env|environment|setting)\b/i, name: '配置' },
113
+ { pattern: /\b(file|fs\.|readFile|writeFile|path\.join)\b/i, name: '文件操作' },
114
+ { pattern: /\b(date|time|moment|dayjs|cron|schedule)\b/i, name: '时间处理' },
115
+ { pattern: /\b(email|sendmail|smtp|nodemailer)\b/i, name: '邮件服务' }
116
+ ];
117
+
118
+ for (const line of lines) {
119
+ // 排除注释和字符串
120
+ if (line.trim().startsWith('//') || line.trim().startsWith('*')) continue;
121
+
122
+ for (const { pattern, name } of concernPatterns) {
123
+ if (pattern.test(line)) {
124
+ concerns.add(name);
125
+ }
126
+ }
127
+ }
128
+
129
+ // 小文件允许更多关注点混合
130
+ const isLargeFile = lines.length > 200;
131
+ const maxConcerns = isLargeFile ? 2 : 3;
132
+
133
+ return {
134
+ passed: concerns.size <= maxConcerns,
135
+ deduction: concerns.size > maxConcerns ? Math.min((concerns.size - maxConcerns) * 4, 15) : 0,
136
+ message: concerns.size <= maxConcerns
137
+ ? `模块内聚良好,专注${concerns.size}个关注点`
138
+ : `模块涉及过多关注点(${[...concerns].join(', ')}),建议拆分`,
139
+ suggestion: '按职责拆分模块,每个文件只负责一个明确的功能领域'
140
+ };
141
+ }
142
+ },
143
+
144
+ {
145
+ name: 'coupling',
146
+ description: '耦合度: 减少对外部模块的直接依赖,使用依赖注入',
147
+ maxScore: 20,
148
+ check(code, ext) {
149
+ const importStatements = [];
150
+ const requireCalls = [];
151
+ const lines = code.split('\n');
152
+
153
+ // 收集所有import/require语句
154
+ for (let i = 0; i < lines.length; i++) {
155
+ const line = lines[i].trim();
156
+
157
+ const importMatch = line.match(/^import\s+.+?\s+from\s+['"]([^'"]+)['"]/);
158
+ if (importMatch) {
159
+ importStatements.push({ module: importMatch[1], line: i + 1 });
160
+ }
161
+
162
+ const requireMatch = line.match(/(?:const|let|var)\s+\w+\s*=\s*require\(['"]([^'"]+)['"]\)/);
163
+ if (requireMatch) {
164
+ requireCalls.push({ module: requireMatch[1], line: i + 1 });
165
+ }
166
+ }
167
+
168
+ const totalDependencies = importStatements.length + requireCalls.length;
169
+
170
+ // 分类依赖类型
171
+ const externalDeps = [...importStatements, ...requireCalls].filter(dep =>
172
+ !dep.module.startsWith('.') &&
173
+ !dep.module.startsWith('/') &&
174
+ !dep.module.startsWith('node:') &&
175
+ dep.module !== 'fs' &&
176
+ dep.module !== 'path' &&
177
+ dep.module !== 'url' &&
178
+ dep.module !== 'http' &&
179
+ dep.module !== 'https'
180
+ );
181
+
182
+ // 高耦合指标:外部依赖过多或循环导入风险
183
+ const highCoupling = totalDependencies > 10 || externalDeps.length > 6;
184
+
185
+ return {
186
+ passed: !highCoupling,
187
+ deduction: highCoupling ? Math.min((totalDependencies - 8) * 2 + (externalDeps.length - 4) * 2, 20) : 0,
188
+ message: totalDependencies <= 10
189
+ ? `依赖数量合理 (${totalDependencies}个,其中${externalDeps.length}个外部)`
190
+ : `高耦合警告: ${totalDependencies}个依赖(${externalDeps.length}个外部模块)`,
191
+ suggestion: '通过依赖注入、工厂模式或门面模式降低耦合;考虑引入IoC容器',
192
+ line: externalDeps.length > 4 ? externalDeps[0]?.line : null
193
+ };
194
+ }
195
+ },
196
+
197
+ {
198
+ name: 'singleResponsibility',
199
+ description: '单一职责: 类/函数应只有一个变更原因',
200
+ maxScore: 15,
201
+ check(code, ext) {
202
+ const violations = [];
203
+ const lines = code.split('\n');
204
+
205
+ // 检测类中的多种行为模式
206
+ const classBlocks = this._extractClassBlocks(code);
207
+
208
+ for (const cls of classBlocks) {
209
+ const behaviors = new Set();
210
+
211
+ // 检测不同的行为模式
212
+ if (/get|fetch|find|read|query|select/i.test(cls.body)) behaviors.add('数据读取');
213
+ if (/set|update|save|write|insert|create|add/i.test(cls.body)) behaviors.add('数据写入');
214
+ if (/delete|remove|destroy|drop/i.test(cls.body)) behaviors.add('数据删除');
215
+ if (/validate|check|verify|ensure|guard/i.test(cls.body)) behaviors.add('数据验证');
216
+ if (/log|report|notify|emit|publish|send/i.test(cls.body)) behaviors.add('通知/日志');
217
+ if (/calculate|compute|transform|convert|parse|format/i.test(cls.body)) behaviors.add('数据处理');
218
+ if (/connect|open|close|disconnect|init|dispose/i.test(cls.body)) behaviors.add('资源管理');
219
+ if (/config|setup|configure|option|setting/i.test(cls.body)) behaviors.add('配置管理');
220
+
221
+ if (behaviors.size > 3) {
222
+ violations.push({
223
+ name: cls.name,
224
+ line: cls.startLine,
225
+ behaviorCount: behaviors.size,
226
+ behaviors: [...behaviors]
227
+ });
228
+ }
229
+ }
230
+
231
+ return {
232
+ passed: violations.length === 0,
233
+ deduction: Math.min(violations.length * 5, 15),
234
+ message: violations.length > 0
235
+ ? `${violations.length}个类可能违反SRP: ${violations.map(v => `${v.name}(${v.behaviorCount}种行为)`).join(', ')}`
236
+ : '类和函数职责划分合理',
237
+ suggestion: '将具有多种行为的类拆分为更小的、职责单一的类',
238
+ line: violations.length > 0 ? violations[0].line : null
239
+ };
240
+ },
241
+
242
+ _extractClassBlocks(code) {
243
+ const classes = [];
244
+ const lines = code.split('\n');
245
+ let braceStack = 0;
246
+ let classStart = null;
247
+ let className = '';
248
+ let classBodyLines = [];
249
+
250
+ for (let i = 0; i < lines.length; i++) {
251
+ const line = lines[i];
252
+
253
+ if (braceStack === 0 && line.match(/^class\s+(\w+)/)) {
254
+ classStart = i;
255
+ className = line.match(/^class\s+(\w+)/)[1];
256
+ classBodyLines = [];
257
+ }
258
+
259
+ if (classStart !== null) {
260
+ classBodyLines.push(line);
261
+ }
262
+
263
+ for (const ch of line) {
264
+ if (ch === '{') braceStack++;
265
+ if (ch === '}') {
266
+ braceStack--;
267
+ if (braceStack === 0 && classStart !== null) {
268
+ classes.push({
269
+ name: className,
270
+ startLine: classStart + 1,
271
+ body: classBodyLines.join('\n')
272
+ });
273
+ classStart = null;
274
+ classBodyLines = [];
275
+ }
276
+ }
277
+ }
278
+ }
279
+
280
+ return classes;
281
+ }
282
+ },
283
+
284
+ {
285
+ name: 'deadCode',
286
+ description: '死代码: 未使用的变量、函数、import语句',
287
+ maxScore: 15,
288
+ check(code, ext) {
289
+ const issues = [];
290
+
291
+ // 检查未使用的import
292
+ const imports = [];
293
+ const importRegex = /^import\s+(?:(\{[^}]+\})|(\w+)|(\*\s+as\s+(\w+)))\s+from\s+['"]([^'"]+)['"]/gm;
294
+ let importMatch;
295
+
296
+ while ((importMatch = importRegex.exec(code)) !== null) {
297
+ const namedImports = importMatch[1]; // { foo, bar }
298
+ const defaultImport = importMatch[2]; // Foo
299
+ const starAs = importMatch[4]; // *
300
+ const source = importMatch[5]; // 'module'
301
+
302
+ if (namedImports) {
303
+ const names = namedImports.replace(/[{}]/g, '').split(',').map(s => s.trim().split(/\s+as\s+/).pop());
304
+ for (const name of names) {
305
+ if (name === 'default') continue;
306
+ // 检查是否在代码中使用(排除声明本身)
307
+ const usagePattern = new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'g');
308
+ const usages = code.match(usagePattern) || [];
309
+ if (usages.length <= 1) { // 只有import语句本身
310
+ issues.push({
311
+ type: 'unused_import',
312
+ name,
313
+ source,
314
+ line: code.substring(0, importMatch.index).split('\n').length
315
+ });
316
+ }
317
+ }
318
+ } else if (defaultImport || starAs) {
319
+ const name = defaultImport || starAs;
320
+ const usagePattern = new RegExp(`\\b${name}\\b`, 'g');
321
+ const usages = code.match(usagePattern) || [];
322
+ if (usages.length <= 2) { // import + 可能的一次赋值
323
+ issues.push({
324
+ type: 'unused_import',
325
+ name,
326
+ source,
327
+ line: code.substring(0, importMatch.index).split('\n').length
328
+ });
329
+ }
330
+ }
331
+ }
332
+
333
+ // 检查未使用的函数声明(简化版:查找定义但未调用的函数)
334
+ const funcDeclarations = code.match(/^(?:async\s+)?function\s+(\w+)\s*\(/gm) || [];
335
+ for (const decl of funcDeclarations) {
336
+ const name = decl.match(/function\s+(\w+)/)[1];
337
+ if (name === 'constructor') continue;
338
+
339
+ const usagePattern = new RegExp(`\\b${name}\\s*\\(`, 'g');
340
+ const usages = code.match(usagePattern) || [];
341
+ if (usages.length <= 1) {
342
+ // 排除exported函数
343
+ const isExported = new RegExp(`export\\s+(?:default\\s+)?(?:async\\s+)?function\\s+${name}`).test(code);
344
+ if (!isExported) {
345
+ issues.push({
346
+ type: 'unused_function',
347
+ name,
348
+ line: code.substring(0, code.indexOf(decl)).split('\n').length + 1
349
+ });
350
+ }
351
+ }
352
+ }
353
+
354
+ // 去重并限制数量
355
+ const uniqueIssues = issues.filter((v, i, a) =>
356
+ a.findIndex(t => t.name === v.name && t.type === v.type) === i
357
+ ).slice(0, 5);
358
+
359
+ return {
360
+ passed: uniqueIssues.length === 0,
361
+ deduction: Math.min(uniqueIssues.length * 3, 15),
362
+ message: uniqueIssues.length > 0
363
+ ? `发现${uniqueIssues.length}处可能的死代码: ${uniqueIssues.map(i => i.name).join(', ')}`
364
+ : '未发现明显的死代码',
365
+ suggestion: '删除未使用的import和函数,保持代码整洁',
366
+ line: uniqueIssues.length > 0 ? uniqueIssues[0].line : null
367
+ };
368
+ }
369
+ },
370
+
371
+ {
372
+ name: 'dependencyDirection',
373
+ description: '依赖方向: 低层模块不应依赖高层模块,避免循环依赖',
374
+ maxScore: 10,
375
+ check(code, ext) {
376
+ const filePath = ''; // 实际使用时需要传入文件路径
377
+ const issues = [];
378
+
379
+ // 检测可能的问题依赖模式
380
+ const lines = code.split('\n');
381
+ const importedModules = [];
382
+
383
+ for (let i = 0; i < lines.length; i++) {
384
+ const impMatch = lines[i].match(/from\s+['"](\.\.[\/][^'"]+)['"]/);
385
+ if (impMatch) {
386
+ importedModules.push({
387
+ path: impMatch[1],
388
+ line: i + 1,
389
+ depth: (impMatch[1].match(/\.\./g) || []).length
390
+ });
391
+ }
392
+ }
393
+
394
+ // 检测深层相对路径引用(可能是架构问题)
395
+ const deepImports = importedModules.filter(m => m.depth >= 3);
396
+
397
+ if (deepImports.length > 0) {
398
+ issues.push({
399
+ type: 'deep_import',
400
+ count: deepImports.length,
401
+ details: deepImports.map(m => m.path),
402
+ line: deepImports[0].line
403
+ });
404
+ }
405
+
406
+ return {
407
+ passed: deepImports.length === 0,
408
+ deduction: Math.min(deepImports.length * 3, 10),
409
+ message: deepImports.length > 0
410
+ ? `发现${deepImports.length}处深层路径引用(../..及以上),可能存在依赖方向问题`
411
+ : '依赖方向正常',
412
+ suggestion: '使用别名路径(@/)替代深层相对路径;考虑重构目录结构以减少层级'
413
+ };
414
+ }
415
+ }
416
+ ];
417
+
418
+ export default rules;