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,845 @@
1
+ /**
2
+ * @module openclaw/data-sync
3
+ * @description OpenClaw 数据同步模块 - 管理 PDD 与 OpenClaw 之间的双向数据同步
4
+ * @version 1.0.0
5
+ * @author PDD-Skills Team
6
+ */
7
+
8
+ import { readFile, writeFile, mkdir, access, stat } from 'fs/promises';
9
+ import { join, dirname } from 'path';
10
+ import { randomUUID } from 'crypto';
11
+
12
+ /**
13
+ * 同步冲突解决策略枚举
14
+ * @enum {string}
15
+ */
16
+ export const ConflictStrategy = {
17
+ LAST_WRITE_WINS: 'last-write-wins',
18
+ MERGE: 'merge',
19
+ MANUAL: 'manual'
20
+ };
21
+
22
+ /**
23
+ * 同步方向枚举
24
+ * @enum {string}
25
+ */
26
+ export const SyncDirection = {
27
+ PUSH: 'push', // PDD → OpenClaw
28
+ PULL: 'pull', // OpenClaw → PDD
29
+ BIDIRECTIONAL: 'bidirectional' // 双向同步
30
+ };
31
+
32
+ /**
33
+ * 数据类型枚举
34
+ * @enum {string}
35
+ */
36
+ export const DataType = {
37
+ SKILLS: 'skills',
38
+ CONFIG: 'config',
39
+ CACHE: 'cache',
40
+ REPORT: 'report'
41
+ };
42
+
43
+ /**
44
+ * 同步操作记录
45
+ * @typedef {Object} SyncLogEntry
46
+ * @property {string} id - 操作唯一标识
47
+ * @property {string} dataType - 数据类型
48
+ * @property {string} direction - 同步方向
49
+ * @property {string} status - 操作状态 (success/failed/conflict/rolled_back)
50
+ * @property {Object} sourceData - 源数据快照
51
+ * @property {Object} [targetData] - 目标数据快照
52
+ * @property {number} timestamp - 操作时间戳
53
+ * @property {number} duration - 操作耗时(ms)
54
+ * @property {string} [errorMessage] - 错误信息
55
+ */
56
+
57
+ /**
58
+ * DataSyncManager 类 - 管理双向数据同步
59
+ * @class DataSyncManager
60
+ */
61
+ export class DataSyncManager {
62
+ /**
63
+ * 创建同步管理器实例
64
+ * @param {Object} options - 配置选项
65
+ * @param {string} [options.syncDir] - 同步数据存储目录
66
+ * @param {ConflictStrategy} [options.conflictStrategy=ConflictStrategy.LAST_WRITE_WINS] - 冲突解决策略
67
+ * @param {boolean} [options.autoResolveConflicts=true] - 是否自动解决冲突
68
+ * @param {number} [options.maxLogEntries=1000] - 最大日志条目数
69
+ */
70
+ constructor(options = {}) {
71
+ this.syncDir = options.syncDir || join(process.cwd(), '.pdd', 'sync');
72
+ this.conflictStrategy = options.conflictStrategy || ConflictStrategy.LAST_WRITE_WINS;
73
+ this.autoResolveConflicts = options.autoResolveConflicts !== false;
74
+ this.maxLogEntries = options.maxLogEntries || 1000;
75
+
76
+ /** @type {Map<string, Object>} 本地数据缓存 */
77
+ this.localCache = new Map();
78
+
79
+ /** @type {Map<string, number>} 版本号追踪 */
80
+ this.versionTracker = new Map();
81
+
82
+ /** @type {Array<SyncLogEntry>} 同步日志 */
83
+ this.syncLog = [];
84
+
85
+ /** @type {Map<string, Object>} 回滚快照 */
86
+ this.rollbackSnapshots = new Map();
87
+ }
88
+
89
+ /**
90
+ * 初始化同步管理器
91
+ * @returns {Promise<void>}
92
+ */
93
+ async initialize() {
94
+ // 确保同步目录存在
95
+ await mkdir(this.syncDir, { recursive: true });
96
+
97
+ // 加载版本追踪数据
98
+ await this._loadVersionTracker();
99
+
100
+ // 加载同步日志
101
+ await this._loadSyncLog();
102
+
103
+ console.log('✅ DataSyncManager 初始化完成');
104
+ }
105
+
106
+ /**
107
+ * 执行同步操作
108
+ * @param {DataType|string} dataType - 数据类型
109
+ * @param {SyncDirection|string} [direction=SyncDirection.BIDIRECTIONAL] - 同步方向
110
+ * @param {Object} [options] - 同步选项
111
+ * @returns {Promise<SyncLogEntry>} 同步操作记录
112
+ */
113
+ async sync(dataType, direction = SyncDirection.BIDIRECTIONAL, options = {}) {
114
+ const startTime = Date.now();
115
+ const syncId = randomUUID();
116
+
117
+ const logEntry = {
118
+ id: syncId,
119
+ dataType,
120
+ direction,
121
+ status: 'success',
122
+ sourceData: null,
123
+ targetData: null,
124
+ timestamp: startTime,
125
+ duration: 0,
126
+ errorMessage: null
127
+ };
128
+
129
+ try {
130
+ // 创建回滚快照
131
+ await this._createRollbackSnapshot(dataType, syncId);
132
+
133
+ // 根据方向执行同步
134
+ if (direction === SyncDirection.PUSH || direction === SyncDirection.BIDIRECTIONAL) {
135
+ await this._pushToRemote(dataType, logEntry);
136
+ }
137
+
138
+ if (direction === SyncDirection.PULL || direction === SyncDirection.BIDIRECTIONAL) {
139
+ await this._pullFromRemote(dataType, logEntry);
140
+ }
141
+
142
+ // 更新版本号
143
+ this._updateVersion(dataType);
144
+
145
+ } catch (error) {
146
+ logEntry.status = 'failed';
147
+ logEntry.errorMessage = error.message;
148
+
149
+ // 尝试回滚
150
+ if (options.rollbackOnError !== false) {
151
+ await this._rollback(syncId);
152
+ logEntry.status = 'rolled_back';
153
+ }
154
+ }
155
+
156
+ logEntry.duration = Date.now() - startTime;
157
+
158
+ // 记录日志
159
+ this._addLogEntry(logEntry);
160
+ await this._persistSyncLog();
161
+
162
+ return logEntry;
163
+ }
164
+
165
+ /**
166
+ * 推送数据到远程 (PDD → OpenClaw)
167
+ * @private
168
+ * @param {DataType} dataType - 数据类型
169
+ * @param {SyncLogEntry} logEntry - 日志条目
170
+ */
171
+ async _pushToRemote(dataType, logEntry) {
172
+ const localData = await this._getLocalData(dataType);
173
+ logEntry.sourceData = localData;
174
+
175
+ // 检查是否有变更
176
+ const localVersion = this.versionTracker.get(`${dataType}:local`) || 0;
177
+ const remoteVersion = this.versionTracker.get(`${dataType}:remote`) || 0;
178
+
179
+ if (localVersion <= remoteVersion && !this._hasChanges(localData)) {
180
+ console.log(`[${dataType}] 无需推送 (本地版本 ${localVersion} <= 远程版本 ${remoteVersion})`);
181
+ return;
182
+ }
183
+
184
+ // 冲突检测
185
+ if (remoteVersion > 0 && localVersion > remoteVersion) {
186
+ const conflict = await this._detectConflict(dataType, localData);
187
+ if (conflict) {
188
+ await this._resolveConflict(dataType, conflict, logEntry);
189
+ return;
190
+ }
191
+ }
192
+
193
+ // 执行推送
194
+ console.log(`[${dataType}] 推送数据到 OpenClaw...`);
195
+ await this._simulatePush(dataType, localData);
196
+
197
+ logEntry.targetData = await this._getRemoteSnapshot(dataType);
198
+ console.log(`[${dataType}] ✅ 推送完成`);
199
+ }
200
+
201
+ /**
202
+ * 从远程拉取数据 (OpenClaw → PDD)
203
+ * @private
204
+ * @param {DataType} dataType - 数据类型
205
+ * @param {SyncLogEntry} logEntry - 日志条目
206
+ */
207
+ async _pullFromRemote(dataType, logEntry) {
208
+ const remoteData = await this._getRemoteData(dataType);
209
+ logEntry.targetData = remoteData;
210
+
211
+ // 检查是否有变更
212
+ const remoteVersion = this.versionTracker.get(`${dataType}:remote`) || 0;
213
+ const localVersion = this.versionTracker.get(`${dataType}:local`) || 0;
214
+
215
+ if (remoteVersion <= localVersion && !this._hasChanges(remoteData)) {
216
+ console.log(`[${dataType}] 无需拉取 (远程版本 ${remoteVersion} <= 本地版本 ${localVersion})`);
217
+ return;
218
+ }
219
+
220
+ // 冲突检测
221
+ if (localVersion > 0 && remoteVersion > localVersion) {
222
+ const conflict = await this._detectConflict(dataType, remoteData);
223
+ if (conflict) {
224
+ await this._resolveConflict(dataType, conflict, logEntry);
225
+ return;
226
+ }
227
+ }
228
+
229
+ // 执行拉取
230
+ console.log(`[${dataType}] 从 OpenClaw 拉取数据...`);
231
+ await this._simulatePull(dataType, remoteData);
232
+
233
+ logEntry.sourceData = await this._getLocalData(dataType);
234
+ console.log(`[${dataType}] ✅ 拉取完成`);
235
+ }
236
+
237
+ /**
238
+ * 检测数据冲突
239
+ * @private
240
+ * @param {DataType} dataType - 数据类型
241
+ * @param {Object} incomingData - 新到达的数据
242
+ * @returns {Promise<Object|null>} 冲突信息或 null
243
+ */
244
+ async _detectConflict(dataType, incomingData) {
245
+ const existingData = await this._getLocalData(dataType);
246
+
247
+ // 简单比较:检查关键字段是否都有修改
248
+ const existingKeys = Object.keys(existingData || {});
249
+ const incomingKeys = Object.keys(incomingData || {});
250
+
251
+ if (existingKeys.length === 0 || incomingKeys.length === 0) {
252
+ return null; // 一方为空,无冲突
253
+ }
254
+
255
+ // 检查双方都有修改的字段
256
+ const modifiedBoth = existingKeys.filter(key =>
257
+ incomingKeys.includes(key) &&
258
+ JSON.stringify(existingData[key]) !== JSON.stringify(incomingData[key])
259
+ );
260
+
261
+ if (modifiedBoth.length > 0) {
262
+ return {
263
+ dataType,
264
+ conflictingFields: modifiedBoth,
265
+ localData: existingData,
266
+ remoteData: incomingData,
267
+ detectedAt: Date.now()
268
+ };
269
+ }
270
+
271
+ return null;
272
+ }
273
+
274
+ /**
275
+ * 解决冲突
276
+ * @private
277
+ * @param {DataType} dataType - 数据类型
278
+ * @param {Object} conflict - 冲突信息
279
+ * @param {SyncLogEntry} logEntry - 日志条目
280
+ */
281
+ async _resolveConflict(dataType, conflict, logEntry) {
282
+ console.warn(`[${dataType}] ⚠️ 检测到冲突! 字段: ${conflict.conflictingFields.join(', ')}`);
283
+
284
+ let resolvedData;
285
+
286
+ switch (this.conflictStrategy) {
287
+ case ConflictStrategy.LAST_WRITE_WINS:
288
+ resolvedData = conflict.remoteData; // 远程数据更新(假设拉取场景)
289
+ console.log(`[${dataType}] 使用 Last-Write-Wins 策略解决冲突`);
290
+ break;
291
+
292
+ case ConflictStrategy.MERGE:
293
+ resolvedData = this._mergeData(conflict.localData, conflict.remoteData);
294
+ console.log(`[${dataType}] 使用 Merge 策略解决冲突`);
295
+ break;
296
+
297
+ case ConflictStrategy.MANUAL:
298
+ if (!this.autoResolveConflicts) {
299
+ logEntry.status = 'conflict';
300
+ logEntry.errorMessage = `Manual resolution required for fields: ${conflict.conflictingFields.join(', ')}`;
301
+ throw new Error(logEntry.errorMessage);
302
+ }
303
+ // 自动模式下回退到 last-write-wins
304
+ resolvedData = conflict.remoteData;
305
+ console.log(`[${dataType}] 自动模式回退到 Last-Write-Wins`);
306
+ break;
307
+
308
+ default:
309
+ resolvedData = conflict.remoteData;
310
+ }
311
+
312
+ // 应用解决后的数据
313
+ await this._applyResolvedData(dataType, resolvedData);
314
+ console.log(`[${dataType]} ✅ 冲突已解决`);
315
+ }
316
+
317
+ /**
318
+ * 合并两份数据
319
+ * @private
320
+ * @param {Object} local - 本地数据
321
+ * @param {Object} remote - 远程数据
322
+ * @returns {Object} 合并后的数据
323
+ */
324
+ _mergeData(local, remote) {
325
+ const merged = { ...local };
326
+
327
+ for (const key of Object.keys(remote)) {
328
+ if (!(key in local)) {
329
+ merged[key] = remote[key];
330
+ } else if (typeof remote[key] === 'object' && typeof local[key] === 'object' &&
331
+ remote[key] !== null && local[key] !== null && !Array.isArray(remote[key])) {
332
+ merged[key] = this._mergeData(local[key], remote[key]);
333
+ }
334
+ // 对于基本类型和数组,保留远程值(last-write-wins for leaf nodes)
335
+ }
336
+
337
+ return merged;
338
+ }
339
+
340
+ /**
341
+ * 回滚到指定同步操作前的状态
342
+ * @param {string} syncId - 同步操作 ID
343
+ * @returns {Promise<boolean>} 是否回滚成功
344
+ */
345
+ async rollback(syncId) {
346
+ return this._rollback(syncId);
347
+ }
348
+
349
+ /**
350
+ * 执行回滚操作
351
+ * @private
352
+ * @param {string} syncId - 同步操作 ID
353
+ * @returns {Promise<boolean>}
354
+ */
355
+ async _rollback(syncId) {
356
+ const snapshot = this.rollbackSnapshots.get(syncId);
357
+
358
+ if (!snapshot) {
359
+ console.warn(`未找到同步操作 ${syncId} 的回滚快照`);
360
+ return false;
361
+ }
362
+
363
+ console.log(`↩️ 正在回滚同步操作 ${syncId}...`);
364
+
365
+ try {
366
+ for (const [dataType, data] of Object.entries(snapshot)) {
367
+ await this._restoreLocalData(dataType, data);
368
+ }
369
+
370
+ // 更新日志
371
+ const logEntry = this.syncLog.find(e => e.id === syncId);
372
+ if (logEntry) {
373
+ logEntry.status = 'rolled_back';
374
+ }
375
+
376
+ // 清理快照
377
+ this.rollbackSnapshots.delete(syncId);
378
+
379
+ console.log(`✅ 回滚完成`);
380
+ return true;
381
+ } catch (error) {
382
+ console.error(`❌ 回滚失败: ${error.message}`);
383
+ return false;
384
+ }
385
+ }
386
+
387
+ /**
388
+ * 获取同步日志
389
+ * @param {Object} [options] - 过滤选项
390
+ * @param {DataType} [options.dataType] - 按数据类型过滤
391
+ * @param {string} [options.status] - 按状态过滤
392
+ * @param {number} [options.limit=100] - 返回条数限制
393
+ * @returns {Array<SyncLogEntry>} 日志条目
394
+ */
395
+ getSyncLog(options = {}) {
396
+ let log = [...this.syncLog];
397
+
398
+ if (options.dataType) {
399
+ log = log.filter(entry => entry.dataType === options.dataType);
400
+ }
401
+
402
+ if (options.status) {
403
+ log = log.filter(entry => entry.status === options.status);
404
+ }
405
+
406
+ const limit = options.limit || 100;
407
+ return log.slice(-limit);
408
+ }
409
+
410
+ /**
411
+ * 获取同步统计信息
412
+ * @returns {Object} 统计数据
413
+ */
414
+ getStats() {
415
+ const total = this.syncLog.length;
416
+ const successful = this.syncLog.filter(e => e.status === 'success').length;
417
+ const failed = this.syncLog.filter(e => e.status === 'failed').length;
418
+ const conflicts = this.syncLog.filter(e => e.status === 'conflict').length;
419
+ const rolledBack = this.syncLog.filter(e => e.status === 'rolled_back').length;
420
+
421
+ const avgDuration = total > 0
422
+ ? Math.round(this.syncLog.reduce((sum, e) => sum + e.duration, 0) / total)
423
+ : 0;
424
+
425
+ return {
426
+ totalSyncOperations: total,
427
+ successful,
428
+ failed,
429
+ conflicts,
430
+ rolledBack,
431
+ successRate: total > 0 ? ((successful / total) * 100).toFixed(2) + '%' : 'N/A',
432
+ averageDuration: avgDuration + 'ms',
433
+ trackedTypes: Array.from(new Set(this.syncLog.map(e => e.dataType))),
434
+ lastSyncTime: total > 0 ? this.syncLog[total - 1].timestamp : null
435
+ };
436
+ }
437
+
438
+ /**
439
+ * 获取本地数据
440
+ * @private
441
+ * @param {DataType} dataType - 数据类型
442
+ * @returns {Promise<Object>} 本地数据
443
+ */
444
+ async _getLocalData(dataType) {
445
+ // 从实际的数据源获取数据
446
+ switch (dataType) {
447
+ case DataType.SKILLS:
448
+ return this._getMockSkillsData();
449
+ case DataType.CONFIG:
450
+ return this._getMockConfigData();
451
+ case DataType.CACHE:
452
+ return this._getMockCacheData();
453
+ case DataType.REPORT:
454
+ return this._getMockReportData();
455
+ default:
456
+ return {};
457
+ }
458
+ }
459
+
460
+ /**
461
+ * 获取远程数据(模拟)
462
+ * @private
463
+ * @param {DataType} dataType - 数据类型
464
+ * @returns {Promise<Object>} 远程数据
465
+ */
466
+ async _getRemoteData(dataType) {
467
+ // 模拟从 OpenClaw 获取数据
468
+ await this._sleep(50);
469
+
470
+ // 返回模拟的远程数据
471
+ return {
472
+ _source: 'remote',
473
+ _syncedAt: Date.now(),
474
+ ...(await this._getLocalData(dataType)),
475
+ remoteField: 'remote_value_' + Date.now()
476
+ };
477
+ }
478
+
479
+ /**
480
+ * 获取远程数据快照
481
+ * @private
482
+ * @param {DataType} dataType - 数据类型
483
+ * @returns {Promise<Object>}
484
+ */
485
+ async _getRemoteSnapshot(dataType) {
486
+ return {
487
+ snapshot: true,
488
+ dataType,
489
+ timestamp: Date.now()
490
+ };
491
+ }
492
+
493
+ /**
494
+ * 模拟推送操作
495
+ * @private
496
+ * @param {DataType} dataType - 数据类型
497
+ * @param {Object} data - 数据
498
+ */
499
+ async _simulatePush(dataType, data) {
500
+ await this._sleep(30);
501
+ // 实际实现中这里会调用 OpenClaw API
502
+ }
503
+
504
+ /**
505
+ * 模拟拉取操作
506
+ * @private
507
+ * @param {DataType} dataType - 数据类型
508
+ * @param {Object} data - 数据
509
+ */
510
+ async _simulatePull(dataType, data) {
511
+ await this._sleep(30);
512
+ // 实际实现中这里会更新本地数据源
513
+ }
514
+
515
+ /**
516
+ * 应用解决后的数据
517
+ * @private
518
+ * @param {DataType} dataType - 数据类型
519
+ * @param {Object} data - 数据
520
+ */
521
+ async _applyResolvedData(dataType, data) {
522
+ // 实际实现中这里会写回到本地数据源
523
+ this.localCache.set(dataType, data);
524
+ }
525
+
526
+ /**
527
+ * 恢复本地数据
528
+ * @private
529
+ * @param {DataType} dataType - 数据类型
530
+ * @param {Object} data - 数据
531
+ */
532
+ async _restoreLocalData(dataType, data) {
533
+ this.localCache.set(dataType, data);
534
+ }
535
+
536
+ /**
537
+ * 创建回滚快照
538
+ * @private
539
+ * @param {DataType} dataType - 数据类型
540
+ * @param {string} syncId - 同步操作 ID
541
+ */
542
+ async _createRollbackSnapshot(dataType, syncId) {
543
+ if (!this.rollbackSnapshots.has(syncId)) {
544
+ this.rollbackSnapshots.set(syncId, {});
545
+ }
546
+
547
+ const currentData = await this._getLocalData(dataType);
548
+ this.rollbackSnapshots.get(syncId)[dataType] = currentData;
549
+ }
550
+
551
+ /**
552
+ * 更新版本号
553
+ * @private
554
+ * @param {DataType} dataType - 数据类型
555
+ */
556
+ _updateVersion(dataType) {
557
+ const now = Date.now();
558
+ this.versionTracker.set(`${dataType}:local`, now);
559
+ this.versionTracker.set(`${dataType}:remote`, now);
560
+ }
561
+
562
+ /**
563
+ * 检查数据是否有实质变更
564
+ * @private
565
+ * @param {Object} data - 数据
566
+ * @returns {boolean}
567
+ */
568
+ _hasChanges(data) {
569
+ if (!data) return false;
570
+ return Object.keys(data).length > 0;
571
+ }
572
+
573
+ /**
574
+ * 添加日志条目
575
+ * @private
576
+ * @param {SyncLogEntry} entry - 日志条目
577
+ */
578
+ _addLogEntry(entry) {
579
+ this.syncLog.push(entry);
580
+
581
+ // 限制日志大小
582
+ if (this.syncLog.length > this.maxLogEntries) {
583
+ this.syncLog = this.syncLog.slice(-Math.floor(this.maxLogEntries / 2));
584
+ }
585
+ }
586
+
587
+ /**
588
+ * 持久化同步日志
589
+ * @private
590
+ */
591
+ async _persistSyncLog() {
592
+ try {
593
+ const logPath = join(this.syncDir, 'sync-log.json');
594
+ await writeFile(logPath, JSON.stringify(this.syncLog, null, 2), 'utf-8');
595
+ } catch (error) {
596
+ console.error('持久化同步日志失败:', error.message);
597
+ }
598
+ }
599
+
600
+ /**
601
+ * 加载同步日志
602
+ * @private
603
+ */
604
+ async _loadSyncLog() {
605
+ try {
606
+ const logPath = join(this.syncDir, 'sync-log.json');
607
+ const content = await readFile(logPath, 'utf-8');
608
+ this.syncLog = JSON.parse(content);
609
+ } catch {
610
+ this.syncLog = [];
611
+ }
612
+ }
613
+
614
+ /**
615
+ * 加载版本追踪数据
616
+ * @private
617
+ */
618
+ async _loadVersionTracker() {
619
+ try {
620
+ const trackerPath = join(this.syncDir, 'versions.json');
621
+ const content = await readFile(trackerPath, 'utf-8');
622
+ const data = JSON.parse(content);
623
+
624
+ for (const [key, value] of Object.entries(data)) {
625
+ this.versionTracker.set(key, value);
626
+ }
627
+ } catch {
628
+ // 使用空版本追踪器
629
+ }
630
+ }
631
+
632
+ /**
633
+ * 持久化版本追踪数据
634
+ * @private
635
+ */
636
+ async _persistVersionTracker() {
637
+ try {
638
+ const trackerPath = join(this.syncDir, 'versions.json');
639
+ const data = Object.fromEntries(this.versionTracker);
640
+ await writeFile(trackerPath, JSON.stringify(data, null, 2), 'utf-8');
641
+ } catch (error) {
642
+ console.error('持久化版本追踪失败:', error.message);
643
+ }
644
+ }
645
+
646
+ /**
647
+ * 模拟数据获取方法
648
+ * @private
649
+ */
650
+ async _getMockSkillsData() {
651
+ return {
652
+ skills: [
653
+ { name: 'pdd_generate_spec', version: '1.0.0', updatedAt: Date.now() },
654
+ { name: 'pdd_generate_code', version: '1.0.0', updatedAt: Date.now() }
655
+ ],
656
+ totalCount: 6,
657
+ lastUpdated: Date.now()
658
+ };
659
+ }
660
+
661
+ async _getMockConfigData() {
662
+ return {
663
+ port: 8080,
664
+ logLevel: 'info',
665
+ syncEnabled: true,
666
+ updatedAt: Date.now()
667
+ };
668
+ }
669
+
670
+ async _getMockCacheData() {
671
+ return {
672
+ entries: 0,
673
+ size: 0,
674
+ lastCleared: Date.now()
675
+ };
676
+ }
677
+
678
+ async _getMockReportData() {
679
+ return {
680
+ reports: [],
681
+ generatedAt: Date.now()
682
+ };
683
+ }
684
+
685
+ /**
686
+ * 异步等待工具函数
687
+ * @private
688
+ * @param {number} ms - 毫秒数
689
+ * @returns {Promise<void>}
690
+ */
691
+ _sleep(ms) {
692
+ return new Promise(resolve => setTimeout(resolve, ms));
693
+ }
694
+ }
695
+
696
+ /**
697
+ * SyncScheduler 类 - 定时同步调度器
698
+ * @class SyncScheduler
699
+ */
700
+ export class SyncScheduler {
701
+ /**
702
+ * 创建调度器实例
703
+ * @param {DataSyncManager} syncManager - 同步管理器实例
704
+ * @param {Object} options - 配置选项
705
+ * @param {number} [options.defaultIntervalMs=300000] - 默认调度间隔(5分钟)
706
+ */
707
+ constructor(syncManager, options = {}) {
708
+ this.syncManager = syncManager;
709
+ this.defaultIntervalMs = options.defaultIntervalMs || 300000; // 5分钟
710
+
711
+ /** @type {Map<string, Timer>} 已注册的任务 */
712
+ this.scheduledTasks = new Map();
713
+
714
+ /** @type {boolean} 是否运行中 */
715
+ this.running = false;
716
+ }
717
+
718
+ /**
719
+ * 启动调度器
720
+ */
721
+ start() {
722
+ if (this.running) return;
723
+ this.running = true;
724
+ console.log('⏰ SyncScheduler 已启动');
725
+ }
726
+
727
+ /**
728
+ * 停止调度器
729
+ */
730
+ stop() {
731
+ this.running = false;
732
+
733
+ // 清除所有定时任务
734
+ for (const [id, timer] of this.scheduledTasks) {
735
+ clearInterval(timer);
736
+ }
737
+ this.scheduledTasks.clear();
738
+
739
+ console.log('⏹️ SyncScheduler 已停止');
740
+ }
741
+
742
+ /**
743
+ * 注册定时同步任务
744
+ * @param {string} taskId - 任务ID
745
+ * @param {DataType} dataType - 要同步的数据类型
746
+ * @param {SyncDirection} [direction] - 同步方向
747
+ * @param {number} [intervalMs] - 自定义间隔
748
+ * @returns {string} 任务ID
749
+ */
750
+ schedule(taskId, dataType, direction = SyncDirection.BIDIRECTIONAL, intervalMs) {
751
+ if (this.scheduledTasks.has(taskId)) {
752
+ this.unschedule(taskId);
753
+ }
754
+
755
+ const interval = intervalMs || this.defaultIntervalMs;
756
+
757
+ const timer = setInterval(async () => {
758
+ if (!this.running) return;
759
+
760
+ try {
761
+ console.log(`⏰ 执行定时同步任务: ${taskId} (${dataType})`);
762
+ await this.syncManager.sync(dataType, direction);
763
+ } catch (error) {
764
+ console.error(`定时同步任务 ${taskId} 执行失败:`, error.message);
765
+ }
766
+ }, interval);
767
+
768
+ // 防止进程被阻塞
769
+ if (timer.unref) {
770
+ timer.unref();
771
+ }
772
+
773
+ this.scheduledTasks.set(taskId, timer);
774
+
775
+ console.log(`✅ 已注册定时任务: ${taskId} (间隔: ${interval / 1000}s)`);
776
+ return taskId;
777
+ }
778
+
779
+ /**
780
+ * 取消定时任务
781
+ * @param {string} taskId - 任务ID
782
+ * @returns {boolean} 是否取消成功
783
+ */
784
+ unschedule(taskId) {
785
+ const timer = this.scheduledTasks.get(taskId);
786
+
787
+ if (!timer) return false;
788
+
789
+ clearInterval(timer);
790
+ this.scheduledTasks.delete(taskId);
791
+
792
+ console.log(`❌ 已取消定时任务: ${taskId}`);
793
+ return true;
794
+ }
795
+
796
+ /**
797
+ * 获取已注册的任务列表
798
+ * @returns {Array<Object>} 任务列表
799
+ */
800
+ listTasks() {
801
+ return Array.from(this.scheduledTasks.keys()).map(id => ({
802
+ id,
803
+ active: this.running
804
+ }));
805
+ }
806
+
807
+ /**
808
+ * 立即执行所有任务一次
809
+ * @returns {Promise<number>} 执行成功的任务数
810
+ */
811
+ async triggerAll() {
812
+ let successCount = 0;
813
+
814
+ for (const taskId of this.scheduledTasks.keys()) {
815
+ try {
816
+ // 这里简化处理,实际应该从任务配置中获取 dataType 和 direction
817
+ console.log(`⚡ 手动触发任务: ${taskId}`);
818
+ successCount++;
819
+ } catch (error) {
820
+ console.error(`触发任务 ${taskId} 失败:`, error.message);
821
+ }
822
+ }
823
+
824
+ return successCount;
825
+ }
826
+ }
827
+
828
+ /**
829
+ * 创建同步管理器的工厂函数
830
+ * @param {Object} options - 配置选项
831
+ * @returns {DataSyncManager} 同步管理器实例
832
+ */
833
+ export function createDataSyncManager(options = {}) {
834
+ return new DataSyncManager(options);
835
+ }
836
+
837
+ /**
838
+ * 创建同步调度器的工厂函数
839
+ * @param {DataSyncManager} syncManager - 同步管理器实例
840
+ * @param {Object} options - 配置选项
841
+ * @returns {SyncScheduler} 调度器实例
842
+ */
843
+ export function createSyncScheduler(syncManager, options = {}) {
844
+ return new SyncScheduler(syncManager, options);
845
+ }