@proletariat/cli 0.3.94 → 0.3.96

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 (390) hide show
  1. package/dist/commands/agent/cleanup.d.ts +3 -4
  2. package/dist/commands/agent/cleanup.js +5 -4
  3. package/dist/commands/agent/cleanup.js.map +1 -1
  4. package/dist/commands/agent/gc.d.ts +3 -4
  5. package/dist/commands/agent/gc.js +5 -4
  6. package/dist/commands/agent/gc.js.map +1 -1
  7. package/dist/commands/agent/index.d.ts +3 -4
  8. package/dist/commands/agent/index.js +5 -4
  9. package/dist/commands/agent/index.js.map +1 -1
  10. package/dist/commands/agent/list.d.ts +3 -4
  11. package/dist/commands/agent/list.js +5 -4
  12. package/dist/commands/agent/list.js.map +1 -1
  13. package/dist/commands/agent/remove.d.ts +3 -4
  14. package/dist/commands/agent/remove.js +5 -4
  15. package/dist/commands/agent/remove.js.map +1 -1
  16. package/dist/commands/agent/staff/index.d.ts +3 -4
  17. package/dist/commands/agent/staff/index.js +5 -4
  18. package/dist/commands/agent/staff/index.js.map +1 -1
  19. package/dist/commands/agent/staff/remove.d.ts +3 -4
  20. package/dist/commands/agent/staff/remove.js +5 -4
  21. package/dist/commands/agent/staff/remove.js.map +1 -1
  22. package/dist/commands/agent/status.d.ts +3 -4
  23. package/dist/commands/agent/status.js +5 -4
  24. package/dist/commands/agent/status.js.map +1 -1
  25. package/dist/commands/agent/visit.d.ts +3 -4
  26. package/dist/commands/agent/visit.js +5 -4
  27. package/dist/commands/agent/visit.js.map +1 -1
  28. package/dist/commands/branch/create.js +1 -12
  29. package/dist/commands/branch/create.js.map +1 -1
  30. package/dist/commands/branch/list.d.ts +3 -4
  31. package/dist/commands/branch/list.js +5 -4
  32. package/dist/commands/branch/list.js.map +1 -1
  33. package/dist/commands/branch/validate.d.ts +3 -4
  34. package/dist/commands/branch/validate.js +6 -5
  35. package/dist/commands/branch/validate.js.map +1 -1
  36. package/dist/commands/branch/where.d.ts +3 -4
  37. package/dist/commands/branch/where.js +5 -4
  38. package/dist/commands/branch/where.js.map +1 -1
  39. package/dist/commands/commit.js +1 -1
  40. package/dist/commands/commit.js.map +1 -1
  41. package/dist/commands/config/index.js +1 -1
  42. package/dist/commands/config/index.js.map +1 -1
  43. package/dist/commands/db/backup.d.ts +16 -0
  44. package/dist/commands/db/backup.js +125 -0
  45. package/dist/commands/db/backup.js.map +1 -0
  46. package/dist/commands/db/repair.js +167 -45
  47. package/dist/commands/db/repair.js.map +1 -1
  48. package/dist/commands/execution/config.d.ts +3 -4
  49. package/dist/commands/execution/config.js +5 -4
  50. package/dist/commands/execution/config.js.map +1 -1
  51. package/dist/commands/execution/index.d.ts +3 -4
  52. package/dist/commands/execution/index.js +5 -4
  53. package/dist/commands/execution/index.js.map +1 -1
  54. package/dist/commands/execution/list.d.ts +3 -4
  55. package/dist/commands/execution/list.js +5 -4
  56. package/dist/commands/execution/list.js.map +1 -1
  57. package/dist/commands/execution/logs.d.ts +3 -4
  58. package/dist/commands/execution/logs.js +5 -4
  59. package/dist/commands/execution/logs.js.map +1 -1
  60. package/dist/commands/execution/stop.d.ts +3 -4
  61. package/dist/commands/execution/stop.js +5 -4
  62. package/dist/commands/execution/stop.js.map +1 -1
  63. package/dist/commands/execution/view.d.ts +3 -4
  64. package/dist/commands/execution/view.js +5 -4
  65. package/dist/commands/execution/view.js.map +1 -1
  66. package/dist/commands/gc.d.ts +13 -0
  67. package/dist/commands/gc.js +178 -0
  68. package/dist/commands/gc.js.map +1 -0
  69. package/dist/commands/hook/export.d.ts +3 -4
  70. package/dist/commands/hook/export.js +5 -4
  71. package/dist/commands/hook/export.js.map +1 -1
  72. package/dist/commands/hook/fire.d.ts +3 -4
  73. package/dist/commands/hook/fire.js +5 -4
  74. package/dist/commands/hook/fire.js.map +1 -1
  75. package/dist/commands/hook/list.d.ts +3 -4
  76. package/dist/commands/hook/list.js +5 -4
  77. package/dist/commands/hook/list.js.map +1 -1
  78. package/dist/commands/hook/preset.d.ts +3 -4
  79. package/dist/commands/hook/preset.js +5 -4
  80. package/dist/commands/hook/preset.js.map +1 -1
  81. package/dist/commands/orchestrate/index.d.ts +3 -4
  82. package/dist/commands/orchestrate/index.js +5 -4
  83. package/dist/commands/orchestrate/index.js.map +1 -1
  84. package/dist/commands/orchestrator/index.d.ts +3 -4
  85. package/dist/commands/orchestrator/index.js +5 -4
  86. package/dist/commands/orchestrator/index.js.map +1 -1
  87. package/dist/commands/pr/checks.d.ts +3 -4
  88. package/dist/commands/pr/checks.js +5 -4
  89. package/dist/commands/pr/checks.js.map +1 -1
  90. package/dist/commands/pr/merge.js +1 -1
  91. package/dist/commands/pr/merge.js.map +1 -1
  92. package/dist/commands/repo/add.d.ts +3 -4
  93. package/dist/commands/repo/add.js +5 -4
  94. package/dist/commands/repo/add.js.map +1 -1
  95. package/dist/commands/repo/create.d.ts +3 -4
  96. package/dist/commands/repo/create.js +5 -4
  97. package/dist/commands/repo/create.js.map +1 -1
  98. package/dist/commands/repo/fix-remotes.d.ts +3 -4
  99. package/dist/commands/repo/fix-remotes.js +5 -4
  100. package/dist/commands/repo/fix-remotes.js.map +1 -1
  101. package/dist/commands/repo/index.d.ts +3 -4
  102. package/dist/commands/repo/index.js +5 -4
  103. package/dist/commands/repo/index.js.map +1 -1
  104. package/dist/commands/repo/list.d.ts +3 -4
  105. package/dist/commands/repo/list.js +5 -4
  106. package/dist/commands/repo/list.js.map +1 -1
  107. package/dist/commands/session/attach.d.ts +3 -3
  108. package/dist/commands/session/attach.js +37 -79
  109. package/dist/commands/session/attach.js.map +1 -1
  110. package/dist/commands/session/cleanup.d.ts +3 -4
  111. package/dist/commands/session/cleanup.js +5 -4
  112. package/dist/commands/session/cleanup.js.map +1 -1
  113. package/dist/commands/session/create.d.ts +3 -4
  114. package/dist/commands/session/create.js +5 -4
  115. package/dist/commands/session/create.js.map +1 -1
  116. package/dist/commands/session/exec.d.ts +3 -4
  117. package/dist/commands/session/exec.js +6 -5
  118. package/dist/commands/session/exec.js.map +1 -1
  119. package/dist/commands/session/health.js +19 -1
  120. package/dist/commands/session/health.js.map +1 -1
  121. package/dist/commands/session/index.d.ts +3 -4
  122. package/dist/commands/session/index.js +9 -4
  123. package/dist/commands/session/index.js.map +1 -1
  124. package/dist/commands/session/inspect.d.ts +3 -4
  125. package/dist/commands/session/inspect.js +6 -5
  126. package/dist/commands/session/inspect.js.map +1 -1
  127. package/dist/commands/session/list.d.ts +4 -4
  128. package/dist/commands/session/list.js +129 -82
  129. package/dist/commands/session/list.js.map +1 -1
  130. package/dist/commands/session/peek.js +1 -1
  131. package/dist/commands/session/peek.js.map +1 -1
  132. package/dist/commands/session/poke.js +1 -1
  133. package/dist/commands/session/poke.js.map +1 -1
  134. package/dist/commands/session/prune.d.ts +3 -4
  135. package/dist/commands/session/prune.js +55 -15
  136. package/dist/commands/session/prune.js.map +1 -1
  137. package/dist/commands/session/report.d.ts +25 -5
  138. package/dist/commands/session/report.js +201 -5
  139. package/dist/commands/session/report.js.map +1 -1
  140. package/dist/commands/session/restart.d.ts +3 -4
  141. package/dist/commands/session/restart.js +6 -5
  142. package/dist/commands/session/restart.js.map +1 -1
  143. package/dist/commands/session/watch.d.ts +17 -0
  144. package/dist/commands/session/watch.js +139 -0
  145. package/dist/commands/session/watch.js.map +1 -0
  146. package/dist/commands/{action → sync}/index.d.ts +2 -6
  147. package/dist/commands/sync/index.js +68 -0
  148. package/dist/commands/sync/index.js.map +1 -0
  149. package/dist/commands/sync/pause.d.ts +10 -0
  150. package/dist/commands/sync/pause.js +35 -0
  151. package/dist/commands/sync/pause.js.map +1 -0
  152. package/dist/commands/sync/queue.d.ts +10 -0
  153. package/dist/commands/sync/queue.js +106 -0
  154. package/dist/commands/sync/queue.js.map +1 -0
  155. package/dist/commands/sync/resume.d.ts +10 -0
  156. package/dist/commands/sync/resume.js +34 -0
  157. package/dist/commands/sync/resume.js.map +1 -0
  158. package/dist/commands/sync/start.d.ts +11 -0
  159. package/dist/commands/sync/start.js +73 -0
  160. package/dist/commands/sync/start.js.map +1 -0
  161. package/dist/commands/sync/status.d.ts +10 -0
  162. package/dist/commands/sync/status.js +40 -0
  163. package/dist/commands/sync/status.js.map +1 -0
  164. package/dist/commands/sync/stop.d.ts +10 -0
  165. package/dist/commands/sync/stop.js +39 -0
  166. package/dist/commands/sync/stop.js.map +1 -0
  167. package/dist/commands/ticket/create.js +1 -1
  168. package/dist/commands/ticket/create.js.map +1 -1
  169. package/dist/commands/ticket/edit.js +1 -1
  170. package/dist/commands/ticket/edit.js.map +1 -1
  171. package/dist/commands/ticket/index.d.ts +3 -4
  172. package/dist/commands/ticket/index.js +5 -4
  173. package/dist/commands/ticket/index.js.map +1 -1
  174. package/dist/commands/ticket/list.js +1 -1
  175. package/dist/commands/ticket/list.js.map +1 -1
  176. package/dist/commands/ticket/update.js +1 -1
  177. package/dist/commands/ticket/update.js.map +1 -1
  178. package/dist/commands/web.d.ts +20 -0
  179. package/dist/commands/web.js +82 -0
  180. package/dist/commands/web.js.map +1 -0
  181. package/dist/commands/work/complete.js +1 -1
  182. package/dist/commands/work/complete.js.map +1 -1
  183. package/dist/commands/work/hooks/add.d.ts +3 -4
  184. package/dist/commands/work/hooks/add.js +5 -4
  185. package/dist/commands/work/hooks/add.js.map +1 -1
  186. package/dist/commands/work/hooks/list.d.ts +3 -4
  187. package/dist/commands/work/hooks/list.js +5 -4
  188. package/dist/commands/work/hooks/list.js.map +1 -1
  189. package/dist/commands/work/hooks/toggle.d.ts +3 -4
  190. package/dist/commands/work/hooks/toggle.js +5 -4
  191. package/dist/commands/work/hooks/toggle.js.map +1 -1
  192. package/dist/commands/work/peek.d.ts +3 -4
  193. package/dist/commands/work/peek.js +5 -4
  194. package/dist/commands/work/peek.js.map +1 -1
  195. package/dist/commands/work/propose.d.ts +23 -0
  196. package/dist/commands/work/propose.js +57 -0
  197. package/dist/commands/work/propose.js.map +1 -0
  198. package/dist/commands/work/ready.js +3 -3
  199. package/dist/commands/work/ready.js.map +1 -1
  200. package/dist/commands/work/rebase.d.ts +1 -1
  201. package/dist/commands/work/rebase.js +52 -49
  202. package/dist/commands/work/rebase.js.map +1 -1
  203. package/dist/commands/work/resolve.js +1 -1
  204. package/dist/commands/work/resolve.js.map +1 -1
  205. package/dist/commands/work/ship.d.ts +6 -0
  206. package/dist/commands/work/ship.js +218 -49
  207. package/dist/commands/work/ship.js.map +1 -1
  208. package/dist/commands/work/start.js +16 -90
  209. package/dist/commands/work/start.js.map +1 -1
  210. package/dist/commands/work/stop.d.ts +3 -4
  211. package/dist/commands/work/stop.js +5 -4
  212. package/dist/commands/work/stop.js.map +1 -1
  213. package/dist/hooks/init.js +11 -1
  214. package/dist/hooks/init.js.map +1 -1
  215. package/dist/lib/branch/index.d.ts +6 -5
  216. package/dist/lib/branch/index.js +8 -13
  217. package/dist/lib/branch/index.js.map +1 -1
  218. package/dist/lib/dashboard/data.js +1 -1
  219. package/dist/lib/dashboard/data.js.map +1 -1
  220. package/dist/lib/dashboard/html.d.ts +2 -1
  221. package/dist/lib/dashboard/html.js +150 -522
  222. package/dist/lib/dashboard/html.js.map +1 -1
  223. package/dist/lib/database/db-safety.d.ts +70 -9
  224. package/dist/lib/database/db-safety.js +395 -43
  225. package/dist/lib/database/db-safety.js.map +1 -1
  226. package/dist/lib/database/index.d.ts +1 -1
  227. package/dist/lib/database/index.js +1 -1
  228. package/dist/lib/database/index.js.map +1 -1
  229. package/dist/lib/database/migrations/0017_drop_agent_work_fk.d.ts +13 -0
  230. package/dist/lib/database/migrations/0017_drop_agent_work_fk.js +85 -0
  231. package/dist/lib/database/migrations/0017_drop_agent_work_fk.js.map +1 -0
  232. package/dist/lib/database/migrations/0018_create_ticket_refs.d.ts +11 -0
  233. package/dist/lib/database/migrations/0018_create_ticket_refs.js +40 -0
  234. package/dist/lib/database/migrations/0018_create_ticket_refs.js.map +1 -0
  235. package/dist/lib/database/migrations/index.js +4 -0
  236. package/dist/lib/database/migrations/index.js.map +1 -1
  237. package/dist/lib/database/workspace.js +3 -1
  238. package/dist/lib/database/workspace.js.map +1 -1
  239. package/dist/lib/execution/devcontainer.js +1 -1
  240. package/dist/lib/execution/devcontainer.js.map +1 -1
  241. package/dist/lib/execution/runners/docker-management.d.ts +9 -0
  242. package/dist/lib/execution/runners/docker-management.js +14 -3
  243. package/dist/lib/execution/runners/docker-management.js.map +1 -1
  244. package/dist/lib/execution/runners/docker.js +8 -0
  245. package/dist/lib/execution/runners/docker.js.map +1 -1
  246. package/dist/lib/execution/runners/prompt-builder.js +24 -9
  247. package/dist/lib/execution/runners/prompt-builder.js.map +1 -1
  248. package/dist/lib/execution/runners/shared.d.ts +1 -1
  249. package/dist/lib/execution/runners/shared.js +1 -1
  250. package/dist/lib/execution/runners/shared.js.map +1 -1
  251. package/dist/lib/execution/session-utils.d.ts +16 -0
  252. package/dist/lib/execution/session-utils.js +42 -0
  253. package/dist/lib/execution/session-utils.js.map +1 -1
  254. package/dist/lib/execution/spawner.js +4 -5
  255. package/dist/lib/execution/spawner.js.map +1 -1
  256. package/dist/lib/execution/storage.d.ts +29 -1
  257. package/dist/lib/execution/storage.js +79 -1
  258. package/dist/lib/execution/storage.js.map +1 -1
  259. package/dist/lib/execution/types.d.ts +17 -6
  260. package/dist/lib/execution/types.js +10 -7
  261. package/dist/lib/execution/types.js.map +1 -1
  262. package/dist/lib/external-issues/utils.d.ts +25 -0
  263. package/dist/lib/external-issues/utils.js +32 -0
  264. package/dist/lib/external-issues/utils.js.map +1 -0
  265. package/dist/lib/gc/index.d.ts +126 -0
  266. package/dist/lib/gc/index.js +410 -0
  267. package/dist/lib/gc/index.js.map +1 -0
  268. package/dist/lib/machine-config.d.ts +8 -0
  269. package/dist/lib/machine-config.js +37 -0
  270. package/dist/lib/machine-config.js.map +1 -1
  271. package/dist/lib/mcp/tools/action.js +1 -1
  272. package/dist/lib/mcp/tools/action.js.map +1 -1
  273. package/dist/lib/mcp/tools/ticket.js +1 -1
  274. package/dist/lib/mcp/tools/ticket.js.map +1 -1
  275. package/dist/lib/orchestrate/actions.js +30 -0
  276. package/dist/lib/orchestrate/actions.js.map +1 -1
  277. package/dist/lib/orchestrate/poller.d.ts +1 -1
  278. package/dist/lib/orchestrate/poller.js +4 -4
  279. package/dist/lib/orchestrate/poller.js.map +1 -1
  280. package/dist/lib/orchestrate/presets.js +2 -1
  281. package/dist/lib/orchestrate/presets.js.map +1 -1
  282. package/dist/lib/orchestrate/types.d.ts +1 -1
  283. package/dist/lib/orchestrate/types.js +1 -0
  284. package/dist/lib/orchestrate/types.js.map +1 -1
  285. package/dist/lib/pmo/index.js +1 -1
  286. package/dist/lib/pmo/index.js.map +1 -1
  287. package/dist/lib/pmo/markdown.js +1 -1
  288. package/dist/lib/pmo/markdown.js.map +1 -1
  289. package/dist/lib/pmo/schema.d.ts +3 -1
  290. package/dist/lib/pmo/schema.js +26 -2
  291. package/dist/lib/pmo/schema.js.map +1 -1
  292. package/dist/lib/pmo/storage/actions.js +1 -1
  293. package/dist/lib/pmo/storage/actions.js.map +1 -1
  294. package/dist/lib/pmo/storage/base.js +42 -18
  295. package/dist/lib/pmo/storage/base.js.map +1 -1
  296. package/dist/lib/pmo/storage/projects.js +2 -1
  297. package/dist/lib/pmo/storage/projects.js.map +1 -1
  298. package/dist/lib/pmo/storage/statuses.js +1 -1
  299. package/dist/lib/pmo/storage/statuses.js.map +1 -1
  300. package/dist/lib/pmo/storage/subtasks.js +1 -1
  301. package/dist/lib/pmo/storage/subtasks.js.map +1 -1
  302. package/dist/lib/pmo/storage/templates.js +1 -1
  303. package/dist/lib/pmo/storage/templates.js.map +1 -1
  304. package/dist/lib/pmo/storage/tickets.js +2 -1
  305. package/dist/lib/pmo/storage/tickets.js.map +1 -1
  306. package/dist/lib/pmo/storage/workflow-rules.js +1 -1
  307. package/dist/lib/pmo/storage/workflow-rules.js.map +1 -1
  308. package/dist/lib/pmo/utils.d.ts +6 -189
  309. package/dist/lib/pmo/utils.js +6 -306
  310. package/dist/lib/pmo/utils.js.map +1 -1
  311. package/dist/lib/providers/index.d.ts +1 -0
  312. package/dist/lib/providers/index.js +1 -0
  313. package/dist/lib/providers/index.js.map +1 -1
  314. package/dist/lib/providers/resolver.js +16 -0
  315. package/dist/lib/providers/resolver.js.map +1 -1
  316. package/dist/lib/providers/trello-provider.d.ts +28 -0
  317. package/dist/lib/providers/trello-provider.js +381 -0
  318. package/dist/lib/providers/trello-provider.js.map +1 -0
  319. package/dist/lib/session/heartbeat.d.ts +45 -0
  320. package/dist/lib/session/heartbeat.js +150 -0
  321. package/dist/lib/session/heartbeat.js.map +1 -0
  322. package/dist/lib/session/index.d.ts +7 -0
  323. package/dist/lib/session/index.js +8 -0
  324. package/dist/lib/session/index.js.map +1 -0
  325. package/dist/lib/session/watcher.d.ts +79 -0
  326. package/dist/lib/session/watcher.js +142 -0
  327. package/dist/lib/session/watcher.js.map +1 -0
  328. package/dist/lib/shipping/auto-merge.d.ts +57 -0
  329. package/dist/lib/shipping/auto-merge.js +311 -0
  330. package/dist/lib/shipping/auto-merge.js.map +1 -0
  331. package/dist/lib/shipping/github.d.ts +68 -0
  332. package/dist/lib/shipping/github.js +217 -0
  333. package/dist/lib/shipping/github.js.map +1 -0
  334. package/dist/lib/shipping/index.d.ts +13 -0
  335. package/dist/lib/shipping/index.js +11 -0
  336. package/dist/lib/shipping/index.js.map +1 -0
  337. package/dist/lib/shipping/rebase.d.ts +35 -0
  338. package/dist/lib/shipping/rebase.js +107 -0
  339. package/dist/lib/shipping/rebase.js.map +1 -0
  340. package/dist/lib/shipping/types.d.ts +181 -0
  341. package/dist/lib/shipping/types.js +9 -0
  342. package/dist/lib/shipping/types.js.map +1 -0
  343. package/dist/lib/sync/daemon-process.d.ts +9 -0
  344. package/dist/lib/sync/daemon-process.js +184 -0
  345. package/dist/lib/sync/daemon-process.js.map +1 -0
  346. package/dist/lib/sync/daemon.d.ts +39 -0
  347. package/dist/lib/sync/daemon.js +91 -0
  348. package/dist/lib/sync/daemon.js.map +1 -0
  349. package/dist/lib/sync/engine.d.ts +38 -0
  350. package/dist/lib/sync/engine.js +145 -0
  351. package/dist/lib/sync/engine.js.map +1 -0
  352. package/dist/lib/sync/merge-queue.d.ts +116 -0
  353. package/dist/lib/sync/merge-queue.js +321 -0
  354. package/dist/lib/sync/merge-queue.js.map +1 -0
  355. package/dist/lib/sync/reconciler.d.ts +44 -0
  356. package/dist/lib/sync/reconciler.js +88 -0
  357. package/dist/lib/sync/reconciler.js.map +1 -0
  358. package/dist/lib/trello/client.d.ts +1 -0
  359. package/dist/lib/trello/client.js +6 -0
  360. package/dist/lib/trello/client.js.map +1 -1
  361. package/dist/lib/utils/text.d.ts +44 -0
  362. package/dist/lib/utils/text.js +72 -0
  363. package/dist/lib/utils/text.js.map +1 -0
  364. package/dist/lib/work-lifecycle/post-execution.js +1 -1
  365. package/dist/lib/work-lifecycle/post-execution.js.map +1 -1
  366. package/dist/lib/work-lifecycle/settings.d.ts +138 -0
  367. package/dist/lib/work-lifecycle/settings.js +213 -0
  368. package/dist/lib/work-lifecycle/settings.js.map +1 -0
  369. package/oclif.manifest.json +2549 -2901
  370. package/package.json +8 -8
  371. package/dist/commands/action/create.d.ts +0 -26
  372. package/dist/commands/action/create.js +0 -245
  373. package/dist/commands/action/create.js.map +0 -1
  374. package/dist/commands/action/delete.d.ts +0 -18
  375. package/dist/commands/action/delete.js +0 -78
  376. package/dist/commands/action/delete.js.map +0 -1
  377. package/dist/commands/action/index.js +0 -103
  378. package/dist/commands/action/index.js.map +0 -1
  379. package/dist/commands/action/list.d.ts +0 -15
  380. package/dist/commands/action/list.js +0 -90
  381. package/dist/commands/action/list.js.map +0 -1
  382. package/dist/commands/action/run.d.ts +0 -20
  383. package/dist/commands/action/run.js +0 -188
  384. package/dist/commands/action/run.js.map +0 -1
  385. package/dist/commands/action/show.d.ts +0 -17
  386. package/dist/commands/action/show.js +0 -78
  387. package/dist/commands/action/show.js.map +0 -1
  388. package/dist/commands/action/update.d.ts +0 -27
  389. package/dist/commands/action/update.js +0 -288
  390. package/dist/commands/action/update.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/lib/dashboard/html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAwpBD,CAAA;AACR,CAAC"}
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../../src/lib/dashboard/html.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA2SD,CAAA;AACR,CAAC"}
@@ -3,11 +3,12 @@
3
3
  *
