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
package/config/pmd.xml ADDED
@@ -0,0 +1,91 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ruleset name="PDD-PMD-Rules" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
5
+
6
+ <description>PDD-Skills PMD规则集 - Java代码质量检查</description>
7
+
8
+ <rule ref="category/java/bestpractices.xml">
9
+ <exclude name="GuardLogStatement"/>
10
+ <exclude name="GuardLogStatementJavaUtil"/>
11
+ </rule>
12
+
13
+ <rule ref="category/java/codestyle.xml">
14
+ <exclude name="CommentRequired"/>
15
+ <exclude name="ConfusingTernary"/>
16
+ <exclude name="OnlyOneReturn"/>
17
+ <exclude name="ShortClassName"/>
18
+ <exclude name="ShortVariable"/>
19
+ </rule>
20
+
21
+ <rule ref="category/java/design.xml">
22
+ <exclude name="CouplingBetweenObjects"/>
23
+ <exclude name="DataClass"/>
24
+ <exclude name="LawOfDemeter"/>
25
+ <exclude name="LoosePackageCoupling"/>
26
+ </rule>
27
+
28
+ <rule ref="category/java/errorprone.xml"/>
29
+
30
+ <rule ref="category/java/multithreading.xml">
31
+ <exclude name="DoNotUseThreads"/>
32
+ <exclude name="UseConcurrentHashMap"/>
33
+ </rule>
34
+
35
+ <rule ref="category/java/performance.xml">
36
+ <exclude name="AvoidInstantiatingObjectsInLoops"/>
37
+ </rule>
38
+
39
+ <rule ref="category/java/security.xml"/>
40
+
41
+ <rule ref="category/java/codestyle.xml/LongVariable">
42
+ <properties>
43
+ <property name="minimum" value="40"/>
44
+ </properties>
45
+ </rule>
46
+
47
+ <rule ref="category/java/design.xml/CyclomaticComplexity">
48
+ <properties>
49
+ <property name="classReportLevel" value="80"/>
50
+ <property name="methodReportLevel" value="15"/>
51
+ <property name="cycloOptions" value="{statementSwitchAsDecision:false}"/>
52
+ </properties>
53
+ </rule>
54
+
55
+ <rule ref="category/java/design.xml/NcssTypeCount">
56
+ <properties>
57
+ <property name="maximum" value="800"/>
58
+ </properties>
59
+ </rule>
60
+
61
+ <rule ref="category/java/design.xml/NcssMethodCount">
62
+ <properties>
63
+ <property name="maximum" value="60"/>
64
+ </properties>
65
+ </rule>
66
+
67
+ <rule ref="category/java/design.xml/NcssConstructorCount">
68
+ <properties>
69
+ <property name="maximum" value="10"/>
70
+ </properties>
71
+ </rule>
72
+
73
+ <rule ref="category/java/design.xml/TooManyFields">
74
+ <properties>
75
+ <property name="maxfields" value="15"/>
76
+ </properties>
77
+ </rule>
78
+
79
+ <rule ref="category/java/design.xml/TooManyMethods">
80
+ <properties>
81
+ <property name="maxmethods" value="30"/>
82
+ </properties>
83
+ </rule>
84
+
85
+ <rule ref="category/java/codestyle.xml/MethodNamingConventions">
86
+ <properties>
87
+ <property name="methodPattern" value="^[a-z][a-zA-Z0-9_]*$"/>
88
+ </properties>
89
+ </rule>
90
+
91
+ </ruleset>
@@ -0,0 +1,113 @@
1
+ # PRD Linter 规则配置
2
+ #
3
+ # 用于检查PRD文档的完整性和规范性
4
+
5
+ rules:
6
+ structure:
7
+ enabled: true
8
+ checks:
9
+ - id: prd-has-title
10
+ name: "PRD必须有标题"
11
+ pattern: "^#+\\s+.+"
12
+ severity: error
13
+ message: "PRD文档缺少标题(一级标题)"
14
+
15
+ - id: prd-has-version
16
+ name: "PRD必须包含版本信息"
17
+ pattern: "(?i)(version|版本|v\\d+\\.\\d+)"
18
+ severity: error
19
+ message: "PRD文档缺少版本号标识"
20
+
21
+ - id: prd-has-background
22
+ name: "PRD必须包含背景说明"
23
+ pattern: "(?i)(背景|background|项目背景|业务背景)"
24
+ severity: error
25
+ message: "PRD文档缺少背景说明章节"
26
+
27
+ - id: prd-has-goals
28
+ name: "PRD必须包含目标"
29
+ pattern: "(?i)(目标|goals?| objectives?)"
30
+ severity: error
31
+ message: "PRD文档缺少目标定义章节"
32
+
33
+ - id: prd-has-scope
34
+ name: "PRD必须包含范围定义"
35
+ pattern: "(?i)(范围|scope| in scope| out of scope|边界)"
36
+ severity: warn
37
+ message: "建议添加范围/边界定义章节"
38
+
39
+ - id: prd-has-user-stories
40
+ name: "PRD应包含用户故事"
41
+ pattern: "(?i)(用户故事|user story|功能需求|需求列表)"
42
+ severity: warn
43
+ message: "建议包含用户故事或需求列表"
44
+
45
+ - id: prd-has-acceptance-criteria
46
+ name: "PRD应包含验收标准"
47
+ pattern: "(?i)(验收标准|acceptance criteria|ac)"
48
+ severity: warn
49
+ message: "建议为每个需求添加验收标准"
50
+
51
+ content:
52
+ enabled: true
53
+ checks:
54
+ - id: prd-no-vague-language
55
+ name: "避免模糊语言"
56
+ pattern: "(大概|可能|也许|适当|合理|优化|改善|提升)"
57
+ severity: warn
58
+ message: "检测到模糊语言,建议使用具体可量化的描述"
59
+ exclude_patterns:
60
+ - "作为.*参考"
61
+ - "示例"
62
+
63
+ - id: prd-has-metrics
64
+ name: "关键指标应量化"
65
+ pattern: "(?i)(指标|kpi|性能|响应时间|并发|吞吐量)"
66
+ severity: info
67
+ message: "涉及指标的描述应尽量量化(如:<100ms, >99.9%)"
68
+
69
+ - id: prd-smart-goals
70
+ name: "目标应符合SMART原则"
71
+ pattern: "(?i)(目标|goal).*:.*"
72
+ severity: info
73
+ message: "目标描述应符合SMART原则(具体、可衡量、可实现、相关、有时限)"
74
+
75
+ format:
76
+ enabled: true
77
+ checks:
78
+ - id: prd-max-section-depth
79
+ name: "标题层级不超过5级"
80
+ max_depth: 5
81
+ severity: warn
82
+ message: "文档层级过深,建议控制在5级以内"
83
+
84
+ - id: prd-table-format
85
+ name: "表格格式正确性"
86
+ check_table_borders: true
87
+ severity: info
88
+ message: "表格格式不规范"
89
+
90
+ - id: prd-code-blocks
91
+ name: "代码块应有语言标注"
92
+ pattern: "```(?!\\w+)"
93
+ severity: info
94
+ message: "代码块缺少语言标注,建议使用 ```语言名 格式"
95
+
96
+ consistency:
97
+ enabled: true
98
+ checks:
99
+ - id: prd-consistent-terminology
100
+ name: "术语一致性"
101
+ severity: warn
102
+ message: "同一概念使用了不同的术语表达"
103
+
104
+ - id: prd-id-format
105
+ name: "需求ID格式统一"
106
+ pattern: "[A-Z]+-\\d{3,}"
107
+ severity: info
108
+ message: "需求ID建议采用统一的编号格式(如:REQ-001)"
109
+
110
+ - id: prd-reference-exists
111
+ name: "引用完整性"
112
+ severity: error
113
+ message: "文档中引用了不存在的章节或外部链接失效"
@@ -0,0 +1,45 @@
1
+ [tool.ruff]
2
+ target-version = "py39"
3
+ line-length = 120
4
+
5
+ [tool.ruff.lint]
6
+ select = [
7
+ "E", # pycodestyle errors
8
+ "W", # pycodestyle warnings
9
+ "F", # Pyflakes
10
+ "I", # isort
11
+ "N", # pep8-naming
12
+ "UP", # pyupgrade
13
+ "B", # flake8-bugbear
14
+ "SIM", # flake8-simplify
15
+ "C90", # mccabe complexity
16
+ "A", # flake8-builtins
17
+ "COM", # flake8-commas
18
+ "T20", # flake8-print
19
+ "ARG", # flake8-unused-arguments
20
+ "RUF", # Ruff-specific rules
21
+ ]
22
+ ignore = [
23
+ "E501", # line too long (handled by formatter)
24
+ "B008", # do not perform function calls in argument defaults
25
+ "N802", # function name should be lowercase
26
+ "N806", # variable in function should be lowercase
27
+ "ARG001", # unused-function-argument
28
+ "COM812", # trailing comma missing (conflicts with formatter)
29
+ ]
30
+
31
+ [tool.ruff.lint.per-file-ignores]
32
+ "tests/*" = ["T20", "ARG"]
33
+ "__init__.py" = ["F401"]
34
+ "*_test.py" = ["T20", "ARG"]
35
+
36
+ [tool.ruff.lint.mccabe]
37
+ max-complexity = 12
38
+
39
+ [tool.ruff.lint.pycodestyle]
40
+ max-doc-length = 100
41
+
42
+ [tool.ruff.format]
43
+ quote-style = "double"
44
+ indent-style = "space"
45
+ docstring-code-format = true
@@ -0,0 +1,82 @@
1
+ [sqlfluff]
2
+ dialect = ansi
3
+ templater = pure
4
+ exclude_rules = L031,L042
5
+
6
+ [sqlfluff:indentation]
7
+ indented_joins = false
8
+ tab_space_size = 2
9
+ allow_implicit_indents = True
10
+
11
+ [sqlfluff:layout]
12
+ type = jinja
13
+ max_line_length = 120
14
+
15
+ [sqlfluff:rules]
16
+ tab_space_size = 2
17
+ indent_unit = space
18
+ comma_style = trailing
19
+ allow_scalar = True
20
+ single_table_references = consistent
21
+ unquoted_identifiers_policy = aliases
22
+
23
+ [sqlfluff:rules:L010] # Keywords
24
+ capitalisation_policy = upper
25
+
26
+ [sqlfluff:rules:L014] # Unquoted identifiers
27
+ extended_capitalisation_policy = lower
28
+
29
+ [sqlfluff:rules:L030] # Function names
30
+ capitalisation_policy = upper
31
+
32
+ [sqlfluff:rules:L040] # Null & Boolean Literals
33
+ capitalisation_policy = upper
34
+
35
+ [sqlfluff:rules:L063] # Consistent data types
36
+ extended_capitalisation_policy = upper
37
+
38
+ [sqlfluff:rules:L057] # Special characters
39
+ allow_space_in_identifier = False
40
+ additional_allowed_characters = ""
41
+
42
+ [sqlfluff:rules:L016] # Line length
43
+ max_line_length = 120
44
+ indent_unit = space
45
+ tab_space_size = 2
46
+
47
+ [sqlfluff:rules:L019] # Comma presence
48
+ comma_style = trailing
49
+
50
+ [sqlfluff:rules:L029] # Keyword identifiers
51
+ unquoted_identifying_policy = aliases
52
+
53
+ [sqlfluff:rules:L044] # Query folding
54
+ collapse_keyword_suffixes = True
55
+
56
+ [sqlfluff:rules:L052] # Lines after semicolons
57
+ multiline_newline = True
58
+ require_final_semicolon = False
59
+
60
+ [sqlfluff:rules:L061] # Trailing commas - include in CASE statements
61
+ include_case_statements = True
62
+
63
+ [sqlfluff:rules:L062] # COUNT(*)
64
+ prefer_count_0 = True
65
+ prefer_count_nonnull = False
66
+
67
+ [sqlfluff:rules:L050] # Built-in functions
68
+ fully_qualify_all_types = True
69
+ allow_double_qualified_references = True
70
+ capitalisation_policy = upper
71
+
72
+ [sqlfluff:rules:L003] # Indentation
73
+ indent_unit = space
74
+ tab_space_size = 2
75
+
76
+ [sqlfluff:rules:L007] # Operators near newlines
77
+ operator_new_lines = before
78
+ align_within = off
79
+ align_scope = column_def
80
+
81
+ [sqlfluff:rules:L009] # Files end with newlines
82
+ files_end_with_newline = True
@@ -0,0 +1,332 @@
1
+ import chalk from 'chalk';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import { execSync } from 'child_process';
5
+ import { log } from '../lib/utils/logger.js';
6
+
7
+ const HOOK_EVENTS = {
8
+ session_start: {
9
+ description: '会话启动时触发',
10
+ phase: 'pre'
11
+ },
12
+ pre_feature: {
13
+ description: '功能开发前触发',
14
+ phase: 'pre'
15
+ },
16
+ post_feature: {
17
+ description: '功能开发后触发',
18
+ phase: 'post'
19
+ },
20
+ pre_commit: {
21
+ description: '提交代码前触发',
22
+ phase: 'pre'
23
+ },
24
+ post_commit: {
25
+ description: '提交代码后触发',
26
+ phase: 'post'
27
+ },
28
+ linter_fail: {
29
+ description: 'Linter检查失败时触发',
30
+ phase: 'post'
31
+ }
32
+ };
33
+
34
+ export class HookExecutor {
35
+ constructor(projectRoot) {
36
+ this.projectRoot = projectRoot;
37
+ this.hooksConfig = null;
38
+ this.hooksDir = path.join(projectRoot, '.pdd', 'hooks.d');
39
+ }
40
+
41
+ async load() {
42
+ const configPath = path.join(this.projectRoot, '.pdd', 'hooks.yaml');
43
+
44
+ if (fs.existsSync(configPath)) {
45
+ try {
46
+ const content = fs.readFileSync(configPath, 'utf-8');
47
+ const yaml = await import('yaml');
48
+ this.hooksConfig = yaml.parse(content);
49
+ log('info', 'Hook配置加载完成');
50
+ } catch (e) {
51
+ log('warn', `Hook配置解析失败: ${e.message}`);
52
+ this.hooksConfig = {};
53
+ }
54
+ } else {
55
+ this.hooksConfig = {};
56
+ }
57
+ }
58
+
59
+ async trigger(eventName, context = {}) {
60
+ if (!this.hooksConfig) await this.load();
61
+
62
+ const eventConfig = this.hooksConfig[eventName];
63
+ if (!eventConfig || eventConfig.enabled === false) {
64
+ return { skipped: true, reason: '事件未启用或未配置' };
65
+ }
66
+
67
+ log('info', `触发Hook事件: ${eventName}`);
68
+ console.log(chalk.gray(` 🪝 ${HOOK_EVENTS[eventName]?.description || eventName}`));
69
+
70
+ const results = [];
71
+
72
+ if (eventConfig.checks) {
73
+ for (const check of eventConfig.checks) {
74
+ const result = await this.executeCheck(check, eventName, context);
75
+ results.push(result);
76
+
77
+ if (!result.passed && eventConfig.fail_fast !== false) {
78
+ break;
79
+ }
80
+ }
81
+ }
82
+
83
+ if (eventConfig.actions) {
84
+ for (const action of eventConfig.actions) {
85
+ const result = await this.executeAction(action, eventName, context);
86
+ results.push(result);
87
+ }
88
+ }
89
+
90
+ const customHooks = await this.loadCustomHooks(eventName);
91
+ for (const hook of customHooks) {
92
+ const result = await this.executeCustomHook(hook, context);
93
+ results.push(result);
94
+ }
95
+
96
+ const allPassed = results.every(r => r.passed !== false);
97
+ return { success: allPassed, results, eventName };
98
+ }
99
+
100
+ async executeCheck(checkName, eventName, context) {
101
+ const checkHandlers = {
102
+ spec_exists: () => this.checkSpecExists(context),
103
+ dependencies_met: () => this.checkDependenciesMet(context),
104
+ linter_pass: () => this.checkLinterPass(context),
105
+ tests_pass: () => this.checkTestsPass(context)
106
+ };
107
+
108
+ const handler = checkHandlers[checkName];
109
+ if (!handler) {
110
+ return { check: checkName, passed: true, skipped: true, message: `未知检查项: ${checkName}` };
111
+ }
112
+
113
+ try {
114
+ const result = await handler();
115
+ log(result.passed ? 'info' : 'warn', ` Check [${checkName}]: ${result.message}`);
116
+ return { check: checkName, ...result };
117
+ } catch (e) {
118
+ return { check: checkName, passed: false, error: e.message };
119
+ }
120
+ }
121
+
122
+ async executeAction(actionName, eventName, context) {
123
+ const actionHandlers = {
124
+ run_linter: () => this.actionRunLinter(context),
125
+ run_tests: () => this.actionRunTests(context),
126
+ generate_report: () => this.actionGenerateReport(context),
127
+ cache_clear: () => this.actionCacheClear(context)
128
+ };
129
+
130
+ const handler = actionHandlers[actionName];
131
+ if (!handler) {
132
+ return { action: actionName, passed: true, skipped: true, message: `未知动作: ${actionName}` };
133
+ }
134
+
135
+ try {
136
+ const result = await handler();
137
+ log('info', ` Action [${actionName}]: ${result.message}`);
138
+ return { action: actionName, ...result };
139
+ } catch (e) {
140
+ return { action: actionName, passed: false, error: e.message };
141
+ }
142
+ }
143
+
144
+ async loadCustomHooks(eventName) {
145
+ const hooks = [];
146
+
147
+ if (!fs.existsSync(this.hooksDir)) return hooks;
148
+
149
+ try {
150
+ const files = fs.readdirSync(this.hooksDir)
151
+ .filter(f => f.startsWith(`${eventName}.`) || f.startsWith(`${eventName}_`));
152
+
153
+ for (const file of files) {
154
+ const hookPath = path.join(this.hooksDir, file);
155
+ const ext = path.extname(file);
156
+
157
+ if (ext === '.js') {
158
+ hooks.push({ type: 'script', path: hookPath, language: 'javascript' });
159
+ } else if (ext === '.py') {
160
+ hooks.push({ type: 'script', path: hookPath, language: 'python' });
161
+ } else if (ext === '.sh' || ext === '.cmd') {
162
+ hooks.push({ type: 'command', path: hookPath });
163
+ }
164
+ }
165
+ } catch {}
166
+
167
+ return hooks;
168
+ }
169
+
170
+ async executeCustomHook(hook, context) {
171
+ try {
172
+ let output = '';
173
+
174
+ if (hook.type === 'script' && hook.language === 'javascript') {
175
+ output = execSync(`node "${hook.path}"`, {
176
+ cwd: this.projectRoot,
177
+ encoding: 'utf-8',
178
+ timeout: 30000,
179
+ env: { ...process.env, ...this.buildEnvContext(context) }
180
+ });
181
+ } else if (hook.type === 'script' && hook.language === 'python') {
182
+ output = execSync(`python "${hook.path}"`, {
183
+ cwd: this.projectRoot,
184
+ encoding: 'utf-8',
185
+ timeout: 30000,
186
+ env: { ...process.env, ...this.buildEnvContext(context) }
187
+ });
188
+ } else if (hook.type === 'command') {
189
+ output = execSync(`"${hook.path}"`, {
190
+ cwd: this.projectRoot,
191
+ encoding: 'utf-8',
192
+ timeout: 30000,
193
+ env: { ...process.env, ...this.buildEnvContext(context) }
194
+ });
195
+ }
196
+
197
+ return {
198
+ hook: path.basename(hook.path),
199
+ passed: true,
200
+ output: output.trim()
201
+ };
202
+ } catch (e) {
203
+ return {
204
+ hook: path.basename(hook.path),
205
+ passed: false,
206
+ error: e.message,
207
+ output: e.stdout?.trim() || ''
208
+ };
209
+ }
210
+ }
211
+
212
+ buildEnvContext(context) {
213
+ return {
214
+ PDD_EVENT: context.event || '',
215
+ PDD_FEATURE_ID: context.featureId || '',
216
+ PDD_PROJECT_DIR: this.projectRoot || '',
217
+ PDD_SPEC_DIR: path.join(this.projectRoot, 'specs') || ''
218
+ };
219
+ }
220
+
221
+ async checkSpecExists(context) {
222
+ const specDir = path.join(this.projectRoot, 'specs');
223
+ const hasSpecs = fs.existsSync(specDir) && fs.readdirSync(specDir).some(
224
+ f => f.endsWith('.md') || f.endsWith('.yaml')
225
+ );
226
+
227
+ return {
228
+ passed: hasSpecs,
229
+ message: hasSpecs ? '规格文件已存在' : '⚠ 未找到规格文件'
230
+ };
231
+ }
232
+
233
+ async checkDependenciesMet(context) {
234
+ const pkgPath = path.join(this.projectRoot, 'package.json');
235
+ if (!fs.existsSync(pkgPath)) {
236
+ return { passed: true, message: '非Node.js项目,跳过依赖检查' };
237
+ }
238
+
239
+ try {
240
+ execSync('npm ls --depth=0', { cwd: this.projectRoot, encoding: 'utf-8', timeout: 30000 });
241
+ return { passed: true, message: '依赖检查通过' };
242
+ } catch (e) {
243
+ return { passed: false, message: '依赖安装不完整' };
244
+ }
245
+ }
246
+
247
+ async checkLinterPass(context) {
248
+ try {
249
+ const { default: runLinters } = await import('../scripts/linter/run-linters.js');
250
+ const results = await runLinters({ type: ['code'], incremental: true });
251
+ const allPassed = results.every(r => r.success);
252
+ return {
253
+ passed: allPassed,
254
+ message: allPassed ? 'Linter检查通过' : 'Linter检查发现问题'
255
+ };
256
+ } catch (e) {
257
+ return { passed: false, message: `Linter执行异常: ${e.message}` };
258
+ }
259
+ }
260
+
261
+ async checkTestsPass(context) {
262
+ const testCommands = [
263
+ { cmd: 'npm test', pkg: 'package.json' },
264
+ { cmd: 'pytest', file: 'pytest.ini' },
265
+ { cmd: 'mvn test', file: 'pom.xml' }
266
+ ];
267
+
268
+ for (const tc of testCommands) {
269
+ if (tc.pkg && fs.existsSync(path.join(this.projectRoot, tc.pkg))) {
270
+ try {
271
+ execSync(tc.cmd, { cwd: this.projectRoot, encoding: 'utf-8', timeout: 120000 });
272
+ return { passed: true, message: '测试通过' };
273
+ } catch (e) {
274
+ return { passed: false, message: '测试未通过' };
275
+ }
276
+ }
277
+ if (tc.file && fs.existsSync(path.join(this.projectRoot, tc.file))) {
278
+ try {
279
+ execSync(tc.cmd, { cwd: this.projectRoot, encoding: 'utf-8', timeout: 120000 });
280
+ return { passed: true, message: '测试通过' };
281
+ } catch (e) {
282
+ return { passed: false, message: '测试未通过' };
283
+ }
284
+ }
285
+ }
286
+
287
+ return { passed: true, skipped: true, message: '未找到测试配置,跳过' };
288
+ }
289
+
290
+ async actionRunLinter(context) {
291
+ try {
292
+ const { default: runLinters } = await import('../scripts/linter/run-linters.js');
293
+ await runLinters({ type: ['code'] });
294
+ return { passed: true, message: 'Linter执行完成' };
295
+ } catch (e) {
296
+ return { passed: false, message: e.message };
297
+ }
298
+ }
299
+
300
+ async actionRunTests(context) {
301
+ const testResult = await this.checkTestsPass(context);
302
+ return { passed: testResult.passed, message: testResult.message };
303
+ }
304
+
305
+ async actionGenerateReport(context) {
306
+ const reportDir = path.join(this.projectRoot, '.pdd', 'cache', 'reports');
307
+ await fs.ensureDir(reportDir);
308
+
309
+ const reportPath = path.join(reportDir, `hook-report-${Date.now()}.md`);
310
+ const content = [
311
+ '# Hook 执行报告',
312
+ '',
313
+ `- 时间: ${new Date().toISOString()}`,
314
+ `- 项目: ${path.basename(this.projectRoot)}`,
315
+ ''
316
+ ].join('\n');
317
+
318
+ await fs.writeFile(reportPath, content, 'utf-8');
319
+ return { passed: true, message: `报告生成: ${reportPath}` };
320
+ }
321
+
322
+ async actionCacheClear(context) {
323
+ const cacheDir = path.join(this.projectRoot, '.pdd', 'cache');
324
+ if (fs.existsSync(cacheDir)) {
325
+ const specsCache = path.join(cacheDir, 'specs');
326
+ if (fs.existsSync(specsCache)) {
327
+ fs.emptyDirSync(specsCache);
328
+ }
329
+ }
330
+ return { passed: true, message: '缓存已清理' };
331
+ }
332
+ }