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,669 @@
1
+ /**
2
+ * PDD Visual Manager - REST API Routes (VM-B003)
3
+ *
4
+ * Dashboard 的 RESTful API 路由处理函数集合
5
+ * 提供项目数据、功能点、质量矩阵等数据的查询接口
6
+ *
7
+ * @module vm/dashboard/api-routes
8
+ */
9
+
10
+ import { URL } from 'url';
11
+
12
+ // 可选的 chalk 彩色输出支持
13
+ let chalk;
14
+ try {
15
+ const chalkModule = await import('chalk');
16
+ chalk = chalkModule.default;
17
+ } catch {
18
+ chalk = {
19
+ cyan: (s) => s,
20
+ green: (s) => s,
21
+ yellow: (s) => s,
22
+ red: (s) => s
23
+ };
24
+ }
25
+
26
+ // ============================================================
27
+ // 路由表定义
28
+ // ============================================================
29
+
30
+ /**
31
+ * 路由配置表
32
+ * 每个路由包含:method, pattern, handler
33
+ */
34
+ const ROUTES = [
35
+ // 项目信息
36
+ {
37
+ method: 'GET',
38
+ pattern: /^\/api\/project$/,
39
+ handler: getProject
40
+ },
41
+ // 功能点列表
42
+ {
43
+ method: 'GET',
44
+ pattern: /^\/api\/features$/,
45
+ handler: getFeatures
46
+ },
47
+ // 功能点详情
48
+ {
49
+ method: 'GET',
50
+ pattern: /^\/api\/feature\/(.+)$/,
51
+ handler: getFeatureDetail
52
+ },
53
+ // 项目摘要
54
+ {
55
+ method: 'GET',
56
+ pattern: /^\/api\/summary$/,
57
+ handler: getSummary
58
+ },
59
+ // 质量矩阵
60
+ {
61
+ method: 'GET',
62
+ pattern: /^\/api\/quality$/,
63
+ handler: getQualityMatrix
64
+ },
65
+ // Token 统计
66
+ {
67
+ method: 'GET',
68
+ pattern: /^\/api\/tokens$/,
69
+ handler: getTokenStats
70
+ },
71
+ // 缓存统计
72
+ {
73
+ method: 'GET',
74
+ pattern: /^\/api\/cache$/,
75
+ handler: getCacheStats
76
+ },
77
+ // 迭代列表
78
+ {
79
+ method: 'GET',
80
+ pattern: /^\/api\/iterations$/,
81
+ handler: getIterationList
82
+ },
83
+ // 系统健康状态
84
+ {
85
+ method: 'GET',
86
+ pattern: /^\/api\/system$/,
87
+ handler: getSystemHealth
88
+ },
89
+ // 数据导出
90
+ {
91
+ method: 'GET',
92
+ pattern: /^\/api\/export$/,
93
+ handler: getExport
94
+ },
95
+ // 手动刷新数据
96
+ {
97
+ method: 'POST',
98
+ pattern: /^\/api\/refresh$/,
99
+ handler: postRefresh
100
+ }
101
+ ];
102
+
103
+ // ============================================================
104
+ // API 处理函数
105
+ // ============================================================
106
+
107
+ /**
108
+ * 获取项目基本信息
109
+ * @param {http.IncomingMessage} req
110
+ * @param {Object} dataProvider - PDDDataProvider 实例
111
+ * @returns {Promise<Object>}
112
+ */
113
+ async function getProject(req, dataProvider) {
114
+ try {
115
+ const summary = dataProvider.getSummary();
116
+ return {
117
+ success: true,
118
+ data: {
119
+ summary: summary || {},
120
+ schemaVersion: dataProvider.schemaVersion || '1.0.0',
121
+ timestamp: new Date().toISOString()
122
+ }
123
+ };
124
+ } catch (err) {
125
+ throw new Error(`获取项目信息失败: ${err.message}`);
126
+ }
127
+ }
128
+
129
+ /**
130
+ * 获取功能点列表
131
+ * @param {http.IncomingMessage} req
132
+ * @param {Object} dataProvider - PDDDataProvider 实例
133
+ * @returns {Promise<Object>}
134
+ */
135
+ async function getFeatures(req, dataProvider) {
136
+ try {
137
+ const features = dataProvider.getFeatures() || [];
138
+ return {
139
+ success: true,
140
+ data: {
141
+ features: features.map(f => ({
142
+ id: f.id,
143
+ name: f.name,
144
+ stage: f.stage,
145
+ status: f.status,
146
+ priority: f.priority,
147
+ grade: f.grade,
148
+ progress: f.progress || 0
149
+ })),
150
+ total: features.length,
151
+ timestamp: new Date().toISOString()
152
+ }
153
+ };
154
+ } catch (err) {
155
+ throw new Error(`获取功能点列表失败: ${err.message}`);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * 获取功能点详情
161
+ * @param {http.IncomingMessage} req
162
+ * @param {Object} dataProvider - PDDDataProvider 实例
163
+ * @returns {Promise<Object>}
164
+ */
165
+ async function getFeatureDetail(req, dataProvider) {
166
+ try {
167
+ // 从 URL 中提取功能点 ID
168
+ const url = new URL(req.url, `http://${req.headers.host}`);
169
+ const pathname = url.pathname;
170
+ const match = pathname.match(/^\/api\/feature\/(.+)$/);
171
+
172
+ if (!match || !match[1]) {
173
+ return { success: false, error: '缺少功能点 ID' };
174
+ }
175
+
176
+ const featureId = decodeURIComponent(match[1]);
177
+ const feature = dataProvider.getFeatureById(featureId);
178
+
179
+ if (!feature) {
180
+ return {
181
+ success: false,
182
+ error: `未找到功能点: ${featureId}`,
183
+ code: 'NOT_FOUND'
184
+ };
185
+ }
186
+
187
+ return {
188
+ success: true,
189
+ data: {
190
+ feature: feature,
191
+ timestamp: new Date().toISOString()
192
+ }
193
+ };
194
+ } catch (err) {
195
+ throw new Error(`获取功能点详情失败: ${err.message}`);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * 获取项目摘要信息
201
+ * @param {http.IncomingMessage} req
202
+ * @param {Object} dataProvider - PDDDataProvider 实例
203
+ * @returns {Promise<Object>}
204
+ */
205
+ async function getSummary(req, dataProvider) {
206
+ try {
207
+ const summary = dataProvider.getSummary() || {};
208
+ return {
209
+ success: true,
210
+ data: {
211
+ summary: summary,
212
+ timestamp: new Date().toISOString()
213
+ }
214
+ };
215
+ } catch (err) {
216
+ throw new Error(`获取项目摘要失败: ${err.message}`);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * 获取质量矩阵数据
222
+ * @param {http.IncomingMessage} req
223
+ * @param {Object} dataProvider - PDDDataProvider 实例
224
+ * @returns {Promise<Object>}
225
+ */
226
+ async function getQualityMatrix(req, dataProvider) {
227
+ try {
228
+ const matrix = dataProvider.getQualityMatrix() || {};
229
+ return {
230
+ success: true,
231
+ data: {
232
+ matrix: matrix,
233
+ timestamp: new Date().toISOString()
234
+ }
235
+ };
236
+ } catch (err) {
237
+ throw new Error(`获取质量矩阵失败: ${err.message}`);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * 获取 Token 使用统计
243
+ * @param {http.IncomingMessage} req
244
+ * @param {Object} dataProvider - PDDDataProvider 实例
245
+ * @returns {Promise<Object>}
246
+ */
247
+ async function getTokenStats(req, dataProvider) {
248
+ try {
249
+ const stats = dataProvider.getTokenStats() || null;
250
+ return {
251
+ success: true,
252
+ data: {
253
+ stats: stats,
254
+ timestamp: new Date().toISOString()
255
+ }
256
+ };
257
+ } catch (err) {
258
+ throw new Error(`获取 Token 统计失败: ${err.message}`);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * 获取缓存统计信息
264
+ * @param {http.IncomingMessage} req
265
+ * @param {Object} dataProvider - PDDDataProvider 实例
266
+ * @returns {Promise<Object>}
267
+ */
268
+ async function getCacheStats(req, dataProvider) {
269
+ try {
270
+ const stats = dataProvider.getCacheStats() || null;
271
+ return {
272
+ success: true,
273
+ data: {
274
+ stats: stats,
275
+ timestamp: new Date().toISOString()
276
+ }
277
+ };
278
+ } catch (err) {
279
+ throw new Error(`获取缓存统计失败: ${err.message}`);
280
+ }
281
+ }
282
+
283
+ /**
284
+ * 获取迭代列表
285
+ * @param {http.IncomingMessage} req
286
+ * @param {Object} dataProvider - PDDDataProvider 实例
287
+ * @returns {Promise<Object>}
288
+ */
289
+ async function getIterationList(req, dataProvider) {
290
+ try {
291
+ // 如果 dataProvider 支持迭代查询
292
+ let iterations = [];
293
+
294
+ if (typeof dataProvider.getIterations === 'function') {
295
+ iterations = dataProvider.getIterations() || [];
296
+ } else if (dataProvider.summary && provider.summary.iterations) {
297
+ iterations = dataProvider.summary.iterations || [];
298
+ }
299
+
300
+ return {
301
+ success: true,
302
+ data: {
303
+ iterations: iterations,
304
+ total: iterations.length,
305
+ timestamp: new Date().toISOString()
306
+ }
307
+ };
308
+ } catch (err) {
309
+ throw new Error(`获取迭代列表失败: ${err.message}`);
310
+ }
311
+ }
312
+
313
+ /**
314
+ * 获取系统健康状态
315
+ * @param {http.IncomingMessage} req
316
+ * @param {Object} dataProvider - PDDDataProvider 实例
317
+ * @returns {Promise<Object>}
318
+ */
319
+ async function getSystemHealth(req, dataProvider) {
320
+ try {
321
+ const health = dataProvider.getSystemHealth() || {};
322
+
323
+ return {
324
+ success: true,
325
+ data: {
326
+ health: {
327
+ status: health.status || 'unknown',
328
+ uptime: process.uptime(),
329
+ memory: process.memoryUsage(),
330
+ platform: process.platform,
331
+ nodeVersion: process.version,
332
+ ...health
333
+ },
334
+ timestamp: new Date().toISOString()
335
+ }
336
+ };
337
+ } catch (err) {
338
+ throw new Error(`获取系统健康状态失败: ${err.message}`);
339
+ }
340
+ }
341
+
342
+ /**
343
+ * 数据导出接口
344
+ * 支持 JSON、Markdown、CSV 三种格式
345
+ * @param {http.IncomingMessage} req
346
+ * @param {Object} dataProvider - PDDDataProvider 实例
347
+ * @returns {Promise<Object>}
348
+ */
349
+ async function getExport(req, dataProvider) {
350
+ try {
351
+ const url = new URL(req.url, `http://${req.headers.host}`);
352
+ const format = (url.searchParams.get('format') || 'json').toLowerCase();
353
+
354
+ let result;
355
+ let contentType;
356
+
357
+ switch (format) {
358
+ case 'md':
359
+ case 'markdown':
360
+ if (typeof dataProvider.exportMarkdown === 'function') {
361
+ result = dataProvider.exportMarkdown();
362
+ } else {
363
+ result = _generateMarkdownExport(dataProvider);
364
+ }
365
+ contentType = 'text/markdown; charset=utf-8';
366
+ break;
367
+
368
+ case 'csv':
369
+ if (typeof dataProvider.exportCSV === 'function') {
370
+ result = dataProvider.exportCSV();
371
+ } else {
372
+ result = _generateCSVExport(dataProvider);
373
+ }
374
+ contentType = 'text/csv; charset=utf-8';
375
+ break;
376
+
377
+ case 'json':
378
+ default:
379
+ if (typeof dataProvider.exportJSON === 'function') {
380
+ result = dataProvider.exportJSON();
381
+ } else {
382
+ result = _generateJSONExport(dataProvider);
383
+ }
384
+ contentType = 'application/json; charset=utf-8';
385
+ break;
386
+ }
387
+
388
+ // 返回特殊标记,让调用者知道这是文件下载
389
+ return {
390
+ success: true,
391
+ isDownload: true,
392
+ contentType: contentType,
393
+ filename: `pdd-export-${Date.now()}.${format === 'markdown' ? 'md' : format}`,
394
+ data: result
395
+ };
396
+ } catch (err) {
397
+ throw new Error(`导出数据失败: ${err.message}`);
398
+ }
399
+ }
400
+
401
+ /**
402
+ * 触发数据刷新
403
+ * @param {http.IncomingMessage} req
404
+ * @param {Object} dataProvider - PDDDataProvider 实例
405
+ * @param {SSEManager} sseManager - SSE 管理器实例(可选)
406
+ * @returns {Promise<Object>}
407
+ */
408
+ async function postRefresh(req, dataProvider, sseManager) {
409
+ try {
410
+ console.log(chalk.cyan('[API] 收到手动刷新请求'));
411
+
412
+ // 触发数据刷新
413
+ if (typeof dataProvider.refresh === 'function') {
414
+ await dataProvider.refresh();
415
+ }
416
+
417
+ // 获取刷新后的数据
418
+ const summary = dataProvider.getSummary() || {};
419
+ const features = dataProvider.getFeatures() || [];
420
+
421
+ // 通过 SSE 广播刷新事件
422
+ if (sseManager && typeof sseManager.broadcastDataRefreshed === 'function') {
423
+ sseManager.broadcastDataRefreshed(summary);
424
+ }
425
+
426
+ return {
427
+ success: true,
428
+ message: '数据已刷新',
429
+ data: {
430
+ featuresCount: features.length,
431
+ summary: summary,
432
+ timestamp: new Date().toISOString()
433
+ }
434
+ };
435
+ } catch (err) {
436
+ throw new Error(`刷新数据失败: ${err.message}`);
437
+ }
438
+ }
439
+
440
+ // ============================================================
441
+ // 导出格式生成辅助函数
442
+ // ============================================================
443
+
444
+ /**
445
+ * 生成 JSON 格式导出数据
446
+ * @param {Object} dataProvider
447
+ * @returns {string}
448
+ * @private
449
+ */
450
+ function _generateJSONExport(dataProvider) {
451
+ const exportData = {
452
+ project: dataProvider.getSummary() || {},
453
+ features: dataProvider.getFeatures() || [],
454
+ quality: dataProvider.getQualityMatrix() || {},
455
+ exportedAt: new Date().toISOString()
456
+ };
457
+
458
+ return JSON.stringify(exportData, null, 2);
459
+ }
460
+
461
+ /**
462
+ * 生成 Markdown 格式导出数据
463
+ * @param {Object} dataProvider
464
+ * @returns {string}
465
+ * @private
466
+ */
467
+ function _generateMarkdownExport(dataProvider) {
468
+ const summary = dataProvider.getSummary() || {};
469
+ const features = dataProvider.getFeatures() || [];
470
+
471
+ let md = '# PDD 项目报告\n\n';
472
+ md += `> 导出时间: ${new Date().toLocaleString('zh-CN')}\n\n`;
473
+
474
+ // 项目概览
475
+ md += '## 项目概览\n\n';
476
+ if (summary.totalFeatures !== undefined) {
477
+ md += `- **总功能点**: ${summary.totalFeatures}\n`;
478
+ }
479
+ if (summary.completedFeatures !== undefined) {
480
+ md += `- **已完成**: ${summary.completedFeatures}\n`;
481
+ }
482
+ if (summary.progress !== undefined) {
483
+ md += `- **进度**: ${(summary.progress * 100).toFixed(1)}%\n`;
484
+ }
485
+ md += '\n';
486
+
487
+ // 功能点列表
488
+ md += '## 功能点列表\n\n';
489
+ md += '| ID | 名称 | 阶段 | 状态 | 优先级 | 质量 |\n';
490
+ md += '|-----|------|------|------|--------|------|\n';
491
+
492
+ for (const f of features) {
493
+ md += `| ${f.id} | ${f.name} | ${f.stage || '-'} | ${f.status || '-'} | ${f.priority || '-'} | ${f.grade || '-'} |\n`;
494
+ }
495
+
496
+ return md;
497
+ }
498
+
499
+ /**
500
+ * 生成 CSV 格式导出数据
501
+ * @param {Object} dataProvider
502
+ * @returns {string}
503
+ * @private
504
+ */
505
+ function _generateCSVExport(dataProvider) {
506
+ const features = dataProvider.getFeatures() || [];
507
+
508
+ // BOM + 表头(确保 Excel 正确识别 UTF-8)
509
+ let csv = '\uFEFFID,名称,阶段,状态,优先级,质量,进度\n';
510
+
511
+ for (const f of features) {
512
+ csv += [
513
+ f.id,
514
+ `"${(f.name || '').replace(/"/g, '""')}"`,
515
+ f.stage || '',
516
+ f.status || '',
517
+ f.priority || '',
518
+ f.grade || '',
519
+ (f.progress || 0) * 100 + '%'
520
+ ].join(',') + '\n';
521
+ }
522
+
523
+ return csv;
524
+ }
525
+
526
+ // ============================================================
527
+ // 路由匹配与请求处理
528
+ // ============================================================
529
+
530
+ /**
531
+ * 匹配路由并返回对应的处理函数
532
+ * @param {http.IncomingMessage} req - HTTP 请求对象
533
+ * @returns {{ route: Object|null, params: string[]|null }}
534
+ */
535
+ function routeMatcher(req) {
536
+ const url = new URL(req.url, `http://${req.headers.host}`);
537
+ const pathname = decodedURI(url.pathname);
538
+ const method = req.method.toUpperCase();
539
+
540
+ for (const route of ROUTES) {
541
+ if (route.method !== method) continue;
542
+
543
+ const match = pathname.match(route.pattern);
544
+ if (match) {
545
+ // 提取路径参数(排除第一个完整匹配项)
546
+ const params = match.slice(1);
547
+ return { route, params };
548
+ }
549
+ }
550
+
551
+ return { route: null, params: null };
552
+ }
553
+
554
+ /**
555
+ * 处理 API 请求的主入口函数
556
+ * 由 server.js 调用,统一处理所有 /api/* 请求
557
+ * @param {http.IncomingMessage} req - HTTP 请求对象
558
+ * @param {http.ServerResponse} res - HTTP 响应对象
559
+ * @param {string} pathname - 请求路径
560
+ * @param {Object} dataProvider - PDDDataProvider 实例
561
+ * @param {SSEManager} [sseManager] - SSE 管理器实例(可选)
562
+ */
563
+ async function handleAPIRequest(req, res, pathname, dataProvider, sseManager) {
564
+ const startTime = Date.now();
565
+
566
+ try {
567
+ // 匹配路由
568
+ const { route, params } = routeMatcher(req);
569
+
570
+ if (!route) {
571
+ sendJSON(res, 404, {
572
+ success: false,
573
+ error: 'API endpoint not found',
574
+ path: pathname
575
+ });
576
+ return;
577
+ }
578
+
579
+ // 调用处理函数
580
+ let result;
581
+ if (route.handler === postRefresh) {
582
+ // POST /api/refresh 需要传入 sseManager
583
+ result = await route.handler(req, dataProvider, sseManager);
584
+ } else {
585
+ result = await route.handler(req, dataProvider);
586
+ }
587
+
588
+ // 处理下载类型的响应
589
+ if (result.isDownload) {
590
+ const buffer = Buffer.from(typeof result.data === 'string' ? result.data : JSON.stringify(result.data));
591
+ res.writeHead(200, {
592
+ 'Content-Type': result.contentType,
593
+ 'Content-Disposition': `attachment; filename="${result.filename}"`,
594
+ 'Content-Length': buffer.length
595
+ });
596
+ res.end(buffer);
597
+ return;
598
+ }
599
+
600
+ // 正常 JSON 响应
601
+ sendJSON(res, 200, result);
602
+
603
+ // 记录日志
604
+ const duration = Date.now() - startTime;
605
+ console.log(
606
+ chalk.gray(`[API] ${req.method} ${pathname} → ${duration}ms`)
607
+ );
608
+
609
+ } catch (err) {
610
+ console.error(chalk.red(`[API] 请求处理错误:`), err.message);
611
+ sendJSON(res, 500, {
612
+ success: false,
613
+ error: 'Internal Server Error',
614
+ message: err.message
615
+ });
616
+ }
617
+ }
618
+
619
+ /**
620
+ * 发送 JSON 响应
621
+ * @param {http.ServerResponse} res
622
+ * @param {number} statusCode
623
+ * @param {Object} data
624
+ */
625
+ function sendJSON(res, statusCode, data) {
626
+ const body = JSON.stringify(data);
627
+ res.writeHead(statusCode, {
628
+ 'Content-Type': 'application/json; charset=utf-8',
629
+ 'Content-Length': Buffer.byteLength(body),
630
+ 'Access-Control-Allow-Origin': '*'
631
+ });
632
+ res.end(body);
633
+ }
634
+
635
+ /**
636
+ * 解码 URI(处理中文路径)
637
+ * @param {string} uri
638
+ * @returns {string}
639
+ */
640
+ function decodedURI(uri) {
641
+ try {
642
+ return decodeURIComponent(uri);
643
+ } catch {
644
+ return uri;
645
+ }
646
+ }
647
+
648
+ // 导出
649
+ export {
650
+ routeMatcher,
651
+ handleAPIRequest,
652
+ // 导出各个处理函数供测试使用
653
+ getProject,
654
+ getFeatures,
655
+ getFeatureDetail,
656
+ getSummary,
657
+ getQualityMatrix,
658
+ getTokenStats,
659
+ getCacheStats,
660
+ getIterationList,
661
+ getSystemHealth,
662
+ getExport,
663
+ postRefresh
664
+ };
665
+
666
+ export default {
667
+ routeMatcher,
668
+ handleAPIRequest
669
+ };