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,712 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ PDD 熵减检测脚本
5
+
6
+ 用于检测代码库中的熵增问题,包括:
7
+ - 文档-代码不一致
8
+ - 架构边界违规
9
+ - 重复代码
10
+ - 过期TODO
11
+ - 复杂度过高
12
+
13
+ 使用方法:
14
+ python entropy_scan.py --project /path/to/project --output report.md
15
+ """
16
+
17
+ import os
18
+ import re
19
+ import json
20
+ import argparse
21
+ from datetime import datetime, timedelta
22
+ from pathlib import Path
23
+ from typing import Dict, List, Tuple, Any
24
+ from dataclasses import dataclass, field
25
+ from collections import defaultdict
26
+
27
+
28
+ @dataclass
29
+ class EntropyIssue:
30
+ """熵增问题"""
31
+ category: str # 类别: doc, arch, code, debt
32
+ severity: str # 严重程度: critical, high, medium, low
33
+ title: str # 问题标题
34
+ location: str # 位置
35
+ description: str # 描述
36
+ suggestion: str # 建议
37
+ score: int = 0 # 熵值贡献
38
+
39
+
40
+ @dataclass
41
+ class EntropyReport:
42
+ """熵减报告"""
43
+ project_path: str
44
+ scan_time: str
45
+ doc_entropy: int = 0
46
+ arch_entropy: int = 0
47
+ code_entropy: int = 0
48
+ debt_entropy: int = 0
49
+ total_entropy: int = 0
50
+ issues: List[EntropyIssue] = field(default_factory=list)
51
+
52
+ def calculate_total(self):
53
+ """计算综合熵值"""
54
+ self.total_entropy = int(
55
+ self.doc_entropy * 0.25 +
56
+ self.arch_entropy * 0.30 +
57
+ self.code_entropy * 0.25 +
58
+ self.debt_entropy * 0.20
59
+ )
60
+
61
+ def get_status(self) -> str:
62
+ """获取状态"""
63
+ if self.total_entropy <= 30:
64
+ return "🟢 健康"
65
+ elif self.total_entropy <= 60:
66
+ return "🟡 警告"
67
+ else:
68
+ return "🔴 危险"
69
+
70
+
71
+ class EntropyScanner:
72
+ """熵减扫描器"""
73
+
74
+ def __init__(self, project_path: str):
75
+ self.project_path = Path(project_path)
76
+ self.issues: List[EntropyIssue] = []
77
+
78
+ # 配置
79
+ self.config = {
80
+ 'todo_stale_days': 30,
81
+ 'doc_stale_days': 90,
82
+ 'complexity_threshold': 20,
83
+ 'function_length_threshold': 100,
84
+ 'nesting_threshold': 5,
85
+ 'duplicate_threshold': 5,
86
+ }
87
+
88
+ # 排除目录
89
+ self.exclude_dirs = {
90
+ 'node_modules', 'dist', 'build', '.git', '__pycache__',
91
+ 'target', 'vendor', '.idea', '.vscode'
92
+ }
93
+
94
+ # 文档目录
95
+ self.doc_dirs = {'docs', 'doc', 'documentation'}
96
+
97
+ # 六层模型
98
+ self.layers = {
99
+ 'types': 1,
100
+ 'config': 2,
101
+ 'repo': 3,
102
+ 'service': 4,
103
+ 'runtime': 5,
104
+ 'ui': 6
105
+ }
106
+
107
+ def scan(self) -> EntropyReport:
108
+ """执行扫描"""
109
+ report = EntropyReport(
110
+ project_path=str(self.project_path),
111
+ scan_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
112
+ )
113
+
114
+ # 1. 文档一致性扫描
115
+ doc_issues = self._scan_document_consistency()
116
+ report.doc_entropy = self._calculate_category_entropy(doc_issues)
117
+ self.issues.extend(doc_issues)
118
+
119
+ # 2. 架构边界扫描
120
+ arch_issues = self._scan_architecture_violations()
121
+ report.arch_entropy = self._calculate_category_entropy(arch_issues)
122
+ self.issues.extend(arch_issues)
123
+
124
+ # 3. 代码质量扫描
125
+ code_issues = self._scan_code_quality()
126
+ report.code_entropy = self._calculate_category_entropy(code_issues)
127
+ self.issues.extend(code_issues)
128
+
129
+ # 4. 技术债务扫描
130
+ debt_issues = self._scan_technical_debt()
131
+ report.debt_entropy = self._calculate_category_entropy(debt_issues)
132
+ self.issues.extend(debt_issues)
133
+
134
+ report.issues = self.issues
135
+ report.calculate_total()
136
+
137
+ return report
138
+
139
+ def _scan_document_consistency(self) -> List[EntropyIssue]:
140
+ """扫描文档一致性"""
141
+ issues = []
142
+
143
+ # 1. 检查过期TODO
144
+ issues.extend(self._check_stale_todos())
145
+
146
+ # 2. 检查孤立文档
147
+ issues.extend(self._check_orphan_docs())
148
+
149
+ # 3. 检查API文档一致性
150
+ issues.extend(self._check_api_doc_consistency())
151
+
152
+ return issues
153
+
154
+ def _check_stale_todos(self) -> List[EntropyIssue]:
155
+ """检查过期TODO"""
156
+ issues = []
157
+ stale_threshold = datetime.now() - timedelta(days=self.config['todo_stale_days'])
158
+
159
+ todo_pattern = re.compile(
160
+ r'//\s*(TODO|FIXME|HACK|XXX):\s*(.+?)(?:\s*-\s*(\d{4}-\d{2}-\d{2}))?$',
161
+ re.IGNORECASE
162
+ )
163
+
164
+ for file_path in self._iter_code_files():
165
+ try:
166
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
167
+ for line_num, line in enumerate(f, 1):
168
+ match = todo_pattern.search(line)
169
+ if match:
170
+ todo_type, content, date_str = match.groups()
171
+
172
+ if date_str:
173
+ todo_date = datetime.strptime(date_str, '%Y-%m-%d')
174
+ if todo_date < stale_threshold:
175
+ issues.append(EntropyIssue(
176
+ category='doc',
177
+ severity='medium',
178
+ title=f'过期{todo_type.upper()}',
179
+ location=f'{file_path}:{line_num}',
180
+ description=f'{todo_type}: {content} (创建于 {date_str})',
181
+ suggestion='完成或删除此TODO',
182
+ score=5
183
+ ))
184
+ except Exception:
185
+ continue
186
+
187
+ return issues
188
+
189
+ def _check_orphan_docs(self) -> List[EntropyIssue]:
190
+ """检查孤立文档"""
191
+ issues = []
192
+
193
+ # 查找文档目录
194
+ for doc_dir in self.doc_dirs:
195
+ doc_path = self.project_path / doc_dir
196
+ if not doc_path.exists():
197
+ continue
198
+
199
+ # 检查每个文档文件
200
+ for doc_file in doc_path.rglob('*.md'):
201
+ # 检查是否有对应的代码文件
202
+ doc_name = doc_file.stem
203
+
204
+ # 特殊文档不需要对应代码
205
+ special_docs = {'README', 'CHANGELOG', 'CONTRIBUTING', 'LICENSE', 'index'}
206
+ if doc_name in special_docs:
207
+ continue
208
+
209
+ # 检查是否被引用
210
+ referenced = self._is_doc_referenced(doc_file)
211
+ if not referenced:
212
+ issues.append(EntropyIssue(
213
+ category='doc',
214
+ severity='low',
215
+ title='孤立文档',
216
+ location=str(doc_file.relative_to(self.project_path)),
217
+ description='文档未被任何代码或其他文档引用',
218
+ suggestion='检查是否需要归档或删除',
219
+ score=3
220
+ ))
221
+
222
+ return issues
223
+
224
+ def _is_doc_referenced(self, doc_file: Path) -> bool:
225
+ """检查文档是否被引用"""
226
+ doc_name = doc_file.name
227
+ doc_path_str = str(doc_file.relative_to(self.project_path))
228
+
229
+ for file_path in self._iter_all_files():
230
+ if file_path.suffix in {'.md', '.ts', '.js', '.tsx', '.jsx', '.py', '.java'}:
231
+ try:
232
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
233
+ content = f.read()
234
+ if doc_name in content or doc_path_str in content:
235
+ return True
236
+ except Exception:
237
+ continue
238
+
239
+ return False
240
+
241
+ def _check_api_doc_consistency(self) -> List[EntropyIssue]:
242
+ """检查API文档一致性"""
243
+ issues = []
244
+
245
+ # 查找API定义文件
246
+ api_files = list(self.project_path.rglob('**/api/*.ts')) + \
247
+ list(self.project_path.rglob('**/api/*.js'))
248
+
249
+ # 查找API文档
250
+ api_docs = list(self.project_path.rglob('**/docs/api/*.md'))
251
+
252
+ if api_files and not api_docs:
253
+ issues.append(EntropyIssue(
254
+ category='doc',
255
+ severity='high',
256
+ title='缺少API文档',
257
+ location='docs/api/',
258
+ description=f'发现 {len(api_files)} 个API文件,但没有对应的API文档',
259
+ suggestion='创建API文档目录并编写文档',
260
+ score=10
261
+ ))
262
+
263
+ return issues
264
+
265
+ def _scan_architecture_violations(self) -> List[EntropyIssue]:
266
+ """扫描架构违规"""
267
+ issues = []
268
+
269
+ # 检查六层模型违规
270
+ issues.extend(self._check_layer_violations())
271
+
272
+ # 检查循环依赖
273
+ issues.extend(self._check_circular_dependencies())
274
+
275
+ return issues
276
+
277
+ def _check_layer_violations(self) -> List[EntropyIssue]:
278
+ """检查层级违规"""
279
+ issues = []
280
+
281
+ # 定义层级检测规则
282
+ layer_patterns = {
283
+ 'ui': ['components/', 'views/', 'pages/', 'ui/'],
284
+ 'service': ['services/', 'service/', 'business/'],
285
+ 'repo': ['repositories/', 'repo/', 'dao/', 'data/'],
286
+ 'config': ['config/', 'configuration/'],
287
+ 'types': ['types/', 'interfaces/', 'models/'],
288
+ }
289
+
290
+ for file_path in self._iter_code_files():
291
+ file_str = str(file_path)
292
+
293
+ # 确定文件所在层级
294
+ file_layer = None
295
+ for layer, patterns in layer_patterns.items():
296
+ if any(p in file_str for p in patterns):
297
+ file_layer = layer
298
+ break
299
+
300
+ if not file_layer:
301
+ continue
302
+
303
+ # 检查导入
304
+ try:
305
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
306
+ content = f.read()
307
+
308
+ # 检查是否导入了更高层级
309
+ for layer, patterns in layer_patterns.items():
310
+ if self.layers.get(layer, 0) > self.layers.get(file_layer, 0):
311
+ for pattern in patterns:
312
+ if f"from '{pattern}" in content or f'from "{pattern}' in content:
313
+ issues.append(EntropyIssue(
314
+ category='arch',
315
+ severity='critical',
316
+ title='层级违规',
317
+ location=str(file_path.relative_to(self.project_path)),
318
+ description=f'{file_layer}层导入了{layer}层',
319
+ suggestion='重构以符合六层依赖模型',
320
+ score=15
321
+ ))
322
+ except Exception:
323
+ continue
324
+
325
+ return issues
326
+
327
+ def _check_circular_dependencies(self) -> List[EntropyIssue]:
328
+ """检查循环依赖"""
329
+ issues = []
330
+
331
+ # 简化的循环依赖检测
332
+ imports = defaultdict(set)
333
+
334
+ for file_path in self._iter_code_files():
335
+ if file_path.suffix not in {'.ts', '.js', '.tsx', '.jsx'}:
336
+ continue
337
+
338
+ try:
339
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
340
+ content = f.read()
341
+
342
+ # 提取导入
343
+ import_pattern = re.compile(r"from\s+['\"](.+?)['\"]")
344
+ for match in import_pattern.finditer(content):
345
+ import_path = match.group(1)
346
+ if not import_path.startswith('.'):
347
+ continue
348
+
349
+ # 解析相对路径
350
+ source = str(file_path.relative_to(self.project_path))
351
+ imports[source].add(import_path)
352
+ except Exception:
353
+ continue
354
+
355
+ # 检测循环(简化版)
356
+ # 实际实现需要完整的依赖图分析
357
+
358
+ return issues
359
+
360
+ def _scan_code_quality(self) -> List[EntropyIssue]:
361
+ """扫描代码质量"""
362
+ issues = []
363
+
364
+ # 检查复杂度
365
+ issues.extend(self._check_complexity())
366
+
367
+ # 检查重复代码
368
+ issues.extend(self._check_duplicates())
369
+
370
+ # 检查命名规范
371
+ issues.extend(self._check_naming())
372
+
373
+ return issues
374
+
375
+ def _check_complexity(self) -> List[EntropyIssue]:
376
+ """检查代码复杂度"""
377
+ issues = []
378
+
379
+ for file_path in self._iter_code_files():
380
+ if file_path.suffix not in {'.ts', '.js', '.tsx', '.jsx', '.py', '.java'}:
381
+ continue
382
+
383
+ try:
384
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
385
+ lines = f.readlines()
386
+
387
+ # 检查函数长度
388
+ self._check_function_length(file_path, lines, issues)
389
+
390
+ # 检查嵌套深度
391
+ self._check_nesting_depth(file_path, lines, issues)
392
+ except Exception:
393
+ continue
394
+
395
+ return issues
396
+
397
+ def _check_function_length(self, file_path: Path, lines: List[str], issues: List[EntropyIssue]):
398
+ """检查函数长度"""
399
+ threshold = self.config['function_length_threshold']
400
+
401
+ # 简化的函数检测
402
+ func_pattern = re.compile(r'^\s*(async\s+)?function\s+(\w+|<[^>]+>)')
403
+ class_pattern = re.compile(r'^\s*class\s+\w+')
404
+
405
+ func_start = None
406
+ func_name = None
407
+ brace_count = 0
408
+
409
+ for i, line in enumerate(lines):
410
+ if func_pattern.match(line):
411
+ if func_start is not None and i - func_start > threshold:
412
+ issues.append(EntropyIssue(
413
+ category='code',
414
+ severity='medium',
415
+ title='函数过长',
416
+ location=f'{file_path}:{func_start + 1}',
417
+ description=f'函数 {func_name} 有 {i - func_start} 行,超过阈值 {threshold}',
418
+ suggestion='拆分为更小的函数',
419
+ score=8
420
+ ))
421
+ func_start = i
422
+ func_name = func_pattern.match(line).group(2)
423
+ brace_count = 0
424
+
425
+ if func_start is not None:
426
+ brace_count += line.count('{') - line.count('}')
427
+ if brace_count == 0 and '{' in ''.join(lines[func_start:i+1]):
428
+ if i - func_start > threshold:
429
+ issues.append(EntropyIssue(
430
+ category='code',
431
+ severity='medium',
432
+ title='函数过长',
433
+ location=f'{file_path}:{func_start + 1}',
434
+ description=f'函数 {func_name} 有 {i - func_start} 行',
435
+ suggestion='拆分为更小的函数',
436
+ score=8
437
+ ))
438
+ func_start = None
439
+
440
+ def _check_nesting_depth(self, file_path: Path, lines: List[str], issues: List[EntropyIssue]):
441
+ """检查嵌套深度"""
442
+ threshold = self.config['nesting_threshold']
443
+ max_depth = 0
444
+ max_depth_line = 0
445
+
446
+ current_depth = 0
447
+ for i, line in enumerate(lines):
448
+ current_depth += line.count('{') - line.count('}')
449
+ if current_depth > max_depth:
450
+ max_depth = current_depth
451
+ max_depth_line = i + 1
452
+
453
+ if max_depth > threshold:
454
+ issues.append(EntropyIssue(
455
+ category='code',
456
+ severity='medium',
457
+ title='嵌套过深',
458
+ location=f'{file_path}:{max_depth_line}',
459
+ description=f'最大嵌套深度 {max_depth},超过阈值 {threshold}',
460
+ suggestion='使用早返回或提取方法减少嵌套',
461
+ score=6
462
+ ))
463
+
464
+ def _check_duplicates(self) -> List[EntropyIssue]:
465
+ """检查重复代码"""
466
+ issues = []
467
+
468
+ # 简化的重复检测
469
+ code_blocks = defaultdict(list)
470
+ block_size = 10 # 代码块大小
471
+
472
+ for file_path in self._iter_code_files():
473
+ if file_path.suffix not in {'.ts', '.js', '.tsx', '.jsx'}:
474
+ continue
475
+
476
+ try:
477
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
478
+ lines = f.readlines()
479
+
480
+ for i in range(len(lines) - block_size):
481
+ block = ''.join(lines[i:i+block_size]).strip()
482
+ if len(block) > 50: # 忽略太短的块
483
+ code_blocks[block].append((str(file_path), i+1))
484
+ except Exception:
485
+ continue
486
+
487
+ # 找出重复的块
488
+ for block, locations in code_blocks.items():
489
+ if len(locations) >= self.config['duplicate_threshold']:
490
+ issues.append(EntropyIssue(
491
+ category='code',
492
+ severity='high',
493
+ title='重复代码',
494
+ location=', '.join([f'{p}:{l}' for p, l in locations[:3]]),
495
+ description=f'发现 {len(locations)} 处相似代码块',
496
+ suggestion='提取到共享函数',
497
+ score=12
498
+ ))
499
+
500
+ return issues
501
+
502
+ def _check_naming(self) -> List[EntropyIssue]:
503
+ """检查命名规范"""
504
+ issues = []
505
+
506
+ # 简化的命名检查
507
+ bad_patterns = [
508
+ (r'\bvar\s+[a-z]\b', '单字母变量名'),
509
+ (r'\bfunction\s+[a-z]\s*\(', '单字母函数名'),
510
+ (r'\b(const|let|var)\s+_\w+', '下划线前缀变量'),
511
+ ]
512
+
513
+ for file_path in self._iter_code_files():
514
+ if file_path.suffix not in {'.ts', '.js', '.tsx', '.jsx'}:
515
+ continue
516
+
517
+ try:
518
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
519
+ for line_num, line in enumerate(f, 1):
520
+ for pattern, desc in bad_patterns:
521
+ if re.search(pattern, line):
522
+ issues.append(EntropyIssue(
523
+ category='code',
524
+ severity='low',
525
+ title='命名不规范',
526
+ location=f'{file_path}:{line_num}',
527
+ description=desc,
528
+ suggestion='使用更具描述性的名称',
529
+ score=2
530
+ ))
531
+ except Exception:
532
+ continue
533
+
534
+ return issues
535
+
536
+ def _scan_technical_debt(self) -> List[EntropyIssue]:
537
+ """扫描技术债务"""
538
+ issues = []
539
+
540
+ # 检查TODO数量
541
+ todo_count = sum(1 for issue in self.issues if 'TODO' in issue.title)
542
+ if todo_count > 20:
543
+ issues.append(EntropyIssue(
544
+ category='debt',
545
+ severity='high',
546
+ title='技术债务过多',
547
+ location='全局',
548
+ description=f'发现 {todo_count} 个未处理的TODO',
549
+ suggestion='分配时间偿还技术债务',
550
+ score=15
551
+ ))
552
+
553
+ # 检查测试覆盖率(简化)
554
+ test_files = list(self.project_path.rglob('**/*.test.ts')) + \
555
+ list(self.project_path.rglob('**/*.spec.ts'))
556
+ source_files = list(self.project_path.rglob('**/src/**/*.ts'))
557
+
558
+ if source_files and len(test_files) / len(source_files) < 0.5:
559
+ issues.append(EntropyIssue(
560
+ category='debt',
561
+ severity='medium',
562
+ title='测试覆盖率低',
563
+ location='tests/',
564
+ description=f'测试文件与源文件比例: {len(test_files)}/{len(source_files)}',
565
+ suggestion='增加单元测试',
566
+ score=8
567
+ ))
568
+
569
+ return issues
570
+
571
+ def _calculate_category_entropy(self, issues: List[EntropyIssue]) -> int:
572
+ """计算类别熵值"""
573
+ if not issues:
574
+ return 0
575
+
576
+ total_score = sum(issue.score for issue in issues)
577
+ # 归一化到 0-100
578
+ return min(100, total_score)
579
+
580
+ def _iter_code_files(self):
581
+ """迭代代码文件"""
582
+ for file_path in self.project_path.rglob('*'):
583
+ if file_path.is_file() and file_path.suffix in {
584
+ '.ts', '.js', '.tsx', '.jsx', '.py', '.java', '.go', '.rs'
585
+ }:
586
+ # 排除目录
587
+ if any(part in self.exclude_dirs for part in file_path.parts):
588
+ continue
589
+ yield file_path
590
+
591
+ def _iter_all_files(self):
592
+ """迭代所有文件"""
593
+ for file_path in self.project_path.rglob('*'):
594
+ if file_path.is_file():
595
+ if any(part in self.exclude_dirs for part in file_path.parts):
596
+ continue
597
+ yield file_path
598
+
599
+
600
+ def generate_report(report: EntropyReport, output_format: str = 'markdown') -> str:
601
+ """生成报告"""
602
+ if output_format == 'json':
603
+ return json.dumps({
604
+ 'project_path': report.project_path,
605
+ 'scan_time': report.scan_time,
606
+ 'entropy': {
607
+ 'doc': report.doc_entropy,
608
+ 'arch': report.arch_entropy,
609
+ 'code': report.code_entropy,
610
+ 'debt': report.debt_entropy,
611
+ 'total': report.total_entropy
612
+ },
613
+ 'status': report.get_status(),
614
+ 'issues': [
615
+ {
616
+ 'category': i.category,
617
+ 'severity': i.severity,
618
+ 'title': i.title,
619
+ 'location': i.location,
620
+ 'description': i.description,
621
+ 'suggestion': i.suggestion
622
+ }
623
+ for i in report.issues
624
+ ]
625
+ }, indent=2, ensure_ascii=False)
626
+
627
+ # Markdown 格式
628
+ md = f"""# PDD 熵减报告
629
+
630
+ ## 报告元信息
631
+
632
+ - **项目路径**: {report.project_path}
633
+ - **扫描时间**: {report.scan_time}
634
+ - **报告ID**: ER-{datetime.now().strftime('%Y%m%d-%H%M%S')}
635
+
636
+ ## 熵值评分总览
637
+
638
+ | 维度 | 熵值 | 状态 |
639
+ |------|------|------|
640
+ | 文档一致性 | {report.doc_entropy} | {'🟢' if report.doc_entropy <= 30 else '🟡' if report.doc_entropy <= 60 else '🔴'} |
641
+ | 架构合规性 | {report.arch_entropy} | {'🟢' if report.arch_entropy <= 30 else '🟡' if report.arch_entropy <= 60 else '🔴'} |
642
+ | 代码质量 | {report.code_entropy} | {'🟢' if report.code_entropy <= 30 else '🟡' if report.code_entropy <= 60 else '🔴'} |
643
+ | 技术债务 | {report.debt_entropy} | {'🟢' if report.debt_entropy <= 30 else '🟡' if report.debt_entropy <= 60 else '🔴'} |
644
+ | **综合评分** | **{report.total_entropy}** | **{report.get_status()}** |
645
+
646
+ ## 问题清单
647
+
648
+ """
649
+
650
+ # 按严重程度分组
651
+ by_severity = defaultdict(list)
652
+ for issue in report.issues:
653
+ by_severity[issue.severity].append(issue)
654
+
655
+ for severity in ['critical', 'high', 'medium', 'low']:
656
+ if by_severity[severity]:
657
+ md += f"### {severity.upper()} ({len(by_severity[severity])})\n\n"
658
+ for issue in by_severity[severity]:
659
+ md += f"- **{issue.title}**\n"
660
+ md += f" - 位置: `{issue.location}`\n"
661
+ md += f" - 描述: {issue.description}\n"
662
+ md += f" - 建议: {issue.suggestion}\n\n"
663
+
664
+ md += f"""
665
+ ## 改进建议
666
+
667
+ ### 立即处理 (Critical)
668
+
669
+ """
670
+
671
+ critical_issues = [i for i in report.issues if i.severity == 'critical']
672
+ if critical_issues:
673
+ for i, issue in enumerate(critical_issues, 1):
674
+ md += f"{i}. [{issue.category}] {issue.title}\n"
675
+ md += f" - 位置: {issue.location}\n"
676
+ md += f" - 建议: {issue.suggestion}\n\n"
677
+ else:
678
+ md += "无 Critical 级别问题。\n\n"
679
+
680
+ md += """
681
+ ---
682
+
683
+ > 本报告由 PDD 熵减机制自动生成
684
+ """
685
+
686
+ return md
687
+
688
+
689
+ def main():
690
+ parser = argparse.ArgumentParser(description='PDD 熵减检测脚本')
691
+ parser.add_argument('--project', '-p', required=True, help='项目路径')
692
+ parser.add_argument('--output', '-o', default='entropy-report.md', help='输出文件')
693
+ parser.add_argument('--format', '-f', choices=['markdown', 'json'], default='markdown', help='输出格式')
694
+
695
+ args = parser.parse_args()
696
+
697
+ print(f"正在扫描项目: {args.project}")
698
+
699
+ scanner = EntropyScanner(args.project)
700
+ report = scanner.scan()
701
+
702
+ output = generate_report(report, args.format)
703
+
704
+ with open(args.output, 'w', encoding='utf-8') as f:
705
+ f.write(output)
706
+
707
+ print(f"报告已生成: {args.output}")
708
+ print(f"综合熵值: {report.total_entropy} ({report.get_status()})")
709
+
710
+
711
+ if __name__ == '__main__':
712
+ main()