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,955 @@
1
+ /**
2
+ * PDD Visual Manager - JSON Schema 定义与验证
3
+ *
4
+ * 定义 project-state.json 的完整 Schema,提供数据校验和版本迁移功能。
5
+ *
6
+ * Schema 版本: 1.0
7
+ * 文件格式: project-state.json (v1.0)
8
+ */
9
+
10
+ import { STAGE_VALUES, Priority, ArtifactType } from './models.js';
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
+ blue: (s) => s,
24
+ gray: (s) => s
25
+ };
26
+ }
27
+
28
+ /**
29
+ * 当前 Schema 版本号
30
+ * @type {string}
31
+ */
32
+ export const SCHEMA_VERSION = '1.0.0';
33
+
34
+ /**
35
+ * 支持的最低迁移源版本
36
+ * @type {string}
37
+ */
38
+ export const MIN_MIGRATABLE_VERSION = '0.9.0';
39
+
40
+ /**
41
+ * 项目状态 JSON Schema 完整定义
42
+ * 定义 project-state.json v1.0 格式规范
43
+ *
44
+ * @type {Object}
45
+ */
46
+ export const STATE_SCHEMA = {
47
+ $schema: 'http://json-schema.org/draft-07/schema#',
48
+ $id: 'https://pdd.dev/schemas/project-state-v1.json',
49
+ title: 'PDD Project State',
50
+ description: 'PDD Visual Manager 项目状态文件格式,记录功能点开发进度和元数据',
51
+ version: SCHEMA_VERSION,
52
+
53
+ type: 'object',
54
+ required: ['version', 'project', 'metadata'],
55
+ additionalProperties: false,
56
+
57
+ properties: {
58
+ /**
59
+ * Schema 版本标识
60
+ */
61
+ version: {
62
+ type: 'string',
63
+ description: 'Schema 格式版本号',
64
+ pattern: '^\\d+\\.\\d+\\.\\d+$',
65
+ const: SCHEMA_VERSION
66
+ },
67
+
68
+ /**
69
+ * 项目信息
70
+ */
71
+ project: {
72
+ type: 'object',
73
+ description: '项目基本信息和功能点列表',
74
+ required: ['name', 'features'],
75
+ additionalProperties: false,
76
+
77
+ properties: {
78
+ name: {
79
+ type: 'string',
80
+ description: '项目名称',
81
+ minLength: 1,
82
+ maxLength: 200
83
+ },
84
+ version: {
85
+ type: 'string',
86
+ description: '项目版本号',
87
+ pattern: '^\\d+\\.\\d+\\.\\d+$',
88
+ default: '1.0.0'
89
+ },
90
+ description: {
91
+ type: 'string',
92
+ description: '项目描述',
93
+ maxLength: 2000,
94
+ default: ''
95
+ },
96
+
97
+ /**
98
+ * 功能点列表
99
+ */
100
+ features: {
101
+ type: 'array',
102
+ description: '功能点定义列表',
103
+ items: {
104
+ type: 'object',
105
+ required: ['id', 'name', 'stage', 'priority'],
106
+ additionalProperties: false,
107
+
108
+ properties: {
109
+ id: {
110
+ type: 'string',
111
+ description: '功能点唯一标识符',
112
+ minLength: 1,
113
+ pattern: '^[a-zA-Z0-9_-]+$'
114
+ },
115
+ name: {
116
+ type: 'string',
117
+ description: '功能点名称',
118
+ minLength: 1,
119
+ maxLength: 200
120
+ },
121
+ description: {
122
+ type: 'string',
123
+ description: '功能点描述',
124
+ maxLength: 5000,
125
+ default: ''
126
+ },
127
+
128
+ /**
129
+ * 开发阶段
130
+ */
131
+ stage: {
132
+ type: 'string',
133
+ description: '当前开发阶段',
134
+ enum: [...STAGE_VALUES]
135
+ },
136
+
137
+ /**
138
+ * 优先级
139
+ */
140
+ priority: {
141
+ type: 'string',
142
+ description: '功能点优先级',
143
+ enum: Object.values(Priority),
144
+ default: 'P2'
145
+ },
146
+
147
+ /**
148
+ * 时间线
149
+ */
150
+ timeline: {
151
+ type: 'array',
152
+ description: '阶段转换时间线',
153
+ items: {
154
+ type: 'object',
155
+ required: ['stage', 'timestamp'],
156
+ additionalProperties: false,
157
+ properties: {
158
+ stage: {
159
+ type: 'string',
160
+ enum: [...STAGE_VALUES]
161
+ },
162
+ timestamp: {
163
+ type: 'number',
164
+ description: 'Unix 时间戳(毫秒)'
165
+ },
166
+ duration: {
167
+ type: 'number',
168
+ description: '阶段持续时间(毫秒)',
169
+ minimum: 0,
170
+ default: 0
171
+ },
172
+ note: {
173
+ type: 'string',
174
+ description: '备注',
175
+ maxLength: 1000,
176
+ default: ''
177
+ }
178
+ }
179
+ },
180
+ default: []
181
+ },
182
+
183
+ /**
184
+ * 制品列表
185
+ */
186
+ artifacts: {
187
+ type: 'array',
188
+ description: '生成的制品文件列表',
189
+ items: {
190
+ type: 'object',
191
+ required: ['type', 'path'],
192
+ additionalProperties: false,
193
+ properties: {
194
+ type: {
195
+ type: 'string',
196
+ enum: Object.values(ArtifactType)
197
+ },
198
+ path: {
199
+ type: 'string',
200
+ description: '文件相对或绝对路径',
201
+ minLength: 1
202
+ },
203
+ size: {
204
+ type: 'number',
205
+ description: '文件大小(字节)',
206
+ minimum: 0,
207
+ default: 0
208
+ },
209
+ lastModified: {
210
+ type: ['number', 'null'],
211
+ description: '最后修改时间戳'
212
+ },
213
+ checksum: {
214
+ type: 'string',
215
+ description: 'SHA256 校验和',
216
+ default: ''
217
+ }
218
+ }
219
+ },
220
+ default: []
221
+ },
222
+
223
+ /**
224
+ * 质量指标
225
+ */
226
+ quality: {
227
+ type: ['object', 'null'],
228
+ description: '质量评估指标',
229
+ additionalProperties: false,
230
+ properties: {
231
+ coverage: {
232
+ type: 'number',
233
+ description: '测试覆盖率 (%)',
234
+ minimum: 0,
235
+ maximum: 100,
236
+ default: 0
237
+ },
238
+ score: {
239
+ type: 'number',
240
+ description: '质量评分 (0-100)',
241
+ minimum: 0,
242
+ maximum: 100,
243
+ default: 0
244
+ },
245
+ grade: {
246
+ type: 'string',
247
+ description: '质量等级',
248
+ enum: ['S', 'A', 'B', 'C', 'D', 'F'],
249
+ default: 'F'
250
+ },
251
+ issues: {
252
+ type: 'array',
253
+ description: '问题列表',
254
+ items: {
255
+ type: 'object',
256
+ required: ['message', 'severity'],
257
+ properties: {
258
+ message: { type: 'string' },
259
+ severity: { type: 'string', enum: ['error', 'warning', 'info'] },
260
+ file: { type: 'string' },
261
+ line: { type: 'number' }
262
+ }
263
+ },
264
+ default: []
265
+ },
266
+ passRate: {
267
+ type: 'number',
268
+ description: '测试通过率 (%)',
269
+ minimum: 0,
270
+ maximum: 100,
271
+ default: 0
272
+ }
273
+ },
274
+ default: null
275
+ },
276
+
277
+ /**
278
+ * Token 使用情况
279
+ */
280
+ tokens: {
281
+ type: 'object',
282
+ description: 'AI Token 消耗统计',
283
+ required: ['total', 'used', 'remaining'],
284
+ additionalProperties: false,
285
+ properties: {
286
+ total: {
287
+ type: 'number',
288
+ minimum: 0,
289
+ default: 0
290
+ },
291
+ used: {
292
+ type: 'number',
293
+ minimum: 0,
294
+ default: 0
295
+ },
296
+ remaining: {
297
+ type: 'number',
298
+ minimum: 0,
299
+ default: 0
300
+ },
301
+ byStage: {
302
+ type: 'object',
303
+ description: '按阶段统计的 Token 使用量',
304
+ additionalProperties: { type: 'number', minimum: 0 },
305
+ default: {}
306
+ },
307
+ history: {
308
+ type: 'array',
309
+ description: 'Token 使用历史记录',
310
+ items: {
311
+ type: 'object',
312
+ required: ['stage', 'amount', 'timestamp'],
313
+ properties: {
314
+ stage: { type: 'string' },
315
+ amount: { type: 'number', minimum: 0 },
316
+ timestamp: { type: 'number' }
317
+ }
318
+ },
319
+ default: []
320
+ }
321
+ }
322
+ },
323
+
324
+ /**
325
+ * 迭代轮次
326
+ */
327
+ iterations: {
328
+ type: 'array',
329
+ description: '迭代修复轮次记录',
330
+ items: {
331
+ type: 'object',
332
+ required: ['round'],
333
+ additionalProperties: false,
334
+ properties: {
335
+ round: {
336
+ type: 'integer',
337
+ minimum: 1,
338
+ description: '轮次编号'
339
+ },
340
+ score: {
341
+ type: 'number',
342
+ description: '该轮得分',
343
+ default: 0
344
+ },
345
+ issuesFixed: {
346
+ type: 'integer',
347
+ minimum: 0,
348
+ description: '修复的问题数',
349
+ default: 0
350
+ },
351
+ tokenUsed: {
352
+ type: 'number',
353
+ minimum: 0,
354
+ description: '消耗的 Token 数',
355
+ default: 0
356
+ },
357
+ changes: {
358
+ type: 'array',
359
+ description: '变更列表',
360
+ items: {
361
+ type: 'object',
362
+ properties: {
363
+ file: { type: 'string' },
364
+ type: { type: 'string' },
365
+ description: { type: 'string' }
366
+ }
367
+ },
368
+ default: []
369
+ },
370
+ timestamp: {
371
+ type: 'number',
372
+ description: '迭代时间戳'
373
+ }
374
+ }
375
+ },
376
+ default: []
377
+ },
378
+
379
+ /**
380
+ * 标签
381
+ */
382
+ tags: {
383
+ type: 'array',
384
+ description: '功能点标签',
385
+ items: {
386
+ type: 'string',
387
+ minLength: 1,
388
+ maxLength: 50
389
+ },
390
+ uniqueItems: true,
391
+ default: []
392
+ },
393
+
394
+ /**
395
+ * 时间戳
396
+ */
397
+ createdAt: {
398
+ type: 'number',
399
+ description: '创建时间戳(毫秒)'
400
+ },
401
+ updatedAt: {
402
+ type: 'number',
403
+ description: '最后更新时间戳(毫秒)'
404
+ }
405
+ }
406
+ },
407
+ uniqueItems: false
408
+ }
409
+ }
410
+ },
411
+
412
+ /**
413
+ * 元数据
414
+ */
415
+ metadata: {
416
+ type: 'object',
417
+ description: '状态文件元数据',
418
+ required: ['lastUpdated', 'schemaVersion'],
419
+ additionalProperties: true, // 允许扩展字段
420
+
421
+ properties: {
422
+ lastUpdated: {
423
+ type: 'number',
424
+ description: '最后更新时间戳(毫秒)'
425
+ },
426
+ schemaVersion: {
427
+ type: 'string',
428
+ description: '使用的 Schema 版本',
429
+ const: SCHEMA_VERSION
430
+ },
431
+ generatedBy: {
432
+ type: 'string',
433
+ description: '生成工具版本',
434
+ default: `pdd-vm/${SCHEMA_VERSION}`
435
+ },
436
+ checksum: {
437
+ type: 'string',
438
+ description: '文件内容校验和'
439
+ }
440
+ }
441
+ }
442
+ }
443
+ };
444
+
445
+ /**
446
+ * 验证结果类型
447
+ * @typedef {Object} ValidationResult
448
+ * @property {boolean} valid - 是否通过验证
449
+ * @property {Array<ValidationError>} errors - 错误列表
450
+ */
451
+
452
+ /**
453
+ * 验证错误类型
454
+ * @typedef {Object} ValidationError
455
+ * @property {string} path - 错误路径(JSON Pointer)
456
+ * @property {string} message - 错误消息
457
+ * @property {string} [value] - 导致错误的值
458
+ * @property {string} [constraint] - 违反的约束类型
459
+ */
460
+
461
+ /**
462
+ * 简易 JSON Schema 验证器
463
+ * 不依赖外部库,实现核心的 Schema 验证逻辑
464
+ *
465
+ * @param {*} data - 待验证的数据
466
+ * @param {Object} schema - Schema 定义
467
+ * @param {string} [path=''] - 当前路径(用于错误定位)
468
+ * @returns {{valid:boolean, errors:Array<{path:string, message:string}>}} 验证结果
469
+ */
470
+ function validateAgainstSchema(data, schema, path = '') {
471
+ const errors = [];
472
+
473
+ // 类型检查
474
+ if (schema.type && !checkType(data, schema.type)) {
475
+ errors.push({
476
+ path: path || '(root)',
477
+ message: `期望类型 ${Array.isArray(schema.type) ? schema.type.join('|') : schema.type},实际为 ${typeof data}`,
478
+ constraint: 'type'
479
+ });
480
+ return { valid: false, errors };
481
+ }
482
+
483
+ // 必填检查
484
+ if (schema.required && typeof data === 'object' && data !== null && !Array.isArray(data)) {
485
+ for (const field of schema.required) {
486
+ if (!(field in data)) {
487
+ errors.push({
488
+ path: `${path}/${field}`,
489
+ message: `缺少必填字段 "${field}"`,
490
+ constraint: 'required'
491
+ });
492
+ }
493
+ }
494
+ }
495
+
496
+ // 枚举值检查
497
+ if (schema.enum && !schema.enum.includes(data)) {
498
+ errors.push({
499
+ path: path || '(root)',
500
+ message: `值 "${data}" 不在允许的范围 ${JSON.stringify(schema.enum)} 内`,
501
+ constraint: 'enum'
502
+ });
503
+ }
504
+
505
+ // 常量检查
506
+ if ('const' in schema && data !== schema.const) {
507
+ errors.push({
508
+ path: path || '(root)',
509
+ message: `值必须是 "${schema.const}"`,
510
+ constraint: 'const'
511
+ });
512
+ }
513
+
514
+ // 字符串约束
515
+ if (typeof data === 'string') {
516
+ if (schema.minLength !== undefined && data.length < schema.minLength) {
517
+ errors.push({
518
+ path: path || '(root)',
519
+ message: `字符串长度 ${data.length} 小于最小长度 ${schema.minLength}`,
520
+ constraint: 'minLength'
521
+ });
522
+ }
523
+ if (schema.maxLength !== undefined && data.length > schema.maxLength) {
524
+ errors.push({
525
+ path: path || '(root)',
526
+ message: `字符串长度 ${data.length} 超过最大长度 ${schema.maxLength}`,
527
+ constraint: 'maxLength'
528
+ });
529
+ }
530
+ if (schema.pattern) {
531
+ const regex = new RegExp(schema.pattern);
532
+ if (!regex.test(data)) {
533
+ errors.push({
534
+ path: path || '(root)',
535
+ message: `字符串不匹配模式 ${schema.pattern}`,
536
+ constraint: 'pattern'
537
+ });
538
+ }
539
+ }
540
+ }
541
+
542
+ // 数字约束
543
+ if (typeof data === 'number') {
544
+ if (schema.minimum !== undefined && data < schema.minimum) {
545
+ errors.push({
546
+ path: path || '(root)',
547
+ message: `数值 ${data} 小于最小值 ${schema.minimum}`,
548
+ constraint: 'minimum'
549
+ });
550
+ }
551
+ if (schema.maximum !== undefined && data > schema.maximum) {
552
+ errors.push({
553
+ path: path || '(root)',
554
+ message: `数值 ${data} 超过最大值 ${schema.maximum}`,
555
+ constraint: 'maximum'
556
+ });
557
+ }
558
+ }
559
+
560
+ // 数组约束
561
+ if (Array.isArray(data)) {
562
+ if (schema.items) {
563
+ data.forEach((item, index) => {
564
+ const result = validateAgainstSchema(item, schema.items, `${path}[${index}]`);
565
+ errors.push(...result.errors);
566
+ });
567
+ }
568
+ if (schema.uniqueItems) {
569
+ const seen = new Set();
570
+ for (let i = 0; i < data.length; i++) {
571
+ const key = JSON.stringify(data[i]);
572
+ if (seen.has(key)) {
573
+ errors.push({
574
+ path: `${path}[${i}]`,
575
+ message: `数组元素重复`,
576
+ constraint: 'uniqueItems'
577
+ });
578
+ }
579
+ seen.add(key);
580
+ }
581
+ }
582
+ }
583
+
584
+ // 对象属性验证
585
+ if (typeof data === 'object' && data !== null && !Array.isArray(data)) {
586
+ if (schema.properties) {
587
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
588
+ if (key in data) {
589
+ const result = validateAgainstSchema(data[key], propSchema, `${path}/${key}`);
590
+ errors.push(...result.errors);
591
+ }
592
+ }
593
+
594
+ // 补充默认值的处理提示(非错误)
595
+ for (const key of Object.keys(schema.properties)) {
596
+ if (!(key in data) && schema.properties[key].default !== undefined) {
597
+ // 可选:记录缺失但有默认值的字段
598
+ }
599
+ }
600
+ }
601
+
602
+ // 额外属性检查
603
+ if (schema.additionalProperties === false && schema.properties) {
604
+ for (const key of Object.keys(data)) {
605
+ if (!(key in schema.properties)) {
606
+ errors.push({
607
+ path: `${path}/${key}`,
608
+ message: `不允许的额外属性 "${key}"`,
609
+ constraint: 'additionalProperties'
610
+ });
611
+ }
612
+ }
613
+ }
614
+ }
615
+
616
+ return {
617
+ valid: errors.length === 0,
618
+ errors
619
+ };
620
+ }
621
+
622
+ /**
623
+ * 检查值是否符合指定类型
624
+ * @param {*} value - 待检查的值
625
+ * @param {string|Array<string>} type - 类型或类型数组
626
+ * @returns {boolean} 是否符合类型
627
+ */
628
+ function checkType(value, type) {
629
+ const types = Array.isArray(type) ? type : [type];
630
+
631
+ for (const t of types) {
632
+ switch (t) {
633
+ case 'string':
634
+ if (typeof value === 'string') return true;
635
+ break;
636
+ case 'number':
637
+ if (typeof value === 'number' && !isNaN(value)) return true;
638
+ break;
639
+ case 'integer':
640
+ if (Number.isInteger(value)) return true;
641
+ break;
642
+ case 'boolean':
643
+ if (typeof value === 'boolean') return true;
644
+ break;
645
+ case 'object':
646
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) return true;
647
+ break;
648
+ case 'array':
649
+ if (Array.isArray(value)) return true;
650
+ break;
651
+ case 'null':
652
+ if (value === null) return true;
653
+ break;
654
+ }
655
+ }
656
+
657
+ return false;
658
+ }
659
+
660
+ /**
661
+ * 验证项目状态数据是否符合 Schema
662
+ *
663
+ * @param {Object} stateData - 待验证的状态数据对象
664
+ * @returns {{valid:boolean, errors:Array<{path:string, message:string, constraint?:string}>}} 验证结果
665
+ *
666
+ * @example
667
+ * const result = validate(stateData);
668
+ * if (!result.valid) {
669
+ * console.error('验证失败:', result.errors);
670
+ * }
671
+ */
672
+ export function validate(stateData) {
673
+ if (stateData === null || stateData === undefined) {
674
+ return {
675
+ valid: false,
676
+ errors: [{
677
+ path: '(root)',
678
+ message: '数据不能为空',
679
+ constraint: 'required'
680
+ }]
681
+ };
682
+ }
683
+
684
+ if (typeof stateData !== 'object' || Array.isArray(stateData)) {
685
+ return {
686
+ valid: false,
687
+ errors: [{
688
+ path: '(root)',
689
+ message: '数据必须是对象类型',
690
+ constraint: 'type'
691
+ }]
692
+ };
693
+ }
694
+
695
+ return validateAgainstSchema(stateData, STATE_SCHEMA);
696
+ }
697
+
698
+ /**
699
+ * 版本比较函数
700
+ * 比较 semver 版本号
701
+ *
702
+ * @param {string} v1 - 版本1
703
+ * @param {string} v2 - 版本2
704
+ * @returns {number} 比较结果:v1<v2 返回负数,相等返回0,v1>v2 返回正数
705
+ */
706
+ export function compareVersions(v1, v2) {
707
+ const parts1 = v1.split('.').map(Number);
708
+ const parts2 = v2.split('.').map(Number);
709
+
710
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
711
+ const p1 = parts1[i] || 0;
712
+ const p2 = parts2[i] || 0;
713
+
714
+ if (p1 < p2) return -1;
715
+ if (p1 > p2) return 1;
716
+ }
717
+
718
+ return 0;
719
+ }
720
+
721
+ /**
722
+ * 数据迁移规则注册表
723
+ * 存储各版本的迁移函数
724
+ * @type {Map<string, Function>}
725
+ */
726
+ const migrationRegistry = new Map();
727
+
728
+ /**
729
+ * 注册迁移规则
730
+ * @param {string} fromVersion - 源版本
731
+ * @param {string} toVersion - 目标版本
732
+ * @param {Function} migrator - 迁移函数
733
+ */
734
+ export function registerMigration(fromVersion, toVersion, migrator) {
735
+ const key = `${fromVersion}->${toVersion}`;
736
+ migrationRegistry.set(key, migrator);
737
+ }
738
+
739
+ // 注册内置迁移规则
740
+
741
+ /**
742
+ * 0.9.0 -> 1.0.0 迁移
743
+ * 重构旧版格式到新的标准格式
744
+ */
745
+ registerMigration('0.9.0', '1.0.0', (oldState) => {
746
+ const migrated = {
747
+ version: SCHEMA_VERSION,
748
+ project: {
749
+ name: oldState.projectName || oldState.name || '',
750
+ version: oldState.version || '1.0.0',
751
+ description: oldState.description || '',
752
+ features: []
753
+ },
754
+ metadata: {
755
+ lastUpdated: Date.now(),
756
+ schemaVersion: SCHEMA_VERSION,
757
+ generatedBy: `pdd-vm/${SCHEMA_VERSION} (migrated from 0.9.0)`
758
+ }
759
+ };
760
+
761
+ // 迁移功能点列表
762
+ if (Array.isArray(oldState.features)) {
763
+ migrated.project.features = oldState.features.map(f => ({
764
+ id: f.id || f.featureId || `feature-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
765
+ name: f.name || f.title || '未命名功能点',
766
+ description: f.description || f.desc || '',
767
+ stage: migrateStage(f.stage || f.status || 'prd'),
768
+ priority: f.priority || 'P2',
769
+ timeline: Array.isArray(f.timeline) ? f.timeline : [],
770
+ artifacts: Array.isArray(f.artifacts) ? f.artifacts : [],
771
+ quality: f.quality || null,
772
+ tokens: f.tokens || f.tokenUsage || {
773
+ total: 0,
774
+ used: 0,
775
+ remaining: 0,
776
+ byStage: {},
777
+ history: []
778
+ },
779
+ iterations: Array.isArray(f.iterations) ? f.iterations : [],
780
+ tags: Array.isArray(f.tags) ? f.tags : [],
781
+ createdAt: f.createdAt || Date.now(),
782
+ updatedAt: f.updatedAt || Date.now()
783
+ }));
784
+ }
785
+
786
+ return migrated;
787
+ });
788
+
789
+ /**
790
+ * 迁移旧的阶段命名到新格式
791
+ * @param {string} oldStage - 旧阶段名
792
+ * @returns {string} 新阶段名
793
+ */
794
+ function migrateStage(oldStage) {
795
+ const stageMapping = {
796
+ 'requirement': 'prd',
797
+ 'requirements': 'prd',
798
+ 'analysis': 'extracted',
799
+ 'analyzed': 'extracted',
800
+ 'design': 'spec',
801
+ 'designed': 'spec',
802
+ 'development': 'implementing',
803
+ 'developing': 'implementing',
804
+ 'coding': 'implementing',
805
+ 'testing': 'verifying',
806
+ 'test': 'verifying',
807
+ 'completed': 'done',
808
+ 'complete': 'done',
809
+ 'finished': 'done'
810
+ };
811
+
812
+ const normalized = String(oldStage).toLowerCase().trim();
813
+ return stageMapping[normalized] || (STAGE_VALUES.includes(normalized) ? normalized : 'prd');
814
+ }
815
+
816
+ /**
817
+ * 执行数据迁移
818
+ * 将旧版本的状态数据升级到目标版本
819
+ *
820
+ * @param {Object} oldState - 旧版本的状态数据
821
+ * @param {string} fromVersion - 源版本号
822
+ * @param {string} toVersion - 目标版本号(默认为当前版本)
823
+ * @returns {Object} 迁移后的状态数据
824
+ * @throws {Error} 当无法找到迁移路径时抛出异常
825
+ *
826
+ * @example
827
+ * const newState = migrate(oldState, '0.9.0', '1.0.0');
828
+ */
829
+ export function migrate(oldState, fromVersion, toVersion = SCHEMA_VERSION) {
830
+ if (!oldState || typeof oldState !== 'object') {
831
+ throw new Error('migrate: 无效的状态数据');
832
+ }
833
+
834
+ // 如果已经是目标版本,直接返回副本
835
+ if (oldState.version === toVersion) {
836
+ return JSON.parse(JSON.stringify(oldState));
837
+ }
838
+
839
+ // 尝试查找直接迁移路径
840
+ const directKey = `${fromVersion}->${toVersion}`;
841
+ if (migrationRegistry.has(directKey)) {
842
+ console.log(chalk.yellow(`[migrate] 执行迁移: ${fromVersion} -> ${toVersion}`));
843
+ const migrator = migrationRegistry.get(directKey);
844
+ const result = migrator(JSON.parse(JSON.stringify(oldState)));
845
+
846
+ // 验证迁移后的数据
847
+ const validation = validate(result);
848
+ if (!validation.valid) {
849
+ console.warn(chalk.yellow('[migrate] 迁移后数据验证警告:'));
850
+ for (const err of validation.errors.slice(0, 5)) {
851
+ console.warn(chalk.yellow(` - ${err.path}: ${err.message}`));
852
+ }
853
+ }
854
+
855
+ return result;
856
+ }
857
+
858
+ // 尝试多步迁移
859
+ const sortedVersions = [...new Set([
860
+ fromVersion,
861
+ ...[...migrationRegistry.keys()].map(k => k.split('->')[1]),
862
+ toVersion
863
+ ])].sort(compareVersions);
864
+
865
+ let currentState = JSON.parse(JSON.stringify(oldState));
866
+ currentState.version = fromVersion;
867
+ let currentVersion = fromVersion;
868
+
869
+ for (let i = 0; i < sortedVersions.length - 1; i++) {
870
+ const stepFrom = sortedVersions[i];
871
+ const stepTo = sortedVersions[i + 1];
872
+
873
+ if (compareVersions(currentVersion, stepTo) >= 0) continue;
874
+
875
+ const stepKey = `${stepFrom}->${stepTo}`;
876
+ if (migrationRegistry.has(stepKey)) {
877
+ console.log(chalk.yellow(`[migrate] 步骤迁移: ${stepFrom} -> ${stepTo}`));
878
+ const migrator = migrationRegistry.get(stepKey);
879
+ currentState = migrator(currentState);
880
+ currentVersion = stepTo;
881
+ }
882
+ }
883
+
884
+ // 确保最终版本正确
885
+ currentState.version = toVersion;
886
+ if (currentState.metadata) {
887
+ currentState.metadata.schemaVersion = toVersion;
888
+ }
889
+
890
+ return currentState;
891
+ }
892
+
893
+ /**
894
+ * 创建空的项目状态模板
895
+ * 用于初始化新项目的状态文件
896
+ *
897
+ * @param {string} projectName - 项目名称
898
+ * @param {string} [version='1.0.0'] - 项目版本
899
+ * @returns {Object} 空白状态对象
900
+ */
901
+ export function createEmptyState(projectName, version = '1.0.0') {
902
+ return {
903
+ version: SCHEMA_VERSION,
904
+ project: {
905
+ name: projectName,
906
+ version,
907
+ description: '',
908
+ features: []
909
+ },
910
+ metadata: {
911
+ lastUpdated: Date.now(),
912
+ schemaVersion: SCHEMA_VERSION,
913
+ generatedBy: `pdd-vm/${SCHEMA_VERSION}`
914
+ }
915
+ };
916
+ }
917
+
918
+ /**
919
+ * 获取 Schema 中某字段的定义
920
+ * 用于动态查询 Schema 结构
921
+ *
922
+ * @param {string} fieldPath - 字段路径(如 "project.features.items")
923
+ * @returns {Object|null} 字段的 Schema 定义
924
+ */
925
+ export function getSchemaField(fieldPath) {
926
+ const parts = fieldPath.split('.');
927
+ let current = STATE_SCHEMA;
928
+
929
+ for (const part of parts) {
930
+ if (current && current.properties) {
931
+ current = current.properties[part];
932
+ } else if (current && current.items) {
933
+ current = current.items;
934
+ } else {
935
+ return null;
936
+ }
937
+ }
938
+
939
+ return current || null;
940
+ }
941
+
942
+ /**
943
+ * 导出默认对象
944
+ */
945
+ export default {
946
+ SCHEMA_VERSION,
947
+ MIN_MIGRATABLE_VERSION,
948
+ STATE_SCHEMA,
949
+ validate,
950
+ compareVersions,
951
+ migrate,
952
+ registerMigration,
953
+ createEmptyState,
954
+ getSchemaField
955
+ };