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,1003 @@
1
+ /**
2
+ * PDD Visual Manager - 数据模型定义
3
+ *
4
+ * 定义 Feature 数据模型的核心类和枚举,包括:
5
+ * - StageEnum: 开发阶段枚举
6
+ * - TimelineEntry: 时间线条目
7
+ * - Artifact: 制品信息
8
+ * - QualityMetrics: 质量指标
9
+ * - TokenUsage: Token消耗
10
+ * - IterationRound: 迭代轮次
11
+ * - Feature: 核心功能点模型
12
+ * - ProjectSummary: 项目汇总
13
+ */
14
+
15
+ // 可选的 chalk 彩色输出支持
16
+ let chalk;
17
+ try {
18
+ const chalkModule = await import('chalk');
19
+ chalk = chalkModule.default;
20
+ } catch {
21
+ chalk = {
22
+ cyan: (s) => s,
23
+ green: (s) => s,
24
+ yellow: (s) => s,
25
+ red: (s) => s,
26
+ blue: (s) => s,
27
+ magenta: (s) => s,
28
+ gray: (s) => s
29
+ };
30
+ }
31
+
32
+ /**
33
+ * 开发阶段枚举
34
+ * @readonly
35
+ * @enum {string}
36
+ */
37
+ export const StageEnum = Object.freeze({
38
+ PRD: 'prd',
39
+ EXTRACTED: 'extracted',
40
+ SPEC: 'spec',
41
+ IMPLEMENTING: 'implementing',
42
+ VERIFYING: 'verifying',
43
+ DONE: 'done'
44
+ });
45
+
46
+ /**
47
+ * 所有有效阶段值列表
48
+ * @type {string[]}
49
+ */
50
+ export const STAGE_VALUES = Object.values(StageEnum);
51
+
52
+ /**
53
+ * 阶段顺序映射(用于计算进度)
54
+ * @type {Object<string, number>}
55
+ */
56
+ export const STAGE_ORDER = Object.freeze({
57
+ [StageEnum.PRD]: 0,
58
+ [StageEnum.EXTRACTED]: 1,
59
+ [StageEnum.SPEC]: 2,
60
+ [StageEnum.IMPLEMENTING]: 3,
61
+ [StageEnum.VERIFYING]: 4,
62
+ [StageEnum.DONE]: 5
63
+ });
64
+
65
+ /**
66
+ * 优先级枚举
67
+ * @readonly
68
+ * @enum {string}
69
+ */
70
+ export const Priority = Object.freeze({
71
+ P0: 'P0',
72
+ P1: 'P1',
73
+ P2: 'P2',
74
+ P3: 'P3'
75
+ });
76
+
77
+ /**
78
+ * 制品类型枚举
79
+ * @readonly
80
+ * @enum {string}
81
+ */
82
+ export const ArtifactType = Object.freeze({
83
+ SPEC: 'spec',
84
+ CODE: 'code',
85
+ TEST: 'test',
86
+ REPORT: 'report'
87
+ });
88
+
89
+ /**
90
+ * 时间线条目
91
+ * 记录功能点在各个阶段的转换时间
92
+ */
93
+ export class TimelineEntry {
94
+ /**
95
+ * 创建时间线条目
96
+ * @param {Object} options - 配置选项
97
+ * @param {string} options.stage - 当前阶段 (StageEnum)
98
+ * @param {Date|number|string} options.timestamp - 进入该阶段的时间戳
99
+ * @param {number} [options.duration=0] - 在该阶段持续的时间(毫秒)
100
+ * @param {string} [options.note=''] - 备注
101
+ */
102
+ constructor({ stage, timestamp, duration = 0, note = '' }) {
103
+ if (!STAGE_VALUES.includes(stage)) {
104
+ throw new Error(`无效的阶段值: ${stage}, 有效值: ${STAGE_VALUES.join(', ')}`);
105
+ }
106
+
107
+ /** @type {string} 当前阶段 */
108
+ this.stage = stage;
109
+
110
+ /** @type {number} 时间戳(毫秒) */
111
+ this.timestamp = typeof timestamp === 'number' ? timestamp :
112
+ timestamp instanceof Date ? timestamp.getTime() :
113
+ new Date(timestamp).getTime();
114
+
115
+ /** @type {number} 持续时间(毫秒) */
116
+ this.duration = duration;
117
+
118
+ /** @type {string} 备注 */
119
+ this.note = note || '';
120
+ }
121
+
122
+ /**
123
+ * 序列化为 JSON 对象
124
+ * @returns {Object} JSON 对象
125
+ */
126
+ toJSON() {
127
+ return {
128
+ stage: this.stage,
129
+ timestamp: this.timestamp,
130
+ duration: this.duration,
131
+ note: this.note
132
+ };
133
+ }
134
+
135
+ /**
136
+ * 从 JSON 对象反序列化
137
+ * @param {Object} data - JSON 数据
138
+ * @returns {TimelineEntry} TimelineEntry 实例
139
+ */
140
+ static fromJSON(data) {
141
+ if (!data || typeof data !== 'object') {
142
+ throw new Error('TimelineEntry.fromJSON: 无效的数据');
143
+ }
144
+ return new TimelineEntry({
145
+ stage: data.stage,
146
+ timestamp: data.timestamp,
147
+ duration: data.duration ?? 0,
148
+ note: data.note ?? ''
149
+ });
150
+ }
151
+
152
+ /**
153
+ * 格式化显示
154
+ * @returns {string} 格式化字符串
155
+ */
156
+ toString() {
157
+ const date = new Date(this.timestamp);
158
+ return `[${chalk.cyan(this.stage)}] ${date.toLocaleString()} (${this.duration}ms)${this.note ? ` - ${this.note}` : ''}`;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * 制品信息
164
+ * 记录功能点生成的各种制品文件
165
+ */
166
+ export class Artifact {
167
+ /**
168
+ * 创建制品信息
169
+ * @param {Object} options - 配置选项
170
+ * @param {string} options.type - 制品类型 (ArtifactType)
171
+ * @param {string} options.path - 文件路径
172
+ * @param {number} [options.size=0] - 文件大小(字节)
173
+ * @param {Date|number|string} [options.lastModified] - 最后修改时间
174
+ * @param {string} [options.checksum=''] - 文件校验和(SHA256)
175
+ */
176
+ constructor({ type, path: filePath, size = 0, lastModified, checksum = '' }) {
177
+ const validTypes = Object.values(ArtifactType);
178
+ if (!validTypes.includes(type)) {
179
+ throw new Error(`无效的制品类型: ${type}, 有效值: ${validTypes.join(', ')}`);
180
+ }
181
+
182
+ /** @type {string} 制品类型 */
183
+ this.type = type;
184
+
185
+ /** @type {string} 文件路径 */
186
+ this.path = filePath;
187
+
188
+ /** @type {number} 文件大小(字节) */
189
+ this.size = size;
190
+
191
+ /** @type {number|null} 最后修改时间戳 */
192
+ this.lastModified = lastModified ? (
193
+ typeof lastModified === 'number' ? lastModified :
194
+ lastModified instanceof Date ? lastModified.getTime() :
195
+ new Date(lastModified).getTime()
196
+ ) : null;
197
+
198
+ /** @type {string} SHA256 校验和 */
199
+ this.checksum = checksum || '';
200
+ }
201
+
202
+ /**
203
+ * 序列化为 JSON 对象
204
+ * @returns {Object} JSON 对象
205
+ */
206
+ toJSON() {
207
+ return {
208
+ type: this.type,
209
+ path: this.path,
210
+ size: this.size,
211
+ lastModified: this.lastModified,
212
+ checksum: this.checksum
213
+ };
214
+ }
215
+
216
+ /**
217
+ * 从 JSON 对象反序列化
218
+ * @param {Object} data - JSON 数据
219
+ * @returns {Artifact} Artifact 实例
220
+ */
221
+ static fromJSON(data) {
222
+ if (!data || typeof data !== 'object') {
223
+ throw new Error('Artifact.fromJSON: 无效的数据');
224
+ }
225
+ return new Artifact({
226
+ type: data.type,
227
+ path: data.path,
228
+ size: data.size ?? 0,
229
+ lastModified: data.lastModified,
230
+ checksum: data.checksum ?? ''
231
+ });
232
+ }
233
+
234
+ /**
235
+ * 格式化文件大小
236
+ * @returns {string} 格式化的文件大小
237
+ */
238
+ getFormattedSize() {
239
+ const units = ['B', 'KB', 'MB', 'GB'];
240
+ let size = this.size;
241
+ let unitIndex = 0;
242
+ while (size >= 1024 && unitIndex < units.length - 1) {
243
+ size /= 1024;
244
+ unitIndex++;
245
+ }
246
+ return `${size.toFixed(2)} ${units[unitIndex]}`;
247
+ }
248
+ }
249
+
250
+ /**
251
+ * 质量指标
252
+ * 记录功能点的质量评估数据
253
+ */
254
+ export class QualityMetrics {
255
+ /**
256
+ * 创建质量指标
257
+ * @param {Object} options - 配置选项
258
+ * @param {number} [options.coverage=0] - 测试覆盖率 (0-100)
259
+ * @param {number} [options.score=0] - 质量评分 (0-100)
260
+ * @param {string} [options.grade='F'] - 等级 (S/A/B/C/D/F)
261
+ * @param {Array<{message:string,severity:string,file?:string,line?:number}>} [options.issues=[]] - 问题列表
262
+ * @param {number} [options.passRate=0] - 通过率 (0-100)
263
+ */
264
+ constructor({
265
+ coverage = 0,
266
+ score = 0,
267
+ grade = 'F',
268
+ issues = [],
269
+ passRate = 0
270
+ }) {
271
+ /** @type {number} 测试覆盖率 (0-100) */
272
+ this.coverage = Math.max(0, Math.min(100, coverage));
273
+
274
+ /** @type {number} 质量评分 (0-100) */
275
+ this.score = Math.max(0, Math.min(100, score));
276
+
277
+ /** @type {string} 等级 */
278
+ this.grade = this._validateGrade(grade);
279
+
280
+ /** @type {Array<Object>} 问题列表 */
281
+ this.issues = Array.isArray(issues) ? issues : [];
282
+
283
+ /** @type {number} 通过率 (0-100) */
284
+ this.passRate = Math.max(0, Math.min(100, passRate));
285
+ }
286
+
287
+ /**
288
+ * 验证等级值
289
+ * @param {string} grade - 等级值
290
+ * @returns {string} 有效的等级值
291
+ * @private
292
+ */
293
+ _validateGrade(grade) {
294
+ const validGrades = ['S', 'A', 'B', 'C', 'D', 'F'];
295
+ const upperGrade = String(grade).toUpperCase();
296
+ if (!validGrades.includes(upperGrade)) {
297
+ return 'F';
298
+ }
299
+ return upperGrade;
300
+ }
301
+
302
+ /**
303
+ * 根据分数自动计算等级
304
+ * @param {number} score - 分数
305
+ * @returns {string} 等级
306
+ */
307
+ static calculateGrade(score) {
308
+ if (score >= 95) return 'S';
309
+ if (score >= 85) return 'A';
310
+ if (score >= 70) return 'B';
311
+ if (score >= 55) return 'C';
312
+ if (score >= 40) return 'D';
313
+ return 'F';
314
+ }
315
+
316
+ /**
317
+ * 序列化为 JSON 对象
318
+ * @returns {Object} JSON 对象
319
+ */
320
+ toJSON() {
321
+ return {
322
+ coverage: this.coverage,
323
+ score: this.score,
324
+ grade: this.grade,
325
+ issues: [...this.issues],
326
+ passRate: this.passRate
327
+ };
328
+ }
329
+
330
+ /**
331
+ * 从 JSON 对象反序列化
332
+ * @param {Object} data - JSON 数据
333
+ * @returns {QualityMetrics} QualityMetrics 实例
334
+ */
335
+ static fromJSON(data) {
336
+ if (!data || typeof data !== 'object') {
337
+ throw new Error('QualityMetrics.fromJSON: 无效的数据');
338
+ }
339
+ return new QualityMetrics({
340
+ coverage: data.coverage ?? 0,
341
+ score: data.score ?? 0,
342
+ grade: data.grade ?? 'F',
343
+ issues: Array.isArray(data.issues) ? data.issues : [],
344
+ passRate: data.passRate ?? 0
345
+ });
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Token 使用情况
351
+ * 记录 AI 模型的 Token 消耗
352
+ */
353
+ export class TokenUsage {
354
+ /**
355
+ * 创建 Token 使用记录
356
+ * @param {Object} options - 配置选项
357
+ * @param {number} [options.total=0] - 总配额
358
+ * @param {number} [options.used=0] - 已使用量
359
+ * @param {number} [options.remaining=0] - 剩余量
360
+ * @param {Object<string,number>} [options.byStage={}] - 按阶段统计的使用量
361
+ * @param {Array<{stage:string,amount:number,timestamp:number}>} [options.history=[]] - 使用历史
362
+ */
363
+ constructor({
364
+ total = 0,
365
+ used = 0,
366
+ remaining = 0,
367
+ byStage = {},
368
+ history = []
369
+ }) {
370
+ /** @type {number} 总配额 */
371
+ this.total = total;
372
+
373
+ /** @type {number} 已使用量 */
374
+ this.used = used;
375
+
376
+ /** @type {number} 剩余量 */
377
+ this.remaining = remaining;
378
+
379
+ /** @type {Object<string,number>} 按阶段统计 */
380
+ this.byStage = { ...byStage };
381
+
382
+ /** @type {Array<Object>} 使用历史 */
383
+ this.history = Array.isArray(history) ? history : [];
384
+ }
385
+
386
+ /**
387
+ * 添加使用记录
388
+ * @param {string} stage - 阶段
389
+ * @param {number} amount - 使用量
390
+ * @param {number} [timestamp] - 时间戳
391
+ */
392
+ addUsage(stage, amount, timestamp) {
393
+ this.used += amount;
394
+ this.remaining = Math.max(0, this.total - this.used);
395
+
396
+ // 更新按阶段统计
397
+ if (!this.byStage[stage]) {
398
+ this.byStage[stage] = 0;
399
+ }
400
+ this.byStage[stage] += amount;
401
+
402
+ // 记录历史
403
+ this.history.push({
404
+ stage,
405
+ amount,
406
+ timestamp: timestamp || Date.now()
407
+ });
408
+ }
409
+
410
+ /**
411
+ * 序列化为 JSON 对象
412
+ * @returns {Object} JSON 对象
413
+ */
414
+ toJSON() {
415
+ return {
416
+ total: this.total,
417
+ used: this.used,
418
+ remaining: this.remaining,
419
+ byStage: { ...this.byStage },
420
+ history: [...this.history]
421
+ };
422
+ }
423
+
424
+ /**
425
+ * 从 JSON 对象反序列化
426
+ * @param {Object} data - JSON 数据
427
+ * @returns {TokenUsage} TokenUsage 实例
428
+ */
429
+ static fromJSON(data) {
430
+ if (!data || typeof data !== 'object') {
431
+ throw new Error('TokenUsage.fromJSON: 无效的数据');
432
+ }
433
+ return new TokenUsage({
434
+ total: data.total ?? 0,
435
+ used: data.used ?? 0,
436
+ remaining: data.remaining ?? 0,
437
+ byStage: data.byStage ? { ...data.byStage } : {},
438
+ history: Array.isArray(data.history) ? data.history : []
439
+ });
440
+ }
441
+
442
+ /**
443
+ * 获取使用率百分比
444
+ * @returns {number} 使用率 (0-100)
445
+ */
446
+ getUsagePercentage() {
447
+ if (this.total === 0) return 0;
448
+ return Math.round((this.used / this.total) * 100);
449
+ }
450
+ }
451
+
452
+ /**
453
+ * 迭代轮次
454
+ * 记录每次迭代修复的详细信息
455
+ */
456
+ export class IterationRound {
457
+ /**
458
+ * 创建迭代轮次
459
+ * @param {Object} options - 配置选项
460
+ * @param {number} options.round - 轮次号(从1开始)
461
+ * @param {number} [options.score=0] - 该轮得分
462
+ * @param {number} [options.issuesFixed=0] - 修复的问题数
463
+ * @param {number} [options.tokenUsed=0] - 消耗的 Token 数
464
+ * @param {Array<{file:string,type:string,description:string}>} [options.changes=[]] - 变更列表
465
+ * @param {Date|number|string} [options.timestamp] - 迭代时间
466
+ */
467
+ constructor({
468
+ round,
469
+ score = 0,
470
+ issuesFixed = 0,
471
+ tokenUsed = 0,
472
+ changes = [],
473
+ timestamp
474
+ }) {
475
+ if (!Number.isInteger(round) || round < 1) {
476
+ throw new Error('轮次号必须是正整数');
477
+ }
478
+
479
+ /** @type {number} 轮次号 */
480
+ this.round = round;
481
+
482
+ /** @type {number} 该轮得分 */
483
+ this.score = score;
484
+
485
+ /** @type {number} 修复的问题数 */
486
+ this.issuesFixed = issuesFixed;
487
+
488
+ /** @type {number} 消耗的 Token 数 */
489
+ this.tokenUsed = tokenUsed;
490
+
491
+ /** @type {Array<Object>} 变更列表 */
492
+ this.changes = Array.isArray(changes) ? changes : [];
493
+
494
+ /** @type {number} 迭代时间戳 */
495
+ this.timestamp = timestamp ? (
496
+ typeof timestamp === 'number' ? timestamp :
497
+ timestamp instanceof Date ? timestamp.getTime() :
498
+ new Date(timestamp).getTime()
499
+ ) : Date.now();
500
+ }
501
+
502
+ /**
503
+ * 序列化为 JSON 对象
504
+ * @returns {Object} JSON 对象
505
+ */
506
+ toJSON() {
507
+ return {
508
+ round: this.round,
509
+ score: this.score,
510
+ issuesFixed: this.issuesFixed,
511
+ tokenUsed: this.tokenUsed,
512
+ changes: [...this.changes],
513
+ timestamp: this.timestamp
514
+ };
515
+ }
516
+
517
+ /**
518
+ * 从 JSON 对象反序列化
519
+ * @param {Object} data - JSON 数据
520
+ * @returns {IterationRound} IterationRound 实例
521
+ */
522
+ static fromJSON(data) {
523
+ if (!data || typeof data !== 'object') {
524
+ throw new Error('IterationRound.fromJSON: 无效的数据');
525
+ }
526
+ return new IterationRound({
527
+ round: data.round,
528
+ score: data.score ?? 0,
529
+ issuesFixed: data.issuesFixed ?? 0,
530
+ tokenUsed: data.tokenUsed ?? 0,
531
+ changes: Array.isArray(data.changes) ? data.changes : [],
532
+ timestamp: data.timestamp
533
+ });
534
+ }
535
+ }
536
+
537
+ /**
538
+ * 功能点核心模型
539
+ * PDD-VM 的核心数据结构,记录一个功能点的完整生命周期
540
+ */
541
+ export class Feature {
542
+ /**
543
+ * 创建功能点
544
+ * @param {Object} options - 配置选项
545
+ * @param {string} options.id - 唯一标识符
546
+ * @param {string} options.name - 功能点名称
547
+ * @param {string} [options.description=''] - 描述
548
+ * @param {string} [options.stage=StageEnum.PRD] - 当前阶段
549
+ * @param {string} [options.priority=Priority.P2] - 优先级
550
+ * @param {TimelineEntry[]} [options.timeline=[]] - 时间线
551
+ * @param {Artifact[]} [options.artifacts=[]] - 制品列表
552
+ * @param {QualityMetrics|null} [options.quality=null] - 质量指标
553
+ * @param {TokenUsage} [options.tokens] - Token 使用
554
+ * @param {IterationRound[]} [options.iterations=[]] - 迭代轮次
555
+ * @param {string[]} [options.tags=[]] - 标签
556
+ * @param {Date|number|string} [options.createdAt] - 创建时间
557
+ * @param {Date|number|string} [options.updatedAt] - 更新时间
558
+ */
559
+ constructor({
560
+ id,
561
+ name,
562
+ description = '',
563
+ stage = StageEnum.PRD,
564
+ priority = Priority.P2,
565
+ timeline = [],
566
+ artifacts = [],
567
+ quality = null,
568
+ tokens,
569
+ iterations = [],
570
+ tags = [],
571
+ createdAt,
572
+ updatedAt
573
+ }) {
574
+ if (!id || typeof id !== 'string') {
575
+ throw new Error('Feature.id 必须是非空字符串');
576
+ }
577
+ if (!name || typeof name !== 'string') {
578
+ throw new Error('Feature.name 必须是非空字符串');
579
+ }
580
+ if (!STAGE_VALUES.includes(stage)) {
581
+ throw new Error(`无效的阶段值: ${stage}`);
582
+ }
583
+ if (!Object.values(Priority).includes(priority)) {
584
+ throw new Error(`无效的优先级: ${priority}`);
585
+ }
586
+
587
+ /** @type {string} 唯一标识符 */
588
+ this.id = id;
589
+
590
+ /** @type {string} 功能点名称 */
591
+ this.name = name;
592
+
593
+ /** @type {string} 描述 */
594
+ this.description = description;
595
+
596
+ /** @type {string} 当前阶段 */
597
+ this.stage = stage;
598
+
599
+ /** @type {string} 优先级 */
600
+ this.priority = priority;
601
+
602
+ /** @type {TimelineEntry[]} 时间线 */
603
+ this.timeline = Array.isArray(timeline) ? timeline.map(
604
+ t => t instanceof TimelineEntry ? t : TimelineEntry.fromJSON(t)
605
+ ) : [];
606
+
607
+ /** @type {Artifact[]} 制品列表 */
608
+ this.artifacts = Array.isArray(artifacts) ? artifacts.map(
609
+ a => a instanceof Artifact ? a : Artifact.fromJSON(a)
610
+ ) : [];
611
+
612
+ /** @type {QualityMetrics|null} 质量指标 */
613
+ this.quality = quality ? (
614
+ quality instanceof QualityMetrics ? quality : QualityMetrics.fromJSON(quality)
615
+ ) : null;
616
+
617
+ /** @type {TokenUsage} Token 使用情况 */
618
+ this.tokens = tokens ? (
619
+ tokens instanceof TokenUsage ? tokens : TokenUsage.fromJSON(tokens)
620
+ ) : new TokenUsage();
621
+
622
+ /** @type {IterationRound[]} 迭代轮次 */
623
+ this.iterations = Array.isArray(iterations) ? iterations.map(
624
+ i => i instanceof IterationRound ? i : IterationRound.fromJSON(i)
625
+ ) : [];
626
+
627
+ /** @type {string[]} 标签 */
628
+ this.tags = Array.isArray(tags) ? tags : [];
629
+
630
+ const now = Date.now();
631
+
632
+ /** @type {number} 创建时间戳 */
633
+ this.createdAt = createdAt ? (
634
+ typeof createdAt === 'number' ? createdAt :
635
+ createdAt instanceof Date ? createdAt.getTime() :
636
+ new Date(createdAt).getTime()
637
+ ) : now;
638
+
639
+ /** @type {number} 更新时间戳 */
640
+ this.updatedAt = updatedAt ? (
641
+ typeof updatedAt === 'number' ? updatedAt :
642
+ updatedAt instanceof Date ? updatedAt.getTime() :
643
+ new Date(updatedAt).getTime()
644
+ ) : now;
645
+ }
646
+
647
+ /**
648
+ * 计算完成进度 (0-100)
649
+ * 基于当前阶段在流程中的位置
650
+ * @returns {number} 进度百分比
651
+ */
652
+ progress() {
653
+ const order = STAGE_ORDER[this.stage];
654
+ if (order === undefined) return 0;
655
+ return Math.round((order / (Object.keys(STAGE_ORDER).length - 1)) * 100);
656
+ }
657
+
658
+ /**
659
+ * 判断是否已收敛(迭代是否稳定)
660
+ * 收敛条件:最近2轮迭代分数变化小于5分,或已完成3轮以上
661
+ * @returns {boolean} 是否收敛
662
+ */
663
+ isConverged() {
664
+ if (this.iterations.length < 2) return false;
665
+ if (this.stage === StageEnum.DONE) return true;
666
+
667
+ // 检查最近两轮的分数变化
668
+ const sorted = [...this.iterations].sort((a, b) => b.round - a.round);
669
+ const recent = sorted.slice(0, 2);
670
+ if (recent.length >= 2) {
671
+ const diff = Math.abs(recent[0].score - recent[1].score);
672
+ if (diff < 5 && this.iterations.length >= 3) {
673
+ return true;
674
+ }
675
+ }
676
+
677
+ // 超过5轮自动认为收敛
678
+ return this.iterations.length >= 5;
679
+ }
680
+
681
+ /**
682
+ * 添加时间线条目
683
+ * @param {string} stage - 阶段
684
+ * @param {string} [note=''] - 备注
685
+ * @returns {TimelineEntry} 新创建的时间线条目
686
+ */
687
+ addTimelineEntry(stage, note = '') {
688
+ const entry = new TimelineEntry({
689
+ stage,
690
+ timestamp: Date.now(),
691
+ note
692
+ });
693
+
694
+ // 计算上一阶段的持续时间
695
+ if (this.timeline.length > 0) {
696
+ const prev = this.timeline[this.timeline.length - 1];
697
+ entry.duration = entry.timestamp - prev.timestamp;
698
+ prev.duration = entry.duration; // 更新前一条的duration
699
+ }
700
+
701
+ this.timeline.push(entry);
702
+ this.stage = stage;
703
+ this.updatedAt = Date.now();
704
+
705
+ return entry;
706
+ }
707
+
708
+ /**
709
+ * 添加制品
710
+ * @param {Artifact} artifact - 制品对象
711
+ * @returns {void}
712
+ */
713
+ addArtifact(artifact) {
714
+ const art = artifact instanceof Artifact ? artifact : new Artifact(artifact);
715
+
716
+ // 移除同类型的旧制品
717
+ this.artifacts = this.artifacts.filter(a => a.type !== art.type);
718
+ this.artifacts.push(art);
719
+ this.updatedAt = Date.now();
720
+ }
721
+
722
+ /**
723
+ * 获取指定类型的制品
724
+ * @param {string} type - 制品类型
725
+ * @returns {Artifact|null} 制品对象或null
726
+ */
727
+ getArtifactByType(type) {
728
+ return this.artifacts.find(a => a.type === type) || null;
729
+ }
730
+
731
+ /**
732
+ * 序列化为 JSON 对象
733
+ * 完整处理所有嵌套对象
734
+ * @returns {Object} JSON 对象
735
+ */
736
+ toJSON() {
737
+ return {
738
+ id: this.id,
739
+ name: this.name,
740
+ description: this.description,
741
+ stage: this.stage,
742
+ priority: this.priority,
743
+ timeline: this.timeline.map(t => t.toJSON()),
744
+ artifacts: this.artifacts.map(a => a.toJSON()),
745
+ quality: this.quality ? this.quality.toJSON() : null,
746
+ tokens: this.tokens.toJSON(),
747
+ iterations: this.iterations.map(i => i.toJSON()),
748
+ tags: [...this.tags],
749
+ createdAt: this.createdAt,
750
+ updatedAt: this.updatedAt
751
+ };
752
+ }
753
+
754
+ /**
755
+ * 从 JSON 对象反序列化
756
+ * @param {Object} data - JSON 数据
757
+ * @returns {Feature} Feature 实例
758
+ */
759
+ static fromJSON(data) {
760
+ if (!data || typeof data !== 'object') {
761
+ throw new Error('Feature.fromJSON: 无效的数据');
762
+ }
763
+ if (!data.id || !data.name) {
764
+ throw new Error('Feature.fromJSON: 缺少必要字段 id 或 name');
765
+ }
766
+ return new Feature({
767
+ id: data.id,
768
+ name: data.name,
769
+ description: data.description ?? '',
770
+ stage: data.stage ?? StageEnum.PRD,
771
+ priority: data.priority ?? Priority.P2,
772
+ timeline: Array.isArray(data.timeline) ? data.timeline : [],
773
+ artifacts: Array.isArray(data.artifacts) ? data.artifacts : [],
774
+ quality: data.quality || null,
775
+ tokens: data.tokens || null,
776
+ iterations: Array.isArray(data.iterations) ? data.iterations : [],
777
+ tags: Array.isArray(data.tags) ? data.tags : [],
778
+ createdAt: data.createdAt,
779
+ updatedAt: data.updatedAt
780
+ });
781
+ }
782
+
783
+ /**
784
+ * 格式化摘要信息
785
+ * @returns {string} 摘要字符串
786
+ */
787
+ toSummary() {
788
+ const progress = this.progress();
789
+ const progressBar = '[' + '='.repeat(Math.floor(progress / 5)) + '-'.repeat(20 - Math.floor(progress / 5)) + ']';
790
+
791
+ return [
792
+ `${chalk.magenta(this.id)} ${chalk.bold(this.name)}`,
793
+ ` 阶段: ${chalk.cyan(this.stage)} | 优先级: ${chalk.yellow(this.priority)}`,
794
+ ` 进度: ${progressBar} ${progress}%`,
795
+ ` 质量: ${this.quality ? `${this.quality.score}(${this.quality.grade})` : chalk.gray('未评估')}`,
796
+ ` 迭代: ${this.iterations.length}轮${this.isConverged ? chalk.green(' [已收敛]') : ''}`
797
+ ].join('\n');
798
+ }
799
+ }
800
+
801
+ /**
802
+ * 项目汇总信息
803
+ * 提供项目级别的统计数据和概览
804
+ */
805
+ export class ProjectSummary {
806
+ /**
807
+ * 创建项目汇总
808
+ * @param {Object} options - 配置选项
809
+ * @param {string} [options.name=''] - 项目名称
810
+ * @param {string} [options.version='1.0.0'] - 版本号
811
+ * @param {number} [options.totalFeatures=0] - 总功能点数
812
+ * @param {Object<string,number>} [options.stageDistribution={}] - 各阶段分布
813
+ * @param {number} [options.overallProgress=0] - 整体进度 (0-100)
814
+ * @param {number} [options.avgQualityScore=0] - 平均质量分数
815
+ * @param {number} [options.totalTokens=0] - 总Token消耗
816
+ * @param {number} [options.avgIterations=0] - 平均迭代次数
817
+ * @param {Date|number|string} [options.lastUpdated] - 最后更新时间
818
+ */
819
+ constructor({
820
+ name = '',
821
+ version = '1.0.0',
822
+ totalFeatures = 0,
823
+ stageDistribution = {},
824
+ overallProgress = 0,
825
+ avgQualityScore = 0,
826
+ totalTokens = 0,
827
+ avgIterations = 0,
828
+ lastUpdated
829
+ }) {
830
+ /** @type {string} 项目名称 */
831
+ this.name = name;
832
+
833
+ /** @type {string} 版本号 */
834
+ this.version = version;
835
+
836
+ /** @type {number} 总功能点数 */
837
+ this.totalFeatures = totalFeatures;
838
+
839
+ /** @type {Object<string,number>} 各阶段分布 */
840
+ this.stageDistribution = { ...stageDistribution };
841
+
842
+ /** @type {number} 整体进度 (0-100) */
843
+ this.overallProgress = Math.max(0, Math.min(100, overallProgress));
844
+
845
+ /** @type {number} 平均质量分数 */
846
+ this.avgQualityScore = avgQualityScore;
847
+
848
+ /** @type {number} 总Token消耗 */
849
+ this.totalTokens = totalTokens;
850
+
851
+ /** @type {number} 平均迭代次数 */
852
+ this.avgIterations = avgIterations;
853
+
854
+ /** @type {number|null} 最后更新时间戳 */
855
+ this.lastUpdated = lastUpdated ? (
856
+ typeof lastUpdated === 'number' ? lastUpdated :
857
+ lastUpdated instanceof Date ? lastUpdated.getTime() :
858
+ new Date(lastUpdated).getTime()
859
+ ) : null;
860
+ }
861
+
862
+ /**
863
+ * 从功能点数组生成汇总
864
+ * @param {Feature[]} features - 功能点数组
865
+ * @param {string} [name=''] - 项目名称
866
+ * @param {string} [version='1.0.0'] - 版本号
867
+ * @returns {ProjectSummary} ProjectSummary 实例
868
+ */
869
+ static fromFeatures(features, name = '', version = '1.0.0') {
870
+ const featArray = Array.isArray(features) ? features : [];
871
+
872
+ // 统计各阶段分布
873
+ const stageDistribution = {};
874
+ for (const stage of STAGE_VALUES) {
875
+ stageDistribution[stage] = 0;
876
+ }
877
+ for (const f of featArray) {
878
+ if (f.stage in stageDistribution) {
879
+ stageDistribution[f.stage]++;
880
+ }
881
+ }
882
+
883
+ // 计算整体进度
884
+ let totalProgress = 0;
885
+ let totalQuality = 0;
886
+ let qualityCount = 0;
887
+ let totalTokens = 0;
888
+ let totalIterations = 0;
889
+
890
+ for (const f of featArray) {
891
+ totalProgress += f.progress();
892
+ if (f.quality) {
893
+ totalQuality += f.quality.score;
894
+ qualityCount++;
895
+ }
896
+ totalTokens += f.tokens.used;
897
+ totalIterations += f.iterations.length;
898
+ }
899
+
900
+ const count = featArray.length || 1;
901
+
902
+ return new ProjectSummary({
903
+ name,
904
+ version,
905
+ totalFeatures: featArray.length,
906
+ stageDistribution,
907
+ overallProgress: Math.round(totalProgress / count),
908
+ avgQualityScore: qualityCount > 0 ? Math.round(totalQuality / qualityCount) : 0,
909
+ totalTokens,
910
+ avgIterations: count > 0 ? Math.round((totalIterations / count) * 10) / 10 : 0,
911
+ lastUpdated: Date.now()
912
+ });
913
+ }
914
+
915
+ /**
916
+ * 序列化为 JSON 对象
917
+ * @returns {Object} JSON 对象
918
+ */
919
+ toJSON() {
920
+ return {
921
+ name: this.name,
922
+ version: this.version,
923
+ totalFeatures: this.totalFeatures,
924
+ stageDistribution: { ...this.stageDistribution },
925
+ overallProgress: this.overallProgress,
926
+ avgQualityScore: this.avgQualityScore,
927
+ totalTokens: this.totalTokens,
928
+ avgIterations: this.avgIterations,
929
+ lastUpdated: this.lastUpdated
930
+ };
931
+ }
932
+
933
+ /**
934
+ * 从 JSON 对象反序列化
935
+ * @param {Object} data - JSON 数据
936
+ * @returns {ProjectSummary} ProjectSummary 实例
937
+ */
938
+ static fromJSON(data) {
939
+ if (!data || typeof data !== 'object') {
940
+ throw new Error('ProjectSummary.fromJSON: 无效的数据');
941
+ }
942
+ return new ProjectSummary({
943
+ name: data.name ?? '',
944
+ version: data.version ?? '1.0.0',
945
+ totalFeatures: data.totalFeatures ?? 0,
946
+ stageDistribution: data.stageDistribution ? { ...data.stageDistribution } : {},
947
+ overallProgress: data.overallProgress ?? 0,
948
+ avgQualityScore: data.avgQualityScore ?? 0,
949
+ totalTokens: data.totalTokens ?? 0,
950
+ avgIterations: data.avgIterations ?? 0,
951
+ lastUpdated: data.lastUpdated
952
+ });
953
+ }
954
+
955
+ /**
956
+ * 格式化显示项目状态
957
+ * @returns {string} 格式化的状态报告
958
+ */
959
+ toReport() {
960
+ const lines = [
961
+ chalk.bold.blue('=== 项目汇总 ==='),
962
+ `项目名称: ${chalk.green(this.name || '(未命名)')}`,
963
+ `版本: ${this.version}`,
964
+ `总功能点: ${this.totalFeatures}`,
965
+ ``,
966
+ chalk.bold('阶段分布:')
967
+ ];
968
+
969
+ for (const [stage, count] of Object.entries(this.stageDistribution)) {
970
+ const bar = '#'.repeat(count) || '-';
971
+ lines.push(` ${chalk.cyan(stage.padEnd(12))}: ${String(count).padStart(3)} ${bar}`);
972
+ }
973
+
974
+ lines.push('', `整体进度: ${this.overallProgress}%`);
975
+ lines.push(`平均质量分: ${this.avgQualityScore || chalk.gray('N/A')}`);
976
+ lines.push(`总Token消耗: ${this.totalTokens.toLocaleString()}`);
977
+ lines.push(`平均迭代次数: ${this.avgIterations}`);
978
+
979
+ if (this.lastUpdated) {
980
+ lines.push(`最后更新: ${new Date(this.lastUpdated).toLocaleString()}`);
981
+ }
982
+
983
+ return lines.join('\n');
984
+ }
985
+ }
986
+
987
+ /**
988
+ * 导出默认对象(包含所有模型类)
989
+ */
990
+ export default {
991
+ StageEnum,
992
+ STAGE_VALUES,
993
+ STAGE_ORDER,
994
+ Priority,
995
+ ArtifactType,
996
+ TimelineEntry,
997
+ Artifact,
998
+ QualityMetrics,
999
+ TokenUsage,
1000
+ IterationRound,
1001
+ Feature,
1002
+ ProjectSummary
1003
+ };