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,11 +1,10 @@
1
+ import { describe, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
1
3
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
4
  import { join } from 'node:path';
3
5
  import { tmpdir } from 'node:os';
4
6
 
5
7
  import { deriveState, isSliceComplete, isMilestoneComplete, isGhostMilestone } from '../state.ts';
6
- import { createTestContext } from './test-helpers.ts';
7
-
8
- const { assertEq, assertTrue, report } = createTestContext();
9
8
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
10
9
 
11
10
  function createFixtureBase(): string {
@@ -65,30 +64,28 @@ function cleanup(base: string): void {
65
64
  // Test Groups
66
65
  // ═══════════════════════════════════════════════════════════════════════════
67
66
 
68
- async function main(): Promise<void> {
67
+ describe('derive-state', async () => {
69
68
 
70
69
  // ─── Test 1: empty milestones dir → pre-planning ───────────────────────
71
- console.log('\n=== empty milestones dir → pre-planning ===');
72
- {
70
+ test('empty milestones dir → pre-planning', async () => {
73
71
  const base = createFixtureBase();
74
72
  try {
75
73
  const state = await deriveState(base);
76
74
 
77
- assertEq(state.phase, 'pre-planning', 'phase is pre-planning');
78
- assertEq(state.activeMilestone, null, 'activeMilestone is null');
79
- assertEq(state.activeSlice, null, 'activeSlice is null');
80
- assertEq(state.activeTask, null, 'activeTask is null');
81
- assertEq(state.registry, [], 'registry is empty');
82
- assertEq(state.progress?.milestones?.done, 0, 'milestones done = 0');
83
- assertEq(state.progress?.milestones?.total, 0, 'milestones total = 0');
75
+ assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning');
76
+ assert.deepStrictEqual(state.activeMilestone, null, 'activeMilestone is null');
77
+ assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
78
+ assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
79
+ assert.deepStrictEqual(state.registry, [], 'registry is empty');
80
+ assert.deepStrictEqual(state.progress?.milestones?.done, 0, 'milestones done = 0');
81
+ assert.deepStrictEqual(state.progress?.milestones?.total, 0, 'milestones total = 0');
84
82
  } finally {
85
83
  cleanup(base);
86
84
  }
87
- }
85
+ });
88
86
 
89
87
  // ─── Test 2: milestone dir exists but no roadmap → pre-planning ────────
90
- console.log('\n=== milestone dir exists but no roadmap → pre-planning ===');
91
- {
88
+ test('milestone dir exists but no roadmap → pre-planning', async () => {
92
89
  const base = createFixtureBase();
93
90
  try {
94
91
  // Create M001 directory with CONTEXT but no roadmap file
@@ -97,21 +94,20 @@ async function main(): Promise<void> {
97
94
 
98
95
  const state = await deriveState(base);
99
96
 
100
- assertEq(state.phase, 'pre-planning', 'phase is pre-planning');
101
- assertTrue(state.activeMilestone !== null, 'activeMilestone is not null');
102
- assertEq(state.activeMilestone?.id, 'M001', 'activeMilestone id is M001');
103
- assertEq(state.activeSlice, null, 'activeSlice is null');
104
- assertEq(state.activeTask, null, 'activeTask is null');
105
- assertEq(state.registry.length, 1, 'registry has 1 entry');
106
- assertEq(state.registry[0]?.status, 'active', 'registry entry status is active');
97
+ assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning');
98
+ assert.ok(state.activeMilestone !== null, 'activeMilestone is not null');
99
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'activeMilestone id is M001');
100
+ assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
101
+ assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
102
+ assert.deepStrictEqual(state.registry.length, 1, 'registry has 1 entry');
103
+ assert.deepStrictEqual(state.registry[0]?.status, 'active', 'registry entry status is active');
107
104
  } finally {
108
105
  cleanup(base);
109
106
  }
110
- }
107
+ });
111
108
 
112
109
  // ─── Test 3: roadmap with incomplete slice, no plan → planning ─────────
113
- console.log('\n=== roadmap with incomplete slice, no plan → planning ===');
114
- {
110
+ test('roadmap with incomplete slice, no plan → planning', async () => {
115
111
  const base = createFixtureBase();
116
112
  try {
117
113
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -126,20 +122,19 @@ async function main(): Promise<void> {
126
122
 
127
123
  const state = await deriveState(base);
128
124
 
129
- assertEq(state.phase, 'planning', 'phase is planning');
130
- assertTrue(state.activeSlice !== null, 'activeSlice is not null');
131
- assertEq(state.activeSlice?.id, 'S01', 'activeSlice id is S01');
132
- assertEq(state.activeTask, null, 'activeTask is null');
133
- assertEq(state.progress?.slices?.done, 0, 'slices done = 0');
134
- assertEq(state.progress?.slices?.total, 1, 'slices total = 1');
125
+ assert.deepStrictEqual(state.phase, 'planning', 'phase is planning');
126
+ assert.ok(state.activeSlice !== null, 'activeSlice is not null');
127
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'activeSlice id is S01');
128
+ assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
129
+ assert.deepStrictEqual(state.progress?.slices?.done, 0, 'slices done = 0');
130
+ assert.deepStrictEqual(state.progress?.slices?.total, 1, 'slices total = 1');
135
131
  } finally {
136
132
  cleanup(base);
137
133
  }
138
- }
134
+ });
139
135
 
140
136
  // ─── Test 4: roadmap + plan with incomplete tasks → executing ──────────
141
- console.log('\n=== roadmap + plan with incomplete tasks → executing ===');
142
- {
137
+ test('roadmap + plan with incomplete tasks → executing', async () => {
143
138
  const base = createFixtureBase();
144
139
  try {
145
140
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -168,19 +163,18 @@ async function main(): Promise<void> {
168
163
 
169
164
  const state = await deriveState(base);
170
165
 
171
- assertEq(state.phase, 'executing', 'phase is executing');
172
- assertTrue(state.activeTask !== null, 'activeTask is not null');
173
- assertEq(state.activeTask?.id, 'T01', 'activeTask id is T01');
174
- assertEq(state.progress?.tasks?.done, 0, 'tasks done = 0');
175
- assertEq(state.progress?.tasks?.total, 2, 'tasks total = 2');
166
+ assert.deepStrictEqual(state.phase, 'executing', 'phase is executing');
167
+ assert.ok(state.activeTask !== null, 'activeTask is not null');
168
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'activeTask id is T01');
169
+ assert.deepStrictEqual(state.progress?.tasks?.done, 0, 'tasks done = 0');
170
+ assert.deepStrictEqual(state.progress?.tasks?.total, 2, 'tasks total = 2');
176
171
  } finally {
177
172
  cleanup(base);
178
173
  }
179
- }
174
+ });
180
175
 
181
176
  // ─── Test 5: executing + continue file → resume message ─────────────
182
- console.log('\n=== executing + continue file → resume message ===');
183
- {
177
+ test('executing + continue file → resume message', async () => {
184
178
  const base = createFixtureBase();
185
179
  try {
186
180
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -228,21 +222,20 @@ Continue from step 2.
228
222
 
229
223
  const state = await deriveState(base);
230
224
 
231
- assertEq(state.phase, 'executing', 'interrupted: phase is executing');
232
- assertTrue(state.activeTask !== null, 'interrupted: activeTask is not null');
233
- assertEq(state.activeTask?.id, 'T01', 'interrupted: activeTask id is T01');
234
- assertTrue(
225
+ assert.deepStrictEqual(state.phase, 'executing', 'interrupted: phase is executing');
226
+ assert.ok(state.activeTask !== null, 'interrupted: activeTask is not null');
227
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'interrupted: activeTask id is T01');
228
+ assert.ok(
235
229
  state.nextAction.includes('Resume') || state.nextAction.includes('resume') || state.nextAction.includes('continue.md'),
236
230
  'interrupted: nextAction mentions Resume/resume/continue.md'
237
231
  );
238
232
  } finally {
239
233
  cleanup(base);
240
234
  }
241
- }
235
+ });
242
236
 
243
237
  // ─── Test 6: all tasks done, slice not [x] → summarizing ──────────────
244
- console.log('\n=== all tasks done, slice not [x] → summarizing ===');
245
- {
238
+ test('all tasks done, slice not [x] → summarizing', async () => {
246
239
  const base = createFixtureBase();
247
240
  try {
248
241
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -271,24 +264,23 @@ Continue from step 2.
271
264
 
272
265
  const state = await deriveState(base);
273
266
 
274
- assertEq(state.phase, 'summarizing', 'summarizing: phase is summarizing');
275
- assertTrue(state.activeSlice !== null, 'summarizing: activeSlice is not null');
276
- assertEq(state.activeSlice?.id, 'S01', 'summarizing: activeSlice id is S01');
277
- assertEq(state.activeTask, null, 'summarizing: activeTask is null');
278
- assertTrue(
267
+ assert.deepStrictEqual(state.phase, 'summarizing', 'summarizing: phase is summarizing');
268
+ assert.ok(state.activeSlice !== null, 'summarizing: activeSlice is not null');
269
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'summarizing: activeSlice id is S01');
270
+ assert.deepStrictEqual(state.activeTask, null, 'summarizing: activeTask is null');
271
+ assert.ok(
279
272
  state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
280
273
  'summarizing: nextAction mentions summary or complete'
281
274
  );
282
- assertEq(state.progress?.tasks?.done, 2, 'summarizing: tasks done = 2');
283
- assertEq(state.progress?.tasks?.total, 2, 'summarizing: tasks total = 2');
275
+ assert.deepStrictEqual(state.progress?.tasks?.done, 2, 'summarizing: tasks done = 2');
276
+ assert.deepStrictEqual(state.progress?.tasks?.total, 2, 'summarizing: tasks total = 2');
284
277
  } finally {
285
278
  cleanup(base);
286
279
  }
287
- }
280
+ });
288
281
 
289
282
  // ─── Test 7: all milestones complete → complete ────────────────────────
290
- console.log('\n=== all milestones complete → complete ===');
291
- {
283
+ test('all milestones complete → complete', async () => {
292
284
  const base = createFixtureBase();
293
285
  try {
294
286
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -306,23 +298,22 @@ Continue from step 2.
306
298
 
307
299
  const state = await deriveState(base);
308
300
 
309
- assertEq(state.phase, 'complete', 'complete: phase is complete');
310
- assertEq(state.activeSlice, null, 'complete: activeSlice is null');
311
- assertEq(state.activeTask, null, 'complete: activeTask is null');
312
- assertTrue(
301
+ assert.deepStrictEqual(state.phase, 'complete', 'complete: phase is complete');
302
+ assert.deepStrictEqual(state.activeSlice, null, 'complete: activeSlice is null');
303
+ assert.deepStrictEqual(state.activeTask, null, 'complete: activeTask is null');
304
+ assert.ok(
313
305
  state.nextAction.toLowerCase().includes('complete'),
314
306
  'complete: nextAction mentions complete'
315
307
  );
316
- assertEq(state.registry.length, 1, 'complete: registry has 1 entry');
317
- assertEq(state.registry[0]?.status, 'complete', 'complete: registry[0] status is complete');
308
+ assert.deepStrictEqual(state.registry.length, 1, 'complete: registry has 1 entry');
309
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'complete: registry[0] status is complete');
318
310
  } finally {
319
311
  cleanup(base);
320
312
  }
321
- }
313
+ });
322
314
 
323
315
  // ─── Test 7b: complete with active requirements → surfaces unmapped reqs ──
324
- console.log('\n=== complete with active requirements → surfaces unmapped reqs ===');
325
- {
316
+ test('complete with active requirements → surfaces unmapped reqs', async () => {
326
317
  const base = createFixtureBase();
327
318
  try {
328
319
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -355,23 +346,22 @@ Continue from step 2.
355
346
 
356
347
  const state = await deriveState(base);
357
348
 
358
- assertEq(state.phase, 'complete', 'complete-with-reqs: phase is complete');
359
- assertTrue(
349
+ assert.deepStrictEqual(state.phase, 'complete', 'complete-with-reqs: phase is complete');
350
+ assert.ok(
360
351
  state.nextAction.includes('2 active requirements'),
361
352
  'complete-with-reqs: nextAction mentions 2 active requirements'
362
353
  );
363
- assertTrue(
354
+ assert.ok(
364
355
  state.nextAction.includes('REQUIREMENTS.md'),
365
356
  'complete-with-reqs: nextAction mentions REQUIREMENTS.md'
366
357
  );
367
358
  } finally {
368
359
  cleanup(base);
369
360
  }
370
- }
361
+ });
371
362
 
372
363
  // ─── Test 7c: complete with no active requirements → standard message ──
373
- console.log('\n=== complete with no active requirements → standard message ===');
374
- {
364
+ test('complete with no active requirements → standard message', async () => {
375
365
  const base = createFixtureBase();
376
366
  try {
377
367
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -396,16 +386,15 @@ Continue from step 2.
396
386
 
397
387
  const state = await deriveState(base);
398
388
 
399
- assertEq(state.phase, 'complete', 'complete-no-active-reqs: phase is complete');
400
- assertEq(state.nextAction, 'All milestones complete.', 'complete-no-active-reqs: standard completion message');
389
+ assert.deepStrictEqual(state.phase, 'complete', 'complete-no-active-reqs: phase is complete');
390
+ assert.deepStrictEqual(state.nextAction, 'All milestones complete.', 'complete-no-active-reqs: standard completion message');
401
391
  } finally {
402
392
  cleanup(base);
403
393
  }
404
- }
394
+ });
405
395
 
406
396
  // ─── Test 8: blocked dependencies ──────────────────────────────────────
407
- console.log('\n=== blocked dependencies ===');
408
- {
397
+ test('blocked dependencies', async () => {
409
398
  // Case A: S01 active (deps satisfied), S02 blocked on S01
410
399
  const base1 = createFixtureBase();
411
400
  try {
@@ -436,8 +425,8 @@ Continue from step 2.
436
425
 
437
426
  const state1 = await deriveState(base1);
438
427
 
439
- assertEq(state1.phase, 'executing', 'blocked-A: phase is executing (S01 active)');
440
- assertEq(state1.activeSlice?.id, 'S01', 'blocked-A: activeSlice is S01');
428
+ assert.deepStrictEqual(state1.phase, 'executing', 'blocked-A: phase is executing (S01 active)');
429
+ assert.deepStrictEqual(state1.activeSlice?.id, 'S01', 'blocked-A: activeSlice is S01');
441
430
  } finally {
442
431
  cleanup(base1);
443
432
  }
@@ -457,17 +446,16 @@ Continue from step 2.
457
446
 
458
447
  const state2 = await deriveState(base2);
459
448
 
460
- assertEq(state2.phase, 'blocked', 'blocked-B: phase is blocked');
461
- assertEq(state2.activeSlice, null, 'blocked-B: activeSlice is null');
462
- assertTrue(state2.blockers.length > 0, 'blocked-B: blockers array is non-empty');
449
+ assert.deepStrictEqual(state2.phase, 'blocked', 'blocked-B: phase is blocked');
450
+ assert.deepStrictEqual(state2.activeSlice, null, 'blocked-B: activeSlice is null');
451
+ assert.ok(state2.blockers.length > 0, 'blocked-B: blockers array is non-empty');
463
452
  } finally {
464
453
  cleanup(base2);
465
454
  }
466
- }
455
+ });
467
456
 
468
457
  // ─── Test 9: multi-milestone registry ──────────────────────────────────
469
- console.log('\n=== multi-milestone registry ===');
470
- {
458
+ test('multi-milestone registry', async () => {
471
459
  const base = createFixtureBase();
472
460
  try {
473
461
  // M001: complete (all slices done)
@@ -501,24 +489,23 @@ Continue from step 2.
501
489
 
502
490
  const state = await deriveState(base);
503
491
 
504
- assertEq(state.registry.length, 3, 'multi-ms: registry has 3 entries');
505
- assertEq(state.registry[0]?.id, 'M001', 'multi-ms: registry[0] is M001');
506
- assertEq(state.registry[0]?.status, 'complete', 'multi-ms: M001 is complete');
507
- assertEq(state.registry[1]?.id, 'M002', 'multi-ms: registry[1] is M002');
508
- assertEq(state.registry[1]?.status, 'active', 'multi-ms: M002 is active');
509
- assertEq(state.registry[2]?.id, 'M003', 'multi-ms: registry[2] is M003');
510
- assertEq(state.registry[2]?.status, 'pending', 'multi-ms: M003 is pending');
511
- assertEq(state.activeMilestone?.id, 'M002', 'multi-ms: activeMilestone is M002');
512
- assertEq(state.progress?.milestones?.done, 1, 'multi-ms: milestones done = 1');
513
- assertEq(state.progress?.milestones?.total, 3, 'multi-ms: milestones total = 3');
492
+ assert.deepStrictEqual(state.registry.length, 3, 'multi-ms: registry has 3 entries');
493
+ assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-ms: registry[0] is M001');
494
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-ms: M001 is complete');
495
+ assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-ms: registry[1] is M002');
496
+ assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-ms: M002 is active');
497
+ assert.deepStrictEqual(state.registry[2]?.id, 'M003', 'multi-ms: registry[2] is M003');
498
+ assert.deepStrictEqual(state.registry[2]?.status, 'pending', 'multi-ms: M003 is pending');
499
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-ms: activeMilestone is M002');
500
+ assert.deepStrictEqual(state.progress?.milestones?.done, 1, 'multi-ms: milestones done = 1');
501
+ assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'multi-ms: milestones total = 3');
514
502
  } finally {
515
503
  cleanup(base);
516
504
  }
517
- }
505
+ });
518
506
 
519
507
  // ─── Test 10: requirements integration ─────────────────────────────────
520
- console.log('\n=== requirements integration ===');
521
- {
508
+ test('requirements integration', async () => {
522
509
  const base = createFixtureBase();
523
510
  try {
524
511
  writeRequirements(base, `# Requirements
@@ -559,20 +546,19 @@ Continue from step 2.
559
546
  // Need at least an empty milestones dir for deriveState
560
547
  const state = await deriveState(base);
561
548
 
562
- assertTrue(state.requirements !== undefined, 'requirements: requirements object exists');
563
- assertEq(state.requirements?.active, 2, 'requirements: active = 2');
564
- assertEq(state.requirements?.validated, 1, 'requirements: validated = 1');
565
- assertEq(state.requirements?.deferred, 2, 'requirements: deferred = 2');
566
- assertEq(state.requirements?.outOfScope, 1, 'requirements: outOfScope = 1');
567
- assertEq(state.requirements?.total, 6, 'requirements: total = 6 (sum of all)');
549
+ assert.ok(state.requirements !== undefined, 'requirements: requirements object exists');
550
+ assert.deepStrictEqual(state.requirements?.active, 2, 'requirements: active = 2');
551
+ assert.deepStrictEqual(state.requirements?.validated, 1, 'requirements: validated = 1');
552
+ assert.deepStrictEqual(state.requirements?.deferred, 2, 'requirements: deferred = 2');
553
+ assert.deepStrictEqual(state.requirements?.outOfScope, 1, 'requirements: outOfScope = 1');
554
+ assert.deepStrictEqual(state.requirements?.total, 6, 'requirements: total = 6 (sum of all)');
568
555
  } finally {
569
556
  cleanup(base);
570
557
  }
571
- }
558
+ });
572
559
 
573
560
  // ─── Test 11: all slices [x], no summary → completing-milestone ────────
574
- console.log('\n=== all slices [x], no summary → completing-milestone ===');
575
- {
561
+ test('all slices [x], no summary → completing-milestone', async () => {
576
562
  const base = createFixtureBase();
577
563
  try {
578
564
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -592,27 +578,26 @@ Continue from step 2.
592
578
 
593
579
  const state = await deriveState(base);
594
580
 
595
- assertEq(state.phase, 'completing-milestone', 'completing-ms: phase is completing-milestone');
596
- assertTrue(state.activeMilestone !== null, 'completing-ms: activeMilestone is not null');
597
- assertEq(state.activeMilestone?.id, 'M001', 'completing-ms: activeMilestone id is M001');
598
- assertEq(state.activeSlice, null, 'completing-ms: activeSlice is null');
599
- assertEq(state.activeTask, null, 'completing-ms: activeTask is null');
600
- assertEq(state.registry.length, 1, 'completing-ms: registry has 1 entry');
601
- assertEq(state.registry[0]?.status, 'active', 'completing-ms: registry[0] status is active (not complete)');
602
- assertEq(state.progress?.slices?.done, 2, 'completing-ms: slices done = 2');
603
- assertEq(state.progress?.slices?.total, 2, 'completing-ms: slices total = 2');
604
- assertTrue(
581
+ assert.deepStrictEqual(state.phase, 'completing-milestone', 'completing-ms: phase is completing-milestone');
582
+ assert.ok(state.activeMilestone !== null, 'completing-ms: activeMilestone is not null');
583
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'completing-ms: activeMilestone id is M001');
584
+ assert.deepStrictEqual(state.activeSlice, null, 'completing-ms: activeSlice is null');
585
+ assert.deepStrictEqual(state.activeTask, null, 'completing-ms: activeTask is null');
586
+ assert.deepStrictEqual(state.registry.length, 1, 'completing-ms: registry has 1 entry');
587
+ assert.deepStrictEqual(state.registry[0]?.status, 'active', 'completing-ms: registry[0] status is active (not complete)');
588
+ assert.deepStrictEqual(state.progress?.slices?.done, 2, 'completing-ms: slices done = 2');
589
+ assert.deepStrictEqual(state.progress?.slices?.total, 2, 'completing-ms: slices total = 2');
590
+ assert.ok(
605
591
  state.nextAction.toLowerCase().includes('summary') || state.nextAction.toLowerCase().includes('complete'),
606
592
  'completing-ms: nextAction mentions summary or complete'
607
593
  );
608
594
  } finally {
609
595
  cleanup(base);
610
596
  }
611
- }
597
+ });
612
598
 
613
599
  // ─── Test 12: all slices [x], summary exists → complete ───────────────
614
- console.log('\n=== all slices [x], summary exists → complete ===');
615
- {
600
+ test('all slices [x], summary exists → complete', async () => {
616
601
  const base = createFixtureBase();
617
602
  try {
618
603
  writeRoadmap(base, 'M001', `# M001: Test Milestone
@@ -630,19 +615,18 @@ Continue from step 2.
630
615
 
631
616
  const state = await deriveState(base);
632
617
 
633
- assertEq(state.phase, 'complete', 'summary-exists: phase is complete');
634
- assertEq(state.registry.length, 1, 'summary-exists: registry has 1 entry');
635
- assertEq(state.registry[0]?.status, 'complete', 'summary-exists: registry[0] status is complete');
636
- assertEq(state.activeSlice, null, 'summary-exists: activeSlice is null');
637
- assertEq(state.activeTask, null, 'summary-exists: activeTask is null');
618
+ assert.deepStrictEqual(state.phase, 'complete', 'summary-exists: phase is complete');
619
+ assert.deepStrictEqual(state.registry.length, 1, 'summary-exists: registry has 1 entry');
620
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'summary-exists: registry[0] status is complete');
621
+ assert.deepStrictEqual(state.activeSlice, null, 'summary-exists: activeSlice is null');
622
+ assert.deepStrictEqual(state.activeTask, null, 'summary-exists: activeTask is null');
638
623
  } finally {
639
624
  cleanup(base);
640
625
  }
641
- }
626
+ });
642
627
 
643
628
  // ─── Test 13: multi-milestone completing-milestone ─────────────────────
644
- console.log('\n=== multi-milestone completing-milestone ===');
645
- {
629
+ test('multi-milestone completing-milestone', async () => {
646
630
  const base = createFixtureBase();
647
631
  try {
648
632
  // M001: all slices done + summary exists → complete
@@ -687,29 +671,28 @@ Continue from step 2.
687
671
 
688
672
  const state = await deriveState(base);
689
673
 
690
- assertEq(state.phase, 'completing-milestone', 'multi-completing: phase is completing-milestone');
691
- assertEq(state.activeMilestone?.id, 'M002', 'multi-completing: activeMilestone is M002');
692
- assertEq(state.activeSlice, null, 'multi-completing: activeSlice is null');
693
- assertEq(state.activeTask, null, 'multi-completing: activeTask is null');
694
- assertEq(state.registry.length, 3, 'multi-completing: registry has 3 entries');
695
- assertEq(state.registry[0]?.id, 'M001', 'multi-completing: registry[0] is M001');
696
- assertEq(state.registry[0]?.status, 'complete', 'multi-completing: M001 is complete');
697
- assertEq(state.registry[1]?.id, 'M002', 'multi-completing: registry[1] is M002');
698
- assertEq(state.registry[1]?.status, 'active', 'multi-completing: M002 is active (completing-milestone)');
699
- assertEq(state.registry[2]?.id, 'M003', 'multi-completing: registry[2] is M003');
700
- assertEq(state.registry[2]?.status, 'pending', 'multi-completing: M003 is pending');
701
- assertEq(state.progress?.milestones?.done, 1, 'multi-completing: milestones done = 1');
702
- assertEq(state.progress?.milestones?.total, 3, 'multi-completing: milestones total = 3');
703
- assertEq(state.progress?.slices?.done, 2, 'multi-completing: slices done = 2');
704
- assertEq(state.progress?.slices?.total, 2, 'multi-completing: slices total = 2');
674
+ assert.deepStrictEqual(state.phase, 'completing-milestone', 'multi-completing: phase is completing-milestone');
675
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-completing: activeMilestone is M002');
676
+ assert.deepStrictEqual(state.activeSlice, null, 'multi-completing: activeSlice is null');
677
+ assert.deepStrictEqual(state.activeTask, null, 'multi-completing: activeTask is null');
678
+ assert.deepStrictEqual(state.registry.length, 3, 'multi-completing: registry has 3 entries');
679
+ assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-completing: registry[0] is M001');
680
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-completing: M001 is complete');
681
+ assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-completing: registry[1] is M002');
682
+ assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-completing: M002 is active (completing-milestone)');
683
+ assert.deepStrictEqual(state.registry[2]?.id, 'M003', 'multi-completing: registry[2] is M003');
684
+ assert.deepStrictEqual(state.registry[2]?.status, 'pending', 'multi-completing: M003 is pending');
685
+ assert.deepStrictEqual(state.progress?.milestones?.done, 1, 'multi-completing: milestones done = 1');
686
+ assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'multi-completing: milestones total = 3');
687
+ assert.deepStrictEqual(state.progress?.slices?.done, 2, 'multi-completing: slices done = 2');
688
+ assert.deepStrictEqual(state.progress?.slices?.total, 2, 'multi-completing: slices total = 2');
705
689
  } finally {
706
690
  cleanup(base);
707
691
  }
708
- }
692
+ });
709
693
 
710
694
  // ═══ Milestone with summary but no roadmap → complete ═══════════════════
711
695
  {
712
- console.log('\n=== milestone with summary and no roadmap → complete ===');
713
696
  const base = createFixtureBase();
714
697
  try {
715
698
  // M001, M002: completed milestones with summaries but no roadmaps
@@ -726,17 +709,17 @@ Continue from step 2.
726
709
 
727
710
  const state = await deriveState(base);
728
711
 
729
- assertEq(state.phase, 'planning', 'summary-no-roadmap: phase is planning (active is M003)');
730
- assertEq(state.activeMilestone?.id, 'M003', 'summary-no-roadmap: active milestone is M003');
731
- assertEq(state.activeMilestone?.title, 'Polish', 'summary-no-roadmap: active title is Polish');
732
- assertEq(state.registry.length, 3, 'summary-no-roadmap: registry has 3 entries');
733
- assertEq(state.registry[0]?.status, 'complete', 'summary-no-roadmap: M001 is complete');
734
- assertEq(state.registry[0]?.title, 'Bootstrap', 'summary-no-roadmap: M001 title from summary');
735
- assertEq(state.registry[1]?.status, 'complete', 'summary-no-roadmap: M002 is complete');
736
- assertEq(state.registry[1]?.title, 'Core Features', 'summary-no-roadmap: M002 title from summary');
737
- assertEq(state.registry[2]?.status, 'active', 'summary-no-roadmap: M003 is active');
738
- assertEq(state.progress?.milestones?.done, 2, 'summary-no-roadmap: milestones done = 2');
739
- assertEq(state.progress?.milestones?.total, 3, 'summary-no-roadmap: milestones total = 3');
712
+ assert.deepStrictEqual(state.phase, 'planning', 'summary-no-roadmap: phase is planning (active is M003)');
713
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'summary-no-roadmap: active milestone is M003');
714
+ assert.deepStrictEqual(state.activeMilestone?.title, 'Polish', 'summary-no-roadmap: active title is Polish');
715
+ assert.deepStrictEqual(state.registry.length, 3, 'summary-no-roadmap: registry has 3 entries');
716
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'summary-no-roadmap: M001 is complete');
717
+ assert.deepStrictEqual(state.registry[0]?.title, 'Bootstrap', 'summary-no-roadmap: M001 title from summary');
718
+ assert.deepStrictEqual(state.registry[1]?.status, 'complete', 'summary-no-roadmap: M002 is complete');
719
+ assert.deepStrictEqual(state.registry[1]?.title, 'Core Features', 'summary-no-roadmap: M002 title from summary');
720
+ assert.deepStrictEqual(state.registry[2]?.status, 'active', 'summary-no-roadmap: M003 is active');
721
+ assert.deepStrictEqual(state.progress?.milestones?.done, 2, 'summary-no-roadmap: milestones done = 2');
722
+ assert.deepStrictEqual(state.progress?.milestones?.total, 3, 'summary-no-roadmap: milestones total = 3');
740
723
  } finally {
741
724
  cleanup(base);
742
725
  }
@@ -744,7 +727,6 @@ Continue from step 2.
744
727
 
745
728
  // ═══ All milestones have summary but no roadmap → complete ═════════════
746
729
  {
747
- console.log('\n=== all milestones summary-only → complete ===');
748
730
  const base = createFixtureBase();
749
731
  try {
750
732
  const m1dir = join(base, '.gsd', 'milestones', 'M001');
@@ -752,16 +734,15 @@ Continue from step 2.
752
734
  writeFileSync(join(m1dir, 'M001-SUMMARY.md'), '---\ntitle: Done\n---\nAll done.');
753
735
 
754
736
  const state = await deriveState(base);
755
- assertEq(state.phase, 'complete', 'all-summary-only: phase is complete');
756
- assertEq(state.registry[0]?.status, 'complete', 'all-summary-only: M001 is complete');
737
+ assert.deepStrictEqual(state.phase, 'complete', 'all-summary-only: phase is complete');
738
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'all-summary-only: M001 is complete');
757
739
  } finally {
758
740
  cleanup(base);
759
741
  }
760
742
  }
761
743
 
762
744
  // ─── Empty plan (zero tasks) stays in planning, not summarizing (#454) ──
763
- console.log('\n=== empty plan → planning (not summarizing) ===');
764
- {
745
+ test('empty plan → planning (not summarizing)', async () => {
765
746
  const base = createFixtureBase();
766
747
  try {
767
748
  writeRoadmap(base, 'M001', `---
@@ -786,17 +767,16 @@ slice: S01
786
767
  ## Tasks
787
768
  `);
788
769
  const state = await deriveState(base);
789
- assertEq(state.phase, 'planning', 'empty plan stays in planning');
790
- assertEq(state.activeSlice?.id, 'S01', 'active slice is S01');
791
- assertEq(state.activeTask, null, 'no active task');
770
+ assert.deepStrictEqual(state.phase, 'planning', 'empty plan stays in planning');
771
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'active slice is S01');
772
+ assert.deepStrictEqual(state.activeTask, null, 'no active task');
792
773
  } finally {
793
774
  cleanup(base);
794
775
  }
795
- }
776
+ });
796
777
 
797
778
  // ─── Test: completed M001 (summary, no validation) skipped for active M003 (#864) ────
798
- console.log('\n=== completed milestone with summary but no validation is not active (#864) ===');
799
- {
779
+ test('completed milestone with summary but no validation is not active (#864)', async () => {
800
780
  const base = createFixtureBase();
801
781
  try {
802
782
  // M001: all slices done, has summary, no validation
@@ -806,17 +786,16 @@ slice: S01
806
786
  writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
807
787
 
808
788
  const state = await deriveState(base);
809
- assertEq(state.activeMilestone?.id, 'M003', 'active milestone is M003, not completed M001');
789
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'active milestone is M003, not completed M001');
810
790
  const m001Entry = state.registry.find(e => e.id === 'M001');
811
- assertEq(m001Entry?.status, 'complete', 'M001 is marked complete despite no validation');
791
+ assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 is marked complete despite no validation');
812
792
  } finally {
813
793
  cleanup(base);
814
794
  }
815
- }
795
+ });
816
796
 
817
797
  // ─── Test: completed M001 with summary AND validation is complete (#864) ────
818
- console.log('\n=== completed milestone with summary and validation is complete ===');
819
- {
798
+ test('completed milestone with summary and validation is complete', async () => {
820
799
  const base = createFixtureBase();
821
800
  try {
822
801
  writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Done.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
@@ -825,32 +804,30 @@ slice: S01
825
804
  writeRoadmap(base, 'M003', `# M003: Active Milestone\n\n**Vision:** Do stuff.\n\n## Slices\n\n- [ ] **S01: Work slice** \`risk:low\` \`depends:[]\`\n > Needs work.\n`);
826
805
 
827
806
  const state = await deriveState(base);
828
- assertEq(state.activeMilestone?.id, 'M003', 'active milestone is M003');
807
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M003', 'active milestone is M003');
829
808
  const m001Entry = state.registry.find(e => e.id === 'M001');
830
- assertEq(m001Entry?.status, 'complete', 'M001 with both summary and validation is complete');
809
+ assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 with both summary and validation is complete');
831
810
  } finally {
832
811
  cleanup(base);
833
812
  }
834
- }
813
+ });
835
814
 
836
815
  // ─── Test: all slices done, no summary, no validation → needs validation (#864) ────
837
- console.log('\n=== all slices done, no summary, no validation → validating-milestone ===');
838
- {
816
+ test('all slices done, no summary, no validation → validating-milestone', async () => {
839
817
  const base = createFixtureBase();
840
818
  try {
841
819
  writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Validate me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
842
820
  // No summary, no validation — this should be active for validation
843
821
 
844
822
  const state = await deriveState(base);
845
- assertEq(state.activeMilestone?.id, 'M001', 'M001 is active for validation');
823
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'M001 is active for validation');
846
824
  } finally {
847
825
  cleanup(base);
848
826
  }
849
- }
827
+ });
850
828
 
851
829
  // ─── Test: all slices done, validation pass, no summary → needs completion (#864) ────
852
- console.log('\n=== all slices done, validation pass, no summary → completing-milestone ===');
853
- {
830
+ test('all slices done, validation pass, no summary → completing-milestone', async () => {
854
831
  const base = createFixtureBase();
855
832
  try {
856
833
  writeRoadmap(base, 'M001', `# M001: First Milestone\n\n**Vision:** Complete me.\n\n## Slices\n\n- [x] **S01: Done slice** \`risk:low\` \`depends:[]\`\n > Completed.\n`);
@@ -858,15 +835,14 @@ slice: S01
858
835
  // No summary — validated but not yet completed
859
836
 
860
837
  const state = await deriveState(base);
861
- assertEq(state.activeMilestone?.id, 'M001', 'M001 is active for completion');
838
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'M001 is active for completion');
862
839
  } finally {
863
840
  cleanup(base);
864
841
  }
865
- }
842
+ });
866
843
 
867
844
  // ─── Test: unchecked roadmap slices + summary → complete (summary is terminal) ────
868
- console.log('\n=== unchecked roadmap slices + summary → complete (summary is terminal) ===');
869
- {
845
+ test('unchecked roadmap slices + summary → complete (summary is terminal)', async () => {
870
846
  const base = createFixtureBase();
871
847
  try {
872
848
  // M001: roadmap has unchecked slices but a summary exists — should be complete
@@ -877,16 +853,15 @@ slice: S01
877
853
 
878
854
  const state = await deriveState(base);
879
855
  const m001Entry = state.registry.find(e => e.id === 'M001');
880
- assertEq(m001Entry?.status, 'complete', 'M001 with unchecked roadmap + summary is complete');
881
- assertEq(state.activeMilestone?.id, 'M002', 'active milestone is M002, not M001');
856
+ assert.deepStrictEqual(m001Entry?.status, 'complete', 'M001 with unchecked roadmap + summary is complete');
857
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'active milestone is M002, not M001');
882
858
  } finally {
883
859
  cleanup(base);
884
860
  }
885
- }
861
+ });
886
862
 
887
863
  // ─── Test: unchecked roadmap + summary counts toward completeMilestoneIds (deps) ────
888
- console.log('\n=== unchecked roadmap + summary satisfies dependency ===');
889
- {
864
+ test('unchecked roadmap + summary satisfies dependency', async () => {
890
865
  const base = createFixtureBase();
891
866
  try {
892
867
  // M001: unchecked roadmap + summary → complete
@@ -899,17 +874,16 @@ slice: S01
899
874
  writeFileSync(join(contextDir, 'M002-CONTEXT.md'), '---\ndepends_on:\n - M001\n---\n\n# M002 Context\n\nDepends on M001.');
900
875
 
901
876
  const state = await deriveState(base);
902
- assertEq(state.activeMilestone?.id, 'M002', 'M002 is active — M001 dependency satisfied via summary');
877
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'M002 is active — M001 dependency satisfied via summary');
903
878
  const m002Entry = state.registry.find(e => e.id === 'M002');
904
- assertEq(m002Entry?.status, 'active', 'M002 status is active, not pending');
879
+ assert.deepStrictEqual(m002Entry?.status, 'active', 'M002 status is active, not pending');
905
880
  } finally {
906
881
  cleanup(base);
907
882
  }
908
- }
883
+ });
909
884
 
910
885
  // ─── Test: ghost milestone (only META.json) is skipped ───────────────
911
- console.log('\n=== ghost milestone (only META.json) is skipped ===');
912
- {
886
+ test('ghost milestone (only META.json) is skipped', async () => {
913
887
  const base = createFixtureBase();
914
888
  try {
915
889
  // Create a ghost milestone directory with only META.json
@@ -918,21 +892,20 @@ slice: S01
918
892
  writeFileSync(join(ghostDir, 'META.json'), JSON.stringify({ id: 'M001' }));
919
893
 
920
894
  // isGhostMilestone should detect it
921
- assertTrue(isGhostMilestone(base, 'M001'), 'M001 is a ghost milestone');
895
+ assert.ok(isGhostMilestone(base, 'M001'), 'M001 is a ghost milestone');
922
896
 
923
897
  // deriveState should treat this as pre-planning (no real milestones)
924
898
  const state = await deriveState(base);
925
- assertEq(state.phase, 'pre-planning', 'ghost-only: phase is pre-planning');
926
- assertEq(state.activeMilestone, null, 'ghost-only: no active milestone');
927
- assertEq(state.registry.length, 0, 'ghost-only: registry is empty');
899
+ assert.deepStrictEqual(state.phase, 'pre-planning', 'ghost-only: phase is pre-planning');
900
+ assert.deepStrictEqual(state.activeMilestone, null, 'ghost-only: no active milestone');
901
+ assert.deepStrictEqual(state.registry.length, 0, 'ghost-only: registry is empty');
928
902
  } finally {
929
903
  cleanup(base);
930
904
  }
931
- }
905
+ });
932
906
 
933
907
  // ─── Test: ghost milestone skipped when real milestones exist ──────────
934
- console.log('\n=== ghost milestone skipped alongside real milestones ===');
935
- {
908
+ test('ghost milestone skipped alongside real milestones', async () => {
936
909
  const base = createFixtureBase();
937
910
  try {
938
911
  // M001: ghost (only META.json)
@@ -946,20 +919,19 @@ slice: S01
946
919
  writeFileSync(join(realDir, 'M002-CONTEXT.md'), '# Real Milestone\n\nThis has content.');
947
920
 
948
921
  const state = await deriveState(base);
949
- assertEq(state.activeMilestone?.id, 'M002', 'ghost+real: active milestone is M002');
922
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'ghost+real: active milestone is M002');
950
923
  // Ghost M001 should not appear in the registry
951
924
  const m001Entry = state.registry.find(e => e.id === 'M001');
952
- assertEq(m001Entry, undefined, 'ghost+real: M001 not in registry');
953
- assertEq(state.registry.length, 1, 'ghost+real: registry has 1 entry');
954
- assertEq(state.registry[0]?.status, 'active', 'ghost+real: M002 is active');
925
+ assert.deepStrictEqual(m001Entry, undefined, 'ghost+real: M001 not in registry');
926
+ assert.deepStrictEqual(state.registry.length, 1, 'ghost+real: registry has 1 entry');
927
+ assert.deepStrictEqual(state.registry[0]?.status, 'active', 'ghost+real: M002 is active');
955
928
  } finally {
956
929
  cleanup(base);
957
930
  }
958
- }
931
+ });
959
932
 
960
933
  // ─── Test: zero-slice roadmap → pre-planning, not blocked (#1785) ────
961
- console.log('\n=== zero-slice roadmap → pre-planning, not blocked (#1785) ===');
962
- {
934
+ test('zero-slice roadmap → pre-planning, not blocked (#1785)', async () => {
963
935
  const base = createFixtureBase();
964
936
  try {
965
937
  // Write a stub roadmap with zero slices (placeholder text, no slice definitions)
@@ -967,22 +939,15 @@ slice: S01
967
939
 
968
940
  const state = await deriveState(base);
969
941
 
970
- assertEq(state.phase, 'pre-planning', 'phase is pre-planning when roadmap has zero slices');
971
- assertTrue(state.activeMilestone !== null, 'activeMilestone is set');
972
- assertEq(state.activeMilestone?.id, 'M001', 'activeMilestone is M001');
973
- assertEq(state.activeSlice, null, 'activeSlice is null');
974
- assertEq(state.activeTask, null, 'activeTask is null');
975
- assertEq(state.blockers.length, 0, 'no blockers reported');
976
- assertTrue(state.nextAction.includes('M001'), 'nextAction references M001');
942
+ assert.deepStrictEqual(state.phase, 'pre-planning', 'phase is pre-planning when roadmap has zero slices');
943
+ assert.ok(state.activeMilestone !== null, 'activeMilestone is set');
944
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'activeMilestone is M001');
945
+ assert.deepStrictEqual(state.activeSlice, null, 'activeSlice is null');
946
+ assert.deepStrictEqual(state.activeTask, null, 'activeTask is null');
947
+ assert.deepStrictEqual(state.blockers.length, 0, 'no blockers reported');
948
+ assert.ok(state.nextAction.includes('M001'), 'nextAction references M001');
977
949
  } finally {
978
950
  cleanup(base);
979
951
  }
980
- }
981
-
982
- report();
983
- }
984
-
985
- main().catch((error) => {
986
- console.error(error);
987
- process.exit(1);
952
+ });
988
953
  });