gsd-pi 2.44.0 → 2.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (402) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  3. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  4. package/dist/resources/extensions/gsd/auto/phases.js +37 -36
  5. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  6. package/dist/resources/extensions/gsd/auto-start.js +31 -2
  7. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  8. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  9. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  10. package/dist/resources/extensions/gsd/auto.js +30 -3
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  12. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  13. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  14. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  16. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  17. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  18. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  19. package/dist/resources/extensions/gsd/doctor.js +8 -0
  20. package/dist/resources/extensions/gsd/git-service.js +8 -3
  21. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  22. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  23. package/dist/resources/extensions/gsd/preferences.js +9 -1
  24. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  25. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  26. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  27. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  28. package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
  29. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  30. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  31. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  32. package/dist/resources/extensions/gsd/rethink.js +115 -0
  33. package/dist/resources/extensions/gsd/state.js +41 -3
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  36. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  37. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +32 -2
  39. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  40. package/dist/resources/extensions/mcp-client/index.js +14 -0
  41. package/dist/web/standalone/.next/BUILD_ID +1 -1
  42. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  43. package/dist/web/standalone/.next/build-manifest.json +3 -3
  44. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  45. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  46. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  48. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found/page.js +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  65. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/index.html +1 -1
  106. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  107. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  108. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  109. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  111. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  112. package/dist/web/standalone/.next/server/app/page.js +1 -1
  113. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  115. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  121. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  122. package/dist/web/standalone/.next/static/chunks/app/{page-7e9530a7122506c5.js → page-12dd5ece0df4badc.js} +1 -1
  123. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  124. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  125. package/package.json +1 -1
  126. package/packages/native/dist/stream-process/index.js +2 -2
  127. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  128. package/packages/native/src/stream-process/index.ts +2 -2
  129. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  130. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  132. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  134. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  136. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  138. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  140. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  141. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  142. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  144. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  146. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  148. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  150. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  152. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  154. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  156. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  158. package/packages/pi-coding-agent/dist/main.js +17 -0
  159. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  165. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  168. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  169. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  170. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  182. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  185. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  190. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  191. package/packages/pi-coding-agent/package.json +1 -1
  192. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  193. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  194. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  195. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  196. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  197. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  198. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  199. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  200. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  201. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  202. package/packages/pi-coding-agent/src/main.ts +19 -0
  203. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  204. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  205. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  206. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  207. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  208. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  210. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  211. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  212. package/pkg/package.json +1 -1
  213. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  214. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  215. package/src/resources/extensions/gsd/auto/phases.ts +46 -48
  216. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  217. package/src/resources/extensions/gsd/auto-start.ts +39 -2
  218. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  219. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  220. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  221. package/src/resources/extensions/gsd/auto.ts +37 -3
  222. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  223. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  224. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  225. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  226. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  227. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  228. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  229. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  230. package/src/resources/extensions/gsd/doctor.ts +7 -1
  231. package/src/resources/extensions/gsd/git-service.ts +6 -2
  232. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  233. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  234. package/src/resources/extensions/gsd/preferences.ts +11 -1
  235. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  236. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  237. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  238. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  239. package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
  240. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  241. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  242. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  243. package/src/resources/extensions/gsd/rethink.ts +154 -0
  244. package/src/resources/extensions/gsd/state.ts +41 -1
  245. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  246. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  247. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  248. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  249. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  250. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  251. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  252. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  253. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  254. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  255. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  256. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  257. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  258. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  259. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  260. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  261. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  262. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  263. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  264. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  265. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  266. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  267. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  268. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  269. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  270. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  271. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  272. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  273. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  274. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  275. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  276. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  277. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  278. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  279. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  280. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  281. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  282. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  283. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  284. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  285. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  286. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  287. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  288. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  289. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  290. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  291. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  292. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  293. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  294. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  295. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  296. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  297. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  298. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  299. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  300. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  301. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  302. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  303. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  304. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  305. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  306. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  307. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  308. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  309. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  310. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  311. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  312. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  313. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  314. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  315. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  316. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  317. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  318. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  319. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  320. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  321. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  322. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  323. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  324. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  325. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  326. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  327. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  328. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  329. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  330. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  331. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  332. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  333. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  334. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  335. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  336. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  337. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  338. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  339. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  340. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  341. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  342. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  343. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  344. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  345. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  346. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  347. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  348. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  349. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  350. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  351. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  352. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  353. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  354. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  355. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  356. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  357. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  358. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  359. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  360. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  361. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  362. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  363. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  364. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  365. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  366. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  367. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  368. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  369. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  370. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  371. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  372. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  373. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  374. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  375. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  376. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  377. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  378. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  379. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  380. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  381. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  382. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  383. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  384. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  385. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  386. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  387. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  388. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  389. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  390. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  391. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  392. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  393. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  394. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  395. package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
  396. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  397. package/src/resources/extensions/mcp-client/index.ts +20 -0
  398. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  399. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  400. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  401. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → wUzEX1U3CmFcMry2SUDJn}/_buildManifest.js +0 -0
  402. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → wUzEX1U3CmFcMry2SUDJn}/_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
+ }
@@ -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,
@@ -257,7 +258,46 @@ function isStatusDone(status: string): boolean {
257
258
  export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
258
259
  const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
259
260
 
260
- const allMilestones = getAllMilestones();
261
+ let allMilestones = getAllMilestones();
262
+
263
+ // Incremental disk→DB sync: milestone directories created outside the DB
264
+ // write path (via /gsd queue, manual mkdir, or complete-milestone writing the
265
+ // next CONTEXT.md) are never inserted by the initial migration guard in
266
+ // auto-start.ts because that guard only runs when gsd.db doesn't exist yet.
267
+ // Reconcile here so deriveStateFromDb never silently misses queued milestones.
268
+ // insertMilestone uses INSERT OR IGNORE, so this is safe to call every time.
269
+ const dbIdSet = new Set(allMilestones.map(m => m.id));
270
+ const diskIds = findMilestoneIds(basePath);
271
+ let synced = false;
272
+ for (const diskId of diskIds) {
273
+ if (!dbIdSet.has(diskId) && !isGhostMilestone(basePath, diskId)) {
274
+ insertMilestone({ id: diskId, status: 'active' });
275
+ synced = true;
276
+ }
277
+ }
278
+ if (synced) allMilestones = getAllMilestones();
279
+
280
+ // Reconcile: discover milestones that exist on disk but are missing from
281
+ // the DB. This happens when milestones were created before the DB migration
282
+ // or were manually added to the filesystem. Without this, disk-only
283
+ // milestones are invisible after migration (#2416).
284
+ const dbMilestoneIds = new Set(allMilestones.map(m => m.id));
285
+ const diskMilestoneIds = findMilestoneIds(basePath);
286
+ for (const diskId of diskMilestoneIds) {
287
+ if (!dbMilestoneIds.has(diskId)) {
288
+ // Synthesize a minimal MilestoneRow for the disk-only milestone.
289
+ // Title and status will be resolved from disk files in the loop below.
290
+ allMilestones.push({
291
+ id: diskId,
292
+ title: diskId,
293
+ status: 'active',
294
+ depends_on: [] as string[],
295
+ created_at: new Date().toISOString(),
296
+ } as MilestoneRow);
297
+ }
298
+ }
299
+ // Re-sort so milestones are in canonical order after injection
300
+ allMilestones.sort((a, b) => milestoneIdSort(a.id, b.id));
261
301
 
262
302
  // Parallel worker isolation: when locked, filter to just the locked milestone
263
303
  const milestoneLock = process.env.GSD_MILESTONE_LOCK;
@@ -130,119 +130,119 @@ test("auto-loop 'all milestones complete' path merges before stopping (#962)", (
130
130
 
131
131
  // ─── Integration: single milestone completes → merged to main ────────────────
132
132
 
133
- test("single milestone worktree is merged to main when all complete (#962)", () => {
133
+ test("single milestone worktree is merged to main when all complete (#962)", (t) => {
134
134
  const savedCwd = process.cwd();
135
135
  let tempDir = "";
136
136
 
137
- try {
138
- tempDir = createTempRepo();
139
-
140
- // Set up a single milestone
141
- createMilestoneArtifacts(tempDir, "M001");
142
- run("git add .", tempDir);
143
- run('git commit -m "add milestone"', tempDir);
144
-
145
- // Create worktree and simulate work
146
- const wt = createAutoWorktree(tempDir, "M001");
147
- assert.ok(isInAutoWorktree(tempDir), "should be in auto-worktree");
148
-
149
- writeFileSync(join(wt, "feature.ts"), "export const feature = true;\n");
150
- run("git add .", wt);
151
- run('git commit -m "feat(M001): add feature"', wt);
152
-
153
- // Simulate the fix: merge before stopping (what the "all complete" path now does)
154
- const roadmapPath = join(
155
- tempDir,
156
- ".gsd",
157
- "milestones",
158
- "M001",
159
- "M001-ROADMAP.md",
160
- );
161
- const roadmapContent = readFileSync(roadmapPath, "utf-8");
162
- const mergeResult = mergeMilestoneToMain(tempDir, "M001", roadmapContent);
163
-
164
- // Verify work is on main
165
- assert.ok(
166
- existsSync(join(tempDir, "feature.ts")),
167
- "feature.ts should be on main after merge",
168
- );
169
- assert.equal(process.cwd(), tempDir, "cwd restored to project root");
170
- assert.ok(!isInAutoWorktree(tempDir), "no longer in auto-worktree");
171
- assert.equal(getAutoWorktreeOriginalBase(), null, "originalBase cleared");
172
-
173
- // Verify milestone branch was cleaned up
174
- const branches = run("git branch", tempDir);
175
- assert.ok(
176
- !branches.includes("milestone/M001"),
177
- "milestone branch should be deleted",
178
- );
179
-
180
- // Verify squash commit on main
181
- const log = run("git log --oneline -3", tempDir);
182
- assert.ok(
183
- log.includes("M001"),
184
- "squash commit on main should reference M001",
185
- );
186
-
187
- assert.ok(mergeResult.commitMessage.length > 0, "commit message returned");
188
- } finally {
137
+ t.after(() => {
189
138
  process.chdir(savedCwd);
190
139
  if (tempDir && existsSync(tempDir)) {
191
- rmSync(tempDir, { recursive: true, force: true });
140
+ rmSync(tempDir, { recursive: true, force: true });
192
141
  }
193
- }
142
+ });
143
+
144
+ tempDir = createTempRepo();
145
+
146
+ // Set up a single milestone
147
+ createMilestoneArtifacts(tempDir, "M001");
148
+ run("git add .", tempDir);
149
+ run('git commit -m "add milestone"', tempDir);
150
+
151
+ // Create worktree and simulate work
152
+ const wt = createAutoWorktree(tempDir, "M001");
153
+ assert.ok(isInAutoWorktree(tempDir), "should be in auto-worktree");
154
+
155
+ writeFileSync(join(wt, "feature.ts"), "export const feature = true;\n");
156
+ run("git add .", wt);
157
+ run('git commit -m "feat(M001): add feature"', wt);
158
+
159
+ // Simulate the fix: merge before stopping (what the "all complete" path now does)
160
+ const roadmapPath = join(
161
+ tempDir,
162
+ ".gsd",
163
+ "milestones",
164
+ "M001",
165
+ "M001-ROADMAP.md",
166
+ );
167
+ const roadmapContent = readFileSync(roadmapPath, "utf-8");
168
+ const mergeResult = mergeMilestoneToMain(tempDir, "M001", roadmapContent);
169
+
170
+ // Verify work is on main
171
+ assert.ok(
172
+ existsSync(join(tempDir, "feature.ts")),
173
+ "feature.ts should be on main after merge",
174
+ );
175
+ assert.equal(process.cwd(), tempDir, "cwd restored to project root");
176
+ assert.ok(!isInAutoWorktree(tempDir), "no longer in auto-worktree");
177
+ assert.equal(getAutoWorktreeOriginalBase(), null, "originalBase cleared");
178
+
179
+ // Verify milestone branch was cleaned up
180
+ const branches = run("git branch", tempDir);
181
+ assert.ok(
182
+ !branches.includes("milestone/M001"),
183
+ "milestone branch should be deleted",
184
+ );
185
+
186
+ // Verify squash commit on main
187
+ const log = run("git log --oneline -3", tempDir);
188
+ assert.ok(
189
+ log.includes("M001"),
190
+ "squash commit on main should reference M001",
191
+ );
192
+
193
+ assert.ok(mergeResult.commitMessage.length > 0, "commit message returned");
194
194
  });
195
195
 
196
196
  // ─── Integration: last of multiple milestones completes → merged ─────────────
197
197
 
198
- test("last milestone worktree is merged when it's the final one (#962)", () => {
198
+ test("last milestone worktree is merged when it's the final one (#962)", (t) => {
199
199
  const savedCwd = process.cwd();
200
200
  let tempDir = "";
201
201
 
202
- try {
203
- tempDir = createTempRepo();
204
-
205
- // Set up two milestones
206
- createMilestoneArtifacts(tempDir, "M001");
207
- createMilestoneArtifacts(tempDir, "M002");
208
- run("git add .", tempDir);
209
- run('git commit -m "add milestones"', tempDir);
210
-
211
- // Complete M001 first (merge it)
212
- const wt1 = createAutoWorktree(tempDir, "M001");
213
- writeFileSync(join(wt1, "m001-work.ts"), "export const m001 = true;\n");
214
- run("git add .", wt1);
215
- run('git commit -m "feat(M001): m001 work"', wt1);
216
- const roadmap1 = readFileSync(
217
- join(tempDir, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
218
- "utf-8",
219
- );
220
- mergeMilestoneToMain(tempDir, "M001", roadmap1);
221
-
222
- // Now complete M002 (the LAST milestone — this is the #962 scenario)
223
- const wt2 = createAutoWorktree(tempDir, "M002");
224
- writeFileSync(join(wt2, "m002-work.ts"), "export const m002 = true;\n");
225
- run("git add .", wt2);
226
- run('git commit -m "feat(M002): m002 work"', wt2);
227
- const roadmap2 = readFileSync(
228
- join(tempDir, ".gsd", "milestones", "M002", "M002-ROADMAP.md"),
229
- "utf-8",
230
- );
231
- mergeMilestoneToMain(tempDir, "M002", roadmap2);
232
-
233
- // Both features should now be on main
234
- assert.ok(existsSync(join(tempDir, "m001-work.ts")), "M001 work on main");
235
- assert.ok(existsSync(join(tempDir, "m002-work.ts")), "M002 work on main");
236
- assert.ok(!isInAutoWorktree(tempDir), "not in worktree after final merge");
237
-
238
- // Both milestone branches should be cleaned up
239
- const branches = run("git branch", tempDir);
240
- assert.ok(!branches.includes("milestone/M001"), "M001 branch deleted");
241
- assert.ok(!branches.includes("milestone/M002"), "M002 branch deleted");
242
- } finally {
202
+ t.after(() => {
243
203
  process.chdir(savedCwd);
244
204
  if (tempDir && existsSync(tempDir)) {
245
- rmSync(tempDir, { recursive: true, force: true });
205
+ rmSync(tempDir, { recursive: true, force: true });
246
206
  }
247
- }
207
+ });
208
+
209
+ tempDir = createTempRepo();
210
+
211
+ // Set up two milestones
212
+ createMilestoneArtifacts(tempDir, "M001");
213
+ createMilestoneArtifacts(tempDir, "M002");
214
+ run("git add .", tempDir);
215
+ run('git commit -m "add milestones"', tempDir);
216
+
217
+ // Complete M001 first (merge it)
218
+ const wt1 = createAutoWorktree(tempDir, "M001");
219
+ writeFileSync(join(wt1, "m001-work.ts"), "export const m001 = true;\n");
220
+ run("git add .", wt1);
221
+ run('git commit -m "feat(M001): m001 work"', wt1);
222
+ const roadmap1 = readFileSync(
223
+ join(tempDir, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
224
+ "utf-8",
225
+ );
226
+ mergeMilestoneToMain(tempDir, "M001", roadmap1);
227
+
228
+ // Now complete M002 (the LAST milestone — this is the #962 scenario)
229
+ const wt2 = createAutoWorktree(tempDir, "M002");
230
+ writeFileSync(join(wt2, "m002-work.ts"), "export const m002 = true;\n");
231
+ run("git add .", wt2);
232
+ run('git commit -m "feat(M002): m002 work"', wt2);
233
+ const roadmap2 = readFileSync(
234
+ join(tempDir, ".gsd", "milestones", "M002", "M002-ROADMAP.md"),
235
+ "utf-8",
236
+ );
237
+ mergeMilestoneToMain(tempDir, "M002", roadmap2);
238
+
239
+ // Both features should now be on main
240
+ assert.ok(existsSync(join(tempDir, "m001-work.ts")), "M001 work on main");
241
+ assert.ok(existsSync(join(tempDir, "m002-work.ts")), "M002 work on main");
242
+ assert.ok(!isInAutoWorktree(tempDir), "not in worktree after final merge");
243
+
244
+ // Both milestone branches should be cleaned up
245
+ const branches = run("git branch", tempDir);
246
+ assert.ok(!branches.includes("milestone/M001"), "M001 branch deleted");
247
+ assert.ok(!branches.includes("milestone/M002"), "M002 branch deleted");
248
248
  });
@@ -110,26 +110,24 @@ test("clearLock is safe when no lock file exists", () => {
110
110
  rmSync(dir, { recursive: true, force: true });
111
111
  });
112
112
 
113
- test("bootstrap cleanup releases session lock artifacts", () => {
113
+ test("bootstrap cleanup releases session lock artifacts", (t) => {
114
114
  const dir = mkdtempSync(join(tmpdir(), "gsd-lock-test-"));
115
115
  mkdirSync(join(dir, ".gsd"), { recursive: true });
116
116
 
117
- try {
118
- const result = acquireSessionLock(dir);
119
- assert.equal(result.acquired, true, "session lock should be acquired");
120
- assert.ok(existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should exist while lock is held");
121
- if (properLockfileAvailable) {
122
- assert.ok(existsSync(join(dir, ".gsd.lock")), ".gsd.lock should exist while lock is held");
123
- }
124
-
125
- releaseSessionLock(dir);
126
- clearLock(dir);
127
-
128
- assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should be removed by bootstrap cleanup");
129
- assert.ok(!existsSync(join(dir, ".gsd.lock")), ".gsd.lock should be removed by bootstrap cleanup");
130
- } finally {
131
- rmSync(dir, { recursive: true, force: true });
117
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
118
+
119
+ const result = acquireSessionLock(dir);
120
+ assert.equal(result.acquired, true, "session lock should be acquired");
121
+ assert.ok(existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should exist while lock is held");
122
+ if (properLockfileAvailable) {
123
+ assert.ok(existsSync(join(dir, ".gsd.lock")), ".gsd.lock should exist while lock is held");
132
124
  }
125
+
126
+ releaseSessionLock(dir);
127
+ clearLock(dir);
128
+
129
+ assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "auto.lock should be removed by bootstrap cleanup");
130
+ assert.ok(!existsSync(join(dir, ".gsd.lock")), ".gsd.lock should be removed by bootstrap cleanup");
133
131
  });
134
132
 
135
133
  // ─── isLockProcessAlive detects live vs dead PIDs ────────────────────────
@@ -51,93 +51,79 @@ function cleanup(base: string): void {
51
51
  try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
52
52
  }
53
53
 
54
- test("resolveMilestonePath returns null for missing milestone", () => {
54
+ test("resolveMilestonePath returns null for missing milestone", (t) => {
55
55
  const base = makeTmpBase();
56
56
  mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
57
- try {
58
- const result = resolveMilestonePath(base, "M999");
59
- assert.equal(result, null, "should return null for non-existent milestone");
60
- } finally {
61
- cleanup(base);
62
- }
57
+ t.after(() => cleanup(base));
58
+
59
+ const result = resolveMilestonePath(base, "M999");
60
+ assert.equal(result, null, "should return null for non-existent milestone");
63
61
  });
64
62
 
65
- test("resolveMilestonePath returns path for existing milestone", () => {
63
+ test("resolveMilestonePath returns path for existing milestone", (t) => {
66
64
  const base = makeTmpBase();
67
65
  mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
68
- try {
69
- const result = resolveMilestonePath(base, "M001");
70
- assert.ok(result, "should return a path for existing milestone");
71
- assert.ok(result.includes("M001"), "path should contain the milestone ID");
72
- } finally {
73
- cleanup(base);
74
- }
66
+ t.after(() => cleanup(base));
67
+
68
+ const result = resolveMilestonePath(base, "M001");
69
+ assert.ok(result, "should return a path for existing milestone");
70
+ assert.ok(result.includes("M001"), "path should contain the milestone ID");
75
71
  });
76
72
 
77
- test("resolveMilestoneFile returns null when no SUMMARY exists", () => {
73
+ test("resolveMilestoneFile returns null when no SUMMARY exists", (t) => {
78
74
  const base = makeTmpBase();
79
75
  mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
80
- try {
81
- const result = resolveMilestoneFile(base, "M001", "SUMMARY");
82
- assert.equal(result, null, "should return null when no SUMMARY file");
83
- } finally {
84
- cleanup(base);
85
- }
76
+ t.after(() => cleanup(base));
77
+
78
+ const result = resolveMilestoneFile(base, "M001", "SUMMARY");
79
+ assert.equal(result, null, "should return null when no SUMMARY file");
86
80
  });
87
81
 
88
- test("resolveMilestoneFile returns path when SUMMARY exists (completed)", () => {
82
+ test("resolveMilestoneFile returns path when SUMMARY exists (completed)", (t) => {
89
83
  const base = makeTmpBase();
90
84
  const mDir = join(base, ".gsd", "milestones", "M001");
91
85
  mkdirSync(mDir, { recursive: true });
92
86
  writeFileSync(join(mDir, "M001-SUMMARY.md"), "# Summary\nDone.");
93
- try {
94
- const result = resolveMilestoneFile(base, "M001", "SUMMARY");
95
- assert.ok(result, "should return a path when SUMMARY exists");
96
- assert.ok(result.includes("SUMMARY"), "path should reference SUMMARY");
97
- } finally {
98
- cleanup(base);
99
- }
87
+ t.after(() => cleanup(base));
88
+
89
+ const result = resolveMilestoneFile(base, "M001", "SUMMARY");
90
+ assert.ok(result, "should return a path when SUMMARY exists");
91
+ assert.ok(result.includes("SUMMARY"), "path should reference SUMMARY");
100
92
  });
101
93
 
102
94
  // ─── Combined validation logic (mirrors auto.ts resume guard) ───────────────
103
95
 
104
- test("stale milestone: missing dir means paused session should be discarded", () => {
96
+ test("stale milestone: missing dir means paused session should be discarded", (t) => {
105
97
  const base = makeTmpBase();
106
98
  mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
107
- try {
108
- const mDir = resolveMilestonePath(base, "M999");
109
- const summaryFile = resolveMilestoneFile(base, "M999", "SUMMARY");
110
- const isStale = !mDir || !!summaryFile;
111
- assert.ok(isStale, "milestone that doesn't exist should be detected as stale");
112
- } finally {
113
- cleanup(base);
114
- }
99
+ t.after(() => cleanup(base));
100
+
101
+ const mDir = resolveMilestonePath(base, "M999");
102
+ const summaryFile = resolveMilestoneFile(base, "M999", "SUMMARY");
103
+ const isStale = !mDir || !!summaryFile;
104
+ assert.ok(isStale, "milestone that doesn't exist should be detected as stale");
115
105
  });
116
106
 
117
- test("stale milestone: completed (has SUMMARY) means paused session should be discarded", () => {
107
+ test("stale milestone: completed (has SUMMARY) means paused session should be discarded", (t) => {
118
108
  const base = makeTmpBase();
119
109
  const mDir = join(base, ".gsd", "milestones", "M001");
120
110
  mkdirSync(mDir, { recursive: true });
121
111
  writeFileSync(join(mDir, "M001-SUMMARY.md"), "# Summary\nDone.");
122
- try {
123
- const dir = resolveMilestonePath(base, "M001");
124
- const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
125
- const isStale = !dir || !!summaryFile;
126
- assert.ok(isStale, "milestone with SUMMARY should be detected as stale");
127
- } finally {
128
- cleanup(base);
129
- }
112
+ t.after(() => cleanup(base));
113
+
114
+ const dir = resolveMilestonePath(base, "M001");
115
+ const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
116
+ const isStale = !dir || !!summaryFile;
117
+ assert.ok(isStale, "milestone with SUMMARY should be detected as stale");
130
118
  });
131
119
 
132
- test("valid milestone: exists and has no SUMMARY means paused session is valid", () => {
120
+ test("valid milestone: exists and has no SUMMARY means paused session is valid", (t) => {
133
121
  const base = makeTmpBase();
134
122
  mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
135
- try {
136
- const dir = resolveMilestonePath(base, "M001");
137
- const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
138
- const isStale = !dir || !!summaryFile;
139
- assert.ok(!isStale, "active milestone should not be detected as stale");
140
- } finally {
141
- cleanup(base);
142
- }
123
+ t.after(() => cleanup(base));
124
+
125
+ const dir = resolveMilestonePath(base, "M001");
126
+ const summaryFile = resolveMilestoneFile(base, "M001", "SUMMARY");
127
+ const isStale = !dir || !!summaryFile;
128
+ assert.ok(!isStale, "active milestone should not be detected as stale");
143
129
  });