gsd-pi 2.44.0-dev.d25d507 → 2.45.0-dev.1afbdaa

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 (407) hide show
  1. package/dist/help-text.js +1 -1
  2. package/dist/loader.js +34 -0
  3. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  4. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  5. package/dist/resources/extensions/gsd/auto/phases.js +63 -77
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  7. package/dist/resources/extensions/gsd/auto/session.js +0 -11
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
  10. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  11. package/dist/resources/extensions/gsd/auto-start.js +23 -5
  12. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  13. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  14. package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
  15. package/dist/resources/extensions/gsd/auto.js +42 -60
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +170 -11
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
  18. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  19. package/dist/resources/extensions/gsd/commands/context.js +0 -4
  20. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  21. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  22. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
  23. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  24. package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
  25. package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
  26. package/dist/resources/extensions/gsd/db-writer.js +40 -22
  27. package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
  28. package/dist/resources/extensions/gsd/doctor.js +13 -3
  29. package/dist/resources/extensions/gsd/git-service.js +8 -3
  30. package/dist/resources/extensions/gsd/gsd-db.js +28 -4
  31. package/dist/resources/extensions/gsd/guided-flow.js +1 -2
  32. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  33. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  34. package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
  35. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  36. package/dist/resources/extensions/gsd/preferences.js +8 -4
  37. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  38. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  39. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  40. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
  41. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  42. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  47. package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
  48. package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
  49. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  50. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  51. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  52. package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
  53. package/dist/resources/extensions/gsd/prompts/rethink.md +83 -0
  54. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  56. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  57. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  58. package/dist/resources/extensions/gsd/rethink.js +115 -0
  59. package/dist/resources/extensions/gsd/session-lock.js +1 -3
  60. package/dist/resources/extensions/gsd/state.js +48 -3
  61. package/dist/resources/extensions/gsd/sync-lock.js +89 -0
  62. package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
  63. package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
  64. package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
  65. package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
  66. package/dist/resources/extensions/gsd/tools/plan-slice.js +31 -1
  67. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -1
  68. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
  69. package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
  70. package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
  71. package/dist/resources/extensions/gsd/tools/replan-slice.js +34 -2
  72. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  73. package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
  74. package/dist/resources/extensions/gsd/workflow-events.js +102 -0
  75. package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
  76. package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
  77. package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
  78. package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
  79. package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
  80. package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
  81. package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
  82. package/dist/resources/extensions/gsd/write-intercept.js +84 -0
  83. package/dist/resources/extensions/mcp-client/index.js +14 -0
  84. package/dist/resources/extensions/voice/index.js +11 -16
  85. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  88. package/dist/web/standalone/.next/build-manifest.json +3 -3
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  91. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  93. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found/page.js +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  110. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.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_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/index.html +1 -1
  151. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  152. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  153. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  154. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  155. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  156. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  157. package/dist/web/standalone/.next/server/app/page.js +1 -1
  158. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  160. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  163. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  164. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  165. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  166. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  167. package/dist/web/standalone/.next/static/chunks/app/{page-b9367c5ae13b99c6.js → page-6654a8cca61a3d1c.js} +1 -1
  168. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  169. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  170. package/package.json +2 -1
  171. package/packages/native/dist/stream-process/index.js +2 -2
  172. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  173. package/packages/native/src/stream-process/index.ts +2 -2
  174. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  175. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  177. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  180. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  182. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  185. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  187. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  189. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  190. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  191. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  192. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  193. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  195. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  196. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  197. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
  199. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  201. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  203. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  205. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  207. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/main.js +17 -0
  210. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  234. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  236. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  238. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  239. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  240. package/packages/pi-coding-agent/package.json +1 -1
  241. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  242. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  243. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  244. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  245. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  246. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  247. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  248. package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
  249. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  250. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  251. package/packages/pi-coding-agent/src/main.ts +19 -0
  252. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  253. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  254. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  255. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  256. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  257. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  258. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  259. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  260. package/pkg/package.json +1 -1
  261. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  262. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  263. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
  264. package/src/resources/extensions/gsd/auto/phases.ts +69 -91
  265. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  266. package/src/resources/extensions/gsd/auto/session.ts +0 -18
  267. package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
  268. package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
  269. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
  270. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  271. package/src/resources/extensions/gsd/auto-start.ts +26 -5
  272. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  273. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  274. package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
  275. package/src/resources/extensions/gsd/auto.ts +44 -86
  276. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +162 -11
  277. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  278. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  279. package/src/resources/extensions/gsd/commands/context.ts +0 -5
  280. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  281. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  282. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
  283. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  284. package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
  285. package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
  286. package/src/resources/extensions/gsd/db-writer.ts +41 -27
  287. package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
  288. package/src/resources/extensions/gsd/doctor-types.ts +7 -1
  289. package/src/resources/extensions/gsd/doctor.ts +13 -4
  290. package/src/resources/extensions/gsd/git-service.ts +6 -2
  291. package/src/resources/extensions/gsd/gsd-db.ts +32 -4
  292. package/src/resources/extensions/gsd/guided-flow.ts +1 -2
  293. package/src/resources/extensions/gsd/journal.ts +6 -1
  294. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  295. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  296. package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
  297. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  298. package/src/resources/extensions/gsd/preferences.ts +7 -3
  299. package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -10
  300. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  301. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  302. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
  303. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  304. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  305. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  306. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  307. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  308. package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -3
  309. package/src/resources/extensions/gsd/prompts/queue.md +2 -2
  310. package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
  311. package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  312. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  313. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  314. package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
  315. package/src/resources/extensions/gsd/prompts/rethink.md +83 -0
  316. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  317. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  318. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  319. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  320. package/src/resources/extensions/gsd/rethink.ts +154 -0
  321. package/src/resources/extensions/gsd/session-lock.ts +0 -4
  322. package/src/resources/extensions/gsd/state.ts +49 -1
  323. package/src/resources/extensions/gsd/sync-lock.ts +94 -0
  324. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
  325. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
  326. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  327. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
  328. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
  329. package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
  330. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  331. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
  332. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
  333. package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
  334. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  335. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
  336. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  337. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  338. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
  339. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  340. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  341. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
  342. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  343. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  344. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  345. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  346. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  347. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
  348. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  349. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
  350. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
  351. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
  352. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
  353. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
  354. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
  355. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
  356. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  357. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
  358. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -21
  359. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  360. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
  361. package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
  362. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
  363. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  364. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
  365. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  366. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
  367. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  368. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
  369. package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
  370. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
  371. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  372. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
  373. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
  374. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  375. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  376. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
  377. package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
  378. package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
  379. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
  380. package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
  381. package/src/resources/extensions/gsd/tools/plan-slice.ts +40 -0
  382. package/src/resources/extensions/gsd/tools/plan-task.ts +37 -1
  383. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
  384. package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
  385. package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
  386. package/src/resources/extensions/gsd/tools/replan-slice.ts +41 -1
  387. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  388. package/src/resources/extensions/gsd/types.ts +8 -0
  389. package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
  390. package/src/resources/extensions/gsd/workflow-events.ts +154 -0
  391. package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
  392. package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
  393. package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
  394. package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
  395. package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
  396. package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
  397. package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
  398. package/src/resources/extensions/gsd/write-intercept.ts +90 -0
  399. package/src/resources/extensions/mcp-client/index.ts +20 -0
  400. package/src/resources/extensions/voice/index.ts +11 -21
  401. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  402. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  403. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  404. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  405. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  406. /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_buildManifest.js +0 -0
  407. /package/dist/web/standalone/.next/static/{tokoGmfkYfWf1_Yl_Gz7i → j-BskPs0nxxPeYY-bSrab}/_ssgManifest.js +0 -0
@@ -0,0 +1,503 @@
1
+ import { join } from "node:path";
2
+ import { mkdirSync, existsSync, readFileSync, unlinkSync } from "node:fs";
3
+ import { readEvents, findForkPoint, appendEvent, getSessionId } from "./workflow-events.js";
4
+ import type { WorkflowEvent } from "./workflow-events.js";
5
+ import {
6
+ transaction,
7
+ updateTaskStatus,
8
+ updateSliceStatus,
9
+ insertVerificationEvidence,
10
+ upsertDecision,
11
+ openDatabase,
12
+ } from "./gsd-db.js";
13
+ import { writeManifest } from "./workflow-manifest.js";
14
+ import { atomicWriteSync } from "./atomic-write.js";
15
+ import { acquireSyncLock, releaseSyncLock } from "./sync-lock.js";
16
+
17
+ // ─── Public Types ─────────────────────────────────────────────────────────────
18
+
19
+ export interface ConflictEntry {
20
+ entityType: string;
21
+ entityId: string;
22
+ mainSideEvents: WorkflowEvent[];
23
+ worktreeSideEvents: WorkflowEvent[];
24
+ }
25
+
26
+ export interface ReconcileResult {
27
+ autoMerged: number;
28
+ conflicts: ConflictEntry[];
29
+ }
30
+
31
+ // ─── replayEvents ─────────────────────────────────────────────────────────────
32
+
33
+ /**
34
+ * Replay a list of WorkflowEvents by dispatching each to the appropriate
35
+ * gsd-db function. This replaces the old engine.replayAll() pattern with
36
+ * direct DB calls.
37
+ */
38
+ function replayEvents(events: WorkflowEvent[]): void {
39
+ transaction(() => {
40
+ for (const event of events) {
41
+ const p = event.params;
42
+ switch (event.cmd) {
43
+ case "complete_task": {
44
+ const milestoneId = p["milestoneId"] as string;
45
+ const sliceId = p["sliceId"] as string;
46
+ const taskId = p["taskId"] as string;
47
+ updateTaskStatus(milestoneId, sliceId, taskId, "done", event.ts);
48
+ break;
49
+ }
50
+ case "start_task": {
51
+ const milestoneId = p["milestoneId"] as string;
52
+ const sliceId = p["sliceId"] as string;
53
+ const taskId = p["taskId"] as string;
54
+ updateTaskStatus(milestoneId, sliceId, taskId, "in-progress", event.ts);
55
+ break;
56
+ }
57
+ case "report_blocker": {
58
+ // report_blocker marks the task with blocker_discovered = 1
59
+ // The DB helper updateTaskStatus doesn't handle blockers,
60
+ // so we just update status to "blocked" as a best-effort replay.
61
+ const milestoneId = p["milestoneId"] as string;
62
+ const sliceId = p["sliceId"] as string;
63
+ const taskId = p["taskId"] as string;
64
+ updateTaskStatus(milestoneId, sliceId, taskId, "blocked");
65
+ break;
66
+ }
67
+ case "record_verification": {
68
+ const milestoneId = p["milestoneId"] as string;
69
+ const sliceId = p["sliceId"] as string;
70
+ const taskId = p["taskId"] as string;
71
+ insertVerificationEvidence({
72
+ taskId,
73
+ sliceId,
74
+ milestoneId,
75
+ command: (p["command"] as string) ?? "",
76
+ exitCode: (p["exitCode"] as number) ?? 0,
77
+ verdict: (p["verdict"] as string) ?? "",
78
+ durationMs: (p["durationMs"] as number) ?? 0,
79
+ });
80
+ break;
81
+ }
82
+ case "complete_slice": {
83
+ const milestoneId = p["milestoneId"] as string;
84
+ const sliceId = p["sliceId"] as string;
85
+ updateSliceStatus(milestoneId, sliceId, "done", event.ts);
86
+ break;
87
+ }
88
+ case "plan_slice": {
89
+ // plan_slice events are informational — slice should already exist.
90
+ // No DB mutation needed during replay (the slice was inserted at plan time).
91
+ break;
92
+ }
93
+ case "save_decision": {
94
+ upsertDecision({
95
+ id: (p["id"] as string) ?? `${p["scope"]}:${p["decision"]}`,
96
+ when_context: (p["when_context"] as string) ?? (p["whenContext"] as string) ?? "",
97
+ scope: (p["scope"] as string) ?? "",
98
+ decision: (p["decision"] as string) ?? "",
99
+ choice: (p["choice"] as string) ?? "",
100
+ rationale: (p["rationale"] as string) ?? "",
101
+ revisable: (p["revisable"] as string) ?? "yes",
102
+ made_by: ((p["made_by"] as string) ?? (p["madeBy"] as string) ?? "agent") as "agent",
103
+ superseded_by: (p["superseded_by"] as string) ?? (p["supersededBy"] as string) ?? null,
104
+ });
105
+ break;
106
+ }
107
+ default:
108
+ // Unknown commands are silently skipped during replay
109
+ break;
110
+ }
111
+ }
112
+ }); // end transaction
113
+ }
114
+
115
+ // ─── extractEntityKey ─────────────────────────────────────────────────────────
116
+
117
+ /**
118
+ * Map a WorkflowEvent command to its affected entity type and ID.
119
+ * Returns null for commands that don't touch a named entity
120
+ * (e.g. unknown or future cmds).
121
+ */
122
+ export function extractEntityKey(
123
+ event: WorkflowEvent,
124
+ ): { type: string; id: string } | null {
125
+ const p = event.params;
126
+
127
+ switch (event.cmd) {
128
+ case "complete_task":
129
+ case "start_task":
130
+ case "report_blocker":
131
+ case "record_verification":
132
+ return typeof p["taskId"] === "string"
133
+ ? { type: "task", id: p["taskId"] }
134
+ : null;
135
+
136
+ case "complete_slice":
137
+ return typeof p["sliceId"] === "string"
138
+ ? { type: "slice", id: p["sliceId"] }
139
+ : null;
140
+
141
+ case "plan_slice":
142
+ return typeof p["sliceId"] === "string"
143
+ ? { type: "slice_plan", id: p["sliceId"] }
144
+ : null;
145
+
146
+ case "save_decision":
147
+ if (typeof p["scope"] === "string" && typeof p["decision"] === "string") {
148
+ return { type: "decision", id: `${p["scope"]}:${p["decision"]}` };
149
+ }
150
+ return null;
151
+
152
+ default:
153
+ return null;
154
+ }
155
+ }
156
+
157
+ // ─── detectConflicts ──────────────────────────────────────────────────────────
158
+
159
+ /**
160
+ * Compare two sets of diverged events. Returns conflict entries for any
161
+ * entity touched by both sides.
162
+ *
163
+ * Entity-level granularity: if both sides touched task T01 (with any cmd),
164
+ * that is one conflict regardless of field-level differences.
165
+ */
166
+ export function detectConflicts(
167
+ mainDiverged: WorkflowEvent[],
168
+ wtDiverged: WorkflowEvent[],
169
+ ): ConflictEntry[] {
170
+ // Group each side's events by entity key
171
+ const mainByEntity = new Map<string, WorkflowEvent[]>();
172
+ for (const event of mainDiverged) {
173
+ const key = extractEntityKey(event);
174
+ if (!key) continue;
175
+ const bucket = mainByEntity.get(`${key.type}:${key.id}`) ?? [];
176
+ bucket.push(event);
177
+ mainByEntity.set(`${key.type}:${key.id}`, bucket);
178
+ }
179
+
180
+ const wtByEntity = new Map<string, WorkflowEvent[]>();
181
+ for (const event of wtDiverged) {
182
+ const key = extractEntityKey(event);
183
+ if (!key) continue;
184
+ const bucket = wtByEntity.get(`${key.type}:${key.id}`) ?? [];
185
+ bucket.push(event);
186
+ wtByEntity.set(`${key.type}:${key.id}`, bucket);
187
+ }
188
+
189
+ // Find entities touched by both sides
190
+ const conflicts: ConflictEntry[] = [];
191
+ for (const [entityKey, mainEvents] of mainByEntity) {
192
+ const wtEvents = wtByEntity.get(entityKey);
193
+ if (!wtEvents) continue;
194
+
195
+ const colonIdx = entityKey.indexOf(":");
196
+ const entityType = entityKey.slice(0, colonIdx);
197
+ const entityId = entityKey.slice(colonIdx + 1);
198
+
199
+ conflicts.push({
200
+ entityType,
201
+ entityId,
202
+ mainSideEvents: mainEvents,
203
+ worktreeSideEvents: wtEvents,
204
+ });
205
+ }
206
+
207
+ return conflicts;
208
+ }
209
+
210
+ // ─── writeConflictsFile ───────────────────────────────────────────────────────
211
+
212
+ /**
213
+ * Write a human-readable CONFLICTS.md to basePath/.gsd/CONFLICTS.md.
214
+ * Lists each conflict with both sides' event payloads and resolution instructions.
215
+ */
216
+ export function writeConflictsFile(
217
+ basePath: string,
218
+ conflicts: ConflictEntry[],
219
+ worktreePath: string,
220
+ ): void {
221
+ const timestamp = new Date().toISOString();
222
+ const lines: string[] = [
223
+ `# Merge Conflicts — ${timestamp}`,
224
+ "",
225
+ `Conflicts detected merging worktree \`${worktreePath}\` into \`${basePath}\`.`,
226
+ `Run \`gsd resolve-conflict\` to resolve each conflict.`,
227
+ "",
228
+ ];
229
+
230
+ conflicts.forEach((conflict, idx) => {
231
+ lines.push(`## Conflict ${idx + 1}: ${conflict.entityType} ${conflict.entityId}`);
232
+ lines.push("");
233
+ lines.push("**Main side events:**");
234
+ for (const event of conflict.mainSideEvents) {
235
+ lines.push(`- ${event.cmd} at ${event.ts} (hash: ${event.hash})`);
236
+ lines.push(` params: ${JSON.stringify(event.params)}`);
237
+ }
238
+ lines.push("");
239
+ lines.push("**Worktree side events:**");
240
+ for (const event of conflict.worktreeSideEvents) {
241
+ lines.push(`- ${event.cmd} at ${event.ts} (hash: ${event.hash})`);
242
+ lines.push(` params: ${JSON.stringify(event.params)}`);
243
+ }
244
+ lines.push("");
245
+ lines.push(`**Resolve with:** \`gsd resolve-conflict --entity ${conflict.entityType}:${conflict.entityId} --pick [main|worktree]\``);
246
+ lines.push("");
247
+ });
248
+
249
+ const content = lines.join("\n");
250
+ const dir = join(basePath, ".gsd");
251
+ mkdirSync(dir, { recursive: true });
252
+ atomicWriteSync(join(dir, "CONFLICTS.md"), content);
253
+ }
254
+
255
+ // ─── reconcileWorktreeLogs ────────────────────────────────────────────────────
256
+
257
+ /**
258
+ * Event-log-based reconciliation algorithm:
259
+ *
260
+ * 1. Read both event logs
261
+ * 2. Find fork point (last common event by hash)
262
+ * 3. Slice diverged sets from each side
263
+ * 4. If no divergence on either side → return autoMerged: 0, conflicts: []
264
+ * 5. detectConflicts() — if any, writeConflictsFile + return early (D-04 all-or-nothing)
265
+ * 6. If clean: sort merged = mainDiverged + wtDiverged by timestamp, replayAll
266
+ * 7. Write merged event log (base + merged in timestamp order)
267
+ * 8. writeManifest
268
+ * 9. Return { autoMerged: merged.length, conflicts: [] }
269
+ */
270
+ export function reconcileWorktreeLogs(
271
+ mainBasePath: string,
272
+ worktreeBasePath: string,
273
+ ): ReconcileResult {
274
+ // Acquire advisory lock to prevent concurrent reconcile + append races
275
+ const lock = acquireSyncLock(mainBasePath);
276
+ if (!lock.acquired) {
277
+ process.stderr.write(
278
+ `[gsd] reconcile: could not acquire sync lock — another reconciliation may be in progress\n`,
279
+ );
280
+ return { autoMerged: 0, conflicts: [] };
281
+ }
282
+
283
+ try {
284
+ return _reconcileWorktreeLogsInner(mainBasePath, worktreeBasePath);
285
+ } finally {
286
+ releaseSyncLock(mainBasePath);
287
+ }
288
+ }
289
+
290
+ function _reconcileWorktreeLogsInner(
291
+ mainBasePath: string,
292
+ worktreeBasePath: string,
293
+ ): ReconcileResult {
294
+ // Step 1: Read both logs
295
+ const mainLogPath = join(mainBasePath, ".gsd", "event-log.jsonl");
296
+ const wtLogPath = join(worktreeBasePath, ".gsd", "event-log.jsonl");
297
+
298
+ const mainEvents = readEvents(mainLogPath);
299
+ const wtEvents = readEvents(wtLogPath);
300
+
301
+ // Step 2: Find fork point
302
+ const forkPoint = findForkPoint(mainEvents, wtEvents);
303
+
304
+ // Step 3: Slice diverged sets
305
+ const mainDiverged = mainEvents.slice(forkPoint + 1);
306
+ const wtDiverged = wtEvents.slice(forkPoint + 1);
307
+
308
+ // Step 4: No divergence on either side
309
+ if (mainDiverged.length === 0 && wtDiverged.length === 0) {
310
+ return { autoMerged: 0, conflicts: [] };
311
+ }
312
+
313
+ // Step 5: Detect conflicts (entity-level)
314
+ const conflicts = detectConflicts(mainDiverged, wtDiverged);
315
+ if (conflicts.length > 0) {
316
+ // D-04: atomic all-or-nothing — block entire merge
317
+ writeConflictsFile(mainBasePath, conflicts, worktreeBasePath);
318
+ process.stderr.write(
319
+ `[gsd] reconcile: ${conflicts.length} conflict(s) detected — see ${join(mainBasePath, ".gsd", "CONFLICTS.md")}\n`,
320
+ );
321
+ return { autoMerged: 0, conflicts };
322
+ }
323
+
324
+ // Step 6: Clean merge — stable sort by timestamp (index-based tiebreaker)
325
+ const indexed = [...mainDiverged, ...wtDiverged].map((e, i) => ({ e, i }));
326
+ indexed.sort((a, b) => a.e.ts.localeCompare(b.e.ts) || a.i - b.i);
327
+ const merged = indexed.map(({ e }) => e);
328
+
329
+ // Step 7: Write merged event log FIRST (so crash recovery can re-derive DB state)
330
+ const baseEvents = mainEvents.slice(0, forkPoint + 1);
331
+ const mergedLog = baseEvents.concat(merged);
332
+ const logContent = mergedLog.map((e) => JSON.stringify(e)).join("\n") + (mergedLog.length > 0 ? "\n" : "");
333
+ mkdirSync(join(mainBasePath, ".gsd"), { recursive: true });
334
+ atomicWriteSync(join(mainBasePath, ".gsd", "event-log.jsonl"), logContent);
335
+
336
+ // Step 8: Replay into DB (wrapped in a transaction by replayEvents)
337
+ openDatabase(join(mainBasePath, ".gsd", "gsd.db"));
338
+ replayEvents(merged);
339
+
340
+ // Step 9: Write manifest
341
+ try {
342
+ writeManifest(mainBasePath);
343
+ } catch (err) {
344
+ process.stderr.write(
345
+ `[gsd] reconcile: manifest write failed (non-fatal): ${(err as Error).message}\n`,
346
+ );
347
+ }
348
+
349
+ return { autoMerged: merged.length, conflicts: [] };
350
+ }
351
+
352
+ // ─── Conflict Resolution (D-06) ─────────────────────────────────────────────
353
+
354
+ /**
355
+ * Parse CONFLICTS.md and return structured ConflictEntry[].
356
+ * Returns empty array when CONFLICTS.md does not exist.
357
+ *
358
+ * Parses the format written by writeConflictsFile:
359
+ * ## Conflict N: {entityType} {entityId}
360
+ * **Main side events:**
361
+ * - {cmd} at {ts} (hash: {hash})
362
+ * params: {JSON}
363
+ * **Worktree side events:**
364
+ * - {cmd} at {ts} (hash: {hash})
365
+ * params: {JSON}
366
+ */
367
+ export function listConflicts(basePath: string): ConflictEntry[] {
368
+ const conflictsPath = join(basePath, ".gsd", "CONFLICTS.md");
369
+ if (!existsSync(conflictsPath)) return [];
370
+
371
+ const content = readFileSync(conflictsPath, "utf-8");
372
+ const conflicts: ConflictEntry[] = [];
373
+
374
+ // Split into per-conflict sections on "## Conflict N:" headings
375
+ const sections = content.split(/^## Conflict \d+:/m).slice(1);
376
+
377
+ for (const section of sections) {
378
+ // Extract entity type and id from first line: " {entityType} {entityId}"
379
+ const headingMatch = section.match(/^\s+(\S+)\s+(\S+)/);
380
+ if (!headingMatch) continue;
381
+ const entityType = headingMatch[1]!;
382
+ const entityId = headingMatch[2]!;
383
+
384
+ // Split into main/worktree blocks
385
+ const mainMatch = section.split("**Main side events:**")[1];
386
+ const wtMatch = mainMatch?.split("**Worktree side events:**");
387
+
388
+ const mainBlock = wtMatch?.[0] ?? "";
389
+ const wtBlock = wtMatch?.[1] ?? "";
390
+
391
+ const mainSideEvents = parseEventBlock(mainBlock);
392
+ const worktreeSideEvents = parseEventBlock(wtBlock);
393
+
394
+ conflicts.push({ entityType, entityId, mainSideEvents, worktreeSideEvents });
395
+ }
396
+
397
+ return conflicts;
398
+ }
399
+
400
+ /**
401
+ * Parse a block of event lines from CONFLICTS.md into WorkflowEvent[].
402
+ * Each event spans two lines:
403
+ * - {cmd} at {ts} (hash: {hash})
404
+ * params: {JSON}
405
+ */
406
+ function parseEventBlock(block: string): WorkflowEvent[] {
407
+ const events: WorkflowEvent[] = [];
408
+ // Find lines starting with "- " (event lines)
409
+ const lines = block.split("\n");
410
+ let i = 0;
411
+ while (i < lines.length) {
412
+ const line = lines[i]!.trim();
413
+ if (line.startsWith("- ")) {
414
+ // Parse: - {cmd} at {ts} (hash: {hash})
415
+ const eventMatch = line.match(/^-\s+(\S+)\s+at\s+(\S+)\s+\(hash:\s+(\S+)\)$/);
416
+ if (eventMatch) {
417
+ const cmd = eventMatch[1]!;
418
+ const ts = eventMatch[2]!;
419
+ const hash = eventMatch[3]!;
420
+
421
+ // Next line: " params: {JSON}"
422
+ let params: Record<string, unknown> = {};
423
+ const nextLine = lines[i + 1];
424
+ if (nextLine) {
425
+ const paramsMatch = nextLine.trim().match(/^params:\s+(.+)$/);
426
+ if (paramsMatch) {
427
+ try {
428
+ params = JSON.parse(paramsMatch[1]!) as Record<string, unknown>;
429
+ } catch {
430
+ // Keep empty params on parse error
431
+ }
432
+ i++; // consume params line
433
+ }
434
+ }
435
+
436
+ events.push({ cmd, params, ts, hash, actor: "agent", session_id: getSessionId() });
437
+ }
438
+ }
439
+ i++;
440
+ }
441
+ return events;
442
+ }
443
+
444
+ /**
445
+ * Resolve a single conflict by picking one side's events.
446
+ * Replays the picked events through the DB helpers, appends them to the event log,
447
+ * and updates or removes CONFLICTS.md.
448
+ *
449
+ * When the last conflict is resolved, non-conflicting events from both sides
450
+ * are also replayed (they were blocked by the all-or-nothing D-04 rule).
451
+ */
452
+ export function resolveConflict(
453
+ basePath: string,
454
+ worktreeBasePath: string,
455
+ entityKey: string, // e.g. "task:T01"
456
+ pick: "main" | "worktree",
457
+ ): void {
458
+ const conflicts = listConflicts(basePath);
459
+ const colonIdx = entityKey.indexOf(":");
460
+ const entityType = entityKey.slice(0, colonIdx);
461
+ const entityId = entityKey.slice(colonIdx + 1);
462
+
463
+ const idx = conflicts.findIndex((c) => c.entityType === entityType && c.entityId === entityId);
464
+ if (idx === -1) throw new Error(`No conflict found for entity ${entityKey}`);
465
+
466
+ const conflict = conflicts[idx]!;
467
+ const eventsToReplay = pick === "main" ? conflict.mainSideEvents : conflict.worktreeSideEvents;
468
+
469
+ // Replay resolved events through the DB (updates DB state)
470
+ openDatabase(join(basePath, ".gsd", "gsd.db"));
471
+ replayEvents(eventsToReplay);
472
+
473
+ // Append resolved events to the event log
474
+ for (const event of eventsToReplay) {
475
+ appendEvent(basePath, { cmd: event.cmd, params: event.params, ts: event.ts, actor: event.actor });
476
+ }
477
+
478
+ // Remove resolved conflict from list
479
+ conflicts.splice(idx, 1);
480
+
481
+ if (conflicts.length === 0) {
482
+ // All conflicts resolved — remove CONFLICTS.md and re-run reconciliation
483
+ // to pick up non-conflicting events that were blocked by D-04 all-or-nothing.
484
+ removeConflictsFile(basePath);
485
+ if (worktreeBasePath) {
486
+ reconcileWorktreeLogs(basePath, worktreeBasePath);
487
+ }
488
+ } else {
489
+ // Re-write CONFLICTS.md with remaining conflicts
490
+ writeConflictsFile(basePath, conflicts, worktreeBasePath);
491
+ }
492
+ }
493
+
494
+ /**
495
+ * Remove CONFLICTS.md — called when all conflicts are resolved.
496
+ * No-op if CONFLICTS.md does not exist.
497
+ */
498
+ export function removeConflictsFile(basePath: string): void {
499
+ const conflictsPath = join(basePath, ".gsd", "CONFLICTS.md");
500
+ if (existsSync(conflictsPath)) {
501
+ unlinkSync(conflictsPath);
502
+ }
503
+ }
@@ -16,8 +16,10 @@
16
16
  */
17
17
 
18
18
  import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync } from "node:fs";
19
+ import { execFileSync } from "node:child_process";
19
20
  import { join, resolve, sep } from "node:path";
20
21
  import { GSDError, GSD_PARSE_ERROR, GSD_STALE_STATE, GSD_LOCK_HELD, GSD_GIT_ERROR, GSD_MERGE_CONFLICT } from "./errors.js";
22
+ import { logWarning } from "./workflow-logger.js";
21
23
  import {
22
24
  nativeBranchDelete,
23
25
  nativeBranchExists,
@@ -135,9 +137,7 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
135
137
  // worktree can be created in its place.
136
138
  const gitFilePath = join(wtPath, ".git");
137
139
  if (!existsSync(gitFilePath)) {
138
- console.error(
139
- `[GSD] Removing stale worktree directory (no .git file): ${wtPath}`,
140
- );
140
+ logWarning("reconcile", `Removing stale worktree directory (no .git file): ${wtPath}`, { worktree: name });
141
141
  rmSync(wtPath, { recursive: true, force: true });
142
142
  } else {
143
143
  throw new GSDError(GSD_STALE_STATE, `Worktree "${name}" already exists at ${wtPath}`);
@@ -321,8 +321,44 @@ export function removeWorktree(
321
321
  return;
322
322
  }
323
323
 
324
- // Remove worktree using the resolved path (force if requested, to handle dirty worktrees)
325
- try { nativeWorktreeRemove(basePath, resolvedWtPath, force); } catch { /* may fail */ }
324
+ // Submodule safety (#2337): detect submodules with uncommitted changes
325
+ // before force-removing the worktree. Force removal destroys all uncommitted
326
+ // state, which is especially destructive for submodule directories.
327
+ let hasSubmoduleChanges = false;
328
+ const gitmodulesPath = join(resolvedWtPath, ".gitmodules");
329
+ if (existsSync(gitmodulesPath)) {
330
+ try {
331
+ const submoduleStatus = execFileSync(
332
+ "git", ["submodule", "status"],
333
+ { cwd: resolvedWtPath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
334
+ ).trim();
335
+ // Lines starting with '+' indicate uncommitted submodule changes
336
+ hasSubmoduleChanges = submoduleStatus.split("\n").some(
337
+ (line: string) => line.startsWith("+") || line.startsWith("-"),
338
+ );
339
+ if (hasSubmoduleChanges) {
340
+ // Stash submodule changes so they are not lost during force removal.
341
+ // The stash is created in the worktree before it's torn down.
342
+ try {
343
+ execFileSync(
344
+ "git", ["stash", "push", "-m", "gsd: auto-stash submodule changes before worktree teardown"],
345
+ { cwd: resolvedWtPath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
346
+ );
347
+ logWarning("reconcile", `Stashed uncommitted submodule changes before worktree teardown`, { worktree: name, path: resolvedWtPath });
348
+ } catch {
349
+ // Stash failed — warn the user that submodule changes may be lost
350
+ logWarning("reconcile", `Submodule changes detected — stash failed, changes may be lost during force removal`, { worktree: name, path: resolvedWtPath });
351
+ }
352
+ }
353
+ } catch {
354
+ // submodule status failed — proceed with normal removal
355
+ }
356
+ }
357
+
358
+ // Remove worktree: try non-force first when submodules have changes,
359
+ // falling back to force only after submodule state has been preserved.
360
+ const useForce = hasSubmoduleChanges ? false : force;
361
+ try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch { /* may fail */ }
326
362
 
327
363
  // If the directory is still there (e.g. locked), try harder with force
328
364
  if (existsSync(resolvedWtPath)) {
@@ -14,9 +14,12 @@
14
14
  */
15
15
 
16
16
  import { existsSync, unlinkSync } from "node:fs";
17
+ import { randomUUID } from "node:crypto";
17
18
  import { join } from "node:path";
18
19
  import type { AutoSession } from "./auto/session.js";
19
20
  import { debugLog } from "./debug-logger.js";
21
+ import { MergeConflictError } from "./git-service.js";
22
+ import { emitJournalEvent } from "./journal.js";
20
23
 
21
24
  // ─── Dependency Interface ──────────────────────────────────────────────────
22
25
 
@@ -154,6 +157,13 @@ export class WorktreeResolver {
154
157
  skipped: true,
155
158
  reason: "isolation-disabled",
156
159
  });
160
+ emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
161
+ ts: new Date().toISOString(),
162
+ flowId: randomUUID(),
163
+ seq: 0,
164
+ eventType: "worktree-skip",
165
+ data: { milestoneId, reason: "isolation-disabled" },
166
+ });
157
167
  return;
158
168
  }
159
169
 
@@ -183,6 +193,13 @@ export class WorktreeResolver {
183
193
  result: "success",
184
194
  wtPath,
185
195
  });
196
+ emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
197
+ ts: new Date().toISOString(),
198
+ flowId: randomUUID(),
199
+ seq: 0,
200
+ eventType: "worktree-enter",
201
+ data: { milestoneId, wtPath, created: !existingPath },
202
+ });
186
203
  ctx.notify(`Entered worktree for ${milestoneId} at ${wtPath}`, "info");
187
204
  } catch (err) {
188
205
  const msg = err instanceof Error ? err.message : String(err);
@@ -192,6 +209,13 @@ export class WorktreeResolver {
192
209
  result: "error",
193
210
  error: msg,
194
211
  });
212
+ emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
213
+ ts: new Date().toISOString(),
214
+ flowId: randomUUID(),
215
+ seq: 0,
216
+ eventType: "worktree-create-failed",
217
+ data: { milestoneId, error: msg, fallback: "project-root" },
218
+ });
195
219
  ctx.notify(
196
220
  `Auto-worktree creation for ${milestoneId} failed: ${msg}. Continuing in project root.`,
197
221
  "warning",
@@ -287,6 +311,13 @@ export class WorktreeResolver {
287
311
  mode,
288
312
  basePath: this.s.basePath,
289
313
  });
314
+ emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
315
+ ts: new Date().toISOString(),
316
+ flowId: randomUUID(),
317
+ seq: 0,
318
+ eventType: "worktree-merge-start",
319
+ data: { milestoneId, mode },
320
+ });
290
321
 
291
322
  if (mode === "none") {
292
323
  debugLog("WorktreeResolver", {
@@ -407,6 +438,13 @@ export class WorktreeResolver {
407
438
  error: msg,
408
439
  fallback: "chdir-to-project-root",
409
440
  });
441
+ emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
442
+ ts: new Date().toISOString(),
443
+ flowId: randomUUID(),
444
+ seq: 0,
445
+ eventType: "worktree-merge-failed",
446
+ data: { milestoneId, error: msg },
447
+ });
410
448
  // Surface a clear, actionable error. The worktree and milestone branch are
411
449
  // intentionally preserved — nothing has been deleted. The user can retry
412
450
  // /gsd dispatch complete-milestone or merge manually once the underlying issue is fixed
@@ -433,6 +471,12 @@ export class WorktreeResolver {
433
471
  /* best-effort */
434
472
  }
435
473
  }
474
+
475
+ // Re-throw MergeConflictError so the auto loop can detect real code
476
+ // conflicts and stop instead of retrying forever (#2330).
477
+ if (err instanceof MergeConflictError) {
478
+ throw err;
479
+ }
436
480
  }
437
481
 
438
482
  // Always restore basePath and rebuild — whether merge succeeded or failed