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,472 @@
1
+ /**
2
+ * Custom Linter 自定义代码检查插件
3
+ *
4
+ * 在 pre-verify 钩子阶段执行自定义规则检查:
5
+ * - 禁止使用 console.log(生产环境泄露风险)
6
+ * - 禁止使用 var 声明(应使用 const/let)
7
+ * - 禁止使用 == 比较(应使用 ===)
8
+ * - 检测函数长度是否超过阈值
9
+ * - 检测文件行数是否超过阈值
10
+ *
11
+ * 同时注册自定义格式化器,支持多种输出格式。
12
+ *
13
+ * @module custom-linter
14
+ * @author PDD Team
15
+ * @version 1.0.0
16
+ */
17
+
18
+ import { PluginBase } from '../plugin-sdk.js';
19
+
20
+ // ==================== 规则严重级别 ====================
21
+
22
+ /** @enum {string} 规则违反的严重级别 */
23
+ const Severity = {
24
+ ERROR: 'error', // 阻断性错误,必须修复
25
+ WARN: 'warn', // 警告,建议修复
26
+ INFO: 'info', // 信息提示,可选修复
27
+ };
28
+
29
+ // ==================== 内置规则定义 ====================
30
+
31
+ /**
32
+ * 预定义的 Lint 规则集合
33
+ * 每条规则包含:id, name, description, severity, check 函数
34
+ */
35
+ const BUILTIN_RULES = {
36
+ 'no-console-log': {
37
+ id: 'no-console-log',
38
+ name: '禁止 console.log',
39
+ description: '检测并阻止 console.log 调用,避免敏感信息泄露到生产环境',
40
+ severity: Severity.WARN,
41
+ /** @param {string} content */
42
+ check(content) {
43
+ const issues = [];
44
+ const lines = content.split('\n');
45
+ // 匹配 console.log / console.warn / console.error (排除注释中的)
46
+ const regex = /^\s*(?!.*\/\/.*)console\.(log|warn|info|debug)\(/gm;
47
+ let match;
48
+ while ((match = regex.exec(content)) !== null) {
49
+ const lineNum = content.substring(0, match.index).split('\n').length;
50
+ issues.push({
51
+ line: lineNum,
52
+ column: match[0].indexOf('console') + 1,
53
+ message: `不应使用 ${match[1]} 输出到控制台`,
54
+ ruleId: this.id,
55
+ severity: this.severity,
56
+ });
57
+ }
58
+ return issues;
59
+ },
60
+ },
61
+
62
+ 'no-var-declaration': {
63
+ id: 'no-var-declaration',
64
+ name: '禁止 var 声明',
65
+ description: '使用 var 声明变量存在作用域问题,应使用 const 或 let 替代',
66
+ severity: Severity.ERROR,
67
+ /** @param {string} content */
68
+ check(content) {
69
+ const issues = [];
70
+ const lines = content.split('\n');
71
+ // 匹配 var 声明(排除注释和字符串)
72
+ const regex = /^(?!\s*\/\/).*\bvar\s+\w+\s*[=;]/gm;
73
+ let match;
74
+ while ((match = regex.exec(content)) !== null) {
75
+ const lineNum = content.substring(0, match.index).split('\n').length;
76
+ issues.push({
77
+ line: lineNum,
78
+ column: match[0].indexOf('var') + 1,
79
+ message: '不应使用 var,请改用 const 或 let',
80
+ ruleId: this.id,
81
+ severity: this.severity,
82
+ });
83
+ }
84
+ return issues;
85
+ },
86
+ },
87
+
88
+ 'no-equals-equals': {
89
+ id: 'no-equals-equals',
90
+ name: '禁止 == 比较',
91
+ description: '== 存在类型隐式转换问题,应使用 === 进行严格比较',
92
+ severity: Severity.ERROR,
93
+ /** @param {string} content */
94
+ check(content) {
95
+ const issues = [];
96
+ const lines = content.split('\n');
97
+ // 排除 !== 和 ===
98
+ const regex = /(?<!=)=(?!=)/g;
99
+ let match;
100
+ while ((match = regex.exec(content)) !== null) {
101
+ // 确保不是 !== 或 === 的一部分
102
+ const before = content[match.index - 1] || '';
103
+ const after = content[match.index + 2] || '';
104
+ if (before !== '=' && after !== '=') {
105
+ const lineNum = content.substring(0, match.index).split('\n').length;
106
+ issues.push({
107
+ line: lineNum,
108
+ column: match.index - content.lastIndexOf('\n', match.index),
109
+ message: '应使用 === 代替 == 进行严格比较',
110
+ ruleId: this.id,
111
+ severity: this.severity,
112
+ });
113
+ }
114
+ }
115
+ return issues;
116
+ },
117
+ },
118
+
119
+ 'max-lines-per-function': {
120
+ id: 'max-lines-per-function',
121
+ name: '函数最大行数限制',
122
+ description: '单个函数过长会增加理解和维护难度,建议拆分为更小的函数',
123
+ severity: Severity.WARN,
124
+ options: { maxLines: 50 },
125
+ /** @param {string} content */
126
+ check(content) {
127
+ const issues = [];
128
+ const maxLines = this.options?.maxLines || 50;
129
+
130
+ // 简单检测:通过花括号匹配估算函数体长度
131
+ const funcRegex = /(function\s+\w+|=>\s*\{|\w+\s*\([^)]*\)\s*\{)/g;
132
+ let match;
133
+ while ((match = funcRegex.exec(content)) !== null) {
134
+ const startLine = content.substring(0, match.index).split('\n').length;
135
+
136
+ // 找匹配的闭合括号
137
+ let braceCount = 0;
138
+ let endPos = match.index;
139
+ let inFunc = false;
140
+
141
+ for (let i = match.index; i < content.length; i++) {
142
+ if (content[i] === '{') {
143
+ braceCount++;
144
+ inFunc = true;
145
+ } else if (content[i] === '}') {
146
+ braceCount--;
147
+ if (inFunc && braceCount === 0) {
148
+ endPos = i;
149
+ break;
150
+ }
151
+ }
152
+ }
153
+
154
+ const endLine = content.substring(0, endPos).split('\n').length;
155
+ const funcLength = endLine - startLine + 1;
156
+
157
+ if (funcLength > maxLines) {
158
+ issues.push({
159
+ line: startLine,
160
+ column: 1,
161
+ message: `函数长度为 ${funcLength} 行,超过限制 (${maxLines} 行),建议拆分`,
162
+ ruleId: this.id,
163
+ severity: this.severity,
164
+ meta: { actualLength: funcLength, maxLength: maxLines },
165
+ });
166
+ }
167
+ }
168
+ return issues;
169
+ },
170
+ },
171
+
172
+ 'max-file-length': {
173
+ id: 'max-file-length',
174
+ name: '文件最大行数限制',
175
+ description: '单个文件过长时建议拆分为多个模块',
176
+ severity: Severity.INFO,
177
+ options: { maxLines: 300 },
178
+ /** @param {string} content */
179
+ check(content) {
180
+ const issues = [];
181
+ const lines = content.split('\n');
182
+ const maxLines = this.options?.maxLines || 300;
183
+
184
+ if (lines.length > maxLines) {
185
+ issues.push({
186
+ line: 1,
187
+ column: 1,
188
+ message: `文件总行数为 ${lines.length} 行,超过建议值 (${maxLines} 行),考虑模块化拆分`,
189
+ ruleId: this.id,
190
+ severity: this.severity,
191
+ meta: { actualLength: lines.length, maxLength: maxLines },
192
+ });
193
+ }
194
+ return issues;
195
+ },
196
+ },
197
+ };
198
+
199
+ /**
200
+ * Custom Linter 插件类
201
+ *
202
+ * @class CustomLinterPlugin
203
+ * @extends PluginBase
204
+ */
205
+ export default class CustomLinterPlugin extends PluginBase {
206
+ constructor() {
207
+ super({
208
+ name: 'custom-linter',
209
+ version: '1.0.0',
210
+ description: '自定义 Linter 规则扩展 - 在 pre-verify 阶段执行自定义代码检查',
211
+ author: 'PDD Team',
212
+ license: 'MIT',
213
+ keywords: ['linter', 'lint', 'quality', 'rules'],
214
+ pddVersionRange: '>=1.0.0',
215
+ });
216
+
217
+ /** @type {Map<string, Object>} 已加载的规则集 */
218
+ this._rules = new Map();
219
+
220
+ /** @type {Object} 配置选项 */
221
+ this._options = {
222
+ maxLinesPerFunction: 50,
223
+ maxFileLength: 300,
224
+ enabledRules: ['no-console-log', 'no-var-declaration', 'no-equals-equals'],
225
+ };
226
+ }
227
+
228
+ /**
229
+ * 插件激活时调用
230
+ * 注册钩子和格式化器
231
+ *
232
+ * @param {import('../plugin-sdk.js').PluginContext} context - 插件上下文
233
+ */
234
+ async onActivate(context) {
235
+ context.logger.info('Custom Linter 插件正在激活...');
236
+
237
+ // 加载内置规则
238
+ for (const [ruleId, rule] of Object.entries(BUILTIN_RULES)) {
239
+ this._rules.set(ruleId, rule);
240
+ }
241
+
242
+ // 从配置中读取选项
243
+ const configMaxFunc = context.getConfig('maxLinesPerFunction');
244
+ if (configMaxFunc) this._options.maxLinesPerFunction = configMaxFunc;
245
+
246
+ const configFileLen = context.getConfig('maxFileLength');
247
+ if (configFileLen) this._options.maxFileLength = configFileLen;
248
+
249
+ const configRules = context.getConfig('enabledRules');
250
+ if (Array.isArray(configRules)) this._options.enabledRules = configRules;
251
+
252
+ // 注册 pre-verify 钩子:在验证前执行 Lint 检查
253
+ this.registerHook('pre-verify', async (input) => {
254
+ context.logger.info('[custom-linter] 开始执行 pre-verify Lint 检查...');
255
+
256
+ const result = await this.lint(input.content || input.source || '');
257
+
258
+ // 如果有 error 级别的问题,返回失败状态
259
+ const hasErrors = result.issues.some(i => i.severity === Severity.ERROR);
260
+
261
+ context.logger.info(
262
+ `[custom-linter] 检查完成 - 发现 ${result.issueCounts.error} 个错误, ` +
263
+ `${result.issueCounts.warn} 个警告, ${result.issueCounts.info} 个提示`
264
+ );
265
+
266
+ return {
267
+ ...input,
268
+ lintResult: result,
269
+ passed: !hasErrors,
270
+ };
271
+ }, 10); // 高优先级,尽早运行
272
+
273
+ // 注册 table 格式化器
274
+ this.registerFormatter('table', (data) => {
275
+ if (!data.lintResult) return JSON.stringify(data, null, 2);
276
+ return this._formatAsTable(data.lintResult);
277
+ });
278
+
279
+ // 注册 json 格式化器
280
+ this.registerFormatter('json', (data) => {
281
+ return JSON.stringify(data, null, 2);
282
+ });
283
+
284
+ // 注册 markdown 格式化器
285
+ this.registerFormatter('markdown', (data) => {
286
+ if (!data.lintResult) return '```json\n' + JSON.stringify(data, null, 2) + '\n```';
287
+ return this._formatAsMarkdown(data.lintResult);
288
+ });
289
+
290
+ context.logger.info(
291
+ `Custom Linter 插件激活成功!已加载 ${this._rules.size} 条规则, ` +
292
+ `启用 ${this._options.enabledRules.length} 条`
293
+ );
294
+ }
295
+
296
+ /**
297
+ * 执行 Lint 检查
298
+ * 对输入内容应用所有启用的规则
299
+ *
300
+ * @param {string} content - 待检查的代码内容
301
+ * @returns {Object} 检查结果
302
+ */
303
+ async lint(content) {
304
+ const startTime = Date.now();
305
+ const allIssues = [];
306
+
307
+ for (const ruleId of this._options.enabledRules) {
308
+ const rule = this._rules.get(ruleId);
309
+ if (!rule) continue;
310
+
311
+ try {
312
+ const issues = rule.check(content);
313
+ allIssues.push(...issues);
314
+ } catch (err) {
315
+ allIssues.push({
316
+ line: 0,
317
+ column: 0,
318
+ message: `规则 "${ruleId}" 执行出错: ${err.message}`,
319
+ ruleId: 'system-error',
320
+ severity: Severity.ERROR,
321
+ });
322
+ }
323
+ }
324
+
325
+ // 按行号排序
326
+ allIssues.sort((a, b) => a.line - b.line || a.column - b.column);
327
+
328
+ // 统计各级别数量
329
+ const issueCounts = {
330
+ error: allIssues.filter(i => i.severity === Severity.ERROR).length,
331
+ warn: allIssues.filter(i => i.severity === Severity.WARN).length,
332
+ info: allIssues.filter(i => i.severity === Severity.INFO).length,
333
+ total: allIssues.length,
334
+ };
335
+
336
+ return {
337
+ pluginName: this.name,
338
+ version: this.version,
339
+ issues: allIssues,
340
+ issueCounts,
341
+ rulesApplied: [...this._options.enabledRules],
342
+ durationMs: Date.now() - startTime,
343
+ checkedAt: new Date().toISOString(),
344
+ };
345
+ }
346
+
347
+ /**
348
+ * 将结果格式化为 ASCII 表格
349
+ * @param {Object} result - Lint 结果
350
+ * @returns {string} 格式化的表格文本
351
+ * @private
352
+ */
353
+ _formatAsTable(result) {
354
+ const header = ' Line │ Col │ Severity │ Rule │ Message';
355
+ const separator = '──────┼─────┼──────────┼───────────────────────┼──────────────────────────';
356
+ const lines = [header, separator];
357
+
358
+ for (const issue of result.issues) {
359
+ const line = String(issue.line).padEnd(6);
360
+ const col = String(issue.column ?? '-').padStart(3);
361
+ const sev = issue.severity.padEnd(8);
362
+ const rule = (issue.ruleId || '').padEnd(21);
363
+ const msg = issue.message || '';
364
+ lines.push(` ${line}│ ${col} │ ${sev}│ ${rule}│ ${msg}`);
365
+ }
366
+
367
+ lines.push('');
368
+ lines.push(` Total: ${result.issueCounts.total} issues (` +
369
+ `${result.issueCounts.error} errors, ` +
370
+ `${result.issueCounts.warn} warnings, ` +
371
+ `${result.issueCounts.info} info)`);
372
+
373
+ return lines.join('\n');
374
+ }
375
+
376
+ /**
377
+ * 将结果格式化为 Markdown 文本
378
+ * @param {Object} result - Lint 结果
379
+ * @returns {string} Markdown 格式文本
380
+ * @private
381
+ */
382
+ _formatAsMarkdown(result) {
383
+ const lines = [
384
+ `# Lint Report - ${result.pluginName} v${result.version}`,
385
+ '',
386
+ '**Summary:**',
387
+ `- Errors: **${result.issueCounts.error}**`,
388
+ `- Warnings: **${result.issueCounts.warn}**`,
389
+ `- Info: **${result.issueCounts.info}**`,
390
+ `- Total: **${result.issueCounts.total}**`,
391
+ `- Duration: **${result.durationMs}ms**`,
392
+
393
+ '',
394
+ '| Line | Col | Severity | Rule | Message |',
395
+ '|------|-----|----------|------|---------|',
396
+ ];
397
+
398
+ for (const issue of result.issues) {
399
+ lines.push(
400
+ `| ${issue.line} | ${issue.column ?? '-'} | ${issue.severity} | ${issue.ruleId || '-'} | ${issue.message || '-'} |`
401
+ );
402
+ }
403
+
404
+ return lines.join('\n');
405
+ }
406
+
407
+ /**
408
+ * 动态添加自定义规则
409
+ * 允许用户在运行时扩展规则集
410
+ *
411
+ * @param {Object} rule - 规则定义对象
412
+ * @param {string} rule.id - 规则唯一标识
413
+ * @param {string} rule.name - 规则名称
414
+ * @param {string} rule.description - 规则描述
415
+ * @param {string} rule.severity - 默认严重级别
416
+ * @param {Function} rule.check - 检查函数 (content) => Issue[]
417
+ */
418
+ addRule(rule) {
419
+ if (!rule.id || !rule.check || typeof rule.check !== 'function') {
420
+ throw new Error('无效的规则定义:必须包含 id 和 check 函数');
421
+ }
422
+
423
+ this._rules.set(rule.id, {
424
+ id: rule.id,
425
+ name: rule.name || rule.id,
426
+ description: rule.description || '',
427
+ severity: rule.severity || Severity.WARN,
428
+ check: rule.check,
429
+ options: rule.options || {},
430
+ });
431
+
432
+ // 自动将新规则加入启用列表
433
+ if (!this._options.enabledRules.includes(rule.id)) {
434
+ this._options.enabledRules.push(rule.id);
435
+ }
436
+ }
437
+
438
+ /**
439
+ * 移除指定规则
440
+ * @param {string} ruleId - 规则 ID
441
+ * @returns {boolean} 是否成功移除
442
+ */
443
+ removeRule(ruleId) {
444
+ const removed = this._rules.delete(ruleId);
445
+ if (removed) {
446
+ this._options.enabledRules = this._options.enabledRules.filter(r => r !== ruleId);
447
+ }
448
+ return removed;
449
+ }
450
+
451
+ /**
452
+ * 获取所有可用规则列表
453
+ * @returns {Object[]} 规则信息数组
454
+ */
455
+ listRules() {
456
+ return Array.from(this._rules.values()).map(r => ({
457
+ id: r.id,
458
+ name: r.name,
459
+ description: r.description,
460
+ severity: r.severity,
461
+ enabled: this._options.enabledRules.includes(r.id),
462
+ }));
463
+ }
464
+
465
+ /**
466
+ * 插件停用时调用
467
+ * @param {import('../plugin-sdk.js').PluginContext} context - 插件上下文
468
+ */
469
+ async onDeactivate(context) {
470
+ context.logger.info('Custom Linter 插件正在停用...');
471
+ }
472
+ }
@@ -0,0 +1 @@
1
+ {"name": "custom-linter", "version": "1.0.0", "description": "自定义 Linter 规则扩展 - 在 pre-verify 阶段执行自定义代码检查", "main": "index.js", "hooks": ["pre-verify"], "author": "PDD Team", "license": "MIT", "keywords": ["linter", "lint", "quality", "rules"], "pdd": ">=1.0.0"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Hello World 示例插件
3
+ *
4
+ * PDD 插件系统的最简入门示例,演示:
5
+ * - 继承 PluginBase 基类
6
+ * - 实现 onActivate 生命周期钩子
7
+ * - 使用 registerCommand 注册自定义命令
8
+ *
9
+ * @module hello-world
10
+ * @author PDD Team
11
+ * @version 1.0.0
12
+ */
13
+
14
+ import { PluginBase } from '../plugin-sdk.js';
15
+
16
+ /**
17
+ * Hello World 插件类
18
+ *
19
+ * @class HelloWorldPlugin
20
+ * @extends PluginBase
21
+ */
22
+ export default class HelloWorldPlugin extends PluginBase {
23
+ constructor() {
24
+ super({
25
+ name: 'hello-world',
26
+ version: '1.0.0',
27
+ description: 'Hello World 示例插件 - PDD 插件系统入门示例',
28
+ author: 'PDD Team',
29
+ license: 'MIT',
30
+ keywords: ['example', 'demo', 'getting-started'],
31
+ pddVersionRange: '>=1.0.0',
32
+ });
33
+ }
34
+
35
+ /**
36
+ * 插件激活时调用
37
+ * 注册 hello 命令并输出欢迎信息
38
+ *
39
+ * @param {import('../plugin-sdk.js').PluginContext} context - 插件上下文
40
+ */
41
+ async onActivate(context) {
42
+ context.logger.info('Hello World 插件正在激活...');
43
+
44
+ // 注册 hello 命令
45
+ this.registerCommand('hello', (args = {}) => {
46
+ const name = args.name || 'World';
47
+ return {
48
+ message: `Hello, ${name}! 🎉`,
49
+ from: this.name,
50
+ version: this.version,
51
+ timestamp: new Date().toISOString(),
52
+ };
53
+ }, {
54
+ description: '输出 Hello World 问候语,可指定名称',
55
+ usage: 'hello [--name <名字>]',
56
+ examples: [
57
+ { command: 'hello', output: 'Hello, World!' },
58
+ { command: 'hello --name PDD', output: 'Hello, PDD!' },
59
+ ],
60
+ });
61
+
62
+ // 注册 hello:info 子命令
63
+ this.registerCommand('hello:info', () => ({
64
+ pluginName: this.name,
65
+ version: this.version,
66
+ description: this.description,
67
+ author: this.author,
68
+ commands: Array.from(this.getCommands().keys()),
69
+ }), {
70
+ description: '显示插件自身信息',
71
+ });
72
+
73
+ context.logger.info(`Hello World 插件激活成功!已注册命令: ${Array.from(this.getCommands().keys()).join(', ')}`);
74
+ }
75
+
76
+ /**
77
+ * 插件停用时调用
78
+ * 清理资源(本示例无额外资源需要清理)
79
+ *
80
+ * @param {import('../plugin-sdk.js').PluginContext} context - 插件上下文
81
+ */
82
+ async onDeactivate(context) {
83
+ context.logger.info('Hello World 插件正在停用...');
84
+ // 命令注册表会在 unload 时自动清理
85
+ }
86
+ }
@@ -0,0 +1 @@
1
+ {"name": "hello-world", "version": "1.0.0", "description": "Hello World 示例插件 - PDD 插件系统入门示例", "main": "index.js", "author": "PDD Team", "license": "MIT", "keywords": ["example", "demo", "getting-started"], "pdd": ">=1.0.0"}