gsd-pi 2.44.0 → 2.45.0-dev.6b9da3e

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 (463) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  3. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  4. package/dist/resources/extensions/gsd/auto/phases.js +37 -36
  5. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  6. package/dist/resources/extensions/gsd/auto-start.js +31 -2
  7. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  8. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  9. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  10. package/dist/resources/extensions/gsd/auto.js +30 -3
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  12. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  13. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  14. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  16. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  17. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  18. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  19. package/dist/resources/extensions/gsd/doctor.js +8 -0
  20. package/dist/resources/extensions/gsd/git-service.js +8 -3
  21. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  22. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  23. package/dist/resources/extensions/gsd/preferences.js +9 -1
  24. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  25. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  26. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  27. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  28. package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
  29. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  30. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  31. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  32. package/dist/resources/extensions/gsd/rethink.js +115 -0
  33. package/dist/resources/extensions/gsd/state.js +41 -3
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  36. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  37. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +32 -2
  39. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  40. package/dist/resources/extensions/mcp-client/index.js +14 -0
  41. package/dist/web/standalone/.next/BUILD_ID +1 -1
  42. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  43. package/dist/web/standalone/.next/build-manifest.json +4 -4
  44. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  45. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  46. package/dist/web/standalone/.next/required-server-files.json +3 -3
  47. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  48. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  50. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  58. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.rsc +5 -5
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  67. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  74. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  112. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  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.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  134. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  136. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  138. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +6 -6
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +6 -6
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  154. package/dist/web/standalone/.next/server/app/page.js +2 -2
  155. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  157. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  158. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  159. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/middleware.js +2 -2
  162. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  164. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  165. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  166. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  167. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  168. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  169. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  170. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  171. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  172. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  173. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  174. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  175. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  176. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  177. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  178. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  179. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  180. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  181. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  182. package/dist/web/standalone/server.js +1 -1
  183. package/package.json +1 -1
  184. package/packages/native/dist/stream-process/index.js +2 -2
  185. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  186. package/packages/native/src/stream-process/index.ts +2 -2
  187. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  188. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  190. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  192. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  194. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  196. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  198. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  199. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  200. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  201. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  202. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  203. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  204. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  206. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  208. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  210. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  212. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  214. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/main.js +17 -0
  217. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  236. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  238. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  240. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  243. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  246. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  248. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  249. package/packages/pi-coding-agent/package.json +1 -1
  250. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  251. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  252. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  253. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  254. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  255. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  256. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  257. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  258. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  259. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  260. package/packages/pi-coding-agent/src/main.ts +19 -0
  261. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  263. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  266. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  268. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  269. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  270. package/pkg/package.json +1 -1
  271. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  272. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  273. package/src/resources/extensions/gsd/auto/phases.ts +46 -48
  274. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  275. package/src/resources/extensions/gsd/auto-start.ts +39 -2
  276. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  277. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  278. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  279. package/src/resources/extensions/gsd/auto.ts +37 -3
  280. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  281. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  282. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  283. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  284. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  285. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  286. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  287. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  288. package/src/resources/extensions/gsd/doctor.ts +7 -1
  289. package/src/resources/extensions/gsd/git-service.ts +6 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  291. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  292. package/src/resources/extensions/gsd/preferences.ts +11 -1
  293. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  294. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  295. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  296. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  297. package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
  298. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  299. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  300. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  301. package/src/resources/extensions/gsd/rethink.ts +154 -0
  302. package/src/resources/extensions/gsd/state.ts +41 -1
  303. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  304. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  305. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  306. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  307. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  308. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  309. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  310. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  311. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  312. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  313. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  314. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  315. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  316. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  317. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  318. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  319. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  320. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  321. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  322. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  323. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  324. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  325. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  326. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  327. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  328. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  329. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  330. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  331. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  332. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  333. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  334. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  335. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  336. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  337. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  338. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  339. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  340. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  341. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  342. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  343. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  344. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  345. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  346. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  347. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  348. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  349. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  350. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  351. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  352. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  353. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  354. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  355. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  356. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  357. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  358. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  359. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  360. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  361. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  362. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  363. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  364. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  365. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  366. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  367. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  368. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  369. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  370. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  371. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  372. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  373. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  374. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  375. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  376. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  377. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  378. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  379. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  380. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  381. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  382. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  383. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  384. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  385. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  386. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  387. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  388. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  389. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  390. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  391. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  392. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  393. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  394. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  395. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  396. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  397. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  398. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  399. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  400. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  401. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  402. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  403. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  404. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  405. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  406. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  407. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  408. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  409. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  410. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  411. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  412. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  413. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  414. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  415. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  416. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  417. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  418. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  419. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  420. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  421. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  422. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  423. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  424. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  425. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  426. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  427. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  428. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  429. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  430. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  431. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  432. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  433. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  434. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  435. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  436. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  437. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  438. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  439. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  440. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  441. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  442. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  443. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  444. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  445. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  446. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  447. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  448. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  449. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  450. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  451. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  452. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  453. package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
  454. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  455. package/src/resources/extensions/mcp-client/index.ts +20 -0
  456. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  457. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +0 -1
  458. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  459. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  461. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  462. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_ssgManifest.js +0 -0
@@ -1,4 +1,5 @@
1
- import { createTestContext } from './test-helpers.ts';
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import * as fs from 'node:fs';
3
4
  import * as path from 'node:path';
4
5
  import * as os from 'node:os';
@@ -17,8 +18,6 @@ import {
17
18
  } from '../gsd-db.ts';
18
19
  import { handleCompleteTask } from '../tools/complete-task.ts';
19
20
 
20
- const { assertEq, assertTrue, assertMatch, report } = createTestContext();
21
-
22
21
  // ═══════════════════════════════════════════════════════════════════════════
23
22
  // Helpers
24
23
  // ═══════════════════════════════════════════════════════════════════════════
@@ -99,341 +98,290 @@ function makeValidParams() {
99
98
  }
100
99
 
101
100
  // ═══════════════════════════════════════════════════════════════════════════
102
- // complete-task: Schema v5 migration
101
+ // Tests
103
102
  // ═══════════════════════════════════════════════════════════════════════════
104
103
 
105
- console.log('\n=== complete-task: schema v5 migration ===');
106
- {
107
- const dbPath = tempDbPath();
108
- openDatabase(dbPath);
109
-
110
- const adapter = _getAdapter()!;
111
-
112
- // Verify schema version is current (v10 after M001 planning migrations)
113
- const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
114
- assertEq(versionRow?.['v'], 10, 'schema version should be 10');
104
+ describe("complete-task: schema v5 migration", () => {
105
+ test("schema version and tables exist", () => {
106
+ const dbPath = tempDbPath();
107
+ openDatabase(dbPath);
115
108
 
116
- // Verify all 4 new tables exist
117
- const tables = adapter.prepare(
118
- "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
119
- ).all();
120
- const tableNames = tables.map(t => t['name'] as string);
121
- assertTrue(tableNames.includes('milestones'), 'milestones table should exist');
122
- assertTrue(tableNames.includes('slices'), 'slices table should exist');
123
- assertTrue(tableNames.includes('tasks'), 'tasks table should exist');
124
- assertTrue(tableNames.includes('verification_evidence'), 'verification_evidence table should exist');
109
+ const adapter = _getAdapter()!;
125
110
 
126
- cleanup(dbPath);
127
- }
111
+ // Verify schema version is current (v10 after M001 planning migrations)
112
+ const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
113
+ assert.strictEqual(versionRow?.['v'], 10, 'schema version should be 10');
128
114
 
129
- // ═══════════════════════════════════════════════════════════════════════════
130
- // complete-task: Accessor CRUD
131
- // ═══════════════════════════════════════════════════════════════════════════
132
-
133
- console.log('\n=== complete-task: accessor CRUD ===');
134
- {
135
- const dbPath = tempDbPath();
136
- openDatabase(dbPath);
137
-
138
- // Insert milestone
139
- insertMilestone({ id: 'M001', title: 'Test Milestone' });
140
- const adapter = _getAdapter()!;
141
- const mRow = adapter.prepare("SELECT * FROM milestones WHERE id = 'M001'").get();
142
- assertEq(mRow?.['id'], 'M001', 'milestone id should be M001');
143
- assertEq(mRow?.['title'], 'Test Milestone', 'milestone title should match');
144
-
145
- // Insert slice
146
- insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', risk: 'high' });
147
- const sRow = adapter.prepare("SELECT * FROM slices WHERE id = 'S01' AND milestone_id = 'M001'").get();
148
- assertEq(sRow?.['id'], 'S01', 'slice id should be S01');
149
- assertEq(sRow?.['risk'], 'high', 'slice risk should be high');
150
-
151
- // Insert task with all fields
152
- insertTask({
153
- id: 'T01',
154
- sliceId: 'S01',
155
- milestoneId: 'M001',
156
- title: 'Test Task',
157
- status: 'complete',
158
- oneLiner: 'Did the thing',
159
- narrative: 'Full story here.',
160
- verificationResult: 'passed',
161
- duration: '30m',
162
- blockerDiscovered: false,
163
- deviations: 'None',
164
- knownIssues: 'None',
165
- keyFiles: ['file1.ts', 'file2.ts'],
166
- keyDecisions: ['D001'],
167
- fullSummaryMd: '# Summary',
168
- });
115
+ // Verify all 4 new tables exist
116
+ const tables = adapter.prepare(
117
+ "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
118
+ ).all();
119
+ const tableNames = tables.map(t => t['name'] as string);
120
+ assert.ok(tableNames.includes('milestones'), 'milestones table should exist');
121
+ assert.ok(tableNames.includes('slices'), 'slices table should exist');
122
+ assert.ok(tableNames.includes('tasks'), 'tasks table should exist');
123
+ assert.ok(tableNames.includes('verification_evidence'), 'verification_evidence table should exist');
169
124
 
170
- // getTask verifies all fields
171
- const task = getTask('M001', 'S01', 'T01');
172
- assertTrue(task !== null, 'task should not be null');
173
- assertEq(task!.id, 'T01', 'task id');
174
- assertEq(task!.slice_id, 'S01', 'task slice_id');
175
- assertEq(task!.milestone_id, 'M001', 'task milestone_id');
176
- assertEq(task!.title, 'Test Task', 'task title');
177
- assertEq(task!.status, 'complete', 'task status');
178
- assertEq(task!.one_liner, 'Did the thing', 'task one_liner');
179
- assertEq(task!.narrative, 'Full story here.', 'task narrative');
180
- assertEq(task!.verification_result, 'passed', 'task verification_result');
181
- assertEq(task!.blocker_discovered, false, 'task blocker_discovered');
182
- assertEq(task!.key_files, ['file1.ts', 'file2.ts'], 'task key_files JSON round-trip');
183
- assertEq(task!.key_decisions, ['D001'], 'task key_decisions JSON round-trip');
184
- assertEq(task!.full_summary_md, '# Summary', 'task full_summary_md');
185
-
186
- // getTask returns null for non-existent
187
- const noTask = getTask('M001', 'S01', 'T99');
188
- assertEq(noTask, null, 'non-existent task should return null');
189
-
190
- // Insert verification evidence
191
- insertVerificationEvidence({
192
- taskId: 'T01',
193
- sliceId: 'S01',
194
- milestoneId: 'M001',
195
- command: 'npm test',
196
- exitCode: 0,
197
- verdict: '✅ pass',
198
- durationMs: 3000,
125
+ cleanup(dbPath);
199
126
  });
200
- const evRows = adapter.prepare(
201
- "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'"
202
- ).all();
203
- assertEq(evRows.length, 1, 'should have 1 verification evidence row');
204
- assertEq(evRows[0]['command'], 'npm test', 'evidence command');
205
- assertEq(evRows[0]['exit_code'], 0, 'evidence exit_code');
206
- assertEq(evRows[0]['verdict'], '✅ pass', 'evidence verdict');
207
- assertEq(evRows[0]['duration_ms'], 3000, 'evidence duration_ms');
208
-
209
- // getSliceTasks returns array
210
- const sliceTasks = getSliceTasks('M001', 'S01');
211
- assertEq(sliceTasks.length, 1, 'getSliceTasks should return 1 task');
212
- assertEq(sliceTasks[0].id, 'T01', 'getSliceTasks first task id');
213
-
214
- // updateTaskStatus changes status
215
- updateTaskStatus('M001', 'S01', 'T01', 'failed', new Date().toISOString());
216
- const updatedTask = getTask('M001', 'S01', 'T01');
217
- assertEq(updatedTask!.status, 'failed', 'task status should be updated to failed');
218
- assertTrue(updatedTask!.completed_at !== null, 'completed_at should be set after status update');
219
-
220
- cleanup(dbPath);
221
- }
127
+ });
222
128
 
223
- // ═══════════════════════════════════════════════════════════════════════════
224
- // complete-task: Accessor stale-state error
225
- // ═══════════════════════════════════════════════════════════════════════════
129
+ describe("complete-task: accessor CRUD", () => {
130
+ test("insert and query milestones, slices, tasks, evidence", () => {
131
+ const dbPath = tempDbPath();
132
+ openDatabase(dbPath);
226
133
 
227
- console.log('\n=== complete-task: accessor stale-state error ===');
228
- {
229
- // No DB open — accessors should throw GSD_STALE_STATE
230
- closeDatabase();
231
- let threw = false;
232
- try {
233
- insertMilestone({ id: 'M001' });
234
- } catch (err: any) {
235
- threw = true;
236
- assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
237
- 'should throw GSD_STALE_STATE when no DB open');
238
- }
239
- assertTrue(threw, 'insertMilestone should throw when no DB open');
240
-
241
- threw = false;
242
- try {
243
- insertSlice({ id: 'S01', milestoneId: 'M001' });
244
- } catch (err: any) {
245
- threw = true;
246
- assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
247
- 'insertSlice should throw GSD_STALE_STATE');
248
- }
249
- assertTrue(threw, 'insertSlice should throw when no DB open');
250
-
251
- threw = false;
252
- try {
253
- insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001' });
254
- } catch (err: any) {
255
- threw = true;
256
- assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
257
- 'insertTask should throw GSD_STALE_STATE');
258
- }
259
- assertTrue(threw, 'insertTask should throw when no DB open');
260
-
261
- threw = false;
262
- try {
263
- insertVerificationEvidence({
264
- taskId: 'T01', sliceId: 'S01', milestoneId: 'M001',
265
- command: 'test', exitCode: 0, verdict: 'pass', durationMs: 0,
134
+ // Insert milestone
135
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
136
+ const adapter = _getAdapter()!;
137
+ const mRow = adapter.prepare("SELECT * FROM milestones WHERE id = 'M001'").get();
138
+ assert.strictEqual(mRow?.['id'], 'M001', 'milestone id should be M001');
139
+ assert.strictEqual(mRow?.['title'], 'Test Milestone', 'milestone title should match');
140
+
141
+ // Insert slice
142
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', risk: 'high' });
143
+ const sRow = adapter.prepare("SELECT * FROM slices WHERE id = 'S01' AND milestone_id = 'M001'").get();
144
+ assert.strictEqual(sRow?.['id'], 'S01', 'slice id should be S01');
145
+ assert.strictEqual(sRow?.['risk'], 'high', 'slice risk should be high');
146
+
147
+ // Insert task with all fields
148
+ insertTask({
149
+ id: 'T01',
150
+ sliceId: 'S01',
151
+ milestoneId: 'M001',
152
+ title: 'Test Task',
153
+ status: 'complete',
154
+ oneLiner: 'Did the thing',
155
+ narrative: 'Full story here.',
156
+ verificationResult: 'passed',
157
+ duration: '30m',
158
+ blockerDiscovered: false,
159
+ deviations: 'None',
160
+ knownIssues: 'None',
161
+ keyFiles: ['file1.ts', 'file2.ts'],
162
+ keyDecisions: ['D001'],
163
+ fullSummaryMd: '# Summary',
266
164
  });
267
- } catch (err: any) {
268
- threw = true;
269
- assertTrue(err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
270
- 'insertVerificationEvidence should throw GSD_STALE_STATE');
271
- }
272
- assertTrue(threw, 'insertVerificationEvidence should throw when no DB open');
273
- }
274
-
275
- // ═══════════════════════════════════════════════════════════════════════════
276
- // complete-task: Handler happy path
277
- // ═══════════════════════════════════════════════════════════════════════════
278
-
279
- console.log('\n=== complete-task: handler happy path ===');
280
- {
281
- const dbPath = tempDbPath();
282
- openDatabase(dbPath);
283
165
 
284
- const { basePath, planPath } = createTempProject();
285
-
286
- const params = makeValidParams();
287
- const result = await handleCompleteTask(params, basePath);
288
-
289
- assertTrue(!('error' in result), 'handler should succeed without error');
290
- if (!('error' in result)) {
291
- assertEq(result.taskId, 'T01', 'result taskId');
292
- assertEq(result.sliceId, 'S01', 'result sliceId');
293
- assertEq(result.milestoneId, 'M001', 'result milestoneId');
294
- assertTrue(result.summaryPath.endsWith('T01-SUMMARY.md'), 'summaryPath should end with T01-SUMMARY.md');
295
-
296
- // (a) Verify task row in DB with status 'complete'
166
+ // getTask verifies all fields
297
167
  const task = getTask('M001', 'S01', 'T01');
298
- assertTrue(task !== null, 'task should exist in DB after handler');
299
- assertEq(task!.status, 'complete', 'task status should be complete');
300
- assertEq(task!.one_liner, 'Added test functionality', 'task one_liner in DB');
301
- assertEq(task!.key_files, ['src/test.ts', 'src/test.test.ts'], 'task key_files in DB');
302
-
303
- // (b) Verify verification_evidence rows in DB
304
- const adapter = _getAdapter()!;
168
+ assert.ok(task !== null, 'task should not be null');
169
+ assert.strictEqual(task!.id, 'T01', 'task id');
170
+ assert.strictEqual(task!.slice_id, 'S01', 'task slice_id');
171
+ assert.strictEqual(task!.milestone_id, 'M001', 'task milestone_id');
172
+ assert.strictEqual(task!.title, 'Test Task', 'task title');
173
+ assert.strictEqual(task!.status, 'complete', 'task status');
174
+ assert.strictEqual(task!.one_liner, 'Did the thing', 'task one_liner');
175
+ assert.strictEqual(task!.narrative, 'Full story here.', 'task narrative');
176
+ assert.strictEqual(task!.verification_result, 'passed', 'task verification_result');
177
+ assert.strictEqual(task!.blocker_discovered, false, 'task blocker_discovered');
178
+ assert.deepStrictEqual(task!.key_files, ['file1.ts', 'file2.ts'], 'task key_files JSON round-trip');
179
+ assert.deepStrictEqual(task!.key_decisions, ['D001'], 'task key_decisions JSON round-trip');
180
+ assert.strictEqual(task!.full_summary_md, '# Summary', 'task full_summary_md');
181
+
182
+ // getTask returns null for non-existent
183
+ const noTask = getTask('M001', 'S01', 'T99');
184
+ assert.strictEqual(noTask, null, 'non-existent task should return null');
185
+
186
+ // Insert verification evidence
187
+ insertVerificationEvidence({
188
+ taskId: 'T01',
189
+ sliceId: 'S01',
190
+ milestoneId: 'M001',
191
+ command: 'npm test',
192
+ exitCode: 0,
193
+ verdict: '✅ pass',
194
+ durationMs: 3000,
195
+ });
305
196
  const evRows = adapter.prepare(
306
- "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND milestone_id = 'M001'"
197
+ "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'"
307
198
  ).all();
308
- assertEq(evRows.length, 1, 'should have 1 verification evidence row after handler');
309
- assertEq(evRows[0]['command'], 'npm run test:unit', 'evidence command from handler');
310
-
311
- // (c) Verify T01-SUMMARY.md file on disk with correct YAML frontmatter
312
- assertTrue(fs.existsSync(result.summaryPath), 'summary file should exist on disk');
313
- const summaryContent = fs.readFileSync(result.summaryPath, 'utf-8');
314
- assertMatch(summaryContent, /^---\n/, 'summary should start with YAML frontmatter');
315
- assertMatch(summaryContent, /id: T01/, 'summary should contain id: T01');
316
- assertMatch(summaryContent, /parent: S01/, 'summary should contain parent: S01');
317
- assertMatch(summaryContent, /milestone: M001/, 'summary should contain milestone: M001');
318
- assertMatch(summaryContent, /blocker_discovered: false/, 'summary should contain blocker_discovered');
319
- assertMatch(summaryContent, /# T01:/, 'summary should have H1 with task ID');
320
- assertMatch(summaryContent, /\*\*Added test functionality\*\*/, 'summary should have one-liner in bold');
321
- assertMatch(summaryContent, /## What Happened/, 'summary should have What Happened section');
322
- assertMatch(summaryContent, /## Verification Evidence/, 'summary should have Verification Evidence section');
323
- assertMatch(summaryContent, /npm run test:unit/, 'summary evidence should contain command');
324
-
325
- // (d) Verify plan checkbox changed to [x]
326
- const planContent = fs.readFileSync(planPath, 'utf-8');
327
- assertMatch(planContent, /\[x\]\s+\*\*T01:/, 'T01 should be checked in plan');
328
- // T02 should still be unchecked
329
- assertMatch(planContent, /\[ \]\s+\*\*T02:/, 'T02 should still be unchecked in plan');
330
-
331
- // (e) Verify full_summary_md stored in DB for D004 recovery
332
- const taskAfter = getTask('M001', 'S01', 'T01');
333
- assertTrue(taskAfter!.full_summary_md.length > 0, 'full_summary_md should be non-empty in DB');
334
- assertMatch(taskAfter!.full_summary_md, /id: T01/, 'full_summary_md should contain frontmatter');
335
- }
199
+ assert.strictEqual(evRows.length, 1, 'should have 1 verification evidence row');
200
+ assert.strictEqual(evRows[0]['command'], 'npm test', 'evidence command');
201
+ assert.strictEqual(evRows[0]['exit_code'], 0, 'evidence exit_code');
202
+ assert.strictEqual(evRows[0]['verdict'], '✅ pass', 'evidence verdict');
203
+ assert.strictEqual(evRows[0]['duration_ms'], 3000, 'evidence duration_ms');
204
+
205
+ // getSliceTasks returns array
206
+ const sliceTasks = getSliceTasks('M001', 'S01');
207
+ assert.strictEqual(sliceTasks.length, 1, 'getSliceTasks should return 1 task');
208
+ assert.strictEqual(sliceTasks[0].id, 'T01', 'getSliceTasks first task id');
209
+
210
+ // updateTaskStatus changes status
211
+ updateTaskStatus('M001', 'S01', 'T01', 'failed', new Date().toISOString());
212
+ const updatedTask = getTask('M001', 'S01', 'T01');
213
+ assert.strictEqual(updatedTask!.status, 'failed', 'task status should be updated to failed');
214
+ assert.ok(updatedTask!.completed_at !== null, 'completed_at should be set after status update');
215
+
216
+ cleanup(dbPath);
217
+ });
218
+ });
336
219
 
337
- cleanupDir(basePath);
338
- cleanup(dbPath);
339
- }
220
+ describe("complete-task: accessor stale-state error", () => {
221
+ test("accessors throw when no DB open", () => {
222
+ closeDatabase();
340
223
 
341
- // ═══════════════════════════════════════════════════════════════════════════
342
- // complete-task: Handler validation errors
343
- // ═══════════════════════════════════════════════════════════════════════════
224
+ assert.throws(() => insertMilestone({ id: 'M001' }),
225
+ (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
226
+ 'insertMilestone should throw when no DB open');
344
227
 
345
- console.log('\n=== complete-task: handler validation errors ===');
346
- {
347
- const dbPath = tempDbPath();
348
- openDatabase(dbPath);
228
+ assert.throws(() => insertSlice({ id: 'S01', milestoneId: 'M001' }),
229
+ (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
230
+ 'insertSlice should throw when no DB open');
349
231
 
350
- const params = makeValidParams();
232
+ assert.throws(() => insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001' }),
233
+ (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
234
+ 'insertTask should throw when no DB open');
351
235
 
352
- // Empty taskId
353
- const r1 = await handleCompleteTask({ ...params, taskId: '' }, '/tmp/fake');
354
- assertTrue('error' in r1, 'should return error for empty taskId');
355
- if ('error' in r1) {
356
- assertMatch(r1.error, /taskId/, 'error should mention taskId');
357
- }
236
+ assert.throws(() => insertVerificationEvidence({
237
+ taskId: 'T01', sliceId: 'S01', milestoneId: 'M001',
238
+ command: 'test', exitCode: 0, verdict: 'pass', durationMs: 0,
239
+ }),
240
+ (err: any) => err.code === 'GSD_STALE_STATE' || err.message.includes('No database open'),
241
+ 'insertVerificationEvidence should throw when no DB open');
242
+ });
243
+ });
244
+
245
+ describe("complete-task: handler", () => {
246
+ test("happy path", async () => {
247
+ const dbPath = tempDbPath();
248
+ openDatabase(dbPath);
249
+
250
+ const { basePath, planPath } = createTempProject();
251
+
252
+ const params = makeValidParams();
253
+ const result = await handleCompleteTask(params, basePath);
254
+
255
+ assert.ok(!('error' in result), 'handler should succeed without error');
256
+ if (!('error' in result)) {
257
+ assert.strictEqual(result.taskId, 'T01', 'result taskId');
258
+ assert.strictEqual(result.sliceId, 'S01', 'result sliceId');
259
+ assert.strictEqual(result.milestoneId, 'M001', 'result milestoneId');
260
+ assert.ok(result.summaryPath.endsWith('T01-SUMMARY.md'), 'summaryPath should end with T01-SUMMARY.md');
261
+
262
+ // (a) Verify task row in DB with status 'complete'
263
+ const task = getTask('M001', 'S01', 'T01');
264
+ assert.ok(task !== null, 'task should exist in DB after handler');
265
+ assert.strictEqual(task!.status, 'complete', 'task status should be complete');
266
+ assert.strictEqual(task!.one_liner, 'Added test functionality', 'task one_liner in DB');
267
+ assert.deepStrictEqual(task!.key_files, ['src/test.ts', 'src/test.test.ts'], 'task key_files in DB');
268
+
269
+ // (b) Verify verification_evidence rows in DB
270
+ const adapter = _getAdapter()!;
271
+ const evRows = adapter.prepare(
272
+ "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND milestone_id = 'M001'"
273
+ ).all();
274
+ assert.strictEqual(evRows.length, 1, 'should have 1 verification evidence row after handler');
275
+ assert.strictEqual(evRows[0]['command'], 'npm run test:unit', 'evidence command from handler');
276
+
277
+ // (c) Verify T01-SUMMARY.md file on disk with correct YAML frontmatter
278
+ assert.ok(fs.existsSync(result.summaryPath), 'summary file should exist on disk');
279
+ const summaryContent = fs.readFileSync(result.summaryPath, 'utf-8');
280
+ assert.match(summaryContent, /^---\n/, 'summary should start with YAML frontmatter');
281
+ assert.match(summaryContent, /id: T01/, 'summary should contain id: T01');
282
+ assert.match(summaryContent, /parent: S01/, 'summary should contain parent: S01');
283
+ assert.match(summaryContent, /milestone: M001/, 'summary should contain milestone: M001');
284
+ assert.match(summaryContent, /blocker_discovered: false/, 'summary should contain blocker_discovered');
285
+ assert.match(summaryContent, /# T01:/, 'summary should have H1 with task ID');
286
+ assert.match(summaryContent, /\*\*Added test functionality\*\*/, 'summary should have one-liner in bold');
287
+ assert.match(summaryContent, /## What Happened/, 'summary should have What Happened section');
288
+ assert.match(summaryContent, /## Verification Evidence/, 'summary should have Verification Evidence section');
289
+ assert.match(summaryContent, /npm run test:unit/, 'summary evidence should contain command');
290
+
291
+ // (d) Verify plan checkbox changed to [x]
292
+ const planContent = fs.readFileSync(planPath, 'utf-8');
293
+ assert.match(planContent, /\[x\]\s+\*\*T01:/, 'T01 should be checked in plan');
294
+ // T02 should still be unchecked
295
+ assert.match(planContent, /\[ \]\s+\*\*T02:/, 'T02 should still be unchecked in plan');
296
+
297
+ // (e) Verify full_summary_md stored in DB for D004 recovery
298
+ const taskAfter = getTask('M001', 'S01', 'T01');
299
+ assert.ok(taskAfter!.full_summary_md.length > 0, 'full_summary_md should be non-empty in DB');
300
+ assert.match(taskAfter!.full_summary_md, /id: T01/, 'full_summary_md should contain frontmatter');
301
+ }
358
302
 
359
- // Empty milestoneId
360
- const r2 = await handleCompleteTask({ ...params, milestoneId: '' }, '/tmp/fake');
361
- assertTrue('error' in r2, 'should return error for empty milestoneId');
362
- if ('error' in r2) {
363
- assertMatch(r2.error, /milestoneId/, 'error should mention milestoneId');
364
- }
303
+ cleanupDir(basePath);
304
+ cleanup(dbPath);
305
+ });
365
306
 
366
- // Empty sliceId
367
- const r3 = await handleCompleteTask({ ...params, sliceId: '' }, '/tmp/fake');
368
- assertTrue('error' in r3, 'should return error for empty sliceId');
369
- if ('error' in r3) {
370
- assertMatch(r3.error, /sliceId/, 'error should mention sliceId');
371
- }
307
+ test("validation errors", async () => {
308
+ const dbPath = tempDbPath();
309
+ openDatabase(dbPath);
372
310
 
373
- cleanup(dbPath);
374
- }
311
+ const params = makeValidParams();
375
312
 
376
- // ═══════════════════════════════════════════════════════════════════════════
377
- // complete-task: Handler idempotency
378
- // ═══════════════════════════════════════════════════════════════════════════
313
+ // Empty taskId
314
+ const r1 = await handleCompleteTask({ ...params, taskId: '' }, '/tmp/fake');
315
+ assert.ok('error' in r1, 'should return error for empty taskId');
316
+ if ('error' in r1) {
317
+ assert.match(r1.error, /taskId/, 'error should mention taskId');
318
+ }
379
319
 
380
- console.log('\n=== complete-task: handler idempotency ===');
381
- {
382
- const dbPath = tempDbPath();
383
- openDatabase(dbPath);
320
+ // Empty milestoneId
321
+ const r2 = await handleCompleteTask({ ...params, milestoneId: '' }, '/tmp/fake');
322
+ assert.ok('error' in r2, 'should return error for empty milestoneId');
323
+ if ('error' in r2) {
324
+ assert.match(r2.error, /milestoneId/, 'error should mention milestoneId');
325
+ }
384
326
 
385
- const { basePath, planPath } = createTempProject();
327
+ // Empty sliceId
328
+ const r3 = await handleCompleteTask({ ...params, sliceId: '' }, '/tmp/fake');
329
+ assert.ok('error' in r3, 'should return error for empty sliceId');
330
+ if ('error' in r3) {
331
+ assert.match(r3.error, /sliceId/, 'error should mention sliceId');
332
+ }
386
333
 
387
- const params = makeValidParams();
334
+ cleanup(dbPath);
335
+ });
388
336
 
389
- // First call
390
- const r1 = await handleCompleteTask(params, basePath);
391
- assertTrue(!('error' in r1), 'first call should succeed');
337
+ test("idempotency", async () => {
338
+ const dbPath = tempDbPath();
339
+ openDatabase(dbPath);
392
340
 
393
- // Second call with same params — should not crash (INSERT OR REPLACE)
394
- const r2 = await handleCompleteTask(params, basePath);
395
- assertTrue(!('error' in r2), 'second call should succeed (idempotent)');
341
+ const { basePath, planPath } = createTempProject();
396
342
 
397
- // Verify only 1 task row (upserted, not duplicated)
398
- const tasks = getSliceTasks('M001', 'S01');
399
- assertEq(tasks.length, 1, 'should have exactly 1 task row after 2 calls (upsert)');
343
+ const params = makeValidParams();
400
344
 
401
- // File should still exist
402
- if (!('error' in r2)) {
403
- assertTrue(fs.existsSync(r2.summaryPath), 'summary should still exist after second call');
404
- }
345
+ // First call
346
+ const r1 = await handleCompleteTask(params, basePath);
347
+ assert.ok(!('error' in r1), 'first call should succeed');
405
348
 
406
- cleanupDir(basePath);
407
- cleanup(dbPath);
408
- }
349
+ // Second call with same params — should not crash (INSERT OR REPLACE)
350
+ const r2 = await handleCompleteTask(params, basePath);
351
+ assert.ok(!('error' in r2), 'second call should succeed (idempotent)');
409
352
 
410
- // ═══════════════════════════════════════════════════════════════════════════
411
- // complete-task: Handler with missing plan file (graceful)
412
- // ═══════════════════════════════════════════════════════════════════════════
353
+ // Verify only 1 task row (upserted, not duplicated)
354
+ const tasks = getSliceTasks('M001', 'S01');
355
+ assert.strictEqual(tasks.length, 1, 'should have exactly 1 task row after 2 calls (upsert)');
413
356
 
414
- console.log('\n=== complete-task: handler with missing plan file ===');
415
- {
416
- const dbPath = tempDbPath();
417
- openDatabase(dbPath);
357
+ // File should still exist
358
+ if (!('error' in r2)) {
359
+ assert.ok(fs.existsSync(r2.summaryPath), 'summary should still exist after second call');
360
+ }
418
361
 
419
- // Create a temp dir WITHOUT a plan file
420
- const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-no-plan-'));
421
- const tasksDir = path.join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks');
422
- fs.mkdirSync(tasksDir, { recursive: true });
362
+ cleanupDir(basePath);
363
+ cleanup(dbPath);
364
+ });
423
365
 
424
- const params = makeValidParams();
425
- const result = await handleCompleteTask(params, basePath);
366
+ test("missing plan file (graceful)", async () => {
367
+ const dbPath = tempDbPath();
368
+ openDatabase(dbPath);
426
369
 
427
- // Should succeed even without plan file — just skip checkbox toggle
428
- assertTrue(!('error' in result), 'handler should succeed without plan file');
429
- if (!('error' in result)) {
430
- assertTrue(fs.existsSync(result.summaryPath), 'summary should be written even without plan file');
431
- }
370
+ // Create a temp dir WITHOUT a plan file
371
+ const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-no-plan-'));
372
+ const tasksDir = path.join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks');
373
+ fs.mkdirSync(tasksDir, { recursive: true });
432
374
 
433
- cleanupDir(basePath);
434
- cleanup(dbPath);
435
- }
375
+ const params = makeValidParams();
376
+ const result = await handleCompleteTask(params, basePath);
436
377
 
437
- // ═══════════════════════════════════════════════════════════════════════════
378
+ // Should succeed even without plan file — just skip checkbox toggle
379
+ assert.ok(!('error' in result), 'handler should succeed without plan file');
380
+ if (!('error' in result)) {
381
+ assert.ok(fs.existsSync(result.summaryPath), 'summary should be written even without plan file');
382
+ }
438
383
 
439
- report();
384
+ cleanupDir(basePath);
385
+ cleanup(dbPath);
386
+ });
387
+ });