gsd-pi 2.51.0-dev.ae8f7cb → 2.52.0-dev.585e355

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 (337) hide show
  1. package/README.md +4 -4
  2. package/dist/headless-events.d.ts +18 -0
  3. package/dist/headless-events.js +36 -0
  4. package/dist/headless-types.d.ts +28 -0
  5. package/dist/headless-types.js +7 -0
  6. package/dist/headless.d.ts +8 -3
  7. package/dist/headless.js +47 -16
  8. package/dist/help-text.js +16 -5
  9. package/dist/onboarding.js +5 -4
  10. package/dist/remote-questions-config.js +1 -1
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
  12. package/dist/resources/extensions/async-jobs/job-manager.js +4 -1
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
  14. package/dist/resources/extensions/gsd/auto/phases.js +6 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +18 -0
  16. package/dist/resources/extensions/gsd/auto-start.js +2 -0
  17. package/dist/resources/extensions/gsd/auto-timers.js +24 -2
  18. package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
  19. package/dist/resources/extensions/gsd/auto-worktree.js +21 -0
  20. package/dist/resources/extensions/gsd/auto.js +8 -4
  21. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +105 -70
  22. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
  24. package/dist/resources/extensions/gsd/claude-import.js +60 -9
  25. package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
  26. package/dist/resources/extensions/gsd/commands-config.js +10 -5
  27. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +4 -4
  28. package/dist/resources/extensions/gsd/detection.js +6 -6
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  30. package/dist/resources/extensions/gsd/error-classifier.js +105 -0
  31. package/dist/resources/extensions/gsd/git-service.js +4 -3
  32. package/dist/resources/extensions/gsd/gitignore.js +7 -7
  33. package/dist/resources/extensions/gsd/gsd-db.js +298 -45
  34. package/dist/resources/extensions/gsd/init-wizard.js +2 -2
  35. package/dist/resources/extensions/gsd/key-manager.js +7 -16
  36. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
  37. package/dist/resources/extensions/gsd/memory-store.js +28 -13
  38. package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
  39. package/dist/resources/extensions/gsd/preferences-models.js +1 -13
  40. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  41. package/dist/resources/extensions/gsd/preferences.js +13 -13
  42. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  43. package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
  44. package/dist/resources/extensions/gsd/rule-registry.js +1 -1
  45. package/dist/resources/extensions/gsd/service-tier.js +13 -2
  46. package/dist/resources/extensions/gsd/state.js +33 -19
  47. package/dist/resources/extensions/gsd/status-guards.js +12 -0
  48. package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
  49. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
  50. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
  51. package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
  52. package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
  53. package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
  54. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
  55. package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
  56. package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
  57. package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
  58. package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
  59. package/dist/resources/extensions/gsd/validation.js +21 -0
  60. package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
  61. package/dist/resources/extensions/remote-questions/config.js +1 -1
  62. package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
  63. package/dist/resources/extensions/search-the-web/native-search.js +1 -1
  64. package/dist/resources/extensions/search-the-web/provider.js +1 -1
  65. package/dist/resources/extensions/shared/rtk.js +5 -3
  66. package/dist/rtk.js +3 -1
  67. package/dist/web/standalone/.next/BUILD_ID +1 -1
  68. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  69. package/dist/web/standalone/.next/build-manifest.json +3 -3
  70. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  71. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  74. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +2 -2
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  90. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/experimental/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/index.html +1 -1
  134. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  135. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  137. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  139. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  140. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  142. package/dist/web/standalone/.next/server/chunks/2229.js +2 -2
  143. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  146. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  147. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  148. package/dist/web/standalone/.next/static/chunks/4024.21054f459af5cc78.js +9 -0
  149. package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-024d82be84800e52.js} +1 -1
  150. package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +1 -0
  151. package/dist/wizard.js +4 -1
  152. package/package.json +2 -2
  153. package/packages/mcp-server/README.md +202 -0
  154. package/packages/mcp-server/package.json +36 -0
  155. package/packages/mcp-server/src/cli.ts +68 -0
  156. package/packages/mcp-server/src/index.ts +14 -0
  157. package/packages/mcp-server/src/mcp-server.test.ts +628 -0
  158. package/packages/mcp-server/src/server.ts +278 -0
  159. package/packages/mcp-server/src/session-manager.ts +328 -0
  160. package/packages/mcp-server/src/types.ts +107 -0
  161. package/packages/mcp-server/tsconfig.json +24 -0
  162. package/packages/pi-ai/dist/models.d.ts +14 -3
  163. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  164. package/packages/pi-ai/dist/models.js +53 -10
  165. package/packages/pi-ai/dist/models.js.map +1 -1
  166. package/packages/pi-ai/dist/models.test.js +102 -1
  167. package/packages/pi-ai/dist/models.test.js.map +1 -1
  168. package/packages/pi-ai/dist/types.d.ts +30 -0
  169. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  170. package/packages/pi-ai/dist/types.js.map +1 -1
  171. package/packages/pi-ai/src/models.test.ts +114 -1
  172. package/packages/pi-ai/src/models.ts +70 -13
  173. package/packages/pi-ai/src/types.ts +31 -0
  174. package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
  175. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/cli/args.js +3 -0
  177. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
  180. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
  183. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
  185. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
  186. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
  187. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
  188. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
  190. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  192. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  193. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/main.js +5 -3
  196. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
  198. package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
  199. package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  201. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
  202. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
  204. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
  206. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
  208. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
  210. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
  212. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
  214. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
  216. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  217. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  219. package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
  220. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  221. package/packages/pi-coding-agent/package.json +1 -1
  222. package/packages/pi-coding-agent/src/cli/args.ts +4 -0
  223. package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
  224. package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
  225. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
  226. package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
  227. package/packages/pi-coding-agent/src/index.ts +3 -0
  228. package/packages/pi-coding-agent/src/main.ts +5 -3
  229. package/packages/pi-coding-agent/src/modes/index.ts +8 -1
  230. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
  231. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
  232. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
  233. package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
  234. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
  235. package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
  236. package/packages/rpc-client/package.json +20 -0
  237. package/pkg/package.json +1 -1
  238. package/scripts/ensure-workspace-builds.cjs +36 -8
  239. package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
  240. package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
  241. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
  242. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
  243. package/src/resources/extensions/gsd/auto/phases.ts +6 -0
  244. package/src/resources/extensions/gsd/auto-dispatch.ts +19 -0
  245. package/src/resources/extensions/gsd/auto-start.ts +2 -0
  246. package/src/resources/extensions/gsd/auto-timers.ts +25 -1
  247. package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
  248. package/src/resources/extensions/gsd/auto-worktree.ts +21 -0
  249. package/src/resources/extensions/gsd/auto.ts +10 -4
  250. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -73
  251. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
  252. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
  253. package/src/resources/extensions/gsd/claude-import.ts +58 -9
  254. package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
  255. package/src/resources/extensions/gsd/commands-config.ts +11 -5
  256. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -4
  257. package/src/resources/extensions/gsd/detection.ts +6 -6
  258. package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  259. package/src/resources/extensions/gsd/error-classifier.ts +139 -0
  260. package/src/resources/extensions/gsd/git-service.ts +4 -3
  261. package/src/resources/extensions/gsd/gitignore.ts +7 -7
  262. package/src/resources/extensions/gsd/gsd-db.ts +355 -63
  263. package/src/resources/extensions/gsd/init-wizard.ts +2 -2
  264. package/src/resources/extensions/gsd/key-manager.ts +7 -16
  265. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
  266. package/src/resources/extensions/gsd/memory-store.ts +29 -18
  267. package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
  268. package/src/resources/extensions/gsd/preferences-models.ts +1 -13
  269. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  270. package/src/resources/extensions/gsd/preferences.ts +12 -13
  271. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  272. package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
  273. package/src/resources/extensions/gsd/rule-registry.ts +1 -1
  274. package/src/resources/extensions/gsd/service-tier.ts +14 -2
  275. package/src/resources/extensions/gsd/state.ts +34 -20
  276. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  277. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
  278. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
  279. package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
  280. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
  281. package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
  282. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  283. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
  284. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  285. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
  286. package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
  287. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
  288. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
  289. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
  290. package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
  291. package/src/resources/extensions/gsd/tests/git-service.test.ts +37 -4
  292. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  293. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
  294. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
  295. package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
  296. package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
  297. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  298. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  299. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
  300. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
  301. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
  302. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -2
  303. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
  304. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
  305. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
  306. package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
  307. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
  308. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
  309. package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
  310. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
  311. package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
  312. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
  313. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
  314. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -17
  315. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
  316. package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
  317. package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
  318. package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
  319. package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
  320. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
  321. package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
  322. package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
  323. package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
  324. package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
  325. package/src/resources/extensions/gsd/validation.ts +23 -0
  326. package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
  327. package/src/resources/extensions/remote-questions/config.ts +1 -1
  328. package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
  329. package/src/resources/extensions/search-the-web/native-search.ts +1 -1
  330. package/src/resources/extensions/search-the-web/provider.ts +1 -1
  331. package/src/resources/extensions/shared/rtk.ts +12 -3
  332. package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
  333. package/dist/web/standalone/.next/static/css/de141508b083f922.css +0 -1
  334. /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
  335. /package/dist/web/standalone/.next/static/{5_KeZz1X0tXJK-d_4OhjB → KTe1kB5nPLQFIIFz2OcmI}/_buildManifest.js +0 -0
  336. /package/dist/web/standalone/.next/static/{5_KeZz1X0tXJK-d_4OhjB → KTe1kB5nPLQFIIFz2OcmI}/_ssgManifest.js +0 -0
  337. /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
@@ -635,11 +635,11 @@ describe('git-service', async () => {
635
635
  // S05: Enhanced features — snapshots, pre-merge checks
636
636
  // ═══════════════════════════════════════════════════════════════════════
637
637
 
638
- // ─── createSnapshot: prefs enabled ─────────────────────────────────────
638
+ // ─── createSnapshot: default (enabled) ─────────────────────────────────
639
639
 
640
- test('createSnapshot: enabled', () => {
640
+ test('createSnapshot: enabled by default when prefs omitted', () => {
641
641
  const repo = initBranchTestRepo();
642
- const svc = new GitServiceImpl(repo, { snapshots: true });
642
+ const svc = new GitServiceImpl(repo);
643
643
 
644
644
  // Create a branch with a commit
645
645
  run("git checkout -b gsd/M001/S01", repo);
@@ -675,6 +675,39 @@ describe('git-service', async () => {
675
675
  rmSync(repo, { recursive: true, force: true });
676
676
  });
677
677
 
678
+ // ─── runPreMergeCheck: default (auto-detect) ──────────────────────────
679
+
680
+ test('runPreMergeCheck: auto-detects when prefs omitted', () => {
681
+ const repo = initBranchTestRepo();
682
+ createFile(repo, "package.json", JSON.stringify({
683
+ name: "test-default",
684
+ scripts: { test: 'node -e "process.exit(0)"' },
685
+ }));
686
+ run("git add -A", repo);
687
+ run('git commit -m "add package.json"', repo);
688
+
689
+ // No pre_merge_check pref set — should auto-detect and run
690
+ const svc = new GitServiceImpl(repo);
691
+ const result: PreMergeCheckResult = svc.runPreMergeCheck();
692
+
693
+ assert.deepStrictEqual(result.passed, true, "runPreMergeCheck auto-detects and passes when prefs omitted");
694
+ assert.ok(!result.skipped, "runPreMergeCheck is not skipped when prefs omitted and package.json exists");
695
+
696
+ rmSync(repo, { recursive: true, force: true });
697
+ });
698
+
699
+ test('runPreMergeCheck: gracefully skips when prefs omitted and no package.json', () => {
700
+ const repo = initBranchTestRepo();
701
+ // No package.json — auto-detect should skip gracefully
702
+ const svc = new GitServiceImpl(repo);
703
+ const result: PreMergeCheckResult = svc.runPreMergeCheck();
704
+
705
+ assert.deepStrictEqual(result.passed, true, "runPreMergeCheck passes when no package.json (skip)");
706
+ assert.deepStrictEqual(result.skipped, true, "runPreMergeCheck skips when no test runner detected");
707
+
708
+ rmSync(repo, { recursive: true, force: true });
709
+ });
710
+
678
711
  // ─── runPreMergeCheck: pass ────────────────────────────────────────────
679
712
 
680
713
  test('runPreMergeCheck: pass', () => {
@@ -1142,7 +1175,7 @@ describe('git-service', async () => {
1142
1175
  mkdirSync(join(repo, ".gsd", "runtime"), { recursive: true });
1143
1176
  mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
1144
1177
  writeFileSync(join(repo, ".gsd", "milestones", "M001", "ROADMAP.md"), "# Roadmap");
1145
- writeFileSync(join(repo, ".gsd", "preferences.md"), "---\nversion: 1\n---");
1178
+ writeFileSync(join(repo, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---");
1146
1179
  writeFileSync(join(repo, ".gsd", "STATE.md"), "# State");
1147
1180
  writeFileSync(join(repo, ".gsd", "runtime", "units.json"), "{}");
1148
1181
  writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
@@ -64,7 +64,7 @@ describe('gsd-db', () => {
64
64
  // Check schema_version table
65
65
  const adapter = _getAdapter()!;
66
66
  const version = adapter.prepare('SELECT MAX(version) as version FROM schema_version').get();
67
- assert.deepStrictEqual(version?.['version'], 12, 'schema version should be 12');
67
+ assert.deepStrictEqual(version?.['version'], 14, 'schema version should be 14');
68
68
 
69
69
  // Check tables exist by querying them
70
70
  const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Regression tests for #2527: idle watchdog stalled-tool detection.
3
+ *
4
+ * Bug 1: When a tool is stalled longer than idle_timeout, the watchdog
5
+ * notifies but falls through to detectWorkingTreeActivity(), which
6
+ * resets lastProgressAt if files were modified earlier. Recovery is
7
+ * never called — the session burns tokens indefinitely.
8
+ *
9
+ * Bug 2: After async recoverTimedOutUnit(), pauseAuto/stopAuto may set
10
+ * s.currentUnit = null, but the next line accesses .startedAt — crash.
11
+ *
12
+ * These tests verify the auto-timers.ts source contains the structural
13
+ * fixes: the stalledToolDetected flag, clearInFlightTools() call, the
14
+ * filesystem-check guard, and the null guard after recovery.
15
+ */
16
+
17
+ import { readFileSync } from "node:fs";
18
+ import { join } from "node:path";
19
+ import { test, describe } from "node:test";
20
+ import assert from "node:assert/strict";
21
+
22
+ const TIMERS_SRC = readFileSync(
23
+ join(import.meta.dirname, "..", "auto-timers.ts"),
24
+ "utf-8",
25
+ );
26
+
27
+ // ═══ Bug 1: stalledToolDetected flag prevents filesystem-activity override ═══
28
+
29
+ describe("#2527 Bug 1: stalled tool should not be overridden by filesystem activity", () => {
30
+ test("auto-timers.ts imports clearInFlightTools", () => {
31
+ assert.ok(
32
+ TIMERS_SRC.includes("clearInFlightTools"),
33
+ "clearInFlightTools must be imported from auto-tool-tracking",
34
+ );
35
+ });
36
+
37
+ test("auto-timers.ts declares stalledToolDetected flag", () => {
38
+ assert.ok(
39
+ TIMERS_SRC.includes("stalledToolDetected"),
40
+ "stalledToolDetected flag must exist in idle watchdog",
41
+ );
42
+ });
43
+
44
+ test("stalled tool sets flag to true", () => {
45
+ // The flag must be set before the filesystem check
46
+ const flagSet = TIMERS_SRC.indexOf("stalledToolDetected = true");
47
+ assert.ok(flagSet > -1, "stalledToolDetected must be set to true when tool is stalled");
48
+
49
+ const notify = TIMERS_SRC.indexOf("Stalled tool detected:");
50
+ assert.ok(flagSet < notify, "flag must be set before the stall notification");
51
+ });
52
+
53
+ test("stalled tool calls clearInFlightTools", () => {
54
+ // clearInFlightTools() must be called when tool is stalled, so subsequent
55
+ // watchdog ticks don't re-detect the same stale entries
56
+ const clearCall = TIMERS_SRC.indexOf("clearInFlightTools()");
57
+ assert.ok(clearCall > -1, "clearInFlightTools() must be called when tool is stalled");
58
+
59
+ const flagSet = TIMERS_SRC.indexOf("stalledToolDetected = true");
60
+ assert.ok(
61
+ Math.abs(clearCall - flagSet) < 200,
62
+ "clearInFlightTools() should be near stalledToolDetected = true",
63
+ );
64
+ });
65
+
66
+ test("filesystem-activity check is guarded by stalledToolDetected", () => {
67
+ // The detectWorkingTreeActivity check must be skipped when stalledToolDetected is true
68
+ assert.ok(
69
+ TIMERS_SRC.includes("!stalledToolDetected && detectWorkingTreeActivity"),
70
+ "detectWorkingTreeActivity must be guarded by !stalledToolDetected",
71
+ );
72
+ });
73
+
74
+ test("control flow: stalled tool → skip filesystem check → reach recovery", () => {
75
+ // Verify the structural ordering: flag declaration → stall block → guarded fs check → recovery
76
+ const flagDecl = TIMERS_SRC.indexOf("let stalledToolDetected = false");
77
+ const stallBlock = TIMERS_SRC.indexOf("stalledToolDetected = true");
78
+ const fsGuard = TIMERS_SRC.indexOf("!stalledToolDetected && detectWorkingTreeActivity");
79
+ const recovery = TIMERS_SRC.indexOf("recoverTimedOutUnit(ctx, pi, unitType, unitId, \"idle\"");
80
+
81
+ assert.ok(flagDecl > -1, "flag declaration must exist");
82
+ assert.ok(flagDecl < stallBlock, "flag declared before stall block");
83
+ assert.ok(stallBlock < fsGuard, "stall block before filesystem guard");
84
+ assert.ok(fsGuard < recovery, "filesystem guard before recovery call");
85
+ });
86
+ });
87
+
88
+ // ═══ Bug 2: null guard after async recoverTimedOutUnit ═══════════════════════
89
+
90
+ describe("#2527 Bug 2: null guard after async recovery prevents crash", () => {
91
+ test("idle watchdog has null guard after recoverTimedOutUnit", () => {
92
+ // Find the idle recovery call
93
+ const idleRecovery = TIMERS_SRC.indexOf(
94
+ 'recoverTimedOutUnit(ctx, pi, unitType, unitId, "idle"',
95
+ );
96
+ assert.ok(idleRecovery > -1, "idle recovery call must exist");
97
+
98
+ // The null guard must appear between the recovery call and the next
99
+ // writeUnitRuntimeRecord that accesses s.currentUnit.startedAt
100
+ const afterRecovery = TIMERS_SRC.slice(idleRecovery, idleRecovery + 400);
101
+ assert.ok(
102
+ afterRecovery.includes("if (!s.currentUnit) return"),
103
+ "null guard for s.currentUnit must exist after idle recoverTimedOutUnit",
104
+ );
105
+ });
106
+
107
+ test("null guard is between recovery and writeUnitRuntimeRecord", () => {
108
+ const idleRecovery = TIMERS_SRC.indexOf(
109
+ 'recoverTimedOutUnit(ctx, pi, unitType, unitId, "idle"',
110
+ );
111
+ const afterRecovery = TIMERS_SRC.slice(idleRecovery);
112
+
113
+ const recoveredReturn = afterRecovery.indexOf('if (recovery === "recovered") return');
114
+ const nullGuard = afterRecovery.indexOf("if (!s.currentUnit) return");
115
+ const writeRecord = afterRecovery.indexOf("writeUnitRuntimeRecord(s.basePath");
116
+
117
+ assert.ok(recoveredReturn > -1, "recovered return must exist");
118
+ assert.ok(nullGuard > -1, "null guard must exist");
119
+ assert.ok(writeRecord > -1, "writeUnitRuntimeRecord must exist after recovery");
120
+ assert.ok(
121
+ recoveredReturn < nullGuard && nullGuard < writeRecord,
122
+ "order must be: recovered-return → null-guard → writeUnitRuntimeRecord",
123
+ );
124
+ });
125
+ });
@@ -123,7 +123,7 @@ test("init-wizard: v2 .gsd/ preferences detected", (t) => {
123
123
  const dir = makeTempDir("prefs-detect");
124
124
  try {
125
125
  mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
126
- writeFileSync(join(dir, ".gsd", "preferences.md"), "---\nversion: 1\nmode: solo\n---\n", "utf-8");
126
+ writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), "---\nversion: 1\nmode: solo\n---\n", "utf-8");
127
127
 
128
128
  const detection = detectProjectState(dir);
129
129
  assert.ok(detection.v2);
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Tests for #2676: idle watchdog must exempt user-interactive tools
3
+ * (ask_user_questions, secure_env_collect) from stall detection.
4
+ */
5
+ import { describe, test, beforeEach } from "node:test";
6
+ import assert from "node:assert/strict";
7
+ import {
8
+ markToolStart,
9
+ markToolEnd,
10
+ hasInteractiveToolInFlight,
11
+ getInFlightToolCount,
12
+ getOldestInFlightToolStart,
13
+ getOldestInFlightToolAgeMs,
14
+ clearInFlightTools,
15
+ } from "../auto-tool-tracking.ts";
16
+
17
+ // These tests call the tracking module directly (bypassing the auto.ts
18
+ // wrapper which guards on s.active) so we always pass isActive=true.
19
+
20
+ beforeEach(() => {
21
+ clearInFlightTools();
22
+ });
23
+
24
+ describe("hasInteractiveToolInFlight", () => {
25
+ test("returns false when no tools are in-flight", () => {
26
+ assert.equal(hasInteractiveToolInFlight(), false);
27
+ });
28
+
29
+ test("returns false when only non-interactive tools are in-flight", () => {
30
+ markToolStart("call-1", true, "bash");
31
+ markToolStart("call-2", true, "read");
32
+ assert.equal(hasInteractiveToolInFlight(), false);
33
+ });
34
+
35
+ test("returns true when ask_user_questions is in-flight", () => {
36
+ markToolStart("call-1", true, "bash");
37
+ markToolStart("call-2", true, "ask_user_questions");
38
+ assert.equal(hasInteractiveToolInFlight(), true);
39
+ });
40
+
41
+ test("returns true when secure_env_collect is in-flight", () => {
42
+ markToolStart("call-1", true, "secure_env_collect");
43
+ assert.equal(hasInteractiveToolInFlight(), true);
44
+ });
45
+
46
+ test("returns false after interactive tool completes", () => {
47
+ markToolStart("call-1", true, "ask_user_questions");
48
+ assert.equal(hasInteractiveToolInFlight(), true);
49
+ markToolEnd("call-1");
50
+ assert.equal(hasInteractiveToolInFlight(), false);
51
+ });
52
+
53
+ test("returns true if one of multiple tools is interactive", () => {
54
+ markToolStart("call-1", true, "bash");
55
+ markToolStart("call-2", true, "edit");
56
+ markToolStart("call-3", true, "ask_user_questions");
57
+ markToolStart("call-4", true, "write");
58
+ assert.equal(hasInteractiveToolInFlight(), true);
59
+ });
60
+ });
61
+
62
+ describe("toolName tracking in markToolStart", () => {
63
+ test("defaults toolName to 'unknown' when not provided", () => {
64
+ markToolStart("call-1", true);
65
+ // unknown tool should not be treated as interactive
66
+ assert.equal(hasInteractiveToolInFlight(), false);
67
+ assert.equal(getInFlightToolCount(), 1);
68
+ });
69
+
70
+ test("no-ops when isActive is false", () => {
71
+ markToolStart("call-1", false, "ask_user_questions");
72
+ assert.equal(getInFlightToolCount(), 0);
73
+ assert.equal(hasInteractiveToolInFlight(), false);
74
+ });
75
+ });
76
+
77
+ describe("existing tracking behavior preserved with toolName", () => {
78
+ test("getInFlightToolCount tracks correctly", () => {
79
+ assert.equal(getInFlightToolCount(), 0);
80
+ markToolStart("call-1", true, "bash");
81
+ assert.equal(getInFlightToolCount(), 1);
82
+ markToolStart("call-2", true, "ask_user_questions");
83
+ assert.equal(getInFlightToolCount(), 2);
84
+ markToolEnd("call-1");
85
+ assert.equal(getInFlightToolCount(), 1);
86
+ markToolEnd("call-2");
87
+ assert.equal(getInFlightToolCount(), 0);
88
+ });
89
+
90
+ test("getOldestInFlightToolStart returns correct timestamp", () => {
91
+ assert.equal(getOldestInFlightToolStart(), undefined);
92
+ const before = Date.now();
93
+ markToolStart("call-1", true, "bash");
94
+ const after = Date.now();
95
+ const oldest = getOldestInFlightToolStart();
96
+ assert.ok(oldest !== undefined);
97
+ assert.ok(oldest! >= before && oldest! <= after);
98
+ });
99
+
100
+ test("getOldestInFlightToolAgeMs returns 0 with no tools", () => {
101
+ assert.equal(getOldestInFlightToolAgeMs(), 0);
102
+ });
103
+
104
+ test("getOldestInFlightToolAgeMs returns positive value with tools", () => {
105
+ markToolStart("call-1", true, "read");
106
+ const age = getOldestInFlightToolAgeMs();
107
+ assert.ok(age >= 0, `age should be non-negative, got ${age}`);
108
+ });
109
+
110
+ test("clearInFlightTools resets all state", () => {
111
+ markToolStart("call-1", true, "ask_user_questions");
112
+ markToolStart("call-2", true, "bash");
113
+ assert.equal(getInFlightToolCount(), 2);
114
+ assert.equal(hasInteractiveToolInFlight(), true);
115
+ clearInFlightTools();
116
+ assert.equal(getInFlightToolCount(), 0);
117
+ assert.equal(hasInteractiveToolInFlight(), false);
118
+ });
119
+ });
@@ -189,7 +189,22 @@ test("getAllKeyStatuses detects empty keys as not configured", () => {
189
189
  const statuses = getAllKeyStatuses(auth);
190
190
  const groq = statuses.find((s) => s.provider.id === "groq");
191
191
  assert.equal(groq?.configured, false);
192
- assert.ok(groq?.description.includes("empty"));
192
+ // Empty-key entries are filtered out, so provider appears unconfigured
193
+ assert.equal(groq?.source, "none");
194
+ });
195
+
196
+ test("getAllKeyStatuses finds valid keys even when empty-key entry exists at index 0", () => {
197
+ const auth = makeAuth({
198
+ groq: [
199
+ { type: "api_key", key: "" },
200
+ { type: "api_key", key: "gsk-real-key" },
201
+ ],
202
+ });
203
+ const statuses = getAllKeyStatuses(auth);
204
+ const groq = statuses.find((s) => s.provider.id === "groq");
205
+ assert.equal(groq?.configured, true);
206
+ assert.equal(groq?.source, "auth.json");
207
+ assert.equal(groq?.credentialCount, 1); // only the valid key counts
193
208
  });
194
209
 
195
210
  test("getAllKeyStatuses detects env var keys", () => {
@@ -363,7 +363,7 @@ test('md-importer: schema v1→v2 migration', () => {
363
363
  openDatabase(':memory:');
364
364
  const adapter = _getAdapter();
365
365
  const version = adapter?.prepare('SELECT MAX(version) as v FROM schema_version').get();
366
- assert.deepStrictEqual(version?.v, 12, 'new DB should be at schema version 12');
366
+ assert.deepStrictEqual(version?.v, 14, 'new DB should be at schema version 14');
367
367
 
368
368
  // Artifacts table should exist
369
369
  const tableCheck = adapter?.prepare("SELECT count(*) as c FROM sqlite_master WHERE type='table' AND name='artifacts'").get();
@@ -323,9 +323,9 @@ test('memory-store: schema includes memories table', () => {
323
323
  const viewCount = adapter.prepare('SELECT count(*) as cnt FROM active_memories').get();
324
324
  assert.deepStrictEqual(viewCount?.['cnt'], 0, 'active_memories view should exist');
325
325
 
326
- // Verify schema version is 12 (after quality gates table)
326
+ // Verify schema version is 14 (after indexes + slice_dependencies)
327
327
  const version = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
328
- assert.deepStrictEqual(version?.['v'], 12, 'schema version should be 12');
328
+ assert.deepStrictEqual(version?.['v'], 14, 'schema version should be 14');
329
329
 
330
330
  closeDatabase();
331
331
  });
@@ -8,7 +8,7 @@
8
8
  * Uses the writeRunnerPreferences pattern from doctor-git.test.ts:
9
9
  * PROJECT_PREFERENCES_PATH is a module-level constant frozen at import
10
10
  * time, so process.chdir() won't redirect preference loading. We write
11
- * prefs to the runner's cwd .gsd/preferences.md and clean up in finally.
11
+ * prefs to the runner's cwd .gsd/PREFERENCES.md and clean up in finally.
12
12
  */
13
13
 
14
14
  import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
@@ -24,7 +24,7 @@ import assert from 'node:assert/strict';
24
24
 
25
25
  // --- Preferences helpers (same pattern as doctor-git.test.ts K001) ---
26
26
 
27
- const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "preferences.md");
27
+ const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "PREFERENCES.md");
28
28
 
29
29
  function writeRunnerPreferences(isolation: "none" | "worktree" | "branch"): void {
30
30
  mkdirSync(join(process.cwd(), ".gsd"), { recursive: true });
@@ -72,12 +72,12 @@ try {
72
72
 
73
73
  // Test 4: shouldUseWorktreeIsolation returns false for no prefs (default: none)
74
74
  // Worktree isolation requires explicit opt-in — default is "none" so GSD
75
- // works out of the box without preferences.md (#2480).
75
+ // works out of the box without PREFERENCES.md (#2480).
76
76
  // Skip if global prefs exist — they override the default and this test
77
- // cannot control ~/.gsd/preferences.md.
77
+ // cannot control ~/.gsd/PREFERENCES.md.
78
78
 
79
79
  test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', () => {
80
- const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md"))
80
+ const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
81
81
  || existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
82
82
  if (!globalPrefsExist) {
83
83
  try {
@@ -91,9 +91,9 @@ test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', ()
91
91
  }
92
92
  });
93
93
 
94
- // Test 5: getIsolationMode returns "none" when no preferences.md exists (#2480)
94
+ // Test 5: getIsolationMode returns "none" when no PREFERENCES.md exists (#2480)
95
95
  test('getIsolationMode returns "none" with no prefs (default)', () => {
96
- const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md"))
96
+ const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md"))
97
97
  || existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
98
98
  if (!globalPrefsExist) {
99
99
  try {
@@ -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,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);