4
4
  * Provides:
5
5
  * - WAL journal mode configuration
6
- * - Rotating backup (keeps last 5 copies)
6
+ * - Timestamped backup in .proletariat/backups/ (keeps last 5)
7
7
  * - Integrity check on open with auto-recovery
8
8
  * - Manual repair via dump/reimport
9
+ * - Migration of legacy scattered backup files
9
10
  *
10
- * See: PRLT-1081
11
+ * See: PRLT-1081, PRLT-1154
11
12
  */
12
13
  import Database from 'better-sqlite3';
13
14
  /**
@@ -16,18 +17,56 @@ import Database from 'better-sqlite3';
16
17
  * more resistant to corruption than the default journal_mode=delete.
17
18
  */
18
19
  export declare function enableWALMode(db: Database.Database): void;
20
+ /**
21
+ * Get the backups directory for a given database path.
22
+ * The backups directory is always a sibling `backups/` directory
23
+ * relative to the database file (e.g., `.proletariat/backups/`).
24
+ */
25
+ export declare function getBackupsDir(dbPath: string): string;
19
26
  /**
20
27
  * Get the backup path for a given database path and backup number.
28
+ * Backups are stored in the backups/ directory with timestamps.
29
+ *
30
+ * For numbered access (used by backup restore), returns a path
31
+ * based on sorted backup files in the directory.
32
+ * Returns null if the backup doesn't exist.
21
33
  */
