gsd-pi 2.45.0 → 2.46.0-dev.cc9d310

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 (347) hide show
  1. package/dist/help-text.js +1 -1
  2. package/dist/loader.js +34 -0
  3. package/dist/resources/extensions/gsd/auto/phases.js +27 -42
  4. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  5. package/dist/resources/extensions/gsd/auto/session.js +0 -11
  6. package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
  7. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
  8. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  9. package/dist/resources/extensions/gsd/auto-worktree.js +5 -4
  10. package/dist/resources/extensions/gsd/auto.js +12 -57
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +15 -12
  12. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
  13. package/dist/resources/extensions/gsd/commands/context.js +0 -4
  14. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
  15. package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
  16. package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
  17. package/dist/resources/extensions/gsd/db-writer.js +9 -9
  18. package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
  19. package/dist/resources/extensions/gsd/doctor.js +5 -3
  20. package/dist/resources/extensions/gsd/gsd-db.js +16 -3
  21. package/dist/resources/extensions/gsd/guided-flow.js +1 -2
  22. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  23. package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
  24. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  25. package/dist/resources/extensions/gsd/preferences.js +8 -4
  26. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
  27. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  28. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  29. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
  30. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  31. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  32. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  33. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  34. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  35. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  36. package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
  37. package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
  38. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
  40. package/dist/resources/extensions/gsd/prompts/rethink.md +7 -2
  41. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  42. package/dist/resources/extensions/gsd/session-lock.js +1 -3
  43. package/dist/resources/extensions/gsd/state.js +7 -0
  44. package/dist/resources/extensions/gsd/sync-lock.js +89 -0
  45. package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
  46. package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
  47. package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
  48. package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
  49. package/dist/resources/extensions/gsd/tools/plan-slice.js +30 -1
  50. package/dist/resources/extensions/gsd/tools/plan-task.js +27 -1
  51. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
  52. package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
  53. package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
  54. package/dist/resources/extensions/gsd/tools/replan-slice.js +32 -2
  55. package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
  56. package/dist/resources/extensions/gsd/workflow-events.js +102 -0
  57. package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
  58. package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
  59. package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
  60. package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
  61. package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
  62. package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
  63. package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
  64. package/dist/resources/extensions/gsd/write-intercept.js +84 -0
  65. package/dist/resources/extensions/voice/index.js +11 -16
  66. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  67. package/dist/web/standalone/.next/BUILD_ID +1 -1
  68. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  69. package/dist/web/standalone/.next/build-manifest.json +3 -3
  70. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  71. package/dist/web/standalone/.next/required-server-files.json +3 -3
  72. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  73. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  83. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  99. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  137. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  143. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  157. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  159. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  161. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  163. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/index.html +1 -1
  173. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  174. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  175. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  176. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  178. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  179. package/dist/web/standalone/.next/server/app/page.js +2 -2
  180. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  182. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  183. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  184. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/middleware.js +2 -2
  186. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  188. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  189. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  190. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  191. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  192. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  193. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  194. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  195. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  196. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  197. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  198. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  199. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  200. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  201. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  202. package/dist/web/standalone/server.js +1 -1
  203. package/package.json +2 -1
  204. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  206. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  208. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  211. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  213. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  214. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  215. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  216. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  217. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  218. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  219. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
  221. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
  223. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  225. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  226. package/packages/pi-coding-agent/package.json +1 -1
  227. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  228. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  229. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  230. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  231. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  232. package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
  233. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  234. package/pkg/package.json +1 -1
  235. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
  236. package/src/resources/extensions/gsd/auto/phases.ts +24 -44
  237. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  238. package/src/resources/extensions/gsd/auto/session.ts +0 -18
  239. package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
  240. package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
  241. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
  242. package/src/resources/extensions/gsd/auto-start.ts +1 -3
  243. package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
  244. package/src/resources/extensions/gsd/auto.ts +7 -83
  245. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -12
  246. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  247. package/src/resources/extensions/gsd/commands/context.ts +0 -5
  248. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
  249. package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
  250. package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
  251. package/src/resources/extensions/gsd/db-writer.ts +9 -17
  252. package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
  253. package/src/resources/extensions/gsd/doctor-types.ts +7 -1
  254. package/src/resources/extensions/gsd/doctor.ts +6 -3
  255. package/src/resources/extensions/gsd/gsd-db.ts +16 -3
  256. package/src/resources/extensions/gsd/guided-flow.ts +1 -2
  257. package/src/resources/extensions/gsd/journal.ts +6 -1
  258. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  259. package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
  260. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  261. package/src/resources/extensions/gsd/preferences.ts +7 -3
  262. package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
  263. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  264. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  265. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
  266. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  267. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  268. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  269. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  270. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  271. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  272. package/src/resources/extensions/gsd/prompts/queue.md +2 -2
  273. package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
  274. package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  275. package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
  276. package/src/resources/extensions/gsd/prompts/rethink.md +7 -2
  277. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  278. package/src/resources/extensions/gsd/session-lock.ts +0 -4
  279. package/src/resources/extensions/gsd/state.ts +8 -0
  280. package/src/resources/extensions/gsd/sync-lock.ts +94 -0
  281. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
  282. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
  283. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
  284. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
  285. package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
  286. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
  287. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
  288. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  289. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
  291. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  292. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  293. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  294. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -1
  295. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
  296. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  297. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
  298. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
  299. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
  300. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
  301. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
  302. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
  303. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
  304. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  305. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
  306. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +15 -14
  307. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
  308. package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
  309. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
  310. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
  311. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
  312. package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
  313. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
  314. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  315. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
  316. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
  317. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  318. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
  319. package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
  320. package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
  321. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
  322. package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
  323. package/src/resources/extensions/gsd/tools/plan-slice.ts +38 -0
  324. package/src/resources/extensions/gsd/tools/plan-task.ts +35 -1
  325. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
  326. package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
  327. package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
  328. package/src/resources/extensions/gsd/tools/replan-slice.ts +38 -1
  329. package/src/resources/extensions/gsd/types.ts +8 -0
  330. package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
  331. package/src/resources/extensions/gsd/workflow-events.ts +154 -0
  332. package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
  333. package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
  334. package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
  335. package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
  336. package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
  337. package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
  338. package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
  339. package/src/resources/extensions/gsd/write-intercept.ts +90 -0
  340. package/src/resources/extensions/voice/index.ts +11 -21
  341. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  342. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  343. package/dist/web/standalone/.next/static/chunks/app/page-12dd5ece0df4badc.js +0 -1
  344. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  345. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  346. /package/dist/web/standalone/.next/static/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_buildManifest.js +0 -0
  347. /package/dist/web/standalone/.next/static/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_ssgManifest.js +0 -0
@@ -0,0 +1,280 @@
1
+ // GSD Extension — Legacy Markdown to Engine Migration
2
+ // Converts legacy markdown-only projects to engine state by parsing
3
+ // existing ROADMAP.md, *-PLAN.md, and *-SUMMARY.md files.
4
+ // Populates data into the already-existing v10 schema tables.
5
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { _getAdapter, transaction } from "./gsd-db.js";
8
+ import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
9
+ // ─── needsAutoMigration ───────────────────────────────────────────────────
10
+ /**
11
+ * Returns true when engine tables are empty AND a .gsd/milestones/ directory
12
+ * with markdown files exists — signals that this is a legacy project that needs
13
+ * one-time migration from markdown to engine state.
14
+ */
15
+ export function needsAutoMigration(basePath) {
16
+ const db = _getAdapter();
17
+ if (!db)
18
+ return false;
19
+ // If milestones table already has rows, migration already done
20
+ try {
21
+ const row = db.prepare("SELECT COUNT(*) as cnt FROM milestones").get();
22
+ if (row && row["cnt"] > 0)
23
+ return false;
24
+ }
25
+ catch {
26
+ // Table might not exist yet — that's fine, we can still migrate
27
+ return false;
28
+ }
29
+ // Check if .gsd/milestones/ directory exists
30
+ const milestonesDir = join(basePath, ".gsd", "milestones");
31
+ if (!existsSync(milestonesDir))
32
+ return false;
33
+ return true;
34
+ }
35
+ // ─── migrateFromMarkdown ──────────────────────────────────────────────────
36
+ /**
37
+ * Migrate legacy markdown-only .gsd/ projects to engine DB state.
38
+ * Reads .gsd/milestones/<ID>/ directories and parses ROADMAP.md, *-PLAN.md
39
+ * files. All inserts are wrapped in a transaction.
40
+ *
41
+ * This function only INSERTs data into the already-existing v10 schema tables
42
+ * (milestones, slices, tasks). It does NOT create tables or run migrations.
43
+ *
44
+ * Handles all directory shapes:
45
+ * - No DB: caller is responsible for openDatabase + initSchema before calling
46
+ * - Stale DB (empty tables): inserts succeed normally
47
+ * - No markdown at all: returns early with stderr message
48
+ * - Orphaned summary files: logs warning, skips without crash
49
+ */
50
+ export function migrateFromMarkdown(basePath) {
51
+ const db = _getAdapter();
52
+ if (!db) {
53
+ process.stderr.write("workflow-migration: no database connection, cannot migrate\n");
54
+ return;
55
+ }
56
+ const milestonesDir = join(basePath, ".gsd", "milestones");
57
+ if (!existsSync(milestonesDir)) {
58
+ process.stderr.write("workflow-migration: no .gsd/milestones/ directory found, nothing to migrate\n");
59
+ return;
60
+ }
61
+ // Discover milestone directories (any directory at the top level of milestones/)
62
+ let milestoneDirs;
63
+ try {
64
+ milestoneDirs = readdirSync(milestonesDir, { withFileTypes: true })
65
+ .filter(e => e.isDirectory())
66
+ .map(e => e.name);
67
+ }
68
+ catch {
69
+ process.stderr.write("workflow-migration: failed to read milestones directory\n");
70
+ return;
71
+ }
72
+ if (milestoneDirs.length === 0) {
73
+ process.stderr.write("workflow-migration: no milestone directories found in .gsd/milestones/\n");
74
+ return;
75
+ }
76
+ // Collect all data before the transaction
77
+ const migratedMilestoneIds = [];
78
+ const milestoneInserts = [];
79
+ const sliceInserts = [];
80
+ const taskInserts = [];
81
+ for (const mId of milestoneDirs) {
82
+ const mDir = join(milestonesDir, mId);
83
+ // Determine milestone status: done if a milestone-level SUMMARY.md exists
84
+ const milestoneSummaryPath = join(mDir, "SUMMARY.md");
85
+ const milestoneDone = existsSync(milestoneSummaryPath);
86
+ const milestoneStatus = milestoneDone ? "done" : "active";
87
+ // Parse ROADMAP.md for slices list
88
+ const roadmapPath = join(mDir, "ROADMAP.md");
89
+ let roadmapSlices = [];
90
+ if (existsSync(roadmapPath)) {
91
+ try {
92
+ const roadmapContent = readFileSync(roadmapPath, "utf-8");
93
+ const roadmap = parseRoadmap(roadmapContent);
94
+ // Extract milestone title from roadmap
95
+ const mTitle = roadmap.title || mId;
96
+ milestoneInserts.push({ id: mId, title: mTitle, status: milestoneStatus });
97
+ roadmapSlices = roadmap.slices.map(s => ({
98
+ id: s.id,
99
+ title: s.title,
100
+ done: s.done,
101
+ risk: s.risk || "low",
102
+ }));
103
+ }
104
+ catch (err) {
105
+ process.stderr.write(`workflow-migration: failed to parse ROADMAP.md for ${mId}: ${err.message}\n`);
106
+ // Still add milestone with ID as title
107
+ milestoneInserts.push({ id: mId, title: mId, status: milestoneStatus });
108
+ }
109
+ }
110
+ else {
111
+ // No ROADMAP.md — add milestone entry anyway using directory name
112
+ milestoneInserts.push({ id: mId, title: mId, status: milestoneStatus });
113
+ }
114
+ migratedMilestoneIds.push(mId);
115
+ // Collect slices from ROADMAP + their tasks from PLAN files
116
+ const knownSliceIds = new Set(roadmapSlices.map(s => s.id));
117
+ for (let sIdx = 0; sIdx < roadmapSlices.length; sIdx++) {
118
+ const slice = roadmapSlices[sIdx];
119
+ // Per Pitfall #5: if milestone is done, force all child slices to done
120
+ const sliceStatus = milestoneDone ? "done" : (slice.done ? "done" : "pending");
121
+ sliceInserts.push({
122
+ id: slice.id,
123
+ milestoneId: mId,
124
+ title: slice.title,
125
+ status: sliceStatus,
126
+ risk: slice.risk,
127
+ sequence: sIdx,
128
+ forceDone: milestoneDone,
129
+ });
130
+ // Read *-PLAN.md for this slice
131
+ const planPath = join(mDir, `${slice.id}-PLAN.md`);
132
+ if (existsSync(planPath)) {
133
+ try {
134
+ const planContent = readFileSync(planPath, "utf-8");
135
+ const plan = parsePlan(planContent);
136
+ for (let tIdx = 0; tIdx < plan.tasks.length; tIdx++) {
137
+ const task = plan.tasks[tIdx];
138
+ // Per Pitfall #5: if milestone is done, force all tasks to done
139
+ const taskStatus = milestoneDone ? "done" : (task.done ? "done" : "pending");
140
+ taskInserts.push({
141
+ id: task.id,
142
+ sliceId: slice.id,
143
+ milestoneId: mId,
144
+ title: task.title,
145
+ status: taskStatus,
146
+ sequence: tIdx,
147
+ });
148
+ }
149
+ }
150
+ catch (err) {
151
+ process.stderr.write(`workflow-migration: failed to parse ${slice.id}-PLAN.md for ${mId}: ${err.message}\n`);
152
+ }
153
+ }
154
+ }
155
+ // Check for orphaned summary files (summary for a slice not in ROADMAP)
156
+ try {
157
+ const files = readdirSync(mDir);
158
+ const summaryFiles = files.filter(f => f.endsWith("-SUMMARY.md") && f !== "SUMMARY.md");
159
+ for (const summaryFile of summaryFiles) {
160
+ const sliceId = summaryFile.replace("-SUMMARY.md", "");
161
+ if (!knownSliceIds.has(sliceId)) {
162
+ process.stderr.write(`workflow-migration: orphaned summary file ${summaryFile} in ${mId} (slice not found in ROADMAP.md), skipping\n`);
163
+ }
164
+ }
165
+ }
166
+ catch {
167
+ // Non-fatal
168
+ }
169
+ }
170
+ // Execute all inserts atomically
171
+ const now = new Date().toISOString();
172
+ if (migratedMilestoneIds.length === 0) {
173
+ process.stderr.write("workflow-migration: no milestones collected, nothing to insert\n");
174
+ return;
175
+ }
176
+ const placeholders = migratedMilestoneIds.map(() => "?").join(",");
177
+ transaction(() => {
178
+ // Clear existing data to handle stale DB shape (DELETE ... IN (...))
179
+ db.prepare(`DELETE FROM tasks WHERE milestone_id IN (${placeholders})`).run(...migratedMilestoneIds);
180
+ db.prepare(`DELETE FROM slices WHERE milestone_id IN (${placeholders})`).run(...migratedMilestoneIds);
181
+ db.prepare(`DELETE FROM milestones WHERE id IN (${placeholders})`).run(...migratedMilestoneIds);
182
+ // Insert milestones
183
+ const insertMilestone = db.prepare("INSERT INTO milestones (id, title, status, created_at) VALUES (?, ?, ?, ?)");
184
+ for (const m of milestoneInserts) {
185
+ insertMilestone.run(m.id, m.title, m.status, now);
186
+ }
187
+ // Insert slices (using v10 column names: depends, sequence)
188
+ const insertSlice = db.prepare("INSERT INTO slices (id, milestone_id, title, status, risk, depends, sequence, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
189
+ for (const s of sliceInserts) {
190
+ insertSlice.run(s.id, s.milestoneId, s.title, s.status, s.risk, "[]", s.sequence, now);
191
+ }
192
+ // Insert tasks (using v10 column names: sequence, blocker_discovered, full_summary_md)
193
+ const insertTask = db.prepare("INSERT INTO tasks (id, slice_id, milestone_id, title, description, status, estimate, files, sequence) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
194
+ for (const t of taskInserts) {
195
+ insertTask.run(t.id, t.sliceId, t.milestoneId, t.title, "", t.status, "", "[]", t.sequence);
196
+ }
197
+ });
198
+ }
199
+ // ─── validateMigration ────────────────────────────────────────────────────
200
+ /**
201
+ * D-14: Validate that engine state matches what markdown parsers report.
202
+ * Compares milestone count, slice count, task count, and status distributions.
203
+ * Logs each discrepancy to stderr but does NOT throw.
204
+ * Returns array of discrepancy strings (empty = clean migration).
205
+ */
206
+ export function validateMigration(basePath) {
207
+ const db = _getAdapter();
208
+ if (!db) {
209
+ return { discrepancies: ["No database connection for validation"] };
210
+ }
211
+ const discrepancies = [];
212
+ // Get engine counts
213
+ const engMilestones = db.prepare("SELECT COUNT(*) as cnt FROM milestones").get();
214
+ const engSlices = db.prepare("SELECT COUNT(*) as cnt FROM slices").get();
215
+ const engTasks = db.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
216
+ const engineMilestoneCount = engMilestones ? engMilestones["cnt"] : 0;
217
+ const engineSliceCount = engSlices ? engSlices["cnt"] : 0;
218
+ const engineTaskCount = engTasks ? engTasks["cnt"] : 0;
219
+ // Count from markdown
220
+ const milestonesDir = join(basePath, ".gsd", "milestones");
221
+ if (!existsSync(milestonesDir)) {
222
+ return { discrepancies };
223
+ }
224
+ let mdMilestoneCount = 0;
225
+ let mdSliceCount = 0;
226
+ let mdTaskCount = 0;
227
+ try {
228
+ const milestoneDirs = readdirSync(milestonesDir, { withFileTypes: true })
229
+ .filter(e => e.isDirectory())
230
+ .map(e => e.name);
231
+ mdMilestoneCount = milestoneDirs.length;
232
+ for (const mId of milestoneDirs) {
233
+ const mDir = join(milestonesDir, mId);
234
+ const roadmapPath = join(mDir, "ROADMAP.md");
235
+ if (existsSync(roadmapPath)) {
236
+ try {
237
+ const content = readFileSync(roadmapPath, "utf-8");
238
+ const roadmap = parseRoadmap(content);
239
+ mdSliceCount += roadmap.slices.length;
240
+ for (const slice of roadmap.slices) {
241
+ const planPath = join(mDir, `${slice.id}-PLAN.md`);
242
+ if (existsSync(planPath)) {
243
+ try {
244
+ const planContent = readFileSync(planPath, "utf-8");
245
+ const plan = parsePlan(planContent);
246
+ mdTaskCount += plan.tasks.length;
247
+ }
248
+ catch {
249
+ // Skip unreadable plan
250
+ }
251
+ }
252
+ }
253
+ }
254
+ catch {
255
+ // Skip unreadable roadmap
256
+ }
257
+ }
258
+ }
259
+ }
260
+ catch {
261
+ return { discrepancies: ["Failed to read markdown for validation"] };
262
+ }
263
+ // Compare counts
264
+ if (engineMilestoneCount !== mdMilestoneCount) {
265
+ const msg = `Milestone count mismatch: engine=${engineMilestoneCount}, markdown=${mdMilestoneCount}`;
266
+ discrepancies.push(msg);
267
+ process.stderr.write(`workflow-migration: ${msg}\n`);
268
+ }
269
+ if (engineSliceCount !== mdSliceCount) {
270
+ const msg = `Slice count mismatch: engine=${engineSliceCount}, markdown=${mdSliceCount}`;
271
+ discrepancies.push(msg);
272
+ process.stderr.write(`workflow-migration: ${msg}\n`);
273
+ }
274
+ if (engineTaskCount !== mdTaskCount) {
275
+ const msg = `Task count mismatch: engine=${engineTaskCount}, markdown=${mdTaskCount}`;
276
+ discrepancies.push(msg);
277
+ process.stderr.write(`workflow-migration: ${msg}\n`);
278
+ }
279
+ return { discrepancies };
280
+ }
@@ -0,0 +1,373 @@
1
+ // GSD Extension — Projection Renderers (DB -> Markdown)
2
+ // Renders PLAN.md, ROADMAP.md, SUMMARY.md, and STATE.md from database rows.
3
+ // Projections are read-only views of engine state (Layer 3 of the architecture).
4
+ import { _getAdapter, isDbAvailable, getMilestone, getMilestoneSlices, getSliceTasks, } from "./gsd-db.js";
5
+ import { atomicWriteSync } from "./atomic-write.js";
6
+ import { join } from "node:path";
7
+ import { mkdirSync, existsSync } from "node:fs";
8
+ import { logWarning } from "./workflow-logger.js";
9
+ import { deriveState } from "./state.js";
10
+ // ─── PLAN.md Projection ──────────────────────────────────────────────────
11
+ /**
12
+ * Render PLAN.md content from a slice row and its task rows.
13
+ * Pure function — no side effects.
14
+ */
15
+ export function renderPlanContent(sliceRow, taskRows) {
16
+ const lines = [];
17
+ lines.push(`# ${sliceRow.id}: ${sliceRow.title}`);
18
+ lines.push("");
19
+ lines.push(`**Goal:** ${sliceRow.goal || sliceRow.full_summary_md || "TBD"}`);
20
+ lines.push(`**Demo:** After this: ${sliceRow.demo || sliceRow.full_uat_md || "TBD"}`);
21
+ lines.push("");
22
+ lines.push("## Tasks");
23
+ for (const task of taskRows) {
24
+ const checkbox = task.status === "done" || task.status === "complete" ? "[x]" : "[ ]";
25
+ lines.push(`- ${checkbox} **${task.id}: ${task.title}** \u2014 ${task.description}`);
26
+ // Estimate subline (always present if non-empty)
27
+ if (task.estimate) {
28
+ lines.push(` - Estimate: ${task.estimate}`);
29
+ }
30
+ // Files subline (only if non-empty array)
31
+ if (task.files && task.files.length > 0) {
32
+ lines.push(` - Files: ${task.files.join(", ")}`);
33
+ }
34
+ // Verify subline (only if non-null)
35
+ if (task.verify) {
36
+ lines.push(` - Verify: ${task.verify}`);
37
+ }
38
+ // Duration subline (only if recorded)
39
+ if (task.duration) {
40
+ lines.push(` - Duration: ${task.duration}`);
41
+ }
42
+ // Blocker subline (if discovered)
43
+ if (task.blocker_discovered && task.known_issues) {
44
+ lines.push(` - Blocker: ${task.known_issues}`);
45
+ }
46
+ }
47
+ lines.push("");
48
+ return lines.join("\n");
49
+ }
50
+ /**
51
+ * Render PLAN.md projection to disk for a specific slice.
52
+ * Queries DB via helper functions, renders content, writes via atomicWriteSync.
53
+ */
54
+ export function renderPlanProjection(basePath, milestoneId, sliceId) {
55
+ const sliceRows = getMilestoneSlices(milestoneId);
56
+ const sliceRow = sliceRows.find(s => s.id === sliceId);
57
+ if (!sliceRow)
58
+ return;
59
+ const taskRows = getSliceTasks(milestoneId, sliceId);
60
+ const content = renderPlanContent(sliceRow, taskRows);
61
+ const dir = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId);
62
+ mkdirSync(dir, { recursive: true });
63
+ atomicWriteSync(join(dir, `${sliceId}-PLAN.md`), content);
64
+ }
65
+ // ─── ROADMAP.md Projection ───────────────────────────────────────────────
66
+ /**
67
+ * Render ROADMAP.md content from a milestone row and its slice rows.
68
+ * Pure function — no side effects.
69
+ */
70
+ export function renderRoadmapContent(milestoneRow, sliceRows) {
71
+ const lines = [];
72
+ lines.push(`# ${milestoneRow.id}: ${milestoneRow.title}`);
73
+ lines.push("");
74
+ lines.push("## Vision");
75
+ lines.push(milestoneRow.vision || milestoneRow.title || "TBD");
76
+ lines.push("");
77
+ lines.push("## Slice Overview");
78
+ lines.push("| ID | Slice | Risk | Depends | Done | After this |");
79
+ lines.push("|----|-------|------|---------|------|------------|");
80
+ for (const slice of sliceRows) {
81
+ const done = slice.status === "done" || slice.status === "complete" ? "\u2705" : "\u2B1C";
82
+ // depends is already parsed to string[] by rowToSlice
83
+ let depends = "\u2014";
84
+ if (slice.depends && slice.depends.length > 0) {
85
+ depends = slice.depends.join(", ");
86
+ }
87
+ const risk = (slice.risk || "low").toLowerCase();
88
+ const demo = slice.demo || slice.full_uat_md || "TBD";
89
+ lines.push(`| ${slice.id} | ${slice.title} | ${risk} | ${depends} | ${done} | ${demo} |`);
90
+ }
91
+ lines.push("");
92
+ return lines.join("\n");
93
+ }
94
+ /**
95
+ * Render ROADMAP.md projection to disk for a specific milestone.
96
+ * Queries DB via helper functions, renders content, writes via atomicWriteSync.
97
+ */
98
+ export function renderRoadmapProjection(basePath, milestoneId) {
99
+ const milestoneRow = getMilestone(milestoneId);
100
+ if (!milestoneRow)
101
+ return;
102
+ const sliceRows = getMilestoneSlices(milestoneId);
103
+ const content = renderRoadmapContent(milestoneRow, sliceRows);
104
+ const dir = join(basePath, ".gsd", "milestones", milestoneId);
105
+ mkdirSync(dir, { recursive: true });
106
+ atomicWriteSync(join(dir, `${milestoneId}-ROADMAP.md`), content);
107
+ }
108
+ // ─── SUMMARY.md Projection ──────────────────────────────────────────────
109
+ /**
110
+ * Render SUMMARY.md content from a task row.
111
+ * Pure function — no side effects.
112
+ */
113
+ export function renderSummaryContent(taskRow, sliceId, milestoneId) {
114
+ const lines = [];
115
+ // Frontmatter
116
+ lines.push("---");
117
+ lines.push(`id: ${taskRow.id}`);
118
+ lines.push(`parent: ${sliceId}`);
119
+ lines.push(`milestone: ${milestoneId}`);
120
+ lines.push("provides: []");
121
+ lines.push("requires: []");
122
+ lines.push("affects: []");
123
+ // key_files is already parsed to string[]
124
+ if (taskRow.key_files && taskRow.key_files.length > 0) {
125
+ lines.push(`key_files: [${taskRow.key_files.map(f => `"${f}"`).join(", ")}]`);
126
+ }
127
+ else {
128
+ lines.push("key_files: []");
129
+ }
130
+ // key_decisions is already parsed to string[]
131
+ if (taskRow.key_decisions && taskRow.key_decisions.length > 0) {
132
+ lines.push(`key_decisions: [${taskRow.key_decisions.map(d => `"${d}"`).join(", ")}]`);
133
+ }
134
+ else {
135
+ lines.push("key_decisions: []");
136
+ }
137
+ lines.push("patterns_established: []");
138
+ lines.push("drill_down_paths: []");
139
+ lines.push("observability_surfaces: []");
140
+ lines.push(`duration: "${taskRow.duration || ""}"`);
141
+ lines.push(`verification_result: "${taskRow.verification_result || ""}"`);
142
+ lines.push(`completed_at: ${taskRow.completed_at || ""}`);
143
+ lines.push(`blocker_discovered: ${taskRow.blocker_discovered ? "true" : "false"}`);
144
+ lines.push("---");
145
+ lines.push("");
146
+ lines.push(`# ${taskRow.id}: ${taskRow.title}`);
147
+ lines.push("");
148
+ // One-liner (if present)
149
+ if (taskRow.one_liner) {
150
+ lines.push(`> ${taskRow.one_liner}`);
151
+ lines.push("");
152
+ }
153
+ lines.push("## What Happened");
154
+ lines.push(taskRow.full_summary_md || taskRow.narrative || "No summary recorded.");
155
+ lines.push("");
156
+ // Deviations (if present)
157
+ if (taskRow.deviations) {
158
+ lines.push("## Deviations");
159
+ lines.push(taskRow.deviations);
160
+ lines.push("");
161
+ }
162
+ // Known issues (if present)
163
+ if (taskRow.known_issues) {
164
+ lines.push("## Known Issues");
165
+ lines.push(taskRow.known_issues);
166
+ lines.push("");
167
+ }
168
+ return lines.join("\n");
169
+ }
170
+ /**
171
+ * Render SUMMARY.md projection to disk for a specific task.
172
+ * Queries DB via helper functions, renders content, writes via atomicWriteSync.
173
+ */
174
+ export function renderSummaryProjection(basePath, milestoneId, sliceId, taskId) {
175
+ const taskRows = getSliceTasks(milestoneId, sliceId);
176
+ const taskRow = taskRows.find(t => t.id === taskId);
177
+ if (!taskRow)
178
+ return;
179
+ const content = renderSummaryContent(taskRow, sliceId, milestoneId);
180
+ const dir = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, "tasks");
181
+ mkdirSync(dir, { recursive: true });
182
+ atomicWriteSync(join(dir, `${taskId}-SUMMARY.md`), content);
183
+ }
184
+ // ─── STATE.md Projection ────────────────────────────────────────────────
185
+ /**
186
+ * Render STATE.md content from GSDState.
187
+ * Matches the buildStateMarkdown output format from doctor.ts exactly.
188
+ * Pure function — no side effects.
189
+ */
190
+ export function renderStateContent(state) {
191
+ const lines = [];
192
+ lines.push("# GSD State", "");
193
+ const activeMilestone = state.activeMilestone
194
+ ? `${state.activeMilestone.id}: ${state.activeMilestone.title}`
195
+ : "None";
196
+ const activeSlice = state.activeSlice
197
+ ? `${state.activeSlice.id}: ${state.activeSlice.title}`
198
+ : "None";
199
+ lines.push(`**Active Milestone:** ${activeMilestone}`);
200
+ lines.push(`**Active Slice:** ${activeSlice}`);
201
+ lines.push(`**Phase:** ${state.phase}`);
202
+ if (state.requirements) {
203
+ lines.push(`**Requirements Status:** ${state.requirements.active} active \u00b7 ${state.requirements.validated} validated \u00b7 ${state.requirements.deferred} deferred \u00b7 ${state.requirements.outOfScope} out of scope`);
204
+ }
205
+ lines.push("");
206
+ lines.push("## Milestone Registry");
207
+ for (const entry of state.registry) {
208
+ const glyph = entry.status === "complete" ? "\u2705" : entry.status === "active" ? "\uD83D\uDD04" : entry.status === "parked" ? "\u23F8\uFE0F" : "\u2B1C";
209
+ lines.push(`- ${glyph} **${entry.id}:** ${entry.title}`);
210
+ }
211
+ lines.push("");
212
+ lines.push("## Recent Decisions");
213
+ if (state.recentDecisions.length > 0) {
214
+ for (const decision of state.recentDecisions)
215
+ lines.push(`- ${decision}`);
216
+ }
217
+ else {
218
+ lines.push("- None recorded");
219
+ }
220
+ lines.push("");
221
+ lines.push("## Blockers");
222
+ if (state.blockers.length > 0) {
223
+ for (const blocker of state.blockers)
224
+ lines.push(`- ${blocker}`);
225
+ }
226
+ else {
227
+ lines.push("- None");
228
+ }
229
+ lines.push("");
230
+ lines.push("## Next Action");
231
+ lines.push(state.nextAction || "None");
232
+ lines.push("");
233
+ return lines.join("\n");
234
+ }
235
+ /**
236
+ * Render STATE.md projection to disk.
237
+ * Derives state from DB, renders content, writes via atomicWriteSync.
238
+ */
239
+ export async function renderStateProjection(basePath) {
240
+ try {
241
+ if (!isDbAvailable())
242
+ return;
243
+ // Probe DB handle — adapter may be set but underlying handle closed
244
+ const adapter = _getAdapter();
245
+ if (!adapter)
246
+ return;
247
+ try {
248
+ adapter.prepare("SELECT 1").get();
249
+ }
250
+ catch {
251
+ return;
252
+ }
253
+ const state = await deriveState(basePath);
254
+ const content = renderStateContent(state);
255
+ const dir = join(basePath, ".gsd");
256
+ mkdirSync(dir, { recursive: true });
257
+ atomicWriteSync(join(dir, "STATE.md"), content);
258
+ }
259
+ catch (err) {
260
+ logWarning("projection", `renderStateProjection failed: ${err.message}`);
261
+ }
262
+ }
263
+ // ─── renderAllProjections ───────────────────────────────────────────────
264
+ /**
265
+ * Regenerate all projection files for a milestone from DB state.
266
+ * All calls are wrapped in try/catch — projection failure is non-fatal per D-02.
267
+ */
268
+ export async function renderAllProjections(basePath, milestoneId) {
269
+ // Render ROADMAP.md for the milestone
270
+ try {
271
+ renderRoadmapProjection(basePath, milestoneId);
272
+ }
273
+ catch (err) {
274
+ logWarning("projection", `renderRoadmapProjection failed for ${milestoneId}: ${err.message}`);
275
+ }
276
+ // Query all slices for this milestone
277
+ const sliceRows = getMilestoneSlices(milestoneId);
278
+ for (const slice of sliceRows) {
279
+ // Render PLAN.md for each slice
280
+ try {
281
+ renderPlanProjection(basePath, milestoneId, slice.id);
282
+ }
283
+ catch (err) {
284
+ logWarning("projection", `renderPlanProjection failed for ${milestoneId}/${slice.id}: ${err.message}`);
285
+ }
286
+ // Render SUMMARY.md for each completed task
287
+ const taskRows = getSliceTasks(milestoneId, slice.id);
288
+ const doneTasks = taskRows.filter(t => t.status === "done" || t.status === "complete");
289
+ for (const task of doneTasks) {
290
+ try {
291
+ renderSummaryProjection(basePath, milestoneId, slice.id, task.id);
292
+ }
293
+ catch (err) {
294
+ logWarning("projection", `renderSummaryProjection failed for ${milestoneId}/${slice.id}/${task.id}: ${err.message}`);
295
+ }
296
+ }
297
+ }
298
+ // Render STATE.md
299
+ try {
300
+ await renderStateProjection(basePath);
301
+ }
302
+ catch (err) {
303
+ logWarning("projection", `renderStateProjection failed: ${err.message}`);
304
+ }
305
+ }
306
+ // ─── regenerateIfMissing ────────────────────────────────────────────────
307
+ /**
308
+ * Check if a projection file exists on disk. If missing, regenerate it from DB.
309
+ * Returns true if the file was regenerated, false if it already existed.
310
+ * Satisfies PROJ-05 (corrupted/deleted projections regenerate on demand).
311
+ */
312
+ export function regenerateIfMissing(basePath, milestoneId, sliceId, fileType) {
313
+ let filePath;
314
+ switch (fileType) {
315
+ case "PLAN":
316
+ filePath = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, `${sliceId}-PLAN.md`);
317
+ break;
318
+ case "ROADMAP":
319
+ filePath = join(basePath, ".gsd", "milestones", milestoneId, `${milestoneId}-ROADMAP.md`);
320
+ break;
321
+ case "SUMMARY":
322
+ // For SUMMARY, we regenerate all task summaries in the slice
323
+ filePath = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, "tasks");
324
+ break;
325
+ case "STATE":
326
+ filePath = join(basePath, ".gsd", "STATE.md");
327
+ break;
328
+ }
329
+ if (fileType === "SUMMARY") {
330
+ // Check each completed task's SUMMARY file individually (not just the directory)
331
+ const taskRows = getSliceTasks(milestoneId, sliceId);
332
+ const doneTasks = taskRows.filter(t => t.status === "done" || t.status === "complete");
333
+ let regenerated = 0;
334
+ for (const task of doneTasks) {
335
+ const summaryPath = join(basePath, ".gsd", "milestones", milestoneId, "slices", sliceId, "tasks", `${task.id}-SUMMARY.md`);
336
+ if (!existsSync(summaryPath)) {
337
+ try {
338
+ renderSummaryProjection(basePath, milestoneId, sliceId, task.id);
339
+ regenerated++;
340
+ }
341
+ catch (err) {
342
+ console.error(`[projections] regenerateIfMissing SUMMARY failed for ${task.id}:`, err);
343
+ }
344
+ }
345
+ }
346
+ return regenerated > 0;
347
+ }
348
+ if (existsSync(filePath)) {
349
+ return false;
350
+ }
351
+ // Regenerate the missing file
352
+ try {
353
+ switch (fileType) {
354
+ case "PLAN":
355
+ renderPlanProjection(basePath, milestoneId, sliceId);
356
+ break;
357
+ case "ROADMAP":
358
+ renderRoadmapProjection(basePath, milestoneId);
359
+ break;
360
+ case "STATE":
361
+ // renderStateProjection is async — fire-and-forget.
362
+ // Return false since the file isn't written yet; it will appear
363
+ // on the next post-mutation hook cycle.
364
+ void renderStateProjection(basePath);
365
+ return false;
366
+ }
367
+ return true;
368
+ }
369
+ catch (err) {
370
+ console.error(`[projections] regenerateIfMissing ${fileType} failed:`, err);
371
+ return false;
372
+ }
373
+ }