@winspan/claude-forge 3.7.8 → 4.1.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 (275) hide show
  1. package/dist/autopilot/quality-gate-utils.d.ts +6 -0
  2. package/dist/autopilot/quality-gate-utils.d.ts.map +1 -0
  3. package/dist/autopilot/quality-gate-utils.js +48 -0
  4. package/dist/autopilot/quality-gate-utils.js.map +1 -0
  5. package/dist/autopilot/quality-gate.d.ts +0 -8
  6. package/dist/autopilot/quality-gate.d.ts.map +1 -1
  7. package/dist/autopilot/quality-gate.js +6 -61
  8. package/dist/autopilot/quality-gate.js.map +1 -1
  9. package/dist/cli/commands/convention.d.ts.map +1 -1
  10. package/dist/cli/commands/convention.js +1 -115
  11. package/dist/cli/commands/convention.js.map +1 -1
  12. package/dist/cli/commands/init/index.d.ts.map +1 -1
  13. package/dist/cli/commands/init/index.js +0 -14
  14. package/dist/cli/commands/init/index.js.map +1 -1
  15. package/dist/cli/commands/init/project-doctor.d.ts.map +1 -1
  16. package/dist/cli/commands/init/project-doctor.js +0 -63
  17. package/dist/cli/commands/init/project-doctor.js.map +1 -1
  18. package/dist/cli/tui.d.ts.map +1 -1
  19. package/dist/cli/tui.js +7 -131
  20. package/dist/cli/tui.js.map +1 -1
  21. package/dist/constants.d.ts +0 -6
  22. package/dist/constants.d.ts.map +1 -1
  23. package/dist/constants.js +0 -6
  24. package/dist/constants.js.map +1 -1
  25. package/dist/convention/convention-loader.js +2 -2
  26. package/dist/convention/convention-loader.js.map +1 -1
  27. package/dist/convention/convention-manager.d.ts +24 -2
  28. package/dist/convention/convention-manager.d.ts.map +1 -1
  29. package/dist/convention/convention-manager.js +62 -4
  30. package/dist/convention/convention-manager.js.map +1 -1
  31. package/dist/convention/index.d.ts +1 -2
  32. package/dist/convention/index.d.ts.map +1 -1
  33. package/dist/convention/index.js +0 -1
  34. package/dist/convention/index.js.map +1 -1
  35. package/dist/convention/types.d.ts +4 -14
  36. package/dist/convention/types.d.ts.map +1 -1
  37. package/dist/daemon/engine-registry/init-governance.d.ts +10 -0
  38. package/dist/daemon/engine-registry/init-governance.d.ts.map +1 -0
  39. package/dist/daemon/engine-registry/init-governance.js +22 -0
  40. package/dist/daemon/engine-registry/init-governance.js.map +1 -0
  41. package/dist/daemon/engine-registry.d.ts.map +1 -1
  42. package/dist/daemon/engine-registry.js +5 -6
  43. package/dist/daemon/engine-registry.js.map +1 -1
  44. package/dist/daemon/handler-context.d.ts +4 -2
  45. package/dist/daemon/handler-context.d.ts.map +1 -1
  46. package/dist/daemon/handlers/post-tool-use-handler.d.ts.map +1 -1
  47. package/dist/daemon/handlers/post-tool-use-handler.js +28 -3
  48. package/dist/daemon/handlers/post-tool-use-handler.js.map +1 -1
  49. package/dist/daemon/handlers/pre-tool-use-handler.d.ts.map +1 -1
  50. package/dist/daemon/handlers/pre-tool-use-handler.js +32 -22
  51. package/dist/daemon/handlers/pre-tool-use-handler.js.map +1 -1
  52. package/dist/daemon/handlers/session-cleanup.d.ts +0 -4
  53. package/dist/daemon/handlers/session-cleanup.d.ts.map +1 -1
  54. package/dist/daemon/handlers/session-cleanup.js +0 -81
  55. package/dist/daemon/handlers/session-cleanup.js.map +1 -1
  56. package/dist/daemon/handlers/stages/07-pipeline-reply.d.ts.map +1 -1
  57. package/dist/daemon/handlers/stages/07-pipeline-reply.js +5 -1
  58. package/dist/daemon/handlers/stages/07-pipeline-reply.js.map +1 -1
  59. package/dist/daemon/handlers/stages/09-pipeline-active.d.ts.map +1 -1
  60. package/dist/daemon/handlers/stages/09-pipeline-active.js +5 -1
  61. package/dist/daemon/handlers/stages/09-pipeline-active.js.map +1 -1
  62. package/dist/daemon/handlers/stages/13-template-route.d.ts.map +1 -1
  63. package/dist/daemon/handlers/stages/13-template-route.js +5 -1
  64. package/dist/daemon/handlers/stages/13-template-route.js.map +1 -1
  65. package/dist/daemon/handlers/stages/18-complex-task.d.ts +5 -2
  66. package/dist/daemon/handlers/stages/18-complex-task.d.ts.map +1 -1
  67. package/dist/daemon/handlers/stages/18-complex-task.js +87 -9
  68. package/dist/daemon/handlers/stages/18-complex-task.js.map +1 -1
  69. package/dist/daemon/handlers/stop-handler.d.ts.map +1 -1
  70. package/dist/daemon/handlers/stop-handler.js +1 -13
  71. package/dist/daemon/handlers/stop-handler.js.map +1 -1
  72. package/dist/goal/goal-classifier.d.ts.map +1 -1
  73. package/dist/goal/goal-classifier.js +12 -5
  74. package/dist/goal/goal-classifier.js.map +1 -1
  75. package/dist/goal/goal-types.d.ts +1 -3
  76. package/dist/goal/goal-types.d.ts.map +1 -1
  77. package/dist/goal/index.d.ts +1 -1
  78. package/dist/goal/index.d.ts.map +1 -1
  79. package/dist/governance/contract-builder.d.ts +39 -0
  80. package/dist/governance/contract-builder.d.ts.map +1 -0
  81. package/dist/governance/contract-builder.js +216 -0
  82. package/dist/governance/contract-builder.js.map +1 -0
  83. package/dist/governance/engine.d.ts +33 -0
  84. package/dist/governance/engine.d.ts.map +1 -0
  85. package/dist/governance/engine.js +183 -0
  86. package/dist/governance/engine.js.map +1 -0
  87. package/dist/governance/evidence-collector.d.ts +42 -0
  88. package/dist/governance/evidence-collector.d.ts.map +1 -0
  89. package/dist/governance/evidence-collector.js +136 -0
  90. package/dist/governance/evidence-collector.js.map +1 -0
  91. package/dist/governance/plugins/correction-plugin.d.ts +20 -0
  92. package/dist/governance/plugins/correction-plugin.d.ts.map +1 -0
  93. package/dist/governance/plugins/correction-plugin.js +113 -0
  94. package/dist/governance/plugins/correction-plugin.js.map +1 -0
  95. package/dist/governance/plugins/guidance-plugin.d.ts +21 -0
  96. package/dist/governance/plugins/guidance-plugin.d.ts.map +1 -0
  97. package/dist/governance/plugins/guidance-plugin.js +69 -0
  98. package/dist/governance/plugins/guidance-plugin.js.map +1 -0
  99. package/dist/governance/plugins/memory-plugin.d.ts +22 -0
  100. package/dist/governance/plugins/memory-plugin.d.ts.map +1 -0
  101. package/dist/governance/plugins/memory-plugin.js +106 -0
  102. package/dist/governance/plugins/memory-plugin.js.map +1 -0
  103. package/dist/governance/plugins/policy-plugin.d.ts +16 -0
  104. package/dist/governance/plugins/policy-plugin.d.ts.map +1 -0
  105. package/dist/governance/plugins/policy-plugin.js +69 -0
  106. package/dist/governance/plugins/policy-plugin.js.map +1 -0
  107. package/dist/governance/plugins/verification-plugin.d.ts +15 -0
  108. package/dist/governance/plugins/verification-plugin.d.ts.map +1 -0
  109. package/dist/governance/plugins/verification-plugin.js +65 -0
  110. package/dist/governance/plugins/verification-plugin.js.map +1 -0
  111. package/dist/governance/types.d.ts +202 -0
  112. package/dist/governance/types.d.ts.map +1 -0
  113. package/dist/governance/types.js +10 -0
  114. package/dist/governance/types.js.map +1 -0
  115. package/dist/pipeline/completion-engine.js +7 -7
  116. package/dist/pipeline/completion-engine.js.map +1 -1
  117. package/dist/pipeline/completion-gate.js +3 -3
  118. package/dist/pipeline/completion-gate.js.map +1 -1
  119. package/dist/pipeline/diagnosis-service.d.ts +2 -1
  120. package/dist/pipeline/diagnosis-service.d.ts.map +1 -1
  121. package/dist/pipeline/diagnosis-service.js.map +1 -1
  122. package/dist/pipeline/dynamic-node-executor.js +1 -1
  123. package/dist/pipeline/dynamic-node-executor.js.map +1 -1
  124. package/dist/pipeline/execution-engine.d.ts +1 -6
  125. package/dist/pipeline/execution-engine.d.ts.map +1 -1
  126. package/dist/pipeline/execution-engine.js +1 -1
  127. package/dist/pipeline/execution-engine.js.map +1 -1
  128. package/dist/pipeline/execution-plan.js +1 -1
  129. package/dist/pipeline/execution-plan.js.map +1 -1
  130. package/dist/pipeline/i-node-executor.d.ts +4 -0
  131. package/dist/pipeline/i-node-executor.d.ts.map +1 -1
  132. package/dist/pipeline/node-type-evolver.js +1 -1
  133. package/dist/pipeline/node-type-evolver.js.map +1 -1
  134. package/dist/pipeline/node-type-sync.d.ts.map +1 -1
  135. package/dist/pipeline/node-type-sync.js +22 -43
  136. package/dist/pipeline/node-type-sync.js.map +1 -1
  137. package/dist/pipeline/pattern-types.d.ts +3 -1
  138. package/dist/pipeline/pattern-types.d.ts.map +1 -1
  139. package/dist/retrospective/types.d.ts +2 -4
  140. package/dist/retrospective/types.d.ts.map +1 -1
  141. package/dist/skill-registry/evolver/index.js +6 -2
  142. package/dist/skill-registry/evolver/index.js.map +1 -1
  143. package/dist/skill-registry/index.d.ts +5 -0
  144. package/dist/skill-registry/index.d.ts.map +1 -1
  145. package/dist/skill-registry/index.js +6 -0
  146. package/dist/skill-registry/index.js.map +1 -1
  147. package/dist/storage/repositories/dynamic-pipeline-repository.js +15 -5
  148. package/dist/storage/repositories/dynamic-pipeline-repository.js.map +1 -1
  149. package/dist/storage/repositories/node-attempt-repository.d.ts +1 -1
  150. package/dist/storage/repositories/node-attempt-repository.d.ts.map +1 -1
  151. package/dist/storage/repositories/node-attempt-repository.js +1 -1
  152. package/dist/storage/repositories/node-attempt-repository.js.map +1 -1
  153. package/dist/storage/repositories/pipeline-plan-repository.d.ts.map +1 -1
  154. package/dist/storage/repositories/pipeline-plan-repository.js +1 -1
  155. package/dist/storage/repositories/pipeline-plan-repository.js.map +1 -1
  156. package/dist/storage/repositories/template-evolution-repository.d.ts.map +1 -1
  157. package/dist/storage/repositories/template-evolution-repository.js.map +1 -1
  158. package/dist/storage/schema/migration-manager.d.ts +4 -9
  159. package/dist/storage/schema/migration-manager.d.ts.map +1 -1
  160. package/dist/storage/schema/migration-manager.js +24 -992
  161. package/dist/storage/schema/migration-manager.js.map +1 -1
  162. package/dist/storage/schema.sql +591 -0
  163. package/dist/storage/sqlite.d.ts.map +1 -1
  164. package/dist/storage/sqlite.js +22 -6
  165. package/dist/storage/sqlite.js.map +1 -1
  166. package/dist/web/routes/pipelines.d.ts.map +1 -1
  167. package/dist/web/routes/pipelines.js +16 -5
  168. package/dist/web/routes/pipelines.js.map +1 -1
  169. package/dist/web/routes/quality.d.ts.map +1 -1
  170. package/dist/web/routes/quality.js +18 -17
  171. package/dist/web/routes/quality.js.map +1 -1
  172. package/dist/web/server.d.ts.map +1 -1
  173. package/dist/web/server.js +0 -7
  174. package/dist/web/server.js.map +1 -1
  175. package/dist/web-static/assets/Analytics-Bo_OyY9A.js +36 -0
  176. package/dist/web-static/assets/BatchProgress-qW-6664M.js +1 -0
  177. package/dist/web-static/assets/Breadcrumb-C1Mua6se.js +1 -0
  178. package/dist/web-static/assets/Config-Dn-dtdo9.js +1 -0
  179. package/dist/web-static/assets/ConfirmDialog-bdDaAlT1.js +1 -0
  180. package/dist/web-static/assets/Conventions-ByV7r45c.js +1 -0
  181. package/dist/web-static/assets/Dashboard-C-7N8kYZ.js +1 -0
  182. package/dist/web-static/assets/ErrorState-CMLoMrQY.js +1 -0
  183. package/dist/web-static/assets/Events-DHuyQHQe.js +1 -0
  184. package/dist/web-static/assets/Evolution-Dzr5xOLD.js +1 -0
  185. package/dist/web-static/assets/Knowledge-ClwX4cnr.js +2 -0
  186. package/dist/web-static/assets/MiniCharts-DsV2Sqfk.js +1 -0
  187. package/dist/web-static/assets/NodeTypes-DFt8b5gi.js +1 -0
  188. package/dist/web-static/assets/Pagination-CR-eJz36.js +1 -0
  189. package/dist/web-static/assets/PipelineDetail-By1HJlaB.js +4 -0
  190. package/dist/web-static/assets/Pipelines--MGzCPtR.js +2 -0
  191. package/dist/web-static/assets/ProjectDetail-CrpekCeY.js +1 -0
  192. package/dist/web-static/assets/Projects-Dw1qYmjz.js +1 -0
  193. package/dist/web-static/assets/Quality-CYwODfQP.js +3 -0
  194. package/dist/web-static/assets/SessionDetail-c8MYnwnk.js +1 -0
  195. package/dist/web-static/assets/Sessions-BkDd4Mxb.js +2 -0
  196. package/dist/web-static/assets/Skeleton-DbL04wuz.js +1 -0
  197. package/dist/web-static/assets/Skills-2OPSAnaU.js +1 -0
  198. package/dist/web-static/assets/TemplateDetail-DbifRj4h.js +1 -0
  199. package/dist/web-static/assets/Templates-CDw-GGp8.js +1 -0
  200. package/dist/web-static/assets/Toast-CrwuIbKC.js +1 -0
  201. package/dist/web-static/assets/client-C_VWY70M.js +1 -0
  202. package/dist/web-static/assets/index-DD2Z15TY.css +2 -0
  203. package/dist/web-static/assets/index-DKyAorJ6.js +2 -0
  204. package/dist/web-static/assets/ui-DpI1N3yJ.js +1 -0
  205. package/dist/web-static/assets/useDebounce-BLryFdeo.js +1 -0
  206. package/dist/web-static/assets/vendor-2ObLXPrQ.js +9 -0
  207. package/dist/web-static/assets/vendor-motion-BryL_tAt.js +9 -0
  208. package/dist/web-static/assets/vendor-query-CjmRaY7o.js +4 -0
  209. package/dist/web-static/assets/vendor-react-DZi7brq3.js +11 -0
  210. package/dist/web-static/index.html +10 -13
  211. package/package.json +5 -9
  212. package/dist/convention/convention-distiller.d.ts +0 -28
  213. package/dist/convention/convention-distiller.d.ts.map +0 -1
  214. package/dist/convention/convention-distiller.js +0 -172
  215. package/dist/convention/convention-distiller.js.map +0 -1
  216. package/dist/convention/official-sync.d.ts +0 -21
  217. package/dist/convention/official-sync.d.ts.map +0 -1
  218. package/dist/convention/official-sync.js +0 -196
  219. package/dist/convention/official-sync.js.map +0 -1
  220. package/dist/daemon/handlers/stages/19-moderate-task.d.ts +0 -11
  221. package/dist/daemon/handlers/stages/19-moderate-task.d.ts.map +0 -1
  222. package/dist/daemon/handlers/stages/19-moderate-task.js +0 -78
  223. package/dist/daemon/handlers/stages/19-moderate-task.js.map +0 -1
  224. package/dist/web/routes/conventions.d.ts +0 -8
  225. package/dist/web/routes/conventions.d.ts.map +0 -1
  226. package/dist/web/routes/conventions.js +0 -308
  227. package/dist/web/routes/conventions.js.map +0 -1
  228. package/dist/web/routes/logs.d.ts +0 -4
  229. package/dist/web/routes/logs.d.ts.map +0 -1
  230. package/dist/web/routes/logs.js +0 -77
  231. package/dist/web/routes/logs.js.map +0 -1
  232. package/dist/web/routes/project-conventions.d.ts +0 -7
  233. package/dist/web/routes/project-conventions.d.ts.map +0 -1
  234. package/dist/web/routes/project-conventions.js +0 -145
  235. package/dist/web/routes/project-conventions.js.map +0 -1
  236. package/dist/web-static/assets/Analytics-txva5ugO.js +0 -1
  237. package/dist/web-static/assets/BatchProgress-BQ533tSf.js +0 -1
  238. package/dist/web-static/assets/Breadcrumb-DtfwnOx6.js +0 -1
  239. package/dist/web-static/assets/Config-CUb6-ddH.js +0 -1
  240. package/dist/web-static/assets/ConfirmDialog-BKfwMp04.js +0 -1
  241. package/dist/web-static/assets/Conventions-E_2yAYoB.js +0 -1
  242. package/dist/web-static/assets/Dashboard-Dskgf0jG.js +0 -1
  243. package/dist/web-static/assets/ErrorState-BOInXmfg.js +0 -1
  244. package/dist/web-static/assets/Events-CAY9kU9T.js +0 -1
  245. package/dist/web-static/assets/Evolution-Ck_BqRpt.js +0 -1
  246. package/dist/web-static/assets/Knowledge-BHLyFp2U.js +0 -2
  247. package/dist/web-static/assets/NodeTypes-B6wc7VnR.js +0 -1
  248. package/dist/web-static/assets/Pagination-lp8b_3NR.js +0 -1
  249. package/dist/web-static/assets/PipelineDetail-Bc6l2jqX.js +0 -4
  250. package/dist/web-static/assets/PipelineTemplates-sSL-9oRh.js +0 -1
  251. package/dist/web-static/assets/Pipelines-Drat9IqZ.js +0 -2
  252. package/dist/web-static/assets/ProjectDetail-Cx8VZp8O.js +0 -1
  253. package/dist/web-static/assets/Projects-qDolX6Y6.js +0 -1
  254. package/dist/web-static/assets/Quality-Bm7oRSun.js +0 -3
  255. package/dist/web-static/assets/SessionDetail-BMrqH8_W.js +0 -1
  256. package/dist/web-static/assets/Sessions-C0BmdDPK.js +0 -2
  257. package/dist/web-static/assets/Skeleton-B7PVDJJ_.js +0 -1
  258. package/dist/web-static/assets/Skills-B3c1_uFt.js +0 -1
  259. package/dist/web-static/assets/TemplateDetail-ep5h3Cu5.js +0 -1
  260. package/dist/web-static/assets/Templates-Dho__f4l.js +0 -1
  261. package/dist/web-static/assets/Toast-BbB3oD2a.js +0 -1
  262. package/dist/web-static/assets/client-BvVpIixG.js +0 -1
  263. package/dist/web-static/assets/index-By8HsUem.js +0 -2
  264. package/dist/web-static/assets/index-CqwJts5v.css +0 -2
  265. package/dist/web-static/assets/ui-CDL3BZ13.js +0 -1
  266. package/dist/web-static/assets/useDebounce-DNfPs3Tv.js +0 -1
  267. package/dist/web-static/assets/vendor-DRGPi8ui.js +0 -9
  268. package/dist/web-static/assets/vendor-charts-9eVsQvUV.js +0 -36
  269. package/dist/web-static/assets/vendor-editor-CYLOGES5.js +0 -11
  270. package/dist/web-static/assets/vendor-flow-CHpVij2M.css +0 -1
  271. package/dist/web-static/assets/vendor-flow-srkes8If.js +0 -7
  272. package/dist/web-static/assets/vendor-motion-CQmdgnI8.js +0 -9
  273. package/dist/web-static/assets/vendor-query-DqPOMnuX.js +0 -4
  274. package/dist/web-static/assets/vendor-react-DJI9oneq.js +0 -11
  275. /package/dist/web-static/assets/{exportCsv-Dm5Y5M_E.js → exportCsv-CO51kx6P.js} +0 -0
@@ -1,1000 +1,32 @@
1
1
  /**
2
- * MigrationManager - 数据库迁移管理器
2
+ * MigrationManager - 数据库迁移管理器(v1 基线版本)
3
3
  *
4
- * 从 sqlite.ts 提取,负责管理 schema 版本迁移。
5
- * 包含所有增量迁移函数和迁移执行逻辑。
4
+ * schema.sql 包含完整的表定义,不再依赖增量迁移。
5
+ * 检测到旧版本数据库时,备份后删库重建。
6
6
  */
7
7
  import { logger } from '../../utils/logger.js';
8
- export const SCHEMA_VERSION = 27;
9
- /** 增量迁移函数表:key 为目标版本号,value 为迁移函数 */
10
- export const MIGRATIONS = {
11
- 2: (db) => {
12
- db.exec(`
13
- CREATE VIRTUAL TABLE IF NOT EXISTS events_fts
14
- USING fts5(
15
- event_id UNINDEXED,
16
- session_id,
17
- project_path,
18
- tool_name,
19
- tool_input,
20
- user_prompt,
21
- content=events,
22
- tokenize='unicode61'
23
- )
24
- `);
25
- },
26
- // v3: 任务复盘分析器 — task_sessions, failure_signals, task_patterns
27
- 3: (db) => {
28
- db.exec(`
29
- CREATE TABLE IF NOT EXISTS task_sessions (
30
- id TEXT PRIMARY KEY,
31
- session_id TEXT NOT NULL,
32
- project_path TEXT NOT NULL,
33
- pipeline_id TEXT,
34
- requirement TEXT NOT NULL,
35
- task_type TEXT NOT NULL DEFAULT 'other',
36
- complexity TEXT NOT NULL DEFAULT 'moderate',
37
- total_rounds INTEGER NOT NULL DEFAULT 0,
38
- total_tool_calls INTEGER NOT NULL DEFAULT 0,
39
- write_edit_count INTEGER NOT NULL DEFAULT 0,
40
- fix_attempt_count INTEGER NOT NULL DEFAULT 0,
41
- quality_fail_count INTEGER NOT NULL DEFAULT 0,
42
- quality_pass_count INTEGER NOT NULL DEFAULT 0,
43
- failure_signal_count INTEGER NOT NULL DEFAULT 0,
44
- phase_distribution TEXT,
45
- outcome TEXT NOT NULL DEFAULT 'in_progress',
46
- duration_ms INTEGER,
47
- started_at TEXT NOT NULL,
48
- ended_at TEXT,
49
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
50
- );
51
- CREATE INDEX IF NOT EXISTS idx_ts_session ON task_sessions(session_id);
52
- CREATE INDEX IF NOT EXISTS idx_ts_project ON task_sessions(project_path);
53
- CREATE INDEX IF NOT EXISTS idx_ts_task_type ON task_sessions(task_type);
54
-
55
- CREATE TABLE IF NOT EXISTS failure_signals (
56
- id TEXT PRIMARY KEY,
57
- session_id TEXT NOT NULL,
58
- project_path TEXT NOT NULL,
59
- task_session_id TEXT,
60
- signal_type TEXT NOT NULL,
61
- raw_prompt TEXT NOT NULL,
62
- matched_pattern TEXT,
63
- context_phase TEXT,
64
- is_iteration INTEGER NOT NULL DEFAULT 0,
65
- preceding_tool_count INTEGER NOT NULL DEFAULT 0,
66
- timestamp TEXT NOT NULL,
67
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
68
- );
69
- CREATE INDEX IF NOT EXISTS idx_fs_session ON failure_signals(session_id);
70
- CREATE INDEX IF NOT EXISTS idx_fs_project ON failure_signals(project_path);
71
-
72
- CREATE TABLE IF NOT EXISTS task_patterns (
73
- id TEXT PRIMARY KEY,
74
- task_type TEXT NOT NULL,
75
- failure_pattern TEXT NOT NULL,
76
- prevention_strategy TEXT NOT NULL,
77
- confidence_score REAL NOT NULL DEFAULT 0.0,
78
- sample_count INTEGER NOT NULL DEFAULT 0,
79
- last_updated TEXT NOT NULL DEFAULT (datetime('now')),
80
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
81
- );
82
- CREATE INDEX IF NOT EXISTS idx_tp_task_type ON task_patterns(task_type);
83
- `);
84
- },
85
- // v4: 可观测性 — api_usage 表,task_sessions/task_patterns 效果追踪字段
86
- 4: (db) => {
87
- db.exec(`
88
- CREATE TABLE IF NOT EXISTS api_usage (
89
- id TEXT PRIMARY KEY,
90
- session_id TEXT NOT NULL,
91
- label TEXT NOT NULL,
92
- model TEXT NOT NULL DEFAULT '',
93
- tokens_in INTEGER NOT NULL DEFAULT 0,
94
- tokens_out INTEGER NOT NULL DEFAULT 0,
95
- cost_usd REAL NOT NULL DEFAULT 0.0,
96
- latency_ms INTEGER NOT NULL DEFAULT 0,
97
- timestamp TEXT NOT NULL DEFAULT (datetime('now'))
98
- );
99
- CREATE INDEX IF NOT EXISTS idx_au_session ON api_usage(session_id);
100
- CREATE INDEX IF NOT EXISTS idx_au_label ON api_usage(label);
101
- `);
102
- // ALTER TABLE 需单独执行(SQLite 不支持在同一 exec 中混用 DDL)
103
- try {
104
- db.exec(`ALTER TABLE task_sessions ADD COLUMN satisfaction_score REAL DEFAULT NULL`);
105
- }
106
- catch (err) {
107
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
108
- throw err;
109
- }
110
- try {
111
- db.exec(`ALTER TABLE task_patterns ADD COLUMN adoption_count INTEGER NOT NULL DEFAULT 0`);
112
- }
113
- catch (err) {
114
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
115
- throw err;
116
- }
117
- try {
118
- db.exec(`ALTER TABLE task_patterns ADD COLUMN effectiveness_score REAL NOT NULL DEFAULT 0.5`);
119
- }
120
- catch (err) {
121
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
122
- throw err;
123
- }
124
- try {
125
- db.exec(`ALTER TABLE task_patterns ADD COLUMN last_used TEXT DEFAULT NULL`);
126
- }
127
- catch (err) {
128
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
129
- throw err;
130
- }
131
- },
132
- // v5: 学习系统修复 — pattern_executions 关联 task_session,质量门禁持久化,Pattern 进化追踪
133
- 5: (db) => {
134
- // 学习闭环:pattern_executions 关联 task_session
135
- try {
136
- db.exec(`ALTER TABLE pattern_executions ADD COLUMN task_session_id TEXT DEFAULT NULL`);
137
- }
138
- catch (err) {
139
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
140
- throw err;
141
- }
142
- try {
143
- db.exec(`ALTER TABLE pattern_executions ADD COLUMN outcome TEXT DEFAULT NULL`);
144
- }
145
- catch (err) {
146
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
147
- throw err;
148
- }
149
- db.exec(`CREATE INDEX IF NOT EXISTS idx_pe_task_session ON pattern_executions(task_session_id)`);
150
- db.exec(`CREATE INDEX IF NOT EXISTS idx_pe_outcome ON pattern_executions(outcome)`);
151
- // 质量门禁持久化
152
- db.exec(`
153
- CREATE TABLE IF NOT EXISTS quality_gate_suppressions (
154
- id TEXT PRIMARY KEY,
155
- category TEXT NOT NULL,
156
- project_path TEXT NOT NULL,
157
- suppression_count INTEGER NOT NULL DEFAULT 1,
158
- first_suppressed_at TEXT NOT NULL,
159
- last_suppressed_at TEXT NOT NULL,
160
- expires_at TEXT DEFAULT NULL,
161
- created_at TEXT DEFAULT (datetime('now'))
162
- );
163
- CREATE INDEX IF NOT EXISTS idx_qgs_category ON quality_gate_suppressions(category, project_path);
164
- CREATE INDEX IF NOT EXISTS idx_qgs_expires ON quality_gate_suppressions(expires_at);
165
-
166
- CREATE TABLE IF NOT EXISTS quality_gate_history (
167
- id TEXT PRIMARY KEY,
168
- session_id TEXT NOT NULL,
169
- project_path TEXT NOT NULL,
170
- file_path TEXT NOT NULL,
171
- category TEXT NOT NULL,
172
- level TEXT NOT NULL,
173
- message TEXT NOT NULL,
174
- resolved INTEGER DEFAULT 0,
175
- resolved_at TEXT DEFAULT NULL,
176
- created_at TEXT DEFAULT (datetime('now'))
177
- );
178
- CREATE INDEX IF NOT EXISTS idx_qgh_session ON quality_gate_history(session_id, project_path);
179
- CREATE INDEX IF NOT EXISTS idx_qgh_file ON quality_gate_history(file_path, resolved);
180
- `);
181
- // Pattern 进化追踪
182
- db.exec(`
183
- CREATE TABLE IF NOT EXISTS pattern_evolution_history (
184
- id TEXT PRIMARY KEY,
185
- pattern_id TEXT NOT NULL,
186
- old_version TEXT NOT NULL,
187
- new_version TEXT NOT NULL,
188
- old_yaml TEXT NOT NULL,
189
- new_yaml TEXT NOT NULL,
190
- validation_status TEXT DEFAULT 'pending',
191
- test_success_rate REAL DEFAULT NULL,
192
- evolved_at TEXT NOT NULL,
193
- validated_at TEXT DEFAULT NULL,
194
- created_at TEXT DEFAULT (datetime('now'))
195
- );
196
- CREATE INDEX IF NOT EXISTS idx_peh_pattern ON pattern_evolution_history(pattern_id);
197
- CREATE INDEX IF NOT EXISTS idx_peh_status ON pattern_evolution_history(validation_status);
198
- `);
199
- },
200
- // v6: Pipeline 任务类型区分 — 新增 task_type 字段(已废弃,pipelines 表已迁移至 dynamic_pipelines)
201
- 6: (db) => {
202
- const tableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='pipelines'`).get();
203
- if (tableExists) {
204
- try {
205
- db.exec(`ALTER TABLE pipelines ADD COLUMN task_type TEXT DEFAULT 'feature'`);
206
- }
207
- catch (err) {
208
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
209
- throw err;
210
- }
211
- }
212
- },
213
- // v7: Pipeline 智能编排 — 新增 complexity, planned_phases, phase_artifacts, reasoning 字段(已废弃,pipelines 表已迁移至 dynamic_pipelines)
214
- 7: (db) => {
215
- const tableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='pipelines'`).get();
216
- if (tableExists) {
217
- try {
218
- db.exec(`ALTER TABLE pipelines ADD COLUMN complexity TEXT DEFAULT 'moderate'`);
219
- }
220
- catch (err) {
221
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
222
- throw err;
223
- }
224
- try {
225
- db.exec(`ALTER TABLE pipelines ADD COLUMN planned_phases TEXT DEFAULT NULL`);
226
- }
227
- catch (err) {
228
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
229
- throw err;
230
- }
231
- try {
232
- db.exec(`ALTER TABLE pipelines ADD COLUMN phase_artifacts TEXT DEFAULT NULL`);
233
- }
234
- catch (err) {
235
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
236
- throw err;
237
- }
238
- try {
239
- db.exec(`ALTER TABLE pipelines ADD COLUMN reasoning TEXT DEFAULT NULL`);
240
- }
241
- catch (err) {
242
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
243
- throw err;
244
- }
245
- }
246
- },
247
- // v8: 升维 — 端到端延迟追踪、离线规则库、知识图谱、满意度信号
248
- 8: (db) => {
249
- db.exec(`
250
- CREATE TABLE IF NOT EXISTS latency_traces (
251
- id TEXT PRIMARY KEY,
252
- session_id TEXT NOT NULL,
253
- project_path TEXT NOT NULL,
254
- trace_type TEXT NOT NULL,
255
- engine_name TEXT NOT NULL,
256
- duration_ms INTEGER NOT NULL,
257
- metadata TEXT,
258
- timestamp TEXT NOT NULL DEFAULT (datetime('now'))
259
- );
260
- CREATE INDEX IF NOT EXISTS idx_lt_session ON latency_traces(session_id);
261
- CREATE INDEX IF NOT EXISTS idx_lt_type ON latency_traces(trace_type, timestamp DESC);
262
-
263
- CREATE TABLE IF NOT EXISTS intent_rules (
264
- id TEXT PRIMARY KEY,
265
- rule_type TEXT NOT NULL,
266
- pattern TEXT NOT NULL,
267
- confidence REAL NOT NULL DEFAULT 0.8,
268
- hit_count INTEGER NOT NULL DEFAULT 0,
269
- miss_count INTEGER NOT NULL DEFAULT 0,
270
- source TEXT NOT NULL DEFAULT 'distilled',
271
- last_hit_at TEXT,
272
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
273
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
274
- );
275
- CREATE INDEX IF NOT EXISTS idx_ir_type ON intent_rules(rule_type, confidence DESC);
276
-
277
- CREATE TABLE IF NOT EXISTS knowledge_nodes (
278
- id TEXT PRIMARY KEY,
279
- project_path TEXT NOT NULL,
280
- node_type TEXT NOT NULL,
281
- title TEXT NOT NULL,
282
- content TEXT NOT NULL,
283
- tags TEXT,
284
- embedding_hint TEXT,
285
- archived INTEGER NOT NULL DEFAULT 0,
286
- last_accessed_at TEXT,
287
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
288
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
289
- );
290
- CREATE INDEX IF NOT EXISTS idx_kn_project ON knowledge_nodes(project_path, node_type);
291
- CREATE INDEX IF NOT EXISTS idx_kn_archived ON knowledge_nodes(archived, last_accessed_at);
292
-
293
- CREATE TABLE IF NOT EXISTS knowledge_edges (
294
- id TEXT PRIMARY KEY,
295
- from_node_id TEXT NOT NULL,
296
- to_node_id TEXT NOT NULL,
297
- edge_type TEXT NOT NULL,
298
- weight REAL NOT NULL DEFAULT 1.0,
299
- metadata TEXT,
300
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
301
- );
302
- CREATE INDEX IF NOT EXISTS idx_ke_from ON knowledge_edges(from_node_id);
303
- CREATE INDEX IF NOT EXISTS idx_ke_to ON knowledge_edges(to_node_id);
304
-
305
- CREATE TABLE IF NOT EXISTS satisfaction_signals (
306
- id TEXT PRIMARY KEY,
307
- session_id TEXT NOT NULL,
308
- project_path TEXT NOT NULL,
309
- task_session_id TEXT,
310
- signal_type TEXT NOT NULL,
311
- signal_value REAL NOT NULL,
312
- weight REAL NOT NULL DEFAULT 1.0,
313
- raw_data TEXT,
314
- timestamp TEXT NOT NULL DEFAULT (datetime('now'))
315
- );
316
- CREATE INDEX IF NOT EXISTS idx_ss_session ON satisfaction_signals(session_id);
317
- CREATE INDEX IF NOT EXISTS idx_ss_task ON satisfaction_signals(task_session_id);
318
- CREATE INDEX IF NOT EXISTS idx_ss_project ON satisfaction_signals(project_path, timestamp DESC);
319
- `);
320
- // FTS 虚拟表单独执行
321
- try {
322
- db.exec(`
323
- CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_nodes_fts
324
- USING fts5(id UNINDEXED, project_path, title, content, embedding_hint,
325
- content=knowledge_nodes, tokenize='unicode61')
326
- `);
327
- }
328
- catch (err) {
329
- if (!(err instanceof Error && err.message.includes('already exists')))
330
- throw err;
331
- }
332
- try {
333
- db.exec(`ALTER TABLE api_usage ADD COLUMN engine_name TEXT DEFAULT NULL`);
334
- }
335
- catch (err) {
336
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
337
- throw err;
338
- }
339
- },
340
- // v9: Daemon 状态持久化 — daemon_state 表
341
- 9: (db) => {
342
- db.exec(`
343
- CREATE TABLE IF NOT EXISTS daemon_state (
344
- key TEXT PRIMARY KEY,
345
- value TEXT NOT NULL,
346
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
347
- );
348
- `);
349
- },
350
- // v10: 查询性能优化 — 高频查询复合索引
351
- 10: (db) => {
352
- // events 表:project_path + distilled + timestamp 复合索引(消除 ORDER BY 临时 B-Tree)
353
- db.exec(`CREATE INDEX IF NOT EXISTS idx_events_project_distilled_ts ON events(project_path, distilled, timestamp DESC)`);
354
- // events 表:session_id + distilled 复合索引(getUndistilledEvents 高频路径)
355
- db.exec(`CREATE INDEX IF NOT EXISTS idx_events_session_distilled ON events(session_id, distilled, timestamp DESC)`);
356
- // knowledge_nodes 表:project_path + node_type + archived 复合索引
357
- db.exec(`CREATE INDEX IF NOT EXISTS idx_kn_project_type_archived ON knowledge_nodes(project_path, node_type, archived, last_accessed_at DESC)`);
358
- // latency_traces 表:project_path + trace_type + timestamp 复合索引
359
- db.exec(`CREATE INDEX IF NOT EXISTS idx_lt_project_type_ts ON latency_traces(project_path, trace_type, timestamp DESC)`);
360
- // task_sessions 表:project_path + outcome + ended_at 复合索引(getLastCompletedTaskSession)
361
- db.exec(`CREATE INDEX IF NOT EXISTS idx_ts_project_outcome_ts ON task_sessions(project_path, outcome, ended_at DESC)`);
362
- // api_usage 表:timestamp 索引(成本趋势查询)
363
- db.exec(`CREATE INDEX IF NOT EXISTS idx_au_timestamp ON api_usage(timestamp DESC)`);
364
- },
365
- // v11: pipeline_tasks 新增 source 字段(任务来源规范标记)(已废弃,pipeline_tasks 表已迁移至 dynamic_nodes)
366
- 11: (db) => {
367
- const tableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='pipeline_tasks'`).get();
368
- if (tableExists) {
369
- try {
370
- db.exec(`ALTER TABLE pipeline_tasks ADD COLUMN source TEXT DEFAULT NULL`);
371
- }
372
- catch (err) {
373
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
374
- throw err;
375
- }
376
- }
377
- },
378
- // v12: pipeline_tasks 补全 updated_at 字段(新安装 schema.sql 已有,旧库需迁移)(已废弃,pipeline_tasks 表已迁移至 dynamic_nodes)
379
- 12: (db) => {
380
- const tableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='pipeline_tasks'`).get();
381
- if (tableExists) {
382
- try {
383
- db.exec(`ALTER TABLE pipeline_tasks ADD COLUMN updated_at TEXT DEFAULT NULL`);
384
- }
385
- catch (err) {
386
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
387
- throw err;
388
- }
389
- // 回填已有记录的 updated_at(用 created_at 兜底,无 created_at 则用当前时间)
390
- db.exec(`UPDATE pipeline_tasks SET updated_at = datetime('now') WHERE updated_at IS NULL`);
391
- }
392
- },
393
- // v13: quality_gate_history 新增 resolved_type 字段(区分 resolved/dismissed)
394
- 13: (db) => {
395
- try {
396
- db.exec(`ALTER TABLE quality_gate_history ADD COLUMN resolved_type TEXT DEFAULT NULL`);
397
- }
398
- catch (err) {
399
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
400
- throw err;
401
- }
402
- },
403
- // v14: 配置版本历史表
404
- 14: (db) => {
405
- db.exec(`
406
- CREATE TABLE IF NOT EXISTS config_history (
407
- id INTEGER PRIMARY KEY AUTOINCREMENT,
408
- snapshot TEXT NOT NULL,
409
- saved_at TEXT NOT NULL DEFAULT (datetime('now'))
410
- )
411
- `);
412
- },
413
- // v15: 动态节点系统 — dynamic_pipelines, dynamic_nodes, dynamic_pipeline_templates
414
- 15: (db) => {
415
- db.exec(`
416
- CREATE TABLE IF NOT EXISTS dynamic_pipelines (
417
- id TEXT PRIMARY KEY,
418
- requirement TEXT NOT NULL,
419
- project_path TEXT NOT NULL,
420
- session_id TEXT NOT NULL,
421
- template_id TEXT,
422
- convention_id TEXT,
423
- complexity TEXT,
424
- reasoning TEXT,
425
- status TEXT NOT NULL DEFAULT 'in_progress',
426
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
427
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
428
- );
429
- CREATE INDEX IF NOT EXISTS idx_dp_session ON dynamic_pipelines(session_id);
430
- CREATE INDEX IF NOT EXISTS idx_dp_project ON dynamic_pipelines(project_path);
431
- CREATE INDEX IF NOT EXISTS idx_dp_status ON dynamic_pipelines(status);
432
-
433
- CREATE TABLE IF NOT EXISTS dynamic_nodes (
434
- id TEXT PRIMARY KEY,
435
- pipeline_id TEXT NOT NULL,
436
- node_type_id TEXT NOT NULL,
437
- status TEXT NOT NULL DEFAULT 'pending',
438
- dependencies TEXT NOT NULL DEFAULT '[]',
439
- bound_skill_name TEXT,
440
- config_override TEXT,
441
- output TEXT,
442
- started_at TEXT,
443
- completed_at TEXT,
444
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
445
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
446
- FOREIGN KEY (pipeline_id) REFERENCES dynamic_pipelines(id) ON DELETE CASCADE
447
- );
448
- CREATE INDEX IF NOT EXISTS idx_dn_pipeline ON dynamic_nodes(pipeline_id);
449
- CREATE INDEX IF NOT EXISTS idx_dn_status ON dynamic_nodes(status);
450
-
451
- CREATE TABLE IF NOT EXISTS dynamic_pipeline_templates (
452
- id TEXT PRIMARY KEY,
453
- name TEXT NOT NULL,
454
- description TEXT NOT NULL,
455
- source TEXT NOT NULL DEFAULT 'builtin',
456
- convention_id TEXT,
457
- convention_name TEXT,
458
- trigger TEXT NOT NULL,
459
- nodes TEXT NOT NULL,
460
- quality_standards TEXT,
461
- version TEXT,
462
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
463
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
464
- );
465
- CREATE INDEX IF NOT EXISTS idx_dpt_source ON dynamic_pipeline_templates(source);
466
- CREATE INDEX IF NOT EXISTS idx_dpt_convention ON dynamic_pipeline_templates(convention_id);
467
- `);
468
- },
469
- // v16: Legacy Pipeline 数据迁移到 Dynamic Pipeline
470
- 16: (db) => {
471
- const pipelinesExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='pipelines'`).get();
472
- const tasksExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='pipeline_tasks'`).get();
473
- if (!pipelinesExists && !tasksExists) {
474
- logger.info('[SQLite] v16: Legacy Pipeline 表不存在,跳过迁移');
475
- return;
476
- }
477
- logger.info('[SQLite] 开始迁移 Legacy Pipeline 数据到 Dynamic Pipeline...');
478
- // 1. 迁移 pipelines → dynamic_pipelines
479
- if (pipelinesExists) {
480
- const pipelinesMigrated = db.prepare(`
481
- INSERT INTO dynamic_pipelines (
482
- id, requirement, project_path, session_id,
483
- template_id, convention_id, complexity, reasoning,
484
- status, created_at, updated_at
485
- )
486
- SELECT
487
- id, requirement, project_path, session_id,
488
- NULL as template_id,
489
- NULL as convention_id,
490
- COALESCE(complexity, 'moderate') as complexity,
491
- reasoning,
492
- CASE WHEN phase = 'done' THEN 'completed' ELSE 'in_progress' END as status,
493
- created_at, updated_at
494
- FROM pipelines
495
- WHERE id NOT IN (SELECT id FROM dynamic_pipelines)
496
- `).run();
497
- logger.info(`[SQLite] 迁移了 ${pipelinesMigrated.changes} 个 Legacy Pipelines`);
498
- }
499
- // 2. 迁移 pipeline_tasks → dynamic_nodes
500
- if (tasksExists) {
501
- const tasksMigrated = db.prepare(`
502
- INSERT INTO dynamic_nodes (
503
- id, pipeline_id, node_type_id, status,
504
- dependencies, bound_skill_name, output,
505
- started_at, completed_at, created_at, updated_at
506
- )
507
- SELECT
508
- id, pipeline_id, phase as node_type_id,
509
- CASE
510
- WHEN status = 'completed' THEN 'completed'
511
- WHEN status = 'in_progress' THEN 'in_progress'
512
- WHEN status = 'failed' THEN 'failed'
513
- ELSE 'pending'
514
- END as status,
515
- COALESCE(dependencies, '[]') as dependencies,
516
- NULL as bound_skill_name,
517
- output,
518
- NULL as started_at,
519
- NULL as completed_at,
520
- datetime('now') as created_at,
521
- COALESCE(updated_at, datetime('now')) as updated_at
522
- FROM pipeline_tasks
523
- WHERE id NOT IN (SELECT id FROM dynamic_nodes)
524
- `).run();
525
- logger.info(`[SQLite] 迁移了 ${tasksMigrated.changes} 个 Legacy Tasks`);
526
- }
527
- // 3. 转换 pipeline_events 事件类型
528
- if (pipelinesExists) {
529
- const eventsConverted = db.prepare(`
530
- UPDATE pipeline_events
531
- SET type = CASE
532
- WHEN type = 'pipeline.created' THEN 'dynamic_pipeline.created'
533
- WHEN type = 'task.created' THEN 'dynamic_node.created'
534
- WHEN type = 'task.started' THEN 'dynamic_node.started'
535
- WHEN type = 'task.completed' THEN 'dynamic_node.completed'
536
- WHEN type = 'task.failed' THEN 'dynamic_node.failed'
537
- WHEN type = 'phase.advanced' THEN 'dynamic_node.advanced'
538
- WHEN type = 'pipeline.closed' THEN 'dynamic_pipeline.completed'
539
- ELSE type
540
- END
541
- WHERE pipeline_id IN (SELECT id FROM pipelines)
542
- AND type NOT LIKE 'dynamic_%'
543
- `).run();
544
- logger.info(`[SQLite] 转换了 ${eventsConverted.changes} 个事件类型`);
545
- }
546
- logger.info('[SQLite] Legacy Pipeline 数据迁移完成');
547
- },
548
- // v17: 删除已迁移的 Legacy Pipeline 表(pipelines、pipeline_tasks)
549
- 17: (db) => {
550
- db.exec(`
551
- DROP TABLE IF EXISTS pipeline_tasks;
552
- DROP TABLE IF EXISTS pipelines;
553
- `);
554
- logger.info('[SQLite] 已删除 Legacy Pipeline 表(pipelines、pipeline_tasks)');
555
- },
556
- // v18: 补充迁移 pipeline_events 中未同步到 dynamic_pipelines 的 Pipeline
557
- // v16 只迁移了 pipelines 表,但事件溯源系统创建的 Pipeline 只写入 pipeline_events,未写入 pipelines 表
558
- 18: (db) => {
559
- const eventsTableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='pipeline_events'`).get();
560
- if (!eventsTableExists) {
561
- logger.info('[SQLite] v18: pipeline_events 表不存在,跳过补充迁移');
562
- return;
563
- }
564
- logger.info('[SQLite] v18: 补充迁移 pipeline_events 中未同步的 Pipeline...');
565
- // 找出有 pipeline.created 事件但不在 dynamic_pipelines 里的 pipeline IDs
566
- const unmigrated = db.prepare(`
567
- SELECT DISTINCT pipeline_id
568
- FROM pipeline_events
569
- WHERE type = 'pipeline.created'
570
- AND pipeline_id NOT IN (SELECT id FROM dynamic_pipelines)
571
- `).all();
572
- if (unmigrated.length === 0) {
573
- logger.info('[SQLite] v18: 无需补充迁移');
574
- return;
575
- }
576
- logger.info(`[SQLite] v18: 发现 ${unmigrated.length} 个未迁移的 Pipeline`);
577
- const insertPipeline = db.prepare(`
578
- INSERT OR IGNORE INTO dynamic_pipelines
579
- (id, requirement, project_path, session_id, complexity, reasoning, status, created_at, updated_at)
580
- SELECT
581
- pe.pipeline_id,
582
- json_extract(pe.payload, '$.requirement'),
583
- json_extract(pe.payload, '$.projectPath'),
584
- json_extract(pe.payload, '$.sessionId'),
585
- COALESCE(json_extract(pe.payload, '$.complexity'), 'moderate'),
586
- json_extract(pe.payload, '$.reasoning'),
587
- CASE
588
- WHEN EXISTS (
589
- SELECT 1 FROM pipeline_events pe2
590
- WHERE pe2.pipeline_id = pe.pipeline_id
591
- AND pe2.type IN ('pipeline.closed', 'dynamic_pipeline.completed')
592
- ) THEN 'completed'
593
- ELSE 'in_progress'
594
- END,
595
- pe.created_at,
596
- pe.created_at
597
- FROM pipeline_events pe
598
- WHERE pe.pipeline_id = ? AND pe.type = 'pipeline.created'
599
- LIMIT 1
600
- `);
601
- const insertNodes = db.prepare(`
602
- INSERT OR IGNORE INTO dynamic_nodes
603
- (id, pipeline_id, node_type_id, status, dependencies, created_at, updated_at)
604
- SELECT
605
- json_extract(pe.payload, '$.taskId'),
606
- pe.pipeline_id,
607
- json_extract(pe.payload, '$.phase'),
608
- CASE
609
- WHEN EXISTS (
610
- SELECT 1 FROM pipeline_events pe2
611
- WHERE pe2.pipeline_id = pe.pipeline_id
612
- AND pe2.type = 'task.completed'
613
- AND json_extract(pe2.payload, '$.taskId') = json_extract(pe.payload, '$.taskId')
614
- ) THEN 'completed'
615
- WHEN EXISTS (
616
- SELECT 1 FROM pipeline_events pe2
617
- WHERE pe2.pipeline_id = pe.pipeline_id
618
- AND pe2.type = 'task.started'
619
- AND json_extract(pe2.payload, '$.taskId') = json_extract(pe.payload, '$.taskId')
620
- ) THEN 'in_progress'
621
- ELSE 'pending'
622
- END,
623
- COALESCE(json_extract(pe.payload, '$.dependencies'), '[]'),
624
- pe.created_at,
625
- pe.created_at
626
- FROM pipeline_events pe
627
- WHERE pe.pipeline_id = ? AND pe.type = 'task.created'
628
- AND json_extract(pe.payload, '$.taskId') NOT IN (SELECT id FROM dynamic_nodes)
629
- `);
630
- const convertEvents = db.prepare(`
631
- UPDATE pipeline_events
632
- SET type = CASE
633
- WHEN type = 'pipeline.created' THEN 'dynamic_pipeline.created'
634
- WHEN type = 'task.created' THEN 'dynamic_node.created'
635
- WHEN type = 'task.started' THEN 'dynamic_node.started'
636
- WHEN type = 'task.completed' THEN 'dynamic_node.completed'
637
- WHEN type = 'task.failed' THEN 'dynamic_node.failed'
638
- WHEN type = 'phase.advanced' THEN 'dynamic_node.advanced'
639
- WHEN type = 'pipeline.closed' THEN 'dynamic_pipeline.completed'
640
- ELSE type
641
- END
642
- WHERE pipeline_id = ? AND type NOT LIKE 'dynamic_%'
643
- `);
644
- let pipelineCount = 0;
645
- let nodeCount = 0;
646
- for (const { pipeline_id } of unmigrated) {
647
- insertPipeline.run(pipeline_id);
648
- const nodeResult = insertNodes.run(pipeline_id);
649
- nodeCount += nodeResult.changes;
650
- convertEvents.run(pipeline_id);
651
- pipelineCount++;
652
- }
653
- logger.info(`[SQLite] v18: 补充迁移完成 — ${pipelineCount} 个 Pipeline,${nodeCount} 个节点`);
654
- },
655
- // v19: 节点类型持久化 — node_types 表
656
- 19: (db) => {
657
- logger.info('[SQLite] v19: 创建 node_types 表...');
658
- db.exec(`
659
- CREATE TABLE IF NOT EXISTS node_types (
660
- id TEXT PRIMARY KEY,
661
- name TEXT NOT NULL,
662
- description TEXT,
663
- semantic_category TEXT,
664
- completion_config TEXT NOT NULL,
665
- activity_threshold TEXT NOT NULL,
666
- default_skills TEXT,
667
- source TEXT NOT NULL DEFAULT 'builtin' CHECK(source IN ('builtin', 'convention')),
668
- convention_id TEXT,
669
- created_at TEXT DEFAULT (datetime('now')),
670
- updated_at TEXT DEFAULT (datetime('now'))
671
- );
672
- CREATE INDEX IF NOT EXISTS idx_node_types_source ON node_types(source);
673
- CREATE INDEX IF NOT EXISTS idx_node_types_convention ON node_types(convention_id);
674
- CREATE INDEX IF NOT EXISTS idx_node_types_category ON node_types(semantic_category);
675
- `);
676
- logger.info('[SQLite] v19: node_types 表创建完成');
677
- },
678
- 20: (db) => {
679
- logger.info('[SQLite] v20: 升级意图分类系统(统一分类器)...');
680
- // 1. 升级 intent_rules 表:添加质量追踪字段
681
- db.exec(`
682
- ALTER TABLE intent_rules ADD COLUMN priority TEXT DEFAULT 'learned' CHECK(priority IN ('ground_truth', 'learned'));
683
- ALTER TABLE intent_rules ADD COLUMN success_count INTEGER DEFAULT 0;
684
- ALTER TABLE intent_rules ADD COLUMN failure_count INTEGER DEFAULT 0;
685
- ALTER TABLE intent_rules ADD COLUMN avg_satisfaction REAL DEFAULT 0.0;
686
- ALTER TABLE intent_rules ADD COLUMN is_active INTEGER DEFAULT 1 CHECK(is_active IN (0, 1));
687
- ALTER TABLE intent_rules ADD COLUMN conflicts_with TEXT;
688
- ALTER TABLE intent_rules ADD COLUMN last_quality_update_at TEXT;
689
- `);
690
- // 2. 创建索引
691
- db.exec(`
692
- CREATE INDEX IF NOT EXISTS idx_intent_rules_priority_active ON intent_rules(priority, is_active);
693
- CREATE INDEX IF NOT EXISTS idx_intent_rules_confidence_active ON intent_rules(confidence DESC, is_active);
694
- `);
695
- // 3. 新建 intent_decisions 表(决策审计日志)
696
- db.exec(`
697
- CREATE TABLE IF NOT EXISTS intent_decisions (
698
- id TEXT PRIMARY KEY,
699
- session_id TEXT NOT NULL,
700
- project_path TEXT NOT NULL,
701
- requirement TEXT NOT NULL,
702
- predicted_complexity TEXT NOT NULL CHECK(predicted_complexity IN ('simple', 'moderate', 'complex')),
703
- matched_rule_id TEXT,
704
- matched_layer TEXT NOT NULL CHECK(matched_layer IN ('ground_truth', 'learned', 'ai_fallback')),
705
- confidence REAL NOT NULL,
706
- actual_outcome TEXT CHECK(actual_outcome IN ('success', 'partial', 'failure', NULL)),
707
- satisfaction_score REAL,
708
- feedback_recorded_at TEXT,
709
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
710
- FOREIGN KEY (session_id) REFERENCES task_sessions(session_id) ON DELETE CASCADE
711
- );
712
-
713
- CREATE INDEX IF NOT EXISTS idx_intent_decisions_session ON intent_decisions(session_id);
714
- CREATE INDEX IF NOT EXISTS idx_intent_decisions_rule ON intent_decisions(matched_rule_id);
715
- CREATE INDEX IF NOT EXISTS idx_intent_decisions_created ON intent_decisions(created_at DESC);
716
- `);
717
- // 4. 创建质量监控视图
718
- db.exec(`
719
- CREATE VIEW IF NOT EXISTS intent_rules_quality AS
720
- SELECT
721
- id, rule_type, pattern, priority, confidence,
722
- hit_count, miss_count, success_count, failure_count, avg_satisfaction, is_active,
723
- CASE
724
- WHEN (success_count + failure_count) > 0
725
- THEN CAST(success_count AS REAL) / (success_count + failure_count)
726
- ELSE 0.0
727
- END AS accuracy,
728
- CASE
729
- WHEN (hit_count + miss_count) > 0
730
- THEN CAST(hit_count AS REAL) / (hit_count + miss_count)
731
- ELSE 0.0
732
- END AS hit_rate,
733
- CASE
734
- WHEN (success_count + failure_count) > 0 AND (hit_count + miss_count) > 0
735
- THEN (
736
- CAST(success_count AS REAL) / (success_count + failure_count) *
737
- CAST(hit_count AS REAL) / (hit_count + miss_count) *
738
- avg_satisfaction
739
- )
740
- ELSE 0.0
741
- END AS quality_score,
742
- conflicts_with, last_quality_update_at, created_at, updated_at
743
- FROM intent_rules
744
- ORDER BY quality_score DESC, confidence DESC;
745
- `);
746
- logger.info('[SQLite] v20: 意图分类系统升级完成');
747
- },
748
- 21: (db) => {
749
- logger.info('[SQLite] v21: 添加 pipeline_id 到 events 和 quality_gate_history 表...');
750
- // 为 events 表添加 pipeline_id
751
- db.exec(`
752
- ALTER TABLE events ADD COLUMN pipeline_id TEXT;
753
- CREATE INDEX IF NOT EXISTS idx_events_pipeline ON events(pipeline_id);
754
- `);
755
- // 为 quality_gate_history 表添加 pipeline_id
756
- db.exec(`
757
- ALTER TABLE quality_gate_history ADD COLUMN pipeline_id TEXT;
758
- CREATE INDEX IF NOT EXISTS idx_qgh_pipeline ON quality_gate_history(pipeline_id);
759
- `);
760
- logger.info('[SQLite] v21: pipeline_id 字段添加完成');
761
- },
762
- 22: (db) => {
763
- // 添加复合索引,优化常见查询
764
- db.exec(`
765
- CREATE INDEX IF NOT EXISTS idx_sessions_project_status ON sessions(project_path, status);
766
- CREATE INDEX IF NOT EXISTS idx_events_project_tool ON events(project_path, tool_name);
767
- CREATE INDEX IF NOT EXISTS idx_events_project_date ON events(project_path, DATE(timestamp));
768
- `);
769
- logger.info('[SQLite] v22: 复合索引添加完成');
770
- },
771
- 23: (db) => {
772
- logger.info('[SQLite] v23: 添加 Step 分步驱动支持...');
773
- // 1. 扩展 dynamic_nodes 表,添加 Step 状态字段
774
- db.exec(`
775
- ALTER TABLE dynamic_nodes ADD COLUMN current_step_id TEXT;
776
- ALTER TABLE dynamic_nodes ADD COLUMN completed_steps TEXT DEFAULT '[]';
777
- ALTER TABLE dynamic_nodes ADD COLUMN failed_steps TEXT DEFAULT '[]';
778
- ALTER TABLE dynamic_nodes ADD COLUMN step_executions TEXT DEFAULT '[]';
779
- `);
780
- // 2. 创建 step_executions 表(持久化 Step 执行状态)
781
- db.exec(`
782
- CREATE TABLE IF NOT EXISTS step_executions (
783
- id TEXT PRIMARY KEY,
784
- pipeline_id TEXT NOT NULL,
785
- node_id TEXT NOT NULL,
786
- step_definition_id TEXT NOT NULL,
787
- status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'running', 'validating', 'passed', 'failed', 'need_human')),
788
- attempts INTEGER DEFAULT 0,
789
- outputs TEXT DEFAULT '{}',
790
- validation_results TEXT,
791
- failure_reason TEXT,
792
- started_at TEXT,
793
- completed_at TEXT,
794
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
795
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
796
- FOREIGN KEY (pipeline_id) REFERENCES dynamic_pipelines(id) ON DELETE CASCADE,
797
- FOREIGN KEY (node_id) REFERENCES dynamic_nodes(id) ON DELETE CASCADE
798
- );
799
-
800
- CREATE INDEX IF NOT EXISTS idx_step_executions_pipeline ON step_executions(pipeline_id);
801
- CREATE INDEX IF NOT EXISTS idx_step_executions_node ON step_executions(node_id);
802
- CREATE INDEX IF NOT EXISTS idx_step_executions_status ON step_executions(status);
803
- `);
804
- logger.info('[SQLite] v23: Step 分步驱动支持添加完成');
805
- },
806
- // v24: 性能优化 — 添加缺失的复合索引
807
- 24: (db) => {
808
- // events 表:按 pipeline_id + timestamp 查询优化
809
- db.exec(`
810
- CREATE INDEX IF NOT EXISTS idx_events_pipeline_ts
811
- ON events(pipeline_id, timestamp DESC)
812
- `);
813
- // quality_gate_history 表:按 pipeline_id + status 查询优化
814
- try {
815
- db.exec(`
816
- CREATE INDEX IF NOT EXISTS idx_qgh_pipeline_status
817
- ON quality_gate_history(pipeline_id, status)
818
- `);
819
- }
820
- catch { /* pipeline_id 列可能不存在 */ }
821
- // step_executions 表:按 pipeline_id + status + created_at 查询优化
822
- db.exec(`
823
- CREATE INDEX IF NOT EXISTS idx_step_exec_pipeline_status_created
824
- ON step_executions(pipeline_id, status, created_at DESC)
825
- `);
826
- // dynamic_pipelines 表:按 project_path + status + created_at 复合索引
827
- db.exec(`
828
- CREATE INDEX IF NOT EXISTS idx_dp_project_status_created
829
- ON dynamic_pipelines(project_path, status, created_at DESC)
830
- `);
831
- logger.info('[SQLite] v24: 性能优化索引添加完成');
832
- },
833
- // v25: knowledge_nodes FTS5 同步触发器 — 保持全文索引与主表实时一致
834
- 25: (db) => {
835
- // 先重建 FTS 表(确保 content table 模式正确)
836
- try {
837
- db.exec(`
838
- CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_nodes_fts
839
- USING fts5(id UNINDEXED, project_path, title, content, embedding_hint,
840
- content=knowledge_nodes, tokenize='unicode61')
841
- `);
842
- }
843
- catch { /* 已存在则跳过 */ }
844
- // INSERT 触发器
845
- db.exec(`
846
- CREATE TRIGGER IF NOT EXISTS knowledge_nodes_fts_insert
847
- AFTER INSERT ON knowledge_nodes BEGIN
848
- INSERT INTO knowledge_nodes_fts(rowid, id, project_path, title, content, embedding_hint)
849
- VALUES (new.rowid, new.id, new.project_path, new.title, new.content, new.embedding_hint);
850
- END
851
- `);
852
- // UPDATE 触发器(先删旧记录再插新记录)
853
- db.exec(`
854
- CREATE TRIGGER IF NOT EXISTS knowledge_nodes_fts_update
855
- AFTER UPDATE ON knowledge_nodes BEGIN
856
- INSERT INTO knowledge_nodes_fts(knowledge_nodes_fts, rowid, id, project_path, title, content, embedding_hint)
857
- VALUES ('delete', old.rowid, old.id, old.project_path, old.title, old.content, old.embedding_hint);
858
- INSERT INTO knowledge_nodes_fts(rowid, id, project_path, title, content, embedding_hint)
859
- VALUES (new.rowid, new.id, new.project_path, new.title, new.content, new.embedding_hint);
860
- END
861
- `);
862
- // DELETE 触发器
863
- db.exec(`
864
- CREATE TRIGGER IF NOT EXISTS knowledge_nodes_fts_delete
865
- AFTER DELETE ON knowledge_nodes BEGIN
866
- INSERT INTO knowledge_nodes_fts(knowledge_nodes_fts, rowid, id, project_path, title, content, embedding_hint)
867
- VALUES ('delete', old.rowid, old.id, old.project_path, old.title, old.content, old.embedding_hint);
868
- END
869
- `);
870
- // 全量重建 FTS 索引(同步历史数据)
871
- try {
872
- db.exec(`INSERT INTO knowledge_nodes_fts(knowledge_nodes_fts) VALUES ('rebuild')`);
873
- }
874
- catch { /* 忽略重建失败 */ }
875
- logger.info('[SQLite] v25: knowledge_nodes FTS5 触发器创建完成,索引已重建');
876
- },
877
- // v26: 会话标签/备注/归档 — sessions 表新增 tags, notes, archived 字段
878
- 26: (db) => {
879
- try {
880
- db.exec(`ALTER TABLE sessions ADD COLUMN tags TEXT DEFAULT '[]'`);
881
- }
882
- catch (err) {
883
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
884
- throw err;
885
- }
886
- try {
887
- db.exec(`ALTER TABLE sessions ADD COLUMN notes TEXT DEFAULT NULL`);
888
- }
889
- catch (err) {
890
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
891
- throw err;
892
- }
893
- try {
894
- db.exec(`ALTER TABLE sessions ADD COLUMN archived INTEGER NOT NULL DEFAULT 0`);
895
- }
896
- catch (err) {
897
- if (!(err instanceof Error && err.message.includes('duplicate column name')))
898
- throw err;
899
- }
900
- db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_archived ON sessions(archived)`);
901
- logger.info('[SQLite] v26: sessions 表新增 tags/notes/archived 字段完成');
902
- },
903
- // v27: Plan Service 支持 — pipeline_plans, plan_nodes, template_versions, template_evolution_events
904
- 27: (db) => {
905
- db.exec(`
906
- CREATE TABLE IF NOT EXISTS pipeline_plans (
907
- plan_id TEXT PRIMARY KEY,
908
- template_id TEXT NOT NULL,
909
- template_version TEXT NOT NULL,
910
- goal TEXT NOT NULL,
911
- project_path TEXT NOT NULL,
912
- session_id TEXT NOT NULL,
913
- params TEXT,
914
- status TEXT NOT NULL DEFAULT 'draft',
915
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
916
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
917
- );
918
- CREATE INDEX IF NOT EXISTS idx_pipeline_plans_project ON pipeline_plans(project_path);
919
- CREATE INDEX IF NOT EXISTS idx_pipeline_plans_status ON pipeline_plans(status);
920
-
921
- CREATE TABLE IF NOT EXISTS plan_nodes (
922
- node_id TEXT PRIMARY KEY,
923
- plan_id TEXT NOT NULL REFERENCES pipeline_plans(plan_id),
924
- phase TEXT NOT NULL,
925
- title TEXT NOT NULL,
926
- instruction TEXT NOT NULL,
927
- completion_rules TEXT NOT NULL DEFAULT '[]',
928
- strategy_pool TEXT NOT NULL DEFAULT '[]',
929
- retry_policy TEXT NOT NULL DEFAULT '{"max_retries":3}',
930
- timeout_sec INTEGER NOT NULL DEFAULT 300,
931
- dependencies TEXT,
932
- status TEXT NOT NULL DEFAULT 'pending',
933
- current_strategy_id TEXT,
934
- attempt_count INTEGER NOT NULL DEFAULT 0,
935
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
936
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
937
- );
938
- CREATE INDEX IF NOT EXISTS idx_plan_nodes_plan ON plan_nodes(plan_id);
939
-
940
- CREATE TABLE IF NOT EXISTS template_versions (
941
- version_id TEXT PRIMARY KEY,
942
- template_id TEXT NOT NULL,
943
- version TEXT NOT NULL,
944
- content TEXT NOT NULL,
945
- evolved_from TEXT,
946
- evolution_reason TEXT,
947
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
948
- );
949
- CREATE INDEX IF NOT EXISTS idx_template_versions_template ON template_versions(template_id);
950
-
951
- CREATE TABLE IF NOT EXISTS template_evolution_events (
952
- event_id TEXT PRIMARY KEY,
953
- template_id TEXT NOT NULL,
954
- plan_id TEXT NOT NULL,
955
- signal_type TEXT NOT NULL,
956
- evidence TEXT NOT NULL DEFAULT '{}',
957
- action TEXT NOT NULL,
958
- new_version_id TEXT,
959
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
960
- );
961
- CREATE INDEX IF NOT EXISTS idx_tee_template ON template_evolution_events(template_id);
962
- `);
963
- logger.info('[SQLite] v27: pipeline_plans / plan_nodes / template_versions / template_evolution_events 创建完成');
964
- },
965
- }; // end MIGRATIONS
966
- /**
967
- * 执行数据库迁移
968
- */
8
+ import fs from 'fs';
9
+ export const SCHEMA_VERSION = 1;
969
10
  export function runMigrations(db) {
970
- // 确保 schema_version 表存在
971
- db.exec(`
972
- CREATE TABLE IF NOT EXISTS schema_version (
973
- version INTEGER NOT NULL,
974
- applied_at TEXT DEFAULT (datetime('now'))
975
- )
976
- `);
977
- const row = db.prepare('SELECT MAX(version) as v FROM schema_version').get();
978
- const currentVersion = row?.v ?? 0;
979
- if (currentVersion < SCHEMA_VERSION) {
980
- const migrate = db.transaction(() => {
981
- for (let v = currentVersion + 1; v <= SCHEMA_VERSION; v++) {
982
- const fn = MIGRATIONS[v];
983
- if (fn)
984
- fn(db);
985
- db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(v);
986
- logger.info(`[SQLite] 迁移完成:v${v}`);
987
- }
988
- });
989
- migrate();
990
- // 基线版本无迁移函数时也要记录
991
- if (currentVersion === 0) {
992
- const exists = db.prepare('SELECT 1 FROM schema_version WHERE version = 1').get();
993
- if (!exists) {
994
- db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(SCHEMA_VERSION);
995
- }
996
- }
997
- logger.info(`[SQLite] Schema 版本:v${SCHEMA_VERSION}`);
11
+ const hasVersionTable = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='schema_version'").get();
12
+ let currentVersion = 0;
13
+ if (hasVersionTable) {
14
+ const row = db.prepare('SELECT MAX(version) as v FROM schema_version').get();
15
+ currentVersion = row?.v ?? 0;
16
+ }
17
+ if (currentVersion > SCHEMA_VERSION) {
18
+ const dbPath = db.name;
19
+ const backupPath = `${dbPath}.backup-${Date.now()}`;
20
+ logger.warn(`[SQLite] 检测到旧版本数据库 (v${currentVersion}),备份到 ${backupPath} 后重建`);
21
+ db.close();
22
+ fs.copyFileSync(dbPath, backupPath);
23
+ fs.unlinkSync(dbPath);
24
+ throw new Error(`DB_REBUILD_NEEDED:${dbPath}`);
25
+ }
26
+ if (currentVersion === 0) {
27
+ db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(SCHEMA_VERSION);
28
+ logger.info(`[SQLite] 新数据库初始化完成:v${SCHEMA_VERSION}`);
998
29
  }
30
+ logger.info(`[SQLite] Schema 版本:v${SCHEMA_VERSION}`);
999
31
  }
1000
32
  //# sourceMappingURL=migration-manager.js.map