22
- export declare function getBackupPath(dbPath: string, n: number): string;
34
+ export declare function getBackupPath(dbPath: string, n: number): string | null;
23
35
  /**
24
- * Create a rotating backup of the database file.
25
- * Keeps the last MAX_BACKUPS copies, numbered 1 (newest) through MAX_BACKUPS (oldest).
26
- * Rotates existing backups before copying the current database.
36
+ * Create a timestamped backup of the database file in the backups/ directory.
37
+ * Keeps the last MAX_BACKUPS copies, deletes older ones.
38
+ *
39
+ * Returns the backup path if created, null if source didn't exist.
40
+ */
41
+ export declare function createRotatingBackup(dbPath: string): string | null;
42
+ /**
43
+ * Create a manual backup with an optional label.
44
+ * Unlike auto-backups, manual backups include a "manual" prefix.
45
+ * Returns the backup path.
46
+ */
47
+ export declare function createManualBackup(dbPath: string, label?: string): string | null;
48
+ /**
49
+ * Migrate legacy backup files from the .proletariat/ top level into backups/.
50
+ *
51
+ * Handles:
52
+ * - workspace.db.backup (plain backup)
53
+ * - workspace.db.backup-YYYYMMDD-HHMMSS (timestamped backups)
54
+ * - workspace.db.backup.N (numbered backups with -wal/-shm companions)
55
+ * - workspace.db.corrupt (corrupt file preservations)
27
56
  *
28
- * Returns true if backup was created, false if source didn't exist.
57
+ * Called on first run after upgrade. Safe to call multiple times.
29
58
  */
30
- export declare function createRotatingBackup(dbPath: string): boolean;
59
+ export declare function migrateExistingBackups(dbPath: string): void;
60
+ /**
61
+ * List all backup entries with metadata, sorted newest-first.
62
+ * Used by the repair and backup commands to display backup info.
63
+ */
64
+ export declare function listBackups(dbPath: string): Array<{
65
+ path: string;
66
+ filename: string;
67
+ size: number;
68
+ mtime: Date;
69
+ }>;
31
70
  export interface IntegrityCheckResult {
32
71
  ok: boolean;
33
72
  errors: string[];
@@ -42,6 +81,28 @@ export declare function checkIntegrity(db: Database.Database): IntegrityCheckRes
42
81
  * Skips checking that the contents of table rows match the indexes.
43
82
  */
44
83
  export declare function quickCheckIntegrity(db: Database.Database): IntegrityCheckResult;
84
+ export interface SchemaCheckResult {
85
+ ok: boolean;
86
+ missingTables: string[];
87
+ missingColumns: Array<{
88
+ table: string;
89
+ column: string;
90
+ }>;
91
+ }
92
+ /**
93
+ * Check schema completeness — verify all expected columns exist in all tables.
94
+ *
95
+ * Builds the expected schema in a temporary in-memory database from the
96
+ * canonical CREATE TABLE definitions, then compares actual columns via
97
+ * PRAGMA table_info(). Only checks tables that already exist in the target
98
+ * database (PMO tables are skipped if PMO is not initialized).
99
+ *
100
+ * This catches the case where PRAGMA integrity_check returns "ok" but
101
+ * the schema is missing columns added in later migrations.
102
+ *
103
+ * See: PRLT-1131
104
+ */
105
+ export declare function checkSchemaCompleteness(db: Database.Database): SchemaCheckResult;
45
106
  export interface RepairResult {
46
107
  success: boolean;
47
108
  method: 'dump-reimport' | 'backup-restore' | 'none';
@@ -54,6 +115,6 @@ export interface RepairResult {
54
115
  * 1. Try dump/reimport — opens the corrupt DB, dumps all SQL, creates a new DB
55
116
  * 2. If dump fails, fall back to the most recent backup
56
117
  *
57
- * The original corrupt file is preserved as dbPath.corrupt for forensics.
118
+ * The original corrupt file is preserved in backups/ for forensics.
58
119
  */
59
120
  export declare function repairDatabase(dbPath: string): RepairResult;
@@ -3,15 +3,18 @@
3
3
  *
4
4
  * Provides:
5
5
  * - WAL journal mode configuration
6
- * - Rotating backup (keeps last 5 copies)
6
+ * - Timestamped backup in .proletariat/backups/ (keeps last 5)
7
7
  * - Integrity check on open with auto-recovery
8
8
  * - Manual repair via dump/reimport
9
+ * - Migration of legacy scattered backup files
9
10
  *
10
- * See: PRLT-1081
11
+ * See: PRLT-1081, PRLT-1154
11
12
  */
12
13
  import Database from 'better-sqlite3';
13
14
  import * as fs from 'node:fs';
14
15
  import * as path from 'node:path';
16
+ import { CREATE_TABLES_SQL } from './workspace-schema.js';
17
+ import { PMO_TABLE_SCHEMAS } from '../pmo/schema.js';
15
18
  const MAX_BACKUPS = 5;
16
19
  /**
17
20
  * Enable WAL journal mode on a database connection.
@@ -21,53 +24,314 @@ const MAX_BACKUPS = 5;
21
24
  export function enableWALMode(db) {
22
25
  db.pragma('journal_mode = WAL');
23
26
  }
27
+ /**
28
+ * Get the backups directory for a given database path.
29
+ * The backups directory is always a sibling `backups/` directory
30
+ * relative to the database file (e.g., `.proletariat/backups/`).
31
+ */
32
+ export function getBackupsDir(dbPath) {
33
+ return path.join(path.dirname(dbPath), 'backups');
34
+ }
24
35
  /**
25
36
  * Get the backup path for a given database path and backup number.
37
+ * Backups are stored in the backups/ directory with timestamps.
38
+ *
39
+ * For numbered access (used by backup restore), returns a path
40
+ * based on sorted backup files in the directory.
41
+ * Returns null if the backup doesn't exist.
26
42
  */
27
43
  export function getBackupPath(dbPath, n) {
28
- return `${dbPath}.backup.${n}`;
44
+ const backupsDir = getBackupsDir(dbPath);
45
+ if (!fs.existsSync(backupsDir)) {
46
+ return null;
47
+ }
48
+ const backups = listBackupFiles(backupsDir);
49
+ if (n < 1 || n > backups.length) {
50
+ return null;
51
+ }
52
+ // backups are sorted newest-first, so n=1 is the most recent
53
+ return backups[n - 1];
54
+ }
55
+ /**
56
+ * List backup files in the backups directory, sorted newest-first.
57
+ * Only returns .db files (excludes -wal, -shm, and .corrupt files).
58
+ */
59
+ function listBackupFiles(backupsDir) {
60
+ if (!fs.existsSync(backupsDir)) {
61
+ return [];
62
+ }
63
+ const files = fs.readdirSync(backupsDir)
64
+ .filter(f => f.endsWith('.db') && !f.endsWith('.corrupt.db'))
65
+ .map(f => path.join(backupsDir, f))
66
+ .sort((a, b) => {
67
+ // Sort by filename descending (newest first).
68
+ // Filenames encode timestamps (workspace-YYYYMMDD-HHMMSS-mmm.db)
69
+ // so lexicographic order matches chronological order.
70
+ return path.basename(b).localeCompare(path.basename(a));
71
+ });
72
+ return files;
73
+ }
74
+ /**
75
+ * Generate a unique timestamped backup filename.
76
+ * Checks the backups directory to avoid filename collisions.
77
+ */
78
+ function generateUniqueBackupPath(backupsDir, prefix = 'workspace') {
79
+ const ts = formatTimestamp(new Date());
80
+ const baseName = `${prefix}-${ts}`;
81
+ const candidate = path.join(backupsDir, `${baseName}.db`);
82
+ if (!fs.existsSync(candidate)) {
83
+ return candidate;
84
+ }
85
+ // If collision, append a counter
86
+ for (let i = 1; i < 100; i++) {
87
+ const alt = path.join(backupsDir, `${baseName}-${i}.db`);
88
+ if (!fs.existsSync(alt)) {
89
+ return alt;
90
+ }
91
+ }
92
+ // Extremely unlikely fallback
93
+ return path.join(backupsDir, `${baseName}-${Date.now()}.db`);
29
94
  }
30
95
  /**
31
- * Create a rotating backup of the database file.
32
- * Keeps the last MAX_BACKUPS copies, numbered 1 (newest) through MAX_BACKUPS (oldest).
33
- * Rotates existing backups before copying the current database.
96
+ * Create a timestamped backup of the database file in the backups/ directory.
97
+ * Keeps the last MAX_BACKUPS copies, deletes older ones.
34
98
  *
35
- * Returns true if backup was created, false if source didn't exist.
99
+ * Returns the backup path if created, null if source didn't exist.
36
100
  */
37
101
  export function createRotatingBackup(dbPath) {
38
102
  if (!fs.existsSync(dbPath)) {
39
- return false;
103
+ return null;
104
+ }
105
+ const backupsDir = getBackupsDir(dbPath);
106
+ fs.mkdirSync(backupsDir, { recursive: true });
107
+ const backupPath = generateUniqueBackupPath(backupsDir);
108
+ // Copy current database as timestamped backup
109
+ try {
110
+ fs.copyFileSync(dbPath, backupPath);
111
+ // Also copy WAL and SHM files if they exist (for WAL-mode databases)
112
+ const walPath = `${dbPath}-wal`;
113
+ const shmPath = `${dbPath}-shm`;
114
+ if (fs.existsSync(walPath)) {
115
+ fs.copyFileSync(walPath, `${backupPath}-wal`);
116
+ }
117
+ if (fs.existsSync(shmPath)) {
118
+ fs.copyFileSync(shmPath, `${backupPath}-shm`);
119
+ }
40
120
  }
41
- // Rotate existing backups: delete oldest, shift others up
42
- const oldest = getBackupPath(dbPath, MAX_BACKUPS);
43
- if (fs.existsSync(oldest)) {
44
- fs.unlinkSync(oldest);
121
+ catch {
122
+ // Backup failure is not fatal — log and continue
123
+ return null;
45
124
  }
46
- for (let i = MAX_BACKUPS - 1; i >= 1; i--) {
47
- const src = getBackupPath(dbPath, i);
48
- const dst = getBackupPath(dbPath, i + 1);
49
- if (fs.existsSync(src)) {
50
- fs.renameSync(src, dst);
125
+ // Rotate: delete older backups beyond MAX_BACKUPS
126
+ rotateBackups(backupsDir);
127
+ return backupPath;
128
+ }
129
+ /**
130
+ * Delete older backups beyond MAX_BACKUPS.
131
+ * Keeps the newest MAX_BACKUPS .db files, deletes the rest
132
+ * (along with their -wal and -shm companions).
133
+ */
134
+ function rotateBackups(backupsDir) {
135
+ const backups = listBackupFiles(backupsDir);
136
+ // Keep the first MAX_BACKUPS, delete the rest
137
+ for (let i = MAX_BACKUPS; i < backups.length; i++) {
138
+ const bp = backups[i];
139
+ try {
140
+ fs.unlinkSync(bp);
141
+ // Also clean up WAL and SHM companions
142
+ for (const suffix of ['-wal', '-shm']) {
143
+ const companion = `${bp}${suffix}`;
144
+ if (fs.existsSync(companion)) {
145
+ fs.unlinkSync(companion);
146
+ }
147
+ }
148
+ }
149
+ catch {
150
+ // Best-effort cleanup
51
151
  }
52
152
  }
53
- // Copy current database as backup.1 (newest)
153
+ }
154
+ /**
155
+ * Create a manual backup with an optional label.
156
+ * Unlike auto-backups, manual backups include a "manual" prefix.
157
+ * Returns the backup path.
158
+ */
159
+ export function createManualBackup(dbPath, label) {
160
+ if (!fs.existsSync(dbPath)) {
161
+ return null;
162
+ }
163
+ const backupsDir = getBackupsDir(dbPath);
164
+ fs.mkdirSync(backupsDir, { recursive: true });
165
+ const suffix = label ? `-${label}` : '';
166
+ const backupPath = generateUniqueBackupPath(backupsDir, `workspace-manual${suffix}`);
54
167
  try {
55
- fs.copyFileSync(dbPath, getBackupPath(dbPath, 1));
56
- // Also copy WAL and SHM files if they exist (for WAL-mode databases)
168
+ fs.copyFileSync(dbPath, backupPath);
57
169
  const walPath = `${dbPath}-wal`;
58
170
  const shmPath = `${dbPath}-shm`;
59
171
  if (fs.existsSync(walPath)) {
60
- fs.copyFileSync(walPath, `${getBackupPath(dbPath, 1)}-wal`);
172
+ fs.copyFileSync(walPath, `${backupPath}-wal`);
61
173
  }
62
174
  if (fs.existsSync(shmPath)) {
63
- fs.copyFileSync(shmPath, `${getBackupPath(dbPath, 1)}-shm`);
175
+ fs.copyFileSync(shmPath, `${backupPath}-shm`);
64
176
  }
65
- return true;
177
+ return backupPath;
66
178
  }
67
179
  catch {
68
- // Backup failure is not fatal — log and continue
180
+ return null;
181
+ }
182
+ }
183
+ /**
184
+ * Migrate legacy backup files from the .proletariat/ top level into backups/.
185
+ *
186
+ * Handles:
187
+ * - workspace.db.backup (plain backup)
188
+ * - workspace.db.backup-YYYYMMDD-HHMMSS (timestamped backups)
189
+ * - workspace.db.backup.N (numbered backups with -wal/-shm companions)
190
+ * - workspace.db.corrupt (corrupt file preservations)
191
+ *
192
+ * Called on first run after upgrade. Safe to call multiple times.
193
+ */
194
+ export function migrateExistingBackups(dbPath) {
195
+ const proletariatDir = path.dirname(dbPath);
196
+ const backupsDir = getBackupsDir(dbPath);
197
+ const dbBasename = path.basename(dbPath); // workspace.db
198
+ let files;
199
+ try {
200
+ files = fs.readdirSync(proletariatDir);
201
+ }
202
+ catch {
203
+ return;
204
+ }
205
+ // Find legacy backup/corrupt files at the top level
206
+ const legacyFiles = files.filter(f => {
207
+ // Match workspace.db.backup*, workspace.db.corrupt*
208
+ if (f.startsWith(`${dbBasename}.backup`) || f.startsWith(`${dbBasename}.corrupt`)) {
209
+ return true;
210
+ }
211
+ // Match workspace.db.repair-temp (leftover from failed repairs)
212
+ if (f === `${dbBasename}.repair-temp`) {
213
+ return true;
214
+ }
69
215
  return false;
216
+ });
217
+ if (legacyFiles.length === 0) {
218
+ return;
70
219
  }
220
+ // Ensure backups directory exists
221
+ fs.mkdirSync(backupsDir, { recursive: true });
222
+ for (const file of legacyFiles) {
223
+ const srcPath = path.join(proletariatDir, file);
224
+ let destName;
225
+ if (file.endsWith('.corrupt') || file.includes('.corrupt')) {
226
+ // Corrupt files → workspace-corrupt-TIMESTAMP.db or just move as-is
227
+ const stat = fs.statSync(srcPath);
228
+ const ts = formatTimestamp(stat.mtime);
229
+ destName = `workspace-corrupt-${ts}.db`;
230
+ }
231
+ else if (file === `${dbBasename}.backup`) {
232
+ // Plain backup → workspace-migrated-TIMESTAMP.db
233
+ const stat = fs.statSync(srcPath);
234
+ const ts = formatTimestamp(stat.mtime);
235
+ destName = `workspace-migrated-${ts}.db`;
236
+ }
237
+ else if (file.match(/\.backup-\d{8}-\d{6}$/)) {
238
+ // Timestamped backup (workspace.db.backup-20260106-112422)
239
+ const match = file.match(/\.backup-(\d{8}-\d{6})$/);
240
+ destName = `workspace-migrated-${match[1]}.db`;
241
+ }
242
+ else if (file.match(/\.backup\.\d+$/)) {
243
+ // Numbered backup (workspace.db.backup.1)
244
+ const stat = fs.statSync(srcPath);
245
+ const ts = formatTimestamp(stat.mtime);
246
+ destName = `workspace-migrated-${ts}.db`;
247
+ }
248
+ else if (file.match(/\.backup\.\d+-(wal|shm)$/)) {
249
+ // WAL/SHM companion for numbered backup — these follow their .db file
250
+ // Skip them here; they'll be handled when we move the main backup
251
+ continue;
252
+ }
253
+ else if (file === `${dbBasename}.repair-temp`) {
254
+ // Leftover repair temp file — just move it
255
+ destName = 'workspace-repair-temp.db';
256
+ }
257
+ else {
258
+ // Fallback: use file modification time
259
+ const stat = fs.statSync(srcPath);
260
+ const ts = formatTimestamp(stat.mtime);
261
+ destName = `workspace-legacy-${ts}.db`;
262
+ }
263
+ const destPath = path.join(backupsDir, destName);
264
+ // Don't overwrite existing files in backups/
265
+ if (fs.existsSync(destPath)) {
266
+ // Add a suffix to avoid collision
267
+ const ext = path.extname(destName);
268
+ const base = destName.slice(0, -ext.length);
269
+ const uniqueDest = path.join(backupsDir, `${base}-${Date.now()}${ext}`);
270
+ try {
271
+ fs.renameSync(srcPath, uniqueDest);
272
+ }
273
+ catch {
274
+ // Best-effort migration
275
+ }
276
+ }
277
+ else {
278
+ try {
279
+ fs.renameSync(srcPath, destPath);
280
+ }
281
+ catch {
282
+ // Best-effort migration
283
+ }
284
+ }
285
+ // Also move WAL/SHM companions if they exist
286
+ for (const suffix of ['-wal', '-shm']) {
287
+ const companionSrc = `${srcPath}${suffix}`;
288
+ if (fs.existsSync(companionSrc)) {
289
+ const companionDest = `${destPath}${suffix}`;
290
+ try {
291
+ fs.renameSync(companionSrc, companionDest);
292
+ }
293
+ catch {
294
+ // Best-effort
295
+ }
296
+ }
297
+ }
298
+ }
299
+ // After migration, rotate to keep only MAX_BACKUPS
300
+ rotateBackups(backupsDir);
301
+ }
302
+ /**
303
+ * Format a Date as YYYYMMDD-HHMMSS-mmm for backup filenames.
304
+ * Includes milliseconds to avoid collisions when called multiple times per second.
305
+ */
306
+ function formatTimestamp(date) {
307
+ return [
308
+ date.getFullYear(),
309
+ String(date.getMonth() + 1).padStart(2, '0'),
310
+ String(date.getDate()).padStart(2, '0'),
311
+ '-',
312
+ String(date.getHours()).padStart(2, '0'),
313
+ String(date.getMinutes()).padStart(2, '0'),
314
+ String(date.getSeconds()).padStart(2, '0'),
315
+ '-',
316
+ String(date.getMilliseconds()).padStart(3, '0'),
317
+ ].join('');
318
+ }
319
+ /**
320
+ * List all backup entries with metadata, sorted newest-first.
321
+ * Used by the repair and backup commands to display backup info.
322
+ */
323
+ export function listBackups(dbPath) {
324
+ const backupsDir = getBackupsDir(dbPath);
325
+ const backups = listBackupFiles(backupsDir);
326
+ return backups.map(bp => {
327
+ const stat = fs.statSync(bp);
328
+ return {
329
+ path: bp,
330
+ filename: path.basename(bp),
331
+ size: stat.size,
332
+ mtime: stat.mtime,
333
+ };
334
+ });
71
335
  }
72
336
  /**
73
337
  * Run PRAGMA integrity_check on a database.
@@ -107,6 +371,91 @@ export function quickCheckIntegrity(db) {
107
371
  };
108
372
  }
109
373
  }
374
+ /**
375
+ * Check schema completeness — verify all expected columns exist in all tables.
376
+ *
377
+ * Builds the expected schema in a temporary in-memory database from the
378
+ * canonical CREATE TABLE definitions, then compares actual columns via
379
+ * PRAGMA table_info(). Only checks tables that already exist in the target
380
+ * database (PMO tables are skipped if PMO is not initialized).
381
+ *
382
+ * This catches the case where PRAGMA integrity_check returns "ok" but
383
+ * the schema is missing columns added in later migrations.
384
+ *
385
+ * See: PRLT-1131
386
+ */
387
+ export function checkSchemaCompleteness(db) {
388
+ let expected = null;
389
+ try {
390
+ // Build expected schema in a temporary in-memory database
391
+ expected = new Database(':memory:');
392
+ expected.exec(CREATE_TABLES_SQL);
393
+ // Only check PMO tables if PMO is initialized in the actual database
394
+ const hasPMO = !!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='pmo_projects'").get();
395
+ if (hasPMO) {
396
+ for (const sql of Object.values(PMO_TABLE_SCHEMAS)) {
397
+ try {
398
+ expected.exec(sql);
399
+ }
400
+ catch {
401
+ // Some PMO tables may have FK references that fail in the
402
+ // isolated in-memory DB — skip those; the column check on
403
+ // tables that DO exist is still valuable.
404
+ }
405
+ }
406
+ }
407
+ // Get all expected tables from the reference database
408
+ const expectedTables = expected.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
409
+ const missingTables = [];
410
+ const missingColumns = [];
411
+ for (const { name: table } of expectedTables) {
412
+ // Check if table exists in actual database
413
+ const actualTable = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table);
414
+ if (!actualTable) {
415
+ // Only report core workspace tables as missing.
416
+ // PMO tables may legitimately not exist if PMO was partially initialized.
417
+ const coreWorkspaceTables = new Set([
418
+ 'workspace', 'repositories', 'agents', 'agent_themes',
419
+ 'agent_theme_names', 'agent_worktrees', 'workspace_settings', 'media_items',
420
+ ]);
421
+ if (coreWorkspaceTables.has(table)) {
422
+ missingTables.push(table);
423
+ }
424
+ continue;
425
+ }
426
+ // Compare columns
427
+ const expectedCols = expected.prepare(`PRAGMA table_info('${table}')`).all();
428
+ const actualCols = db.prepare(`PRAGMA table_info('${table}')`).all();
429
+ const actualColSet = new Set(actualCols.map(c => c.name));
430
+ for (const { name: col } of expectedCols) {
431
+ if (!actualColSet.has(col)) {
432
+ missingColumns.push({ table, column: col });
433
+ }
434
+ }
435
+ }
436
+ expected.close();
437
+ expected = null;
438
+ return {
439
+ ok: missingTables.length === 0 && missingColumns.length === 0,
440
+ missingTables,
441
+ missingColumns,
442
+ };
443
+ }
444
+ catch (error) {
445
+ if (expected) {
446
+ try {
447
+ expected.close();
448
+ }
449
+ catch { /* cleanup — safe to ignore */ }
450
+ }
451
+ // If schema check itself fails, report as a single error
452
+ return {
453
+ ok: false,
454
+ missingTables: [],
455
+ missingColumns: [{ table: '(unknown)', column: error instanceof Error ? error.message : String(error) }],
456
+ };
457
+ }
458
+ }
110
459
  /**
111
460
  * Attempt to repair a corrupted database.
112
461
  *
@@ -114,7 +463,7 @@ export function quickCheckIntegrity(db) {
114
463
  * 1. Try dump/reimport — opens the corrupt DB, dumps all SQL, creates a new DB
115
464
  * 2. If dump fails, fall back to the most recent backup
116
465
  *
117
- * The original corrupt file is preserved as dbPath.corrupt for forensics.
466
+ * The original corrupt file is preserved in backups/ for forensics.
118
467
  */
119
468
  export function repairDatabase(dbPath) {
120
469
  // Try dump/reimport first
@@ -133,6 +482,17 @@ export function repairDatabase(dbPath) {
133
482
  message: `Could not repair database. Dump failed: ${dumpResult.message}. No usable backups found.`,
134
483
  };
135
484
  }
485
+ /**
486
+ * Move a corrupt database file to the backups/ directory with a corrupt prefix.
487
+ * Returns the destination path.
488
+ */
489
+ function moveCorruptToBackups(dbPath) {
490
+ const backupsDir = getBackupsDir(dbPath);
491
+ fs.mkdirSync(backupsDir, { recursive: true });
492
+ const corruptPath = generateUniqueBackupPath(backupsDir, 'workspace-corrupt');
493
+ fs.renameSync(dbPath, corruptPath);
494
+ return corruptPath;
495
+ }
136
496
  /**
137
497
  * Attempt recovery via .dump and reimport.
138
498
  * Opens the corrupt database, extracts as much SQL as possible,
@@ -189,12 +549,9 @@ function attemptDumpReimport(dbPath) {
189
549
  corruptDb = null;
190
550
  newDb.close();
191
551
  newDb = null;
192
- // Swap files: corrupt .corrupt, repaired → original
193
- const corruptBackupPath = `${dbPath}.corrupt`;
194
- if (fs.existsSync(corruptBackupPath)) {
195
- fs.unlinkSync(corruptBackupPath);
196
- }
197
- fs.renameSync(dbPath, corruptBackupPath);
552
+ // Move corrupt file to backups/ directory
553
+ const corruptBackupPath = moveCorruptToBackups(dbPath);
554
+ // Move repaired database into place
198
555
  fs.renameSync(tempPath, dbPath);
199
556
  // Clean up old WAL/SHM files from the corrupt database
200
557
  for (const suffix of ['-wal', '-shm']) {
@@ -238,14 +595,13 @@ function attemptDumpReimport(dbPath) {
238
595
  }
239
596
  /**
240
597
  * Attempt recovery by restoring from the most recent valid backup.
241
- * Tries backups 1 through MAX_BACKUPS, validates each with integrity_check.
598
+ * Tries backups from newest to oldest, validates each with integrity_check.
242
599
  */
243
600
  function attemptBackupRestore(dbPath) {
244
- for (let i = 1; i <= MAX_BACKUPS; i++) {
245
- const backupPath = getBackupPath(dbPath, i);
246
- if (!fs.existsSync(backupPath)) {
247
- continue;
248
- }
601
+ const backupsDir = getBackupsDir(dbPath);
602
+ const backups = listBackupFiles(backupsDir);
603
+ for (let i = 0; i < backups.length; i++) {
604
+ const backupPath = backups[i];
249
605
  // Validate the backup
250
606
  let backupDb = null;
251
607
  try {
@@ -257,11 +613,7 @@ function attemptBackupRestore(dbPath) {
257
613
  continue;
258
614
  }
259
615
  // Backup is valid — swap it in
260
- const corruptBackupPath = `${dbPath}.corrupt`;
261
- if (fs.existsSync(corruptBackupPath)) {
262
- fs.unlinkSync(corruptBackupPath);
263
- }
264
- fs.renameSync(dbPath, corruptBackupPath);
616
+ const corruptBackupPath = moveCorruptToBackups(dbPath);
265
617
  fs.copyFileSync(backupPath, dbPath);
266
618
  // Clean up old WAL/SHM files
267
619
  for (const suffix of ['-wal', '-shm']) {
@@ -273,7 +625,7 @@ function attemptBackupRestore(dbPath) {
273
625
  return {
274
626
  success: true,
275
627
  method: 'backup-restore',
276
- message: `Restored from backup.${i}. Corrupt file saved as ${path.basename(corruptBackupPath)}.`,
628
+ message: `Restored from ${path.basename(backupPath)}. Corrupt file saved as ${path.basename(corruptBackupPath)}.`,
277
629
  };
278
630
  }
279
631
  catch {