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/lib/verify.js ADDED
@@ -0,0 +1,741 @@
1
+ /**
2
+ * PDD 功能验证模块
3
+ * 验证代码实现是否符合开发规格和验收标准
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ // 引入chalk用于彩色输出
14
+ let chalk;
15
+ try {
16
+ const chalkModule = await import('chalk');
17
+ chalk = chalkModule.default;
18
+ } catch {
19
+ chalk = {
20
+ cyan: (s) => s,
21
+ green: (s) => s,
22
+ yellow: (s) => s,
23
+ red: (s) => s,
24
+ blue: (s) => s,
25
+ magenta: (s) => s
26
+ };
27
+ }
28
+
29
+ /**
30
+ * 验证结果类
31
+ */
32
+ class VerificationResult {
33
+ constructor() {
34
+ this.featureName = '';
35
+ this.specPath = '';
36
+ this.codeDir = '';
37
+ this.timestamp = new Date().toISOString();
38
+ this.checks = [];
39
+ this.passed = 0;
40
+ this.failed = 0;
41
+ this.warnings = 0;
42
+ this.overallStatus = 'PENDING';
43
+ this.issues = [];
44
+ this.recommendations = [];
45
+ }
46
+
47
+ /**
48
+ * 添加检查项
49
+ * @param {string} name - 检查项名称
50
+ * @param {string} status - 状态: PASS/FAIL/WARN/SKIP
51
+ * @param {string} message - 描述信息
52
+ */
53
+ addCheck(name, status, message = '') {
54
+ this.checks.push({
55
+ name,
56
+ status,
57
+ message,
58
+ timestamp: new Date().toISOString()
59
+ });
60
+
61
+ switch (status) {
62
+ case 'PASS':
63
+ this.passed++;
64
+ break;
65
+ case 'FAIL':
66
+ this.failed++;
67
+ this.issues.push({ type: 'error', name, message });
68
+ break;
69
+ case 'WARN':
70
+ this.warnings++;
71
+ this.issues.push({ type: 'warning', name, message });
72
+ break;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * 计算总体状态
78
+ */
79
+ calculateOverallStatus() {
80
+ if (this.failed > 0) {
81
+ this.overallStatus = 'FAILED';
82
+ } else if (this.warnings > 0) {
83
+ this.overallStatus = 'WARNING';
84
+ } else if (this.passed > 0) {
85
+ this.overallStatus = 'PASSED';
86
+ }
87
+ }
88
+
89
+ /**
90
+ * 获取通过率
91
+ * @returns {number} 通过率百分比
92
+ */
93
+ getPassRate() {
94
+ const total = this.passed + this.failed;
95
+ return total > 0 ? Math.round((this.passed / total) * 100) : 0;
96
+ }
97
+
98
+ /**
99
+ * 转换为JSON对象
100
+ * @returns {Object}
101
+ */
102
+ toJSON() {
103
+ this.calculateOverallStatus();
104
+ return {
105
+ featureName: this.featureName,
106
+ specPath: this.specPath,
107
+ codeDir: this.codeDir,
108
+ timestamp: this.timestamp,
109
+ summary: {
110
+ totalChecks: this.checks.length,
111
+ passed: this.passed,
112
+ failed: this.failed,
113
+ warnings: this.warnings,
114
+ passRate: `${this.getPassRate()}%`,
115
+ overallStatus: this.overallStatus
116
+ },
117
+ checks: this.checks,
118
+ issues: this.issues,
119
+ recommendations: this.recommendations
120
+ };
121
+ }
122
+ }
123
+
124
+ /**
125
+ * 验证功能实现是否符合规格
126
+ * @param {Object} options - 命令行选项
127
+ */
128
+ export async function verifyFeature(options) {
129
+ try {
130
+ console.log(chalk.blue('🔍 开始验证功能实现...\n'));
131
+
132
+ // 1. 初始化验证结果
133
+ const result = new VerificationResult();
134
+ result.codeDir = path.resolve(options.code);
135
+
136
+ // 2. 检查代码目录是否存在
137
+ result.addCheck(
138
+ '代码目录存在性检查',
139
+ fs.existsSync(result.codeDir) ? 'PASS' : 'FAIL',
140
+ `代码目录: ${result.codeDir}`
141
+ );
142
+
143
+ if (!fs.existsSync(result.codeDir)) {
144
+ throw new Error(`代码目录不存在: ${result.codeDir}`);
145
+ }
146
+
147
+ // 3. 解析规格文件(如果提供)
148
+ let specData = null;
149
+ if (options.spec) {
150
+ result.specPath = path.resolve(options.spec);
151
+
152
+ result.addCheck(
153
+ '规格文件存在性检查',
154
+ fs.existsSync(result.specPath) ? 'PASS' : 'FAIL',
155
+ `规格文件: ${result.specPath}`
156
+ );
157
+
158
+ if (fs.existsSync(result.specPath)) {
159
+ specData = await parseSpecForVerification(result.specPath);
160
+ result.featureName = specData.title || '未命名功能';
161
+
162
+ // 基于规格进行详细验证
163
+ await verifyAgainstSpec(result, specData, options);
164
+ }
165
+ } else {
166
+ result.featureName = '通用验证';
167
+
168
+ // 无规格文件时进行基础代码质量检查
169
+ await performBasicCodeVerification(result, options);
170
+ }
171
+
172
+ // 4. 计算总体状态并生成报告
173
+ result.calculateOverallStatus();
174
+
175
+ // 5. 输出验证结果
176
+ printVerificationResult(result, options);
177
+
178
+ // 6. 如果是JSON模式,输出JSON格式报告
179
+ if (options.json) {
180
+ const jsonReport = JSON.stringify(result.toJSON(), null, 2);
181
+ const reportPath = path.join(process.cwd(), 'verification-report.json');
182
+ await fs.promises.writeFile(reportPath, jsonReport, 'utf-8');
183
+ console.log(chalk.cyan(`\n📄 JSON报告已保存: ${reportPath}\n`));
184
+ }
185
+
186
+ // 7. 根据验证结果设置退出码
187
+ if (result.overallStatus === 'FAILED') {
188
+ process.exit(1);
189
+ }
190
+
191
+ } catch (error) {
192
+ console.error(chalk.red(`\n❌ 验证失败: ${error.message}\n`));
193
+ process.exit(1);
194
+ }
195
+ }
196
+
197
+ /**
198
+ * 解析规格文件用于验证
199
+ * @param {string} specPath - 规格文件路径
200
+ * @returns {Object} 规格数据
201
+ */
202
+ async function parseSpecForVerification(specPath) {
203
+ const content = await fs.promises.readFile(specPath, 'utf-8');
204
+
205
+ const specData = {
206
+ title: '',
207
+ features: [],
208
+ acceptanceCriteria: [],
209
+ requirements: []
210
+ };
211
+
212
+ // 提取标题
213
+ const titleMatch = content.match(/^#\s+(.+)$/m);
214
+ if (titleMatch) {
215
+ specData.title = titleMatch[1];
216
+ }
217
+
218
+ // 提取验收标准
219
+ const acRegex = /(?:验收标准|Acceptance Criteria)[::\s]*\n((?:[-*]\s+.+\n?)+)/gi;
220
+ let acMatch;
221
+ while ((acMatch = acRegex.exec(content)) !== null) {
222
+ const criteria = acMatch[1]
223
+ .split('\n')
224
+ .filter(line => line.trim())
225
+ .map(line => line.replace(/^[-*]\s*/, '').trim());
226
+ specData.acceptanceCriteria.push(...criteria);
227
+ }
228
+
229
+ // 提取需求列表
230
+ const reqRegex = /(?:需求|Requirements?)[::\s]*\n((?:\d+.\s+.+\n?)+)/gi;
231
+ let reqMatch;
232
+ while ((reqMatch = reqRegex.exec(content)) !== null) {
233
+ const requirements = reqMatch[1]
234
+ .split('\n')
235
+ .filter(line => line.trim())
236
+ .map(line => line.replace(/^\d+.\s*/, '').trim());
237
+ specData.requirements.push(...requirements);
238
+ }
239
+
240
+ // 提取功能点
241
+ const featureRegex = /^(#{2,3})\s+(.+?)$/gm;
242
+ let featureMatch;
243
+ while ((featureMatch = featureRegex.exec(content)) !== null) {
244
+ specData.features.push({
245
+ level: featureMatch[1].length,
246
+ title: featureMatch[2].trim()
247
+ });
248
+ }
249
+
250
+ return specData;
251
+ }
252
+
253
+ /**
254
+ * 基于规格进行验证
255
+ * @param {VerificationResult} result - 验证结果对象
256
+ * @param {Object} specData - 规格数据
257
+ * @param {Object} options - 命令行选项
258
+ */
259
+ async function verifyAgainstSpec(result, specData, options) {
260
+ console.log(chalk.cyan(`📋 验证功能点: ${specData.title}\n`));
261
+
262
+ // 1. 验证需求覆盖度
263
+ if (specData.requirements.length > 0) {
264
+ console.log(chalk.blue('检查需求覆盖度...'));
265
+
266
+ for (let i = 0; i < specData.requirements.length; i++) {
267
+ const req = specData.requirements[i];
268
+ const isCovered = await checkRequirementCoverage(req, result.codeDir);
269
+
270
+ result.addCheck(
271
+ `需求 #${i + 1}: ${req.slice(0, 50)}${req.length > 50 ? '...' : ''}`,
272
+ isCovered ? 'PASS' : 'FAIL',
273
+ isCovered ? '已找到对应实现' : '未找到明确实现'
274
+ );
275
+ }
276
+ }
277
+
278
+ // 2. 验收标准检查
279
+ if (specData.acceptanceCriteria.length > 0) {
280
+ console.log(chalk.blue('\n检查验收标准...'));
281
+
282
+ for (let i = 0; i < specData.acceptanceCriteria.length; i++) {
283
+ const criterion = specData.acceptanceCriteria[i];
284
+ const isMet = await checkAcceptanceCriterion(criterion, result.codeDir);
285
+
286
+ result.addCheck(
287
+ `验收标准 #${i + 1}: ${criterion.slice(0, 50)}${criterion.length > 50 ? '...' : ''}`,
288
+ isMet ? 'PASS' : 'FAIL',
289
+ isMet ? '标准已满足' : '标准未满足'
290
+ );
291
+ }
292
+ }
293
+
294
+ // 3. 功能点完整性检查
295
+ if (specData.features.length > 0) {
296
+ console.log(chalk.blue('\n检查功能点完整性...'));
297
+
298
+ for (const feature of specData.features) {
299
+ const featureFiles = await findFeatureImplementation(feature.title, result.codeDir);
300
+ const hasImplementation = featureFiles.length > 0;
301
+
302
+ result.addCheck(
303
+ `功能点: ${feature.title}`,
304
+ hasImplementation ? 'PASS' : 'WARN',
305
+ hasImplementation
306
+ ? `找到 ${featureFiles.length} 个相关文件`
307
+ : '可能缺少实现文件'
308
+ );
309
+
310
+ if (hasImplementation && options.verbose) {
311
+ for (const file of featureFiles) {
312
+ console.log(chalk.gray(` 📄 ${file}`));
313
+ }
314
+ }
315
+ }
316
+ }
317
+
318
+ // 4. 代码质量基本检查
319
+ await performBasicCodeVerification(result, options);
320
+ }
321
+
322
+ /**
323
+ * 执行基础代码质量验证
324
+ * @param {VerificationResult} result - 验证结果对象
325
+ * @param {Object} options - 命令行选项
326
+ */
327
+ async function performBasicCodeVerification(result, options) {
328
+ console.log(chalk.blue('\n执行基础代码质量检查...\n'));
329
+
330
+ // 1. 文件结构检查
331
+ const files = await getAllCodeFiles(result.codeDir);
332
+
333
+ result.addCheck(
334
+ '代码文件数量',
335
+ files.length > 0 ? 'PASS' : 'FAIL',
336
+ `发现 ${files.length} 个代码文件`
337
+ );
338
+
339
+ // 2. 文件类型分布
340
+ const fileTypes = {};
341
+ files.forEach(file => {
342
+ const ext = path.extname(file).toLowerCase();
343
+ fileTypes[ext] = (fileTypes[ext] || 0) + 1;
344
+ });
345
+
346
+ if (options.verbose) {
347
+ console.log(chalk.cyan('文件类型分布:'));
348
+ Object.entries(fileTypes).forEach(([ext, count]) => {
349
+ console.log(chalk.gray(` ${ext || '(无扩展名)'}: ${count} 个文件`));
350
+ });
351
+ }
352
+
353
+ // 3. JavaScript/TypeScript语法检查
354
+ const jsFiles = files.filter(f =>
355
+ ['.js', '.jsx', '.ts', '.tsx'].includes(path.extname(f).toLowerCase())
356
+ );
357
+
358
+ if (jsFiles.length > 0) {
359
+ console.log(chalk.blue('\nJavaScript/TypeScript语法检查...'));
360
+
361
+ for (const file of jsFiles.slice(0, 10)) { // 最多检查10个文件
362
+ const syntaxOk = await checkSyntaxValidity(file);
363
+ result.addCheck(
364
+ `语法检查: ${path.basename(file)}`,
365
+ syntaxOk ? 'PASS' : 'FAIL',
366
+ syntaxOk ? '语法正确' : '存在语法错误'
367
+ );
368
+ }
369
+
370
+ if (jsFiles.length > 10) {
371
+ result.addCheck(
372
+ '语法检查范围',
373
+ 'WARN',
374
+ `仅检查了前10个文件,共${jsFiles.length}个JS/TS文件`
375
+ );
376
+ }
377
+ }
378
+
379
+ // 4. 测试文件检查
380
+ const testFiles = files.filter(f =>
381
+ f.includes('.test.') || f.includes('.spec.') || f.includes('__tests__')
382
+ );
383
+
384
+ result.addCheck(
385
+ '测试文件存在性',
386
+ testFiles.length > 0 ? 'PASS' : 'WARN',
387
+ `发现 ${testFiles.length} 个测试文件`
388
+ );
389
+
390
+ // 5. README文档检查
391
+ const readmeExists = fs.existsSync(path.join(result.codeDir, 'README.md'));
392
+ result.addCheck(
393
+ 'README文档',
394
+ readmeExists ? 'PASS' : 'WARN',
395
+ readmeExists ? '项目包含README文档' : '建议添加README文档'
396
+ );
397
+
398
+ // 6. 包管理文件检查
399
+ const packageJsonExists = fs.existsSync(path.join(result.codeDir, 'package.json'));
400
+ result.addCheck(
401
+ 'package.json',
402
+ packageJsonExists ? 'PASS' : 'WARN',
403
+ packageJsonExists ? '项目包含package.json' : 'Node.js项目缺少package.json'
404
+ );
405
+
406
+ // 7. 生成改进建议
407
+ generateRecommendations(result);
408
+ }
409
+
410
+ /**
411
+ * 检查需求是否被代码覆盖
412
+ * @param {string} requirement - 需求描述
413
+ * @param {string} codeDir - 代码目录
414
+ * @returns {boolean} 是否覆盖
415
+ */
416
+ async function checkRequirementCoverage(requirement, codeDir) {
417
+ try {
418
+ // 从需求中提取关键词
419
+ const keywords = extractKeywords(requirement);
420
+
421
+ // 在代码文件中搜索这些关键词
422
+ const files = await getAllCodeFiles(codeDir);
423
+
424
+ for (const file of files) {
425
+ try {
426
+ const content = await fs.promises.readFile(file, 'utf-8');
427
+ const matchCount = keywords.filter(kw =>
428
+ content.toLowerCase().includes(kw.toLowerCase())
429
+ ).length;
430
+
431
+ // 如果匹配到超过50%的关键词,认为已覆盖
432
+ if (matchCount >= Math.ceil(keywords.length * 0.5)) {
433
+ return true;
434
+ }
435
+ } catch {
436
+ // 忽略读取错误
437
+ }
438
+ }
439
+
440
+ return false;
441
+ } catch {
442
+ return false;
443
+ }
444
+ }
445
+
446
+ /**
447
+ * 检查验收标准是否满足
448
+ * @param {string} criterion - 验收标准
449
+ * @param {string} codeDir - 代码目录
450
+ * @returns {boolean} 是否满足
451
+ */
452
+ async function checkAcceptanceCriterion(criterion, codeDir) {
453
+ try {
454
+ // 简单的文本匹配检查
455
+ const keywords = extractKeywords(criterion);
456
+ const files = await getAllCodeFiles(codeDir);
457
+
458
+ for (const file of files) {
459
+ const content = await fs.promises.readFile(file, 'utf-8');
460
+
461
+ // 检查是否包含相关的测试用例或实现
462
+ const hasTest = content.toLowerCase().includes('test') ||
463
+ content.toLowerCase().includes('it(') ||
464
+ content.toLowerCase().includes('describe(');
465
+
466
+ const keywordMatches = keywords.filter(kw =>
467
+ content.toLowerCase().includes(kw.toLowerCase())
468
+ ).length;
469
+
470
+ if (keywordMatches > 0 && hasTest) {
471
+ return true;
472
+ }
473
+ }
474
+
475
+ return false;
476
+ } catch {
477
+ return false;
478
+ }
479
+ }
480
+
481
+ /**
482
+ * 查找功能的实现文件
483
+ * @param {string} featureTitle - 功能标题
484
+ * @param {string} codeDir - 代码目录
485
+ * @returns {string[]} 相关文件列表
486
+ */
487
+ async function findFeatureImplementation(featureTitle, codeDir) {
488
+ const keywords = extractKeywords(featureTitle);
489
+ const matchedFiles = [];
490
+
491
+ try {
492
+ const files = await getAllCodeFiles(codeDir);
493
+
494
+ for (const file of files) {
495
+ const fileName = path.basename(file).toLowerCase();
496
+ const matches = keywords.filter(kw =>
497
+ fileName.includes(kw.toLowerCase())
498
+ ).length;
499
+
500
+ if (matches >= Math.ceil(keywords.length * 0.3)) {
501
+ matchedFiles.push(file);
502
+ }
503
+ }
504
+ } catch {
505
+ // 忽略错误
506
+ }
507
+
508
+ return matchedFiles;
509
+ }
510
+
511
+ /**
512
+ * 获取所有代码文件
513
+ * @param {string} dir - 目录路径
514
+ * @returns {string[]} 文件列表
515
+ */
516
+ async function getAllCodeFiles(dir) {
517
+ const files = [];
518
+
519
+ async function traverse(currentDir) {
520
+ try {
521
+ const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });
522
+
523
+ for (const entry of entries) {
524
+ const fullPath = path.join(currentDir, entry.name);
525
+
526
+ // 跳过 node_modules、.git 等目录
527
+ if (entry.isDirectory() && !['node_modules', '.git', 'dist', 'build'].includes(entry.name)) {
528
+ await traverse(fullPath);
529
+ } else if (entry.isFile()) {
530
+ const ext = path.extname(entry.name).toLowerCase();
531
+ // 只收集代码文件
532
+ if (['.js', '.jsx', '.ts', '.tsx', '.java', '.py', '.sql', '.vue', '.json'].includes(ext) ||
533
+ entry.name === 'README.md') {
534
+ files.push(fullPath);
535
+ }
536
+ }
537
+ }
538
+ } catch {
539
+ // 忽略权限错误等
540
+ }
541
+ }
542
+
543
+ await traverse(dir);
544
+ return files;
545
+ }
546
+
547
+ /**
548
+ * 检查JavaScript/TypeScript文件语法有效性
549
+ * @param {string} filePath - 文件路径
550
+ * @returns {boolean} 语法是否正确
551
+ */
552
+ async function checkSyntaxValidity(filePath) {
553
+ try {
554
+ const content = await fs.promises.readFile(filePath, 'utf-8');
555
+
556
+ // 简单的语法检查 - 尝试解析为函数
557
+ if (filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
558
+ // 使用 Function 构造器进行基础语法检查(不执行)
559
+ try {
560
+ new Function(content);
561
+ return true;
562
+ } catch {
563
+ // 对于模块化代码,Function构造器可能会失败,使用更宽松的检查
564
+ return checkBasicSyntax(content);
565
+ }
566
+ }
567
+
568
+ return true;
569
+ } catch {
570
+ return false;
571
+ }
572
+ }
573
+
574
+ /**
575
+ * 基础语法检查
576
+ * @param {string} code - 代码内容
577
+ * @returns {boolean} 语法是否基本正确
578
+ */
579
+ function checkBasicSyntax(code) {
580
+ // 检查括号匹配
581
+ let braceCount = 0;
582
+ let parenCount = 0;
583
+ let bracketCount = 0;
584
+
585
+ // 移除字符串中的字符
586
+ const cleanCode = code
587
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""')
588
+ .replace(/'(?:[^'\\]|\\.)*'/g, "''")
589
+ .replace(/`(?:[^`\\]|\\.)*`/g, '``')
590
+ .replace(/\/\/.*$/gm, '') // 移除单行注释
591
+ .replace(/\/\*[\s\S]*?\*\//g, ''); // 移除多行注释
592
+
593
+ for (const char of cleanCode) {
594
+ switch (char) {
595
+ case '{': braceCount++; break;
596
+ case '}': braceCount--; break;
597
+ case '(': parenCount++; break;
598
+ case ')': parenCount--; break;
599
+ case '[': bracketCount++; break;
600
+ case ']': bracketCount--; break;
601
+ }
602
+
603
+ // 如果计数出现负数,说明括号不匹配
604
+ if (braceCount < 0 || parenCount < 0 || bracketCount < 0) {
605
+ return false;
606
+ }
607
+ }
608
+
609
+ // 最终所有计数应该为0
610
+ return braceCount === 0 && parenCount === 0 && bracketCount === 0;
611
+ }
612
+
613
+ /**
614
+ * 从文本中提取关键词
615
+ * @param {string} text - 输入文本
616
+ * @returns {string[]} 关键词列表
617
+ */
618
+ function extractKeywords(text) {
619
+ // 移除常见的停用词和标点
620
+ const stopWords = ['的', '是', '在', '有', '和', '与', '或', '等', '及', '能', '可以', '需要', '应该', '必须', '支持', '包括', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might', 'must', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'as', 'into', 'through'];
621
+
622
+ return text
623
+ .toLowerCase()
624
+ .replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s]/g, ' ')
625
+ .split(/\s+/)
626
+ .filter(word => word.length > 1 && !stopWords.includes(word))
627
+ .slice(0, 10); // 最多返回10个关键词
628
+ }
629
+
630
+ /**
631
+ * 生成改进建议
632
+ * @param {VerificationResult} result - 验证结果对象
633
+ */
634
+ function generateRecommendations(result) {
635
+ // 根据失败的检查项生成建议
636
+ for (const issue of result.issues) {
637
+ if (issue.type === 'error') {
638
+ if (issue.name.includes('需求')) {
639
+ result.recommendations.push(`请确保实现了以下需求: ${issue.name}`);
640
+ } else if (issue.name.includes('验收')) {
641
+ result.recommendations.push(`请满足以下验收标准: ${issue.name}`);
642
+ } else if (issue.name.includes('语法')) {
643
+ result.recommendations.push(`修复 ${issue.name} 的语法错误`);
644
+ }
645
+ }
646
+ }
647
+
648
+ // 通用建议
649
+ if (result.warnings > 0) {
650
+ result.recommendations.push('建议补充单元测试以提高代码覆盖率');
651
+ }
652
+
653
+ if (!result.issues.some(i => i.name.includes('README'))) {
654
+ result.recommendations.push('建议添加或完善README文档');
655
+ }
656
+
657
+ // 去重
658
+ result.recommendations = [...new Set(result.recommendations)];
659
+ }
660
+
661
+ /**
662
+ * 打印验证结果
663
+ * @param {VerificationResult} result - 验证结果对象
664
+ * @param {Object} options - 命令行选项
665
+ */
666
+ function printVerificationResult(result, options) {
667
+ console.log('\n' + '='.repeat(70));
668
+ console.log(chalk.bold('🔍 验证结果摘要'));
669
+ console.log('='.repeat(70) + '\n');
670
+
671
+ // 总体状态
672
+ const statusColor = {
673
+ 'PASSED': chalk.green,
674
+ 'WARNING': chalk.yellow,
675
+ 'FAILED': chalk.red,
676
+ 'PENDING': chalk.gray
677
+ };
678
+
679
+ console.log(`功能名称: ${chalk.cyan(result.featureName)}`);
680
+ console.log(`验证时间: ${new Date(result.timestamp).toLocaleString()}`);
681
+ console.log(`总体状态: ${statusColor[result.overallStatus](result.overallStatus)}`);
682
+ console.log(`通过率: ${result.getPassRate()}%`);
683
+ console.log('');
684
+
685
+ // 统计信息
686
+ console.log('-'.repeat(70));
687
+ console.log('统计信息:');
688
+ console.log(` ✅ 通过: ${chalk.green(result.passed)} 项`);
689
+ console.log(` ❌ 失败: ${chalk.red(result.failed)} 项`);
690
+ console.log(` ⚠️ 警告: ${chalk.yellow(result.warnings)} 项`);
691
+ console.log(` 📊 总计: ${result.checks.length} 项`);
692
+ console.log('-'.repeat(70) + '\n');
693
+
694
+ // 详细检查结果(verbose模式)
695
+ if (options.verbose && result.checks.length > 0) {
696
+ console.log('详细检查结果:\n');
697
+
698
+ for (const check of result.checks) {
699
+ const icon = {
700
+ 'PASS': '✅',
701
+ 'FAIL': '❌',
702
+ 'WARN': '⚠️ ',
703
+ 'SKIP': '⏭️ '
704
+ };
705
+
706
+ const color = statusColor[check.status] || chalk.gray;
707
+ console.log(`${icon[check.status]} ${color(check.status.padEnd(6))} ${check.name}`);
708
+
709
+ if (check.message && options.verbose) {
710
+ console.log(` ${chalk.gray(check.message)}`);
711
+ }
712
+ }
713
+ console.log('');
714
+ }
715
+
716
+ // 问题列表
717
+ if (result.issues.length > 0) {
718
+ console.log(chalk.red('发现的问题:'));
719
+ for (const issue of result.issues) {
720
+ const icon = issue.type === 'error' ? '❌' : '⚠️ ';
721
+ console.log(` ${icon} ${issue.name}`);
722
+ if (issue.message) {
723
+ console.log(` ${chalk.gray(issue.message)}`);
724
+ }
725
+ }
726
+ console.log('');
727
+ }
728
+
729
+ // 改进建议
730
+ if (result.recommendations.length > 0) {
731
+ console.log(chalk.blue('💡 改进建议:'));
732
+ for (let i = 0; i < result.recommendations.length; i++) {
733
+ console.log(` ${i + 1}. ${result.recommendations[i]}`);
734
+ }
735
+ console.log('');
736
+ }
737
+
738
+ console.log('='.repeat(70) + '\n');
739
+ }
740
+
741
+ export { verifyFeature, VerificationResult };