gsd-pi 2.51.0 → 2.52.0-dev.655ad8a

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 (419) hide show
  1. package/README.md +59 -36
  2. package/dist/headless-events.d.ts +18 -0
  3. package/dist/headless-events.js +36 -0
  4. package/dist/headless-query.js +1 -1
  5. package/dist/headless-types.d.ts +28 -0
  6. package/dist/headless-types.js +7 -0
  7. package/dist/headless.d.ts +8 -3
  8. package/dist/headless.js +47 -16
  9. package/dist/help-text.js +16 -5
  10. package/dist/onboarding.js +5 -4
  11. package/dist/remote-questions-config.js +1 -1
  12. package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
  13. package/dist/resources/extensions/async-jobs/job-manager.js +4 -1
  14. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
  15. package/dist/resources/extensions/get-secrets-from-user.js +7 -0
  16. package/dist/resources/extensions/gsd/auto/phases.js +34 -8
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +23 -1
  18. package/dist/resources/extensions/gsd/auto-start.js +2 -0
  19. package/dist/resources/extensions/gsd/auto-timers.js +24 -2
  20. package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
  21. package/dist/resources/extensions/gsd/auto-worktree.js +91 -14
  22. package/dist/resources/extensions/gsd/auto.js +30 -4
  23. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +99 -70
  24. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
  26. package/dist/resources/extensions/gsd/claude-import.js +60 -9
  27. package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
  28. package/dist/resources/extensions/gsd/commands-config.js +10 -5
  29. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +4 -4
  30. package/dist/resources/extensions/gsd/detection.js +6 -6
  31. package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  32. package/dist/resources/extensions/gsd/error-classifier.js +105 -0
  33. package/dist/resources/extensions/gsd/git-service.js +4 -3
  34. package/dist/resources/extensions/gsd/gitignore.js +7 -7
  35. package/dist/resources/extensions/gsd/gsd-db.js +298 -45
  36. package/dist/resources/extensions/gsd/guided-flow.js +4 -3
  37. package/dist/resources/extensions/gsd/init-wizard.js +2 -2
  38. package/dist/resources/extensions/gsd/key-manager.js +7 -16
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
  40. package/dist/resources/extensions/gsd/memory-store.js +28 -13
  41. package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
  42. package/dist/resources/extensions/gsd/parallel-orchestrator.js +18 -2
  43. package/dist/resources/extensions/gsd/preferences-models.js +1 -13
  44. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  45. package/dist/resources/extensions/gsd/preferences.js +13 -13
  46. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  47. package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
  48. package/dist/resources/extensions/gsd/rule-registry.js +1 -1
  49. package/dist/resources/extensions/gsd/service-tier.js +13 -2
  50. package/dist/resources/extensions/gsd/state.js +38 -30
  51. package/dist/resources/extensions/gsd/status-guards.js +12 -0
  52. package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
  53. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
  54. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
  55. package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
  56. package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
  57. package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
  58. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
  59. package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
  60. package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
  61. package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
  62. package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
  63. package/dist/resources/extensions/gsd/validation.js +21 -0
  64. package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
  65. package/dist/resources/extensions/remote-questions/config.js +1 -1
  66. package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
  67. package/dist/resources/extensions/search-the-web/native-search.js +1 -1
  68. package/dist/resources/extensions/search-the-web/provider.js +1 -1
  69. package/dist/resources/extensions/shared/rtk.js +14 -4
  70. package/dist/rtk.js +3 -1
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  73. package/dist/web/standalone/.next/build-manifest.json +4 -4
  74. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  75. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  76. package/dist/web/standalone/.next/required-server-files.json +3 -3
  77. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  78. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  80. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  88. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  97. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  116. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/files/route.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.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  144. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  150. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  164. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  166. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  168. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  170. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/index.html +1 -1
  180. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  181. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  182. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  183. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  184. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  185. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  186. package/dist/web/standalone/.next/server/app/page.js +2 -2
  187. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  189. package/dist/web/standalone/.next/server/chunks/2229.js +3 -3
  190. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  191. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/middleware.js +2 -2
  194. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  196. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  197. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  198. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  199. package/dist/web/standalone/.next/static/chunks/4024.87fd909ae0110f50.js +9 -0
  200. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  202. package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-bca0e732db0dcec3.js} +1 -1
  206. package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +1 -0
  207. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  208. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  209. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  210. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  211. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  212. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  213. package/dist/web/standalone/server.js +1 -1
  214. package/dist/wizard.js +4 -1
  215. package/package.json +2 -2
  216. package/packages/mcp-server/README.md +202 -0
  217. package/packages/mcp-server/package.json +36 -0
  218. package/packages/mcp-server/src/cli.ts +68 -0
  219. package/packages/mcp-server/src/index.ts +14 -0
  220. package/packages/mcp-server/src/mcp-server.test.ts +628 -0
  221. package/packages/mcp-server/src/server.ts +278 -0
  222. package/packages/mcp-server/src/session-manager.ts +328 -0
  223. package/packages/mcp-server/src/types.ts +107 -0
  224. package/packages/mcp-server/tsconfig.json +24 -0
  225. package/packages/pi-ai/dist/models.d.ts +14 -3
  226. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  227. package/packages/pi-ai/dist/models.js +53 -10
  228. package/packages/pi-ai/dist/models.js.map +1 -1
  229. package/packages/pi-ai/dist/models.test.js +102 -1
  230. package/packages/pi-ai/dist/models.test.js.map +1 -1
  231. package/packages/pi-ai/dist/types.d.ts +30 -0
  232. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  233. package/packages/pi-ai/dist/types.js.map +1 -1
  234. package/packages/pi-ai/src/models.test.ts +114 -1
  235. package/packages/pi-ai/src/models.ts +70 -13
  236. package/packages/pi-ai/src/types.ts +31 -0
  237. package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
  238. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/cli/args.js +3 -0
  240. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
  243. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
  246. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
  248. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
  249. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
  250. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
  251. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  252. package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
  253. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  255. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  256. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  257. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/main.js +5 -3
  259. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
  261. package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  264. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
  265. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  266. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
  267. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
  269. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
  271. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  272. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
  273. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  274. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
  275. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
  276. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
  277. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
  278. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
  279. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  280. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  281. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
  283. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  284. package/packages/pi-coding-agent/package.json +1 -1
  285. package/packages/pi-coding-agent/src/cli/args.ts +4 -0
  286. package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
  287. package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
  288. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
  289. package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
  290. package/packages/pi-coding-agent/src/index.ts +3 -0
  291. package/packages/pi-coding-agent/src/main.ts +5 -3
  292. package/packages/pi-coding-agent/src/modes/index.ts +8 -1
  293. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
  294. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
  295. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
  296. package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
  297. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
  298. package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
  299. package/packages/rpc-client/package.json +20 -0
  300. package/pkg/package.json +1 -1
  301. package/scripts/ensure-workspace-builds.cjs +36 -8
  302. package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
  303. package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
  304. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
  305. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
  306. package/src/resources/extensions/get-secrets-from-user.ts +8 -0
  307. package/src/resources/extensions/gsd/auto/phases.ts +44 -7
  308. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -1
  309. package/src/resources/extensions/gsd/auto-start.ts +2 -0
  310. package/src/resources/extensions/gsd/auto-timers.ts +25 -1
  311. package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
  312. package/src/resources/extensions/gsd/auto-worktree.ts +94 -14
  313. package/src/resources/extensions/gsd/auto.ts +31 -4
  314. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +118 -73
  315. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
  316. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
  317. package/src/resources/extensions/gsd/claude-import.ts +58 -9
  318. package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
  319. package/src/resources/extensions/gsd/commands-config.ts +11 -5
  320. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -4
  321. package/src/resources/extensions/gsd/detection.ts +6 -6
  322. package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  323. package/src/resources/extensions/gsd/error-classifier.ts +139 -0
  324. package/src/resources/extensions/gsd/git-service.ts +4 -3
  325. package/src/resources/extensions/gsd/gitignore.ts +7 -7
  326. package/src/resources/extensions/gsd/gsd-db.ts +355 -63
  327. package/src/resources/extensions/gsd/guided-flow.ts +4 -3
  328. package/src/resources/extensions/gsd/init-wizard.ts +2 -2
  329. package/src/resources/extensions/gsd/key-manager.ts +7 -16
  330. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
  331. package/src/resources/extensions/gsd/memory-store.ts +29 -18
  332. package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
  333. package/src/resources/extensions/gsd/parallel-orchestrator.ts +23 -1
  334. package/src/resources/extensions/gsd/preferences-models.ts +1 -13
  335. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  336. package/src/resources/extensions/gsd/preferences.ts +12 -13
  337. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  338. package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
  339. package/src/resources/extensions/gsd/rule-registry.ts +1 -1
  340. package/src/resources/extensions/gsd/service-tier.ts +14 -2
  341. package/src/resources/extensions/gsd/state.ts +39 -30
  342. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  343. package/src/resources/extensions/gsd/tests/active-milestone-id-guard.test.ts +91 -0
  344. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
  345. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
  346. package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +87 -0
  347. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +80 -0
  348. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +1 -1
  349. package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
  350. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
  351. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +39 -0
  352. package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
  353. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  354. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
  355. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  356. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
  357. package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
  358. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
  359. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
  360. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
  361. package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
  362. package/src/resources/extensions/gsd/tests/git-service.test.ts +65 -31
  363. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  364. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
  365. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
  366. package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
  367. package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
  368. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  369. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  370. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +51 -0
  371. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
  372. package/src/resources/extensions/gsd/tests/parallel-orchestrator-zombie-cleanup.test.ts +277 -0
  373. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
  374. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +103 -0
  375. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
  376. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -2
  377. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
  378. package/src/resources/extensions/gsd/tests/rate-limit-model-fallback.test.ts +90 -0
  379. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
  380. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
  381. package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +9 -8
  382. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +125 -0
  383. package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
  384. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
  385. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
  386. package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
  387. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
  388. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -2
  389. package/src/resources/extensions/gsd/tests/validation-gate-patterns.test.ts +124 -0
  390. package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
  391. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
  392. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
  393. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -17
  394. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
  395. package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
  396. package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
  397. package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
  398. package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
  399. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
  400. package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
  401. package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
  402. package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
  403. package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
  404. package/src/resources/extensions/gsd/validation.ts +23 -0
  405. package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
  406. package/src/resources/extensions/remote-questions/config.ts +1 -1
  407. package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
  408. package/src/resources/extensions/search-the-web/native-search.ts +1 -1
  409. package/src/resources/extensions/search-the-web/provider.ts +1 -1
  410. package/src/resources/extensions/shared/rtk.ts +22 -4
  411. package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
  412. package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
  413. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  414. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  415. package/dist/web/standalone/.next/static/css/de141508b083f922.css +0 -1
  416. /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
  417. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → zpvUPKoW5jRAMB_fWHlPi}/_buildManifest.js +0 -0
  418. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → zpvUPKoW5jRAMB_fWHlPi}/_ssgManifest.js +0 -0
  419. /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Regression tests for zombie worker cleanup (#2736).
3
+ *
4
+ * Verifies that:
5
+ * 1. refreshWorkerStatuses() deactivates the orchestrator when all workers
6
+ * are in terminal states (error/stopped).
7
+ * 2. restoreRuntimeState() (via getWorkerStatuses) returns empty when the
8
+ * cached state has only dead workers.
9
+ */
10
+
11
+ import test from "node:test";
12
+ import assert from "node:assert/strict";
13
+ import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { tmpdir } from "node:os";
16
+ import { randomUUID } from "node:crypto";
17
+
18
+ import {
19
+ persistState,
20
+ resetOrchestrator,
21
+ refreshWorkerStatuses,
22
+ isParallelActive,
23
+ getOrchestratorState,
24
+ getWorkerStatuses,
25
+ type PersistedState,
26
+ } from "../parallel-orchestrator.ts";
27
+
28
+ function makeTmpBase(): string {
29
+ const base = join(tmpdir(), `gsd-test-zombie-${randomUUID()}`);
30
+ mkdirSync(join(base, ".gsd", "parallel"), { recursive: true });
31
+ return base;
32
+ }
33
+
34
+ function cleanup(base: string): void {
35
+ try {
36
+ rmSync(base, { recursive: true, force: true });
37
+ } catch { /* non-fatal */ }
38
+ }
39
+
40
+ /** Write a fake orchestrator.json to simulate persisted state. */
41
+ function writePersistedState(basePath: string, data: PersistedState): void {
42
+ const dest = join(basePath, ".gsd", "orchestrator.json");
43
+ writeFileSync(dest, JSON.stringify(data, null, 2), "utf-8");
44
+ }
45
+
46
+ /** Write a fake session status file to .gsd/parallel/<milestoneId>.status.json */
47
+ function writeSessionStatusFile(
48
+ basePath: string,
49
+ milestoneId: string,
50
+ state: "running" | "paused" | "stopped" | "error",
51
+ pid: number,
52
+ ): void {
53
+ const dest = join(basePath, ".gsd", "parallel", `${milestoneId}.status.json`);
54
+ writeFileSync(
55
+ dest,
56
+ JSON.stringify({
57
+ milestoneId,
58
+ pid,
59
+ state,
60
+ currentUnit: null,
61
+ completedUnits: 0,
62
+ cost: 0.5,
63
+ lastHeartbeat: Date.now(),
64
+ startedAt: Date.now() - 60_000,
65
+ worktreePath: join(basePath, "worktrees", milestoneId),
66
+ }),
67
+ "utf-8",
68
+ );
69
+ }
70
+
71
+ // Use a PID that is guaranteed dead — PID 1 is init/launchd and won't be
72
+ // killable by this process, but 2147483647 is unlikely to exist.
73
+ const DEAD_PID = 2147483647;
74
+
75
+ // ─── refreshWorkerStatuses: deactivates when all workers dead ──────────
76
+
77
+ test("#2736: refreshWorkerStatuses deactivates orchestrator when all workers are error/stopped", (t) => {
78
+ const base = makeTmpBase();
79
+ t.after(() => {
80
+ resetOrchestrator();
81
+ cleanup(base);
82
+ });
83
+
84
+ // Seed persisted state with two workers using current PID (alive) so
85
+ // restoreState() accepts them, then immediately mark them as error via
86
+ // session status files so refreshWorkerStatuses sees terminal states.
87
+ const persisted: PersistedState = {
88
+ active: true,
89
+ workers: [
90
+ {
91
+ milestoneId: "M001",
92
+ title: "Milestone 1",
93
+ pid: process.pid, // alive PID so restoreState accepts it
94
+ worktreePath: join(base, "worktrees", "M001"),
95
+ startedAt: Date.now() - 60_000,
96
+ state: "running",
97
+ cost: 1.0,
98
+ },
99
+ {
100
+ milestoneId: "M002",
101
+ title: "Milestone 2",
102
+ pid: process.pid,
103
+ worktreePath: join(base, "worktrees", "M002"),
104
+ startedAt: Date.now() - 60_000,
105
+ state: "running",
106
+ cost: 0.5,
107
+ },
108
+ ],
109
+ totalCost: 1.5,
110
+ startedAt: Date.now() - 60_000,
111
+ configSnapshot: { max_workers: 3 },
112
+ };
113
+ writePersistedState(base, persisted);
114
+
115
+ // First, restore the state into memory via getWorkerStatuses (triggers restoreIfNeeded)
116
+ const workers = getWorkerStatuses(base);
117
+ assert.equal(workers.length, 2, "should have 2 workers after restore");
118
+ assert.ok(isParallelActive(), "orchestrator should be active after restore");
119
+
120
+ // Now write session status files marking both workers as error
121
+ writeSessionStatusFile(base, "M001", "error", process.pid);
122
+ writeSessionStatusFile(base, "M002", "error", process.pid);
123
+
124
+ // Refresh — should detect all-dead and deactivate
125
+ refreshWorkerStatuses(base);
126
+
127
+ assert.equal(isParallelActive(), false, "orchestrator should be inactive after all workers died");
128
+ assert.equal(getOrchestratorState(), null, "state should be null after cleanup");
129
+ });
130
+
131
+ test("#2736: refreshWorkerStatuses keeps orchestrator active when some workers are still running", (t) => {
132
+ const base = makeTmpBase();
133
+ t.after(() => {
134
+ resetOrchestrator();
135
+ cleanup(base);
136
+ });
137
+
138
+ const persisted: PersistedState = {
139
+ active: true,
140
+ workers: [
141
+ {
142
+ milestoneId: "M001",
143
+ title: "Milestone 1",
144
+ pid: process.pid,
145
+ worktreePath: join(base, "worktrees", "M001"),
146
+ startedAt: Date.now() - 60_000,
147
+ state: "running",
148
+ cost: 1.0,
149
+ },
150
+ {
151
+ milestoneId: "M002",
152
+ title: "Milestone 2",
153
+ pid: process.pid,
154
+ worktreePath: join(base, "worktrees", "M002"),
155
+ startedAt: Date.now() - 60_000,
156
+ state: "running",
157
+ cost: 0.5,
158
+ },
159
+ ],
160
+ totalCost: 1.5,
161
+ startedAt: Date.now() - 60_000,
162
+ configSnapshot: { max_workers: 3 },
163
+ };
164
+ writePersistedState(base, persisted);
165
+
166
+ // Restore state
167
+ getWorkerStatuses(base);
168
+
169
+ // Mark M001 as error but keep M002 running
170
+ writeSessionStatusFile(base, "M001", "error", process.pid);
171
+ writeSessionStatusFile(base, "M002", "running", process.pid);
172
+
173
+ refreshWorkerStatuses(base);
174
+
175
+ assert.ok(isParallelActive(), "orchestrator should remain active with a running worker");
176
+ assert.ok(getOrchestratorState() !== null, "state should still exist");
177
+ });
178
+
179
+ // ─── restoreRuntimeState: returns false when cached state has only dead workers ─
180
+
181
+ test("#2736: getWorkerStatuses returns empty when all cached workers are in error state", (t) => {
182
+ const base = makeTmpBase();
183
+ t.after(() => {
184
+ resetOrchestrator();
185
+ cleanup(base);
186
+ });
187
+
188
+ // First, set up active state with live workers
189
+ const persisted: PersistedState = {
190
+ active: true,
191
+ workers: [
192
+ {
193
+ milestoneId: "M001",
194
+ title: "Milestone 1",
195
+ pid: process.pid,
196
+ worktreePath: join(base, "worktrees", "M001"),
197
+ startedAt: Date.now() - 60_000,
198
+ state: "running",
199
+ cost: 0.5,
200
+ },
201
+ ],
202
+ totalCost: 0.5,
203
+ startedAt: Date.now() - 60_000,
204
+ configSnapshot: { max_workers: 3 },
205
+ };
206
+ writePersistedState(base, persisted);
207
+
208
+ // Restore into memory
209
+ getWorkerStatuses(base);
210
+ assert.ok(isParallelActive(), "should be active initially");
211
+
212
+ // Simulate all workers dying: write error status then refresh to update
213
+ writeSessionStatusFile(base, "M001", "error", process.pid);
214
+ refreshWorkerStatuses(base);
215
+
216
+ // State should now be cleared
217
+ assert.equal(getOrchestratorState(), null, "state should be null after all workers error");
218
+
219
+ // Reset and try again — getWorkerStatuses with restoreIfNeeded should
220
+ // find no live workers on disk (orchestrator.json was cleaned up)
221
+ const workers = getWorkerStatuses(base);
222
+ assert.equal(workers.length, 0, "should return empty when no live workers exist");
223
+ });
224
+
225
+ test("#2736: restoreRuntimeState clears stale state when all workers are stopped", (t) => {
226
+ const base = makeTmpBase();
227
+ t.after(() => {
228
+ resetOrchestrator();
229
+ cleanup(base);
230
+ });
231
+
232
+ // Set up and restore state
233
+ const persisted: PersistedState = {
234
+ active: true,
235
+ workers: [
236
+ {
237
+ milestoneId: "M001",
238
+ title: "Milestone 1",
239
+ pid: process.pid,
240
+ worktreePath: join(base, "worktrees", "M001"),
241
+ startedAt: Date.now() - 60_000,
242
+ state: "running",
243
+ cost: 0.3,
244
+ },
245
+ {
246
+ milestoneId: "M002",
247
+ title: "Milestone 2",
248
+ pid: process.pid,
249
+ worktreePath: join(base, "worktrees", "M002"),
250
+ startedAt: Date.now() - 60_000,
251
+ state: "running",
252
+ cost: 0.7,
253
+ },
254
+ ],
255
+ totalCost: 1.0,
256
+ startedAt: Date.now() - 60_000,
257
+ configSnapshot: { max_workers: 3 },
258
+ };
259
+ writePersistedState(base, persisted);
260
+
261
+ // Restore into memory
262
+ getWorkerStatuses(base);
263
+ assert.ok(isParallelActive(), "should be active initially");
264
+
265
+ // Mark all as stopped via session status, then refresh
266
+ writeSessionStatusFile(base, "M001", "stopped", process.pid);
267
+ writeSessionStatusFile(base, "M002", "stopped", process.pid);
268
+ refreshWorkerStatuses(base);
269
+
270
+ // Orchestrator should be deactivated and state cleaned
271
+ assert.equal(isParallelActive(), false, "should be inactive after all workers stopped");
272
+ assert.equal(getOrchestratorState(), null, "state should be null");
273
+
274
+ // Verify the state file was removed
275
+ const stateFile = join(base, ".gsd", "orchestrator.json");
276
+ assert.equal(existsSync(stateFile), false, "orchestrator.json should be removed");
277
+ });
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Regression test for #2694: parkMilestone and unparkMilestone must
3
+ * update the DB milestone status alongside the filesystem marker.
4
+ *
5
+ * Without this, deriveStateFromDb skips unparked milestones because
6
+ * the DB still has status='parked', causing "All milestones complete".
7
+ */
8
+ import { test } from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+
14
+ import { parkMilestone, unparkMilestone } from "../milestone-actions.ts";
15
+ import {
16
+ openDatabase,
17
+ closeDatabase,
18
+ insertMilestone,
19
+ getMilestone,
20
+ } from "../gsd-db.ts";
21
+
22
+ function createBase(): string {
23
+ const base = mkdtempSync(join(tmpdir(), "gsd-park-db-"));
24
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
25
+ writeFileSync(
26
+ join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"),
27
+ "# M001\n\nContext.",
28
+ );
29
+ return base;
30
+ }
31
+
32
+ test("parkMilestone updates DB status to 'parked' (#2694)", () => {
33
+ const base = createBase();
34
+ try {
35
+ openDatabase(":memory:");
36
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
37
+
38
+ assert.equal(getMilestone("M001")!.status, "active", "starts active");
39
+
40
+ parkMilestone(base, "M001", "deprioritized");
41
+
42
+ assert.equal(getMilestone("M001")!.status, "parked", "DB status should be parked");
43
+
44
+ closeDatabase();
45
+ } finally {
46
+ closeDatabase();
47
+ rmSync(base, { recursive: true, force: true });
48
+ }
49
+ });
50
+
51
+ test("unparkMilestone updates DB status to 'active' (#2694)", () => {
52
+ const base = createBase();
53
+ try {
54
+ openDatabase(":memory:");
55
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
56
+
57
+ // Park first
58
+ parkMilestone(base, "M001", "deprioritized");
59
+ assert.equal(getMilestone("M001")!.status, "parked");
60
+
61
+ // Unpark
62
+ unparkMilestone(base, "M001");
63
+ assert.equal(getMilestone("M001")!.status, "active", "DB status should be active after unpark");
64
+
65
+ closeDatabase();
66
+ } finally {
67
+ closeDatabase();
68
+ rmSync(base, { recursive: true, force: true });
69
+ }
70
+ });
71
+
72
+ test("park/unpark are safe when DB is not available (#2694 guard)", () => {
73
+ const base = createBase();
74
+ try {
75
+ // No openDatabase — DB not available
76
+ // park/unpark should still work (filesystem-only, no throw)
77
+ const parked = parkMilestone(base, "M001", "test");
78
+ assert.ok(parked, "parkMilestone succeeds without DB");
79
+
80
+ const unparked = unparkMilestone(base, "M001");
81
+ assert.ok(unparked, "unparkMilestone succeeds without DB");
82
+ } finally {
83
+ rmSync(base, { recursive: true, force: true });
84
+ }
85
+ });
@@ -0,0 +1,103 @@
1
+ /**
2
+ * phases-merge-error-stops-auto.test.ts — Regression test for #2766.
3
+ *
4
+ * When mergeAndExit throws a non-MergeConflictError, the auto loop must
5
+ * stop instead of continuing with unmerged work. This test verifies that
6
+ * all catch blocks in auto/phases.ts that handle mergeAndExit errors
7
+ * call stopAuto and return { action: "break" } for non-conflict errors.
8
+ */
9
+
10
+ import { readFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { createTestContext } from "./test-helpers.ts";
13
+
14
+ const { assertTrue, report } = createTestContext();
15
+
16
+ const phasesPath = join(import.meta.dirname, "..", "auto", "phases.ts");
17
+ const phasesSrc = readFileSync(phasesPath, "utf-8");
18
+
19
+ console.log("\n=== #2766: Non-MergeConflictError stops auto mode ===");
20
+
21
+ // ── Test 1: phases.ts calls logError for non-conflict merge errors ──────
22
+
23
+ assertTrue(
24
+ phasesPath.length > 0 && phasesPath.endsWith("phases.ts"),
25
+ "phases.ts file exists and is readable",
26
+ );
27
+
28
+ // Count all mergeAndExit catch blocks by finding "} catch (mergeErr)" patterns
29
+ const mergeErrCatches = [...phasesPath.matchAll(/\} catch \(mergeErr\)/g)];
30
+ // Use the source itself for matching
31
+ const mergeErrCatchCount = [...phasesSrc.matchAll(/\} catch \(mergeErr\)/g)].length;
32
+ assertTrue(
33
+ mergeErrCatchCount >= 3,
34
+ `all mergeAndExit call sites have catch (mergeErr) blocks (found ${mergeErrCatchCount}, expected >= 3)`,
35
+ );
36
+
37
+ // ── Test 2: Every mergeErr catch block handles non-MergeConflictError ───
38
+
39
+ // Find each catch block and verify it has the non-conflict error handling pattern
40
+ const catchPattern = /\} catch \(mergeErr\) \{/g;
41
+ let match;
42
+ let blocksWithNonConflictHandling = 0;
43
+ let blocksTotal = 0;
44
+
45
+ while ((match = catchPattern.exec(phasesSrc)) !== null) {
46
+ blocksTotal++;
47
+ // Look at the ~800 chars after the catch to find both the MergeConflictError
48
+ // instanceof check AND the non-conflict handling
49
+ const afterCatch = phasesSrc.slice(match.index, match.index + 1200);
50
+
51
+ const hasInstanceofCheck = afterCatch.includes("instanceof MergeConflictError");
52
+ const hasNonConflictStop = afterCatch.includes('reason: "merge-failed"');
53
+ const hasStopAuto = afterCatch.includes("stopAuto");
54
+ const hasLogError = afterCatch.includes("logError");
55
+
56
+ if (hasInstanceofCheck && hasNonConflictStop && hasStopAuto && hasLogError) {
57
+ blocksWithNonConflictHandling++;
58
+ }
59
+ }
60
+
61
+ assertTrue(
62
+ blocksWithNonConflictHandling === blocksTotal && blocksTotal >= 3,
63
+ `all ${blocksTotal} mergeAndExit catch blocks stop auto on non-conflict errors (${blocksWithNonConflictHandling}/${blocksTotal})`,
64
+ );
65
+
66
+ // ── Test 3: Non-conflict handler returns break (does not continue) ──────
67
+
68
+ // Verify the pattern: after the MergeConflictError instanceof block,
69
+ // the non-conflict path returns { action: "break", reason: "merge-failed" }
70
+ const mergeFailedReasons = [...phasesSrc.matchAll(/reason: "merge-failed"/g)].length;
71
+ assertTrue(
72
+ mergeFailedReasons >= 3,
73
+ `all catch blocks return reason: "merge-failed" (found ${mergeFailedReasons}, expected >= 3)`,
74
+ );
75
+
76
+ // ── Test 4: Non-conflict handler notifies user ──────────────────────────
77
+
78
+ // Each non-conflict block should call ctx.ui.notify with error severity
79
+ const notifyErrorPattern = /Merge failed:.*Resolve and run \/gsd auto to resume/g;
80
+ const notifyCount = [...phasesSrc.matchAll(notifyErrorPattern)].length;
81
+ assertTrue(
82
+ notifyCount >= 3,
83
+ `all catch blocks notify user about merge failure (found ${notifyCount}, expected >= 3)`,
84
+ );
85
+
86
+ // ── Test 5: logError replaces logWarning for non-conflict merge errors ──
87
+
88
+ // The old code used logWarning — verify logError is used instead
89
+ const logWarningMergePattern = /logWarning\(.*Milestone merge failed with non-conflict error/g;
90
+ const logWarningCount = [...phasesSrc.matchAll(logWarningMergePattern)].length;
91
+ assertTrue(
92
+ logWarningCount === 0,
93
+ "logWarning is no longer used for non-conflict merge errors (replaced by logError)",
94
+ );
95
+
96
+ const logErrorMergePattern = /logError\(.*Milestone merge failed with non-conflict error/g;
97
+ const logErrorCount = [...phasesSrc.matchAll(logErrorMergePattern)].length;
98
+ assertTrue(
99
+ logErrorCount >= 3,
100
+ `logError is used for non-conflict merge errors (found ${logErrorCount}, expected >= 3)`,
101
+ );
102
+
103
+ report();
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Regression tests for #2684: preferences.md must be included in both
3
+ * ROOT_STATE_FILES (sync) and copyPlanningArtifacts (initial seed).
4
+ *
5
+ * Without this, post_unit_hooks and all preference-driven config silently
6
+ * stop working inside auto-mode worktrees.
7
+ */
8
+ import { test } from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+
14
+ test("#2684: preferences.md is NOT in ROOT_STATE_FILES (forward-only sync)", () => {
15
+ const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
16
+ const src = readFileSync(srcPath, "utf-8");
17
+
18
+ const constIdx = src.indexOf("ROOT_STATE_FILES");
19
+ assert.ok(constIdx !== -1, "ROOT_STATE_FILES constant exists");
20
+
21
+ const arrayStart = src.indexOf("[", constIdx);
22
+ const arrayEnd = src.indexOf("] as const", arrayStart);
23
+ const block = src.slice(arrayStart, arrayEnd);
24
+
25
+ // preferences.md must NOT be in ROOT_STATE_FILES — it is handled separately
26
+ // in syncGsdStateToWorktree() (forward-only, additive). Including it in
27
+ // ROOT_STATE_FILES would cause syncWorktreeStateBack() to overwrite the
28
+ // authoritative project root copy (#2684).
29
+ const entries = block.split("\n")
30
+ .map(l => l.trim())
31
+ .filter(l => l.startsWith('"') && l.includes(".md"));
32
+ const hasPrefs = entries.some(l => l.includes("preferences.md"));
33
+ assert.ok(
34
+ !hasPrefs,
35
+ "preferences.md must NOT be in ROOT_STATE_FILES (back-sync would overwrite root)",
36
+ );
37
+ });
38
+
39
+ test("#2684: copyPlanningArtifacts file list includes preferences.md", () => {
40
+ const srcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
41
+ const src = readFileSync(srcPath, "utf-8");
42
+
43
+ // Find the copyPlanningArtifacts function body
44
+ const fnIdx = src.indexOf("function copyPlanningArtifacts");
45
+ assert.ok(fnIdx !== -1, "copyPlanningArtifacts function exists");
46
+
47
+ // Extract function body (up to the next top-level function)
48
+ const fnBody = src.slice(fnIdx, fnIdx + 1500);
49
+
50
+ assert.ok(
51
+ fnBody.includes('"preferences.md"'),
52
+ "preferences.md should be in copyPlanningArtifacts file list",
53
+ );
54
+ });
55
+
56
+ test("#2684: syncGsdStateToWorktree copies preferences.md", async () => {
57
+ // Functional test: create a mock source and destination, call the sync
58
+ const srcBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-src-"));
59
+ const dstBase = mkdtempSync(join(tmpdir(), "gsd-wt-prefs-dst-"));
60
+ const srcGsd = join(srcBase, ".gsd");
61
+ const dstGsd = join(dstBase, ".gsd");
62
+ mkdirSync(srcGsd, { recursive: true });
63
+ mkdirSync(dstGsd, { recursive: true });
64
+
65
+ try {
66
+ // Write a preferences.md in source
67
+ writeFileSync(
68
+ join(srcGsd, "preferences.md"),
69
+ "---\nversion: 1\n---\n\npost_unit_hooks:\n - name: notify\n command: echo done\n",
70
+ );
71
+
72
+ // Import and call syncGsdStateToWorktree
73
+ const { syncGsdStateToWorktree } = await import("../auto-worktree.ts");
74
+ syncGsdStateToWorktree(srcBase, dstBase);
75
+
76
+ // Verify preferences.md was copied
77
+ assert.ok(
78
+ existsSync(join(dstGsd, "preferences.md")),
79
+ "preferences.md should be copied to worktree",
80
+ );
81
+
82
+ const content = readFileSync(join(dstGsd, "preferences.md"), "utf-8");
83
+ assert.ok(
84
+ content.includes("post_unit_hooks"),
85
+ "copied preferences.md should contain the hooks config",
86
+ );
87
+ } finally {
88
+ rmSync(srcBase, { recursive: true, force: true });
89
+ rmSync(dstBase, { recursive: true, force: true });
90
+ }
91
+ });
@@ -45,7 +45,7 @@ test("getIsolationMode defaults to none when preferences have no isolation setti
45
45
  // Validate the default via validatePreferences: when no isolation is set,
46
46
  // preferences.git.isolation is undefined, and getIsolationMode returns "none".
47
47
  // Default changed from "worktree" to "none" so GSD works out of the box
48
- // without preferences.md (#2480).
48
+ // without PREFERENCES.md (#2480).
49
49
  const { preferences } = validatePreferences({});
50
50
  assert.equal(preferences.git?.isolation, undefined, "no isolation in empty prefs");
51
51
  const isolation = preferences.git?.isolation;
@@ -59,7 +59,7 @@ test("solo mode applies correct defaults", () => {
59
59
  const result = applyModeDefaults("solo", { mode: "solo" });
60
60
  assert.equal(result.git?.auto_push, true);
61
61
  assert.equal(result.git?.push_branches, false);
62
- assert.equal(result.git?.pre_merge_check, false);
62
+ assert.equal(result.git?.pre_merge_check, "auto");
63
63
  assert.equal(result.git?.merge_strategy, "squash");
64
64
  assert.equal(result.git?.isolation, "none");
65
65
  assert.equal(result.unique_milestone_ids, false);