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,154 @@
1
+ /**
2
+ * GSD Rethink — Conversational project reorganization.
3
+ *
4
+ * Collects a snapshot of all milestones (status, dependencies, slice progress,
5
+ * queue order) and dispatches a prompt that turns Claude into a reorganization
6
+ * assistant. Claude can then reorder, park, unpark, discard, or add milestones
7
+ * through conversation.
8
+ */
9
+
10
+ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
11
+ import { existsSync } from "node:fs";
12
+
13
+ import { isAutoActive } from "./auto.js";
14
+ import { deriveState } from "./state.js";
15
+ import { gsdRoot } from "./paths.js";
16
+ import { findMilestoneIds } from "./milestone-ids.js";
17
+ import { loadQueueOrder, validateQueueOrder } from "./queue-order.js";
18
+ import { isParked, getParkedReason } from "./milestone-actions.js";
19
+ import { getMilestoneSlices, isDbAvailable } from "./gsd-db.js";
20
+ import { buildExistingMilestonesContext } from "./guided-flow-queue.js";
21
+ import { loadPrompt } from "./prompt-loader.js";
22
+
23
+ // ─── Entry Point ──────────────────────────────────────────────────────────────
24
+
25
+ export async function handleRethink(
26
+ _args: string,
27
+ ctx: ExtensionCommandContext,
28
+ pi: ExtensionAPI,
29
+ ): Promise<void> {
30
+ if (isAutoActive()) {
31
+ ctx.ui.notify("Cannot rethink while auto-mode is active. Stop auto-mode first.", "error");
32
+ return;
33
+ }
34
+
35
+ const basePath = process.cwd();
36
+ const root = gsdRoot(basePath);
37
+ if (!existsSync(root)) {
38
+ ctx.ui.notify("No GSD project found. Run /gsd init first.", "warning");
39
+ return;
40
+ }
41
+
42
+ ctx.ui.notify("Building project snapshot for rethink...", "info");
43
+
44
+ const state = await deriveState(basePath);
45
+ const milestoneIds = findMilestoneIds(basePath);
46
+
47
+ if (milestoneIds.length === 0) {
48
+ ctx.ui.notify("No milestones exist yet. Nothing to rethink.", "warning");
49
+ return;
50
+ }
51
+
52
+ const queueOrder = loadQueueOrder(basePath);
53
+ const rethinkData = buildRethinkData(basePath, milestoneIds, state, queueOrder);
54
+ const existingMilestonesContext = await buildExistingMilestonesContext(basePath, milestoneIds, state);
55
+
56
+ const content = loadPrompt("rethink", {
57
+ rethinkData,
58
+ existingMilestonesContext,
59
+ });
60
+
61
+ pi.sendMessage(
62
+ { customType: "gsd-rethink", content, display: false },
63
+ { triggerTurn: true },
64
+ );
65
+ }
66
+
67
+ // ─── Data Builder ─────────────────────────────────────────────────────────────
68
+
69
+ function buildRethinkData(
70
+ basePath: string,
71
+ milestoneIds: string[],
72
+ state: Awaited<ReturnType<typeof deriveState>>,
73
+ queueOrder: string[] | null,
74
+ ): string {
75
+ const lines: string[] = [];
76
+ const dbAvailable = isDbAvailable();
77
+
78
+ // ── Summary stats ───────────────────────────────────────────────────
79
+ const counts = { complete: 0, active: 0, pending: 0, parked: 0 };
80
+ for (const entry of state.registry) {
81
+ if (entry.status in counts) counts[entry.status as keyof typeof counts]++;
82
+ }
83
+
84
+ lines.push("### Summary");
85
+ lines.push(`${counts.complete} complete, ${counts.active} active, ${counts.pending} pending, ${counts.parked} parked — ${milestoneIds.length} total`);
86
+ lines.push(`Queue order source: ${queueOrder ? "explicit QUEUE-ORDER.json" : "default numeric (by ID)"}`);
87
+ if (state.activeMilestone) {
88
+ lines.push(`Active milestone: ${state.activeMilestone}`);
89
+ }
90
+ lines.push("");
91
+
92
+ // ── Milestone table ─────────────────────────────────────────────────
93
+ lines.push("### Execution Order");
94
+ lines.push("");
95
+ lines.push("| # | ID | Title | Status | Dependencies | Slices |");
96
+ lines.push("|---|-----|-------|--------|--------------|--------|");
97
+
98
+ for (let i = 0; i < milestoneIds.length; i++) {
99
+ const mid = milestoneIds[i];
100
+ const entry = state.registry.find(m => m.id === mid);
101
+ const title = entry?.title ?? mid;
102
+ const status = entry?.status ?? "unknown";
103
+ const deps = entry?.dependsOn?.length ? entry.dependsOn.join(", ") : "—";
104
+
105
+ let sliceInfo = "—";
106
+ if (dbAvailable && status !== "complete") {
107
+ const slices = getMilestoneSlices(mid);
108
+ if (slices.length > 0) {
109
+ const done = slices.filter(s => s.status === "complete").length;
110
+ sliceInfo = `${done}/${slices.length} complete`;
111
+ }
112
+ }
113
+
114
+ // Add parked reason if applicable
115
+ let statusDisplay = status;
116
+ if (status === "parked") {
117
+ const reason = getParkedReason(basePath, mid);
118
+ if (reason) statusDisplay = `parked (${reason})`;
119
+ }
120
+
121
+ lines.push(`| ${i + 1} | ${mid} | ${title} | ${statusDisplay} | ${deps} | ${sliceInfo} |`);
122
+ }
123
+
124
+ // ── Dependency validation ───────────────────────────────────────────
125
+ const pendingIds = milestoneIds.filter(mid => {
126
+ const entry = state.registry.find(m => m.id === mid);
127
+ return entry?.status !== "complete";
128
+ });
129
+
130
+ const completedIds = new Set(
131
+ state.registry.filter(m => m.status === "complete").map(m => m.id),
132
+ );
133
+
134
+ const depsMap = new Map<string, string[]>();
135
+ for (const entry of state.registry) {
136
+ if (entry.dependsOn?.length) {
137
+ depsMap.set(entry.id, entry.dependsOn);
138
+ }
139
+ }
140
+
141
+ if (pendingIds.length > 0 && depsMap.size > 0) {
142
+ const validation = validateQueueOrder(pendingIds, depsMap, completedIds);
143
+
144
+ if (validation.violations.length > 0) {
145
+ lines.push("");
146
+ lines.push("### Dependency Issues");
147
+ for (const v of validation.violations) {
148
+ lines.push(`- **${v.type}**: ${v.message}`);
149
+ }
150
+ }
151
+ }
152
+
153
+ return lines.join("\n");
154
+ }
@@ -32,7 +32,6 @@ export interface SessionLockData {
32
32
  unitType: string;
33
33
  unitId: string;
34
34
  unitStartedAt: string;
35
- completedUnits: number;
36
35
  sessionFile?: string;
37
36
  }
38
37
 
@@ -205,7 +204,6 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
205
204
  unitType: "starting",
206
205
  unitId: "bootstrap",
207
206
  unitStartedAt: new Date().toISOString(),
208
- completedUnits: 0,
209
207
  };
210
208
 
211
209
  let lockfile: typeof import("proper-lockfile");
@@ -379,7 +377,6 @@ export function updateSessionLock(
379
377
  basePath: string,
380
378
  unitType: string,
381
379
  unitId: string,
382
- completedUnits: number,
383
380
  sessionFile?: string,
384
381
  ): void {
385
382
  if (_lockedPath !== basePath && _lockedPath !== null) return;
@@ -392,7 +389,6 @@ export function updateSessionLock(
392
389
  unitType,
393
390
  unitId,
394
391
  unitStartedAt: new Date().toISOString(),
395
- completedUnits,
396
392
  sessionFile,
397
393
  };
398
394
  atomicWriteSync(lp, JSON.stringify(data, null, 2));
@@ -48,6 +48,7 @@ import {
48
48
  getSliceTasks,
49
49
  getReplanHistory,
50
50
  getSlice,
51
+ insertMilestone,
51
52
  type MilestoneRow,
52
53
  type SliceRow,
53
54
  type TaskRow,
@@ -117,6 +118,11 @@ interface StateCache {
117
118
  const CACHE_TTL_MS = 100;
118
119
  let _stateCache: StateCache | null = null;
119
120
 
121
+ // ── Telemetry counters for derive-path observability ────────────────────────
122
+ let _telemetry = { dbDeriveCount: 0, markdownDeriveCount: 0 };
123
+ export function getDeriveTelemetry() { return { ..._telemetry }; }
124
+ export function resetDeriveTelemetry() { _telemetry = { dbDeriveCount: 0, markdownDeriveCount: 0 }; }
125
+
120
126
  /**
121
127
  * Invalidate the deriveState() cache. Call this whenever planning files on disk
122
128
  * may have changed (unit completion, merges, file writes).
@@ -203,12 +209,15 @@ export async function deriveState(basePath: string): Promise<GSDState> {
203
209
  const stopDbTimer = debugTime("derive-state-db");
204
210
  result = await deriveStateFromDb(basePath);
205
211
  stopDbTimer({ phase: result.phase, milestone: result.activeMilestone?.id });
212
+ _telemetry.dbDeriveCount++;
206
213
  } else {
207
214
  // DB open but empty hierarchy tables — pre-migration project, use filesystem
208
215
  result = await _deriveStateImpl(basePath);
216
+ _telemetry.markdownDeriveCount++;
209
217
  }
210
218
  } else {
211
219
  result = await _deriveStateImpl(basePath);
220
+ _telemetry.markdownDeriveCount++;
212
221
  }
213
222
 
214
223
  stopTimer({ phase: result.phase, milestone: result.activeMilestone?.id });
@@ -257,7 +266,46 @@ function isStatusDone(status: string): boolean {
257
266
  export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
258
267
  const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
259
268
 
260
- const allMilestones = getAllMilestones();
269
+ let allMilestones = getAllMilestones();
270
+
271
+ // Incremental disk→DB sync: milestone directories created outside the DB
272
+ // write path (via /gsd queue, manual mkdir, or complete-milestone writing the
273
+ // next CONTEXT.md) are never inserted by the initial migration guard in
274
+ // auto-start.ts because that guard only runs when gsd.db doesn't exist yet.
275
+ // Reconcile here so deriveStateFromDb never silently misses queued milestones.
276
+ // insertMilestone uses INSERT OR IGNORE, so this is safe to call every time.
277
+ const dbIdSet = new Set(allMilestones.map(m => m.id));
278
+ const diskIds = findMilestoneIds(basePath);
279
+ let synced = false;
280
+ for (const diskId of diskIds) {
281
+ if (!dbIdSet.has(diskId) && !isGhostMilestone(basePath, diskId)) {
282
+ insertMilestone({ id: diskId, status: 'active' });
283
+ synced = true;
284
+ }
285
+ }
286
+ if (synced) allMilestones = getAllMilestones();
287
+
288
+ // Reconcile: discover milestones that exist on disk but are missing from
289
+ // the DB. This happens when milestones were created before the DB migration
290
+ // or were manually added to the filesystem. Without this, disk-only
291
+ // milestones are invisible after migration (#2416).
292
+ const dbMilestoneIds = new Set(allMilestones.map(m => m.id));
293
+ const diskMilestoneIds = findMilestoneIds(basePath);
294
+ for (const diskId of diskMilestoneIds) {
295
+ if (!dbMilestoneIds.has(diskId)) {
296
+ // Synthesize a minimal MilestoneRow for the disk-only milestone.
297
+ // Title and status will be resolved from disk files in the loop below.
298
+ allMilestones.push({
299
+ id: diskId,
300
+ title: diskId,
301
+ status: 'active',
302
+ depends_on: [] as string[],
303
+ created_at: new Date().toISOString(),
304
+ } as MilestoneRow);
305
+ }
306
+ }
307
+ // Re-sort so milestones are in canonical order after injection
308
+ allMilestones.sort((a, b) => milestoneIdSort(a.id, b.id));
261
309
 
262
310
  // Parallel worker isolation: when locked, filter to just the locked milestone
263
311
  const milestoneLock = process.env.GSD_MILESTONE_LOCK;
@@ -0,0 +1,94 @@
1
+ // GSD Extension — Advisory Sync Lock
2
+ // Prevents concurrent worktree syncs from colliding via a simple file lock.
3
+ // Stale locks (mtime > 60s) are auto-overridden. Lock acquisition waits up
4
+ // to 5 seconds then skips non-fatally.
5
+
6
+ import { existsSync, statSync, unlinkSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { atomicWriteSync } from "./atomic-write.js";
9
+
10
+ const STALE_THRESHOLD_MS = 60_000; // 60 seconds
11
+ const DEFAULT_TIMEOUT_MS = 5_000; // 5 seconds
12
+ const SPIN_INTERVAL_MS = 100; // 100ms polling interval
13
+
14
+ // SharedArrayBuffer for synchronous sleep via Atomics.wait
15
+ const SLEEP_BUFFER = new SharedArrayBuffer(4);
16
+ const SLEEP_VIEW = new Int32Array(SLEEP_BUFFER);
17
+
18
+ function lockFilePath(basePath: string): string {
19
+ return join(basePath, ".gsd", "sync.lock");
20
+ }
21
+
22
+ function sleepSync(ms: number): void {
23
+ Atomics.wait(SLEEP_VIEW, 0, 0, ms);
24
+ }
25
+
26
+ /**
27
+ * Acquire an advisory sync lock for the given basePath.
28
+ * Returns { acquired: true } on success, { acquired: false } after timeout.
29
+ *
30
+ * - Creates lock file at {basePath}/.gsd/sync.lock with JSON { pid, acquired_at }
31
+ * - If lock exists and mtime > 60s (stale), overrides it
32
+ * - If lock exists and not stale, spins up to timeoutMs before giving up
33
+ */
34
+ export function acquireSyncLock(
35
+ basePath: string,
36
+ timeoutMs: number = DEFAULT_TIMEOUT_MS,
37
+ ): { acquired: boolean } {
38
+ const lp = lockFilePath(basePath);
39
+ const deadline = Date.now() + timeoutMs;
40
+
41
+ while (true) {
42
+ // Check if lock file exists
43
+ if (existsSync(lp)) {
44
+ // Check staleness
45
+ try {
46
+ const stat = statSync(lp);
47
+ const age = Date.now() - stat.mtimeMs;
48
+ if (age > STALE_THRESHOLD_MS) {
49
+ // Stale lock — override it
50
+ try { unlinkSync(lp); } catch { /* race: already removed */ }
51
+ } else {
52
+ // Lock is held and not stale — wait or give up
53
+ if (Date.now() >= deadline) {
54
+ return { acquired: false };
55
+ }
56
+ sleepSync(SPIN_INTERVAL_MS);
57
+ continue;
58
+ }
59
+ } catch {
60
+ // stat failed (file removed between exists check and stat) — try to acquire
61
+ }
62
+ }
63
+
64
+ // Lock file does not exist (or was just removed) — try to write it
65
+ try {
66
+ const lockData = {
67
+ pid: process.pid,
68
+ acquired_at: new Date().toISOString(),
69
+ };
70
+ atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
71
+ return { acquired: true };
72
+ } catch {
73
+ // Write failed (race condition with another process) — retry or give up
74
+ if (Date.now() >= deadline) {
75
+ return { acquired: false };
76
+ }
77
+ sleepSync(SPIN_INTERVAL_MS);
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Release the advisory sync lock. No-op if lock file does not exist.
84
+ */
85
+ export function releaseSyncLock(basePath: string): void {
86
+ const lp = lockFilePath(basePath);
87
+ try {
88
+ if (existsSync(lp)) {
89
+ unlinkSync(lp);
90
+ }
91
+ } catch {
92
+ // Non-fatal — lock may have been released by another process
93
+ }
94
+ }
@@ -27,7 +27,7 @@ test("writeLock creates auto.lock with correct structure", () => {
27
27
  const dir = mkdtempSync(join(tmpdir(), "gsd-lock-test-"));
28
28
  mkdirSync(join(dir, ".gsd"), { recursive: true });
29
29
 
30
- writeLock(dir, "starting", "M001", 0);
30
+ writeLock(dir, "starting", "M001");
31
31
 
32
32
  const lockPath = join(dir, ".gsd", "auto.lock");
33
33
  assert.ok(existsSync(lockPath), "auto.lock should exist after writeLock");
@@ -36,7 +36,6 @@ test("writeLock creates auto.lock with correct structure", () => {
36
36
  assert.equal(data.pid, process.pid, "lock should contain current PID");
37
37
  assert.equal(data.unitType, "starting", "lock should contain unit type");
38
38
  assert.equal(data.unitId, "M001", "lock should contain unit ID");
39
- assert.equal(data.completedUnits, 0, "lock should show 0 completed units");
40
39
  assert.ok(data.startedAt, "lock should have startedAt timestamp");
41
40
 
42
41
  rmSync(dir, { recursive: true, force: true });
@@ -46,13 +45,12 @@ test("writeLock updates existing lock with new unit info", () => {
46
45
  const dir = mkdtempSync(join(tmpdir(), "gsd-lock-test-"));
47
46
  mkdirSync(join(dir, ".gsd"), { recursive: true });
48
47
 
49
- writeLock(dir, "starting", "M001", 0);
50
- writeLock(dir, "execute-task", "M001/S01/T01", 2, "/tmp/session.jsonl");
48
+ writeLock(dir, "starting", "M001");
49
+ writeLock(dir, "execute-task", "M001/S01/T01", "/tmp/session.jsonl");
51
50
 
52
51
  const data = JSON.parse(readFileSync(join(dir, ".gsd", "auto.lock"), "utf-8"));
53
52
  assert.equal(data.unitType, "execute-task", "lock should be updated to new unit type");
54
53
  assert.equal(data.unitId, "M001/S01/T01", "lock should be updated to new unit ID");
55
- assert.equal(data.completedUnits, 2, "completed count should be updated");
56
54
  assert.equal(data.sessionFile, "/tmp/session.jsonl", "session file should be recorded");
57
55
 
58
56
  rmSync(dir, { recursive: true, force: true });
@@ -74,13 +72,12 @@ test("readCrashLock returns lock data when file exists", () => {
74
72
  const dir = mkdtempSync(join(tmpdir(), "gsd-lock-test-"));
75
73
  mkdirSync(join(dir, ".gsd"), { recursive: true });
76
74
 
77
- writeLock(dir, "plan-milestone", "M002", 5);
75
+ writeLock(dir, "plan-milestone", "M002");
78
76
  const lock = readCrashLock(dir);
79
77
 
80
78
  assert.ok(lock, "should return lock data");
81
79
  assert.equal(lock!.unitType, "plan-milestone");
82
80
  assert.equal(lock!.unitId, "M002");
83
- assert.equal(lock!.completedUnits, 5);
84
81
 
85
82
  rmSync(dir, { recursive: true, force: true });
86
83
  });
@@ -91,7 +88,7 @@ test("clearLock removes the lock file", () => {
91
88
  const dir = mkdtempSync(join(tmpdir(), "gsd-lock-test-"));
92
89
  mkdirSync(join(dir, ".gsd"), { recursive: true });
93
90
 
94
- writeLock(dir, "starting", "M001", 0);
91
+ writeLock(dir, "starting", "M001");
95
92
  assert.ok(existsSync(join(dir, ".gsd", "auto.lock")), "lock should exist before clear");
96
93
 
97
94
  clearLock(dir);
@@ -139,7 +136,6 @@ test("isLockProcessAlive returns false for dead PID", () => {
139
136
  unitType: "execute-task",
140
137
  unitId: "M001/S01/T01",
141
138
  unitStartedAt: new Date().toISOString(),
142
- completedUnits: 0,
143
139
  };
144
140
  assert.equal(isLockProcessAlive(lock), false, "dead PID should return false");
145
141
  });
@@ -151,7 +147,6 @@ test("isLockProcessAlive returns false for own PID (recycled)", () => {
151
147
  unitType: "execute-task",
152
148
  unitId: "M001/S01/T01",
153
149
  unitStartedAt: new Date().toISOString(),
154
- completedUnits: 0,
155
150
  };
156
151
  assert.equal(isLockProcessAlive(lock), false, "own PID should return false (recycled)");
157
152
  });
@@ -163,7 +158,6 @@ test("isLockProcessAlive returns false for invalid PID", () => {
163
158
  unitType: "execute-task",
164
159
  unitId: "M001/S01/T01",
165
160
  unitStartedAt: new Date().toISOString(),
166
- completedUnits: 0,
167
161
  };
168
162
  assert.equal(isLockProcessAlive(lock), false, "negative PID should return false");
169
163
  });
@@ -183,7 +177,6 @@ test("lock file enables cross-process auto-mode detection", () => {
183
177
  unitType: "execute-task",
184
178
  unitId: "M001/S01/T02",
185
179
  unitStartedAt: new Date().toISOString(),
186
- completedUnits: 3,
187
180
  };
188
181
  writeFileSync(join(dir, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2));
189
182
 
@@ -209,7 +202,6 @@ test("stale lock from dead process is detected as not alive", () => {
209
202
  unitType: "plan-slice",
210
203
  unitId: "M001/S02",
211
204
  unitStartedAt: "2026-03-01T00:05:00Z",
212
- completedUnits: 1,
213
205
  };
214
206
  writeFileSync(join(dir, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2));
215
207
 
@@ -367,9 +367,6 @@ function makeMockDeps(
367
367
  getPriorSliceCompletionBlocker: () => null,
368
368
  getMainBranch: () => "main",
369
369
  closeoutUnit: async () => {},
370
- verifyExpectedArtifact: () => true,
371
- clearUnitRuntimeRecord: () => {},
372
- writeUnitRuntimeRecord: () => {},
373
370
  recordOutcome: () => {},
374
371
  writeLock: () => {},
375
372
  captureAvailableSkills: () => {},
@@ -713,10 +710,10 @@ test("crash lock records session file from AFTER newSession, not before (#1710)"
713
710
  prompt: "do the thing",
714
711
  };
715
712
  },
716
- writeLock: (_base: string, _ut: string, _uid: string, _count: number, sessionFile?: string) => {
713
+ writeLock: (_base: string, _ut: string, _uid: string, sessionFile?: string) => {
717
714
  writeLockCalls.push({ sessionFile });
718
715
  },
719
- updateSessionLock: (_base: string, _ut: string, _uid: string, _count: number, sessionFile?: string) => {
716
+ updateSessionLock: (_base: string, _ut: string, _uid: string, sessionFile?: string) => {
720
717
  updateSessionLockCalls.push({ sessionFile });
721
718
  },
722
719
  getSessionFile: (ctxArg: any) => {
@@ -1104,7 +1101,7 @@ test("auto.ts startAuto calls autoLoop (not dispatchNextUnit as first dispatch)"
1104
1101
  );
1105
1102
  });
1106
1103
 
1107
- test("startAuto calls selfHealRuntimeRecords before autoLoop (#1727)", () => {
1104
+ test("startAuto calls selfHealRuntimeRecords before autoLoop (#1727)", { skip: "selfHealRuntimeRecords moved to crash-recovery pipeline in v3" }, () => {
1108
1105
  const src = readFileSync(
1109
1106
  resolve(import.meta.dirname, "..", "auto.ts"),
1110
1107
  "utf-8",
@@ -1990,7 +1987,6 @@ test("autoLoop does NOT reject non-execute-task units with 0 tool calls (#1833)"
1990
1987
  });
1991
1988
  },
1992
1989
  getLedger: () => mockLedger,
1993
- verifyExpectedArtifact: () => true,
1994
1990
  postUnitPostVerification: async () => {
1995
1991
  deps.callLog.push("postUnitPostVerification");
1996
1992
  s.active = false;
@@ -2014,10 +2010,10 @@ test("autoLoop does NOT reject non-execute-task units with 0 tool calls (#1833)"
2014
2010
  "should NOT flag non-execute-task units with 0 tool calls",
2015
2011
  );
2016
2012
 
2017
- // The unit should have been added to completedUnits normally
2013
+ // Verify the loop ran to completion (postUnitPostVerification was called)
2018
2014
  assert.ok(
2019
- s.completedUnits.length >= 1,
2020
- "complete-slice with 0 tool calls should still be marked as completed",
2015
+ deps.callLog.includes("postUnitPostVerification"),
2016
+ "complete-slice with 0 tool calls should still complete the post-unit pipeline",
2021
2017
  );
2022
2018
  });
2023
2019
 
@@ -0,0 +1,88 @@
1
+ /**
2
+ * auto-pr-bugs.test.ts — Regression tests for #2302.
3
+ *
4
+ * Three interacting bugs prevented auto_pr from ever creating a PR:
5
+ * 1. auto_pr was gated on `pushed` (which requires auto_push)
6
+ * 2. Milestone branch was not pushed to remote before PR creation
7
+ * 3. createDraftPR in git-service.ts lacked --head/--base parameters
8
+ */
9
+
10
+ import test from "node:test";
11
+ import assert from "node:assert/strict";
12
+ import { readFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+
15
+ // ─── Bug 1: auto_pr should not depend on auto_push / pushed flag ────────────
16
+
17
+ const autoWorktreeSrcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
18
+ const autoWorktreeSrc = readFileSync(autoWorktreeSrcPath, "utf-8");
19
+
20
+ test("#2302 bug 1: auto_pr condition should not require pushed flag", () => {
21
+ // Find the auto_pr block in mergeMilestoneToMain
22
+ const autoPrIdx = autoWorktreeSrc.indexOf("auto_pr");
23
+ assert.ok(autoPrIdx !== -1, "auto_pr reference exists in auto-worktree.ts");
24
+
25
+ // Get context around the auto_pr check
26
+ const lineStart = autoWorktreeSrc.lastIndexOf("\n", autoPrIdx) + 1;
27
+ const lineEnd = autoWorktreeSrc.indexOf("\n", autoPrIdx);
28
+ const autoPrLine = autoWorktreeSrc.slice(lineStart, lineEnd);
29
+
30
+ // The condition should NOT include `&& pushed`
31
+ assert.ok(
32
+ !autoPrLine.includes("&& pushed"),
33
+ "auto_pr condition should not be gated on pushed flag (auto_push dependency)",
34
+ );
35
+ });
36
+
37
+ // ─── Bug 2: phases.ts should not duplicate PR creation ──────────────────────
38
+
39
+ const phasesSrcPath = join(import.meta.dirname, "..", "auto", "phases.ts");
40
+ const phasesSrc = readFileSync(phasesSrcPath, "utf-8");
41
+
42
+ test("#2302 bug 2: phases.ts should not call createDraftPR (handled by mergeMilestoneToMain)", () => {
43
+ // After fix, phases.ts should not import or call createDraftPR because
44
+ // PR creation is handled inside mergeMilestoneToMain in auto-worktree.ts
45
+ const createDraftPRCalls = phasesSrc.match(/createDraftPR\(/g) || [];
46
+
47
+ assert.equal(
48
+ createDraftPRCalls.length,
49
+ 0,
50
+ "phases.ts should not call createDraftPR — it's handled by mergeMilestoneToMain",
51
+ );
52
+ });
53
+
54
+ // ─── Bug 3: createDraftPR should accept head and base branch parameters ─────
55
+
56
+ const gitServiceSrcPath = join(import.meta.dirname, "..", "git-service.ts");
57
+ const gitServiceSrc = readFileSync(gitServiceSrcPath, "utf-8");
58
+
59
+ test("#2302 bug 3: createDraftPR should accept head and base branch parameters", () => {
60
+ // Find the createDraftPR function signature
61
+ const fnIdx = gitServiceSrc.indexOf("function createDraftPR");
62
+ assert.ok(fnIdx !== -1, "createDraftPR function exists");
63
+
64
+ // Get the function signature (up to the closing paren)
65
+ const sigEnd = gitServiceSrc.indexOf(")", fnIdx);
66
+ const signature = gitServiceSrc.slice(fnIdx, sigEnd);
67
+
68
+ // Should have head and base parameters
69
+ assert.ok(
70
+ signature.includes("head") || signature.includes("branch"),
71
+ "createDraftPR should accept a head/branch parameter",
72
+ );
73
+ });
74
+
75
+ test("#2302 bug 3: createDraftPR should pass --head and --base to gh pr create", () => {
76
+ const fnIdx = gitServiceSrc.indexOf("function createDraftPR");
77
+ const fnEnd = gitServiceSrc.indexOf("\n}", fnIdx);
78
+ const fnBody = gitServiceSrc.slice(fnIdx, fnEnd);
79
+
80
+ assert.ok(
81
+ fnBody.includes("--head"),
82
+ "createDraftPR should pass --head to gh pr create",
83
+ );
84
+ assert.ok(
85
+ fnBody.includes("--base"),
86
+ "createDraftPR should pass --base to gh pr create",
87
+ );
88
+ });