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,13 +1,11 @@
1
+ import { describe, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
1
3
  import { mkdtempSync, mkdirSync, rmSync, realpathSync } from "node:fs";
2
4
  import { join } from "node:path";
3
5
  import { tmpdir } from "node:os";
4
6
  import { spawnSync } from "node:child_process";
5
7
 
6
8
  import { gsdRoot, _clearGsdRootCache } from "../paths.ts";
7
- import { createTestContext } from "./test-helpers.ts";
8
-
9
- const { assertEq, assertTrue, report } = createTestContext();
10
-
11
9
  /** Create a tmp dir and resolve symlinks + 8.3 short names (macOS /var→/private/var, Windows RUNNER~1→runneradmin). */
12
10
  function tmp(): string {
13
11
  const p = mkdtempSync(join(tmpdir(), "gsd-paths-test-"));
@@ -23,91 +21,78 @@ function initGit(dir: string): void {
23
21
  spawnSync("git", ["commit", "--allow-empty", "-m", "init"], { cwd: dir });
24
22
  }
25
23
 
26
- // ── tests ──────────────────────────────────────────────────────────────────
27
-
28
- {
29
- // Case 1: .gsd exists at basePath — fast path
30
- const root = tmp();
31
- try {
32
- mkdirSync(join(root, ".gsd"));
33
- _clearGsdRootCache();
34
- const result = gsdRoot(root);
35
- assertEq(result, join(root, ".gsd"), "fast path: returns basePath/.gsd");
36
- } finally { cleanup(root); }
37
- }
38
-
39
- {
40
- // Case 2: .gsd exists at git root, cwd is a subdirectory
41
- const root = tmp();
42
- try {
43
- initGit(root);
44
- mkdirSync(join(root, ".gsd"));
45
- const sub = join(root, "src", "deep");
46
- mkdirSync(sub, { recursive: true });
47
- _clearGsdRootCache();
48
- const result = gsdRoot(sub);
49
- assertEq(result, join(root, ".gsd"), "git-root probe: finds .gsd at git root from subdirectory");
50
- } finally { cleanup(root); }
51
- }
24
+ describe('paths', () => {
25
+ test('Case 1: .gsd exists at basePath — fast path', () => {
26
+ const root = tmp();
27
+ try {
28
+ mkdirSync(join(root, ".gsd"));
29
+ _clearGsdRootCache();
30
+ const result = gsdRoot(root);
31
+ assert.deepStrictEqual(result, join(root, ".gsd"), "fast path: returns basePath/.gsd");
32
+ } finally { cleanup(root); }
33
+ });
52
34
 
53
- {
54
- // Case 3: .gsd in an ancestor — walk-up finds it (git repo with no .gsd at root)
55
- const root = tmp();
56
- try {
57
- // Init a git repo so git probe returns root — but put .gsd one level deeper
58
- // to force the walk-up path: root/project/.gsd, cwd = root/project/src/deep
59
- initGit(root);
60
- const project = join(root, "project");
61
- mkdirSync(join(project, ".gsd"), { recursive: true });
62
- const deep = join(project, "src", "deep");
63
- mkdirSync(deep, { recursive: true });
64
- _clearGsdRootCache();
65
- // git probe returns root (no .gsd there), so walk-up takes over and finds project/.gsd
66
- const result = gsdRoot(deep);
67
- assertEq(result, join(project, ".gsd"), "walk-up: finds .gsd in ancestor when git root has none");
68
- } finally { cleanup(root); }
69
- }
35
+ test('Case 2: .gsd exists at git root, cwd is a subdirectory', () => {
36
+ const root = tmp();
37
+ try {
38
+ initGit(root);
39
+ mkdirSync(join(root, ".gsd"));
40
+ const sub = join(root, "src", "deep");
41
+ mkdirSync(sub, { recursive: true });
42
+ _clearGsdRootCache();
43
+ const result = gsdRoot(sub);
44
+ assert.deepStrictEqual(result, join(root, ".gsd"), "git-root probe: finds .gsd at git root from subdirectory");
45
+ } finally { cleanup(root); }
46
+ });
70
47
 
71
- {
72
- // Case 4: .gsd nowhere — fallback returns original basePath/.gsd
73
- // Use an isolated git repo so we fully control the environment above basePath
74
- const root = tmp();
75
- try {
76
- initGit(root); // git root = root, no .gsd anywhere
77
- const sub = join(root, "src");
78
- mkdirSync(sub, { recursive: true });
79
- _clearGsdRootCache();
80
- const result = gsdRoot(sub);
81
- // git probe finds root (no .gsd), walk-up finds nothing fallback = sub/.gsd
82
- assertEq(result, join(sub, ".gsd"), "fallback: returns basePath/.gsd when .gsd not found anywhere");
83
- } finally { cleanup(root); }
84
- }
48
+ test('Case 3: .gsd in an ancestor — walk-up finds it', () => {
49
+ const root = tmp();
50
+ try {
51
+ initGit(root);
52
+ const project = join(root, "project");
53
+ mkdirSync(join(project, ".gsd"), { recursive: true });
54
+ const deep = join(project, "src", "deep");
55
+ mkdirSync(deep, { recursive: true });
56
+ _clearGsdRootCache();
57
+ const result = gsdRoot(deep);
58
+ assert.deepStrictEqual(result, join(project, ".gsd"), "walk-up: finds .gsd in ancestor when git root has none");
59
+ } finally { cleanup(root); }
60
+ });
85
61
 
86
- {
87
- // Case 5: cache — second call returns same value without re-probing
88
- const root = tmp();
89
- try {
90
- mkdirSync(join(root, ".gsd"));
91
- _clearGsdRootCache();
92
- const first = gsdRoot(root);
93
- const second = gsdRoot(root);
94
- assertEq(first, second, "cache: same result returned on second call");
95
- assertTrue(first === second, "cache: identity check (same string)");
96
- } finally { cleanup(root); }
97
- }
62
+ test('Case 4: .gsd nowhere — fallback returns original basePath/.gsd', () => {
63
+ const root = tmp();
64
+ try {
65
+ initGit(root);
66
+ const sub = join(root, "src");
67
+ mkdirSync(sub, { recursive: true });
68
+ _clearGsdRootCache();
69
+ const result = gsdRoot(sub);
70
+ assert.deepStrictEqual(result, join(sub, ".gsd"), "fallback: returns basePath/.gsd when .gsd not found anywhere");
71
+ } finally { cleanup(root); }
72
+ });
98
73
 
99
- {
100
- // Case 6: .gsd at basePath takes precedence over ancestor .gsd
101
- const outer = tmp();
102
- try {
103
- initGit(outer);
104
- mkdirSync(join(outer, ".gsd"));
105
- const inner = join(outer, "nested");
106
- mkdirSync(join(inner, ".gsd"), { recursive: true });
107
- _clearGsdRootCache();
108
- const result = gsdRoot(inner);
109
- assertEq(result, join(inner, ".gsd"), "precedence: nearest .gsd wins over ancestor");
110
- } finally { cleanup(outer); }
111
- }
74
+ test('Case 5: cache — second call returns same value without re-probing', () => {
75
+ const root = tmp();
76
+ try {
77
+ mkdirSync(join(root, ".gsd"));
78
+ _clearGsdRootCache();
79
+ const first = gsdRoot(root);
80
+ const second = gsdRoot(root);
81
+ assert.deepStrictEqual(first, second, "cache: same result returned on second call");
82
+ assert.ok(first === second, "cache: identity check (same string)");
83
+ } finally { cleanup(root); }
84
+ });
112
85
 
113
- report();
86
+ test('Case 6: .gsd at basePath takes precedence over ancestor .gsd', () => {
87
+ const outer = tmp();
88
+ try {
89
+ initGit(outer);
90
+ mkdirSync(join(outer, ".gsd"));
91
+ const inner = join(outer, "nested");
92
+ mkdirSync(join(inner, ".gsd"), { recursive: true });
93
+ _clearGsdRootCache();
94
+ const result = gsdRoot(inner);
95
+ assert.deepStrictEqual(result, join(inner, ".gsd"), "precedence: nearest .gsd wins over ancestor");
96
+ } finally { cleanup(outer); }
97
+ });
98
+ });
@@ -1,9 +1,10 @@
1
1
  // GSD Extension — Hook Engine Tests (Post-Unit, Pre-Dispatch, State Persistence)
2
2
 
3
+ import { describe, test } from 'node:test';
4
+ import assert from 'node:assert/strict';
3
5
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readFileSync } from "node:fs";
4
6
  import { join } from "node:path";
5
7
  import { tmpdir } from "node:os";
6
- import { createTestContext } from "./test-helpers.ts";
7
8
  import {
8
9
  checkPostUnitHooks,
9
10
  getActiveHook,
@@ -20,8 +21,6 @@ import {
20
21
  triggerHookManually,
21
22
  } from "../post-unit-hooks.ts";
22
23
 
23
- const { assertEq, assertTrue, assertMatch, report } = createTestContext();
24
-
25
24
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
26
25
 
27
26
  function createFixtureBase(): string {
@@ -36,14 +35,14 @@ function createFixtureBase(): string {
36
35
 
37
36
  // ─── resolveHookArtifactPath ───────────────────────────────────────────────
38
37
 
39
- console.log("\n=== resolveHookArtifactPath ===");
40
38
 
41
- {
39
+ describe('post-unit-hooks', () => {
40
+ test('resolveHookArtifactPath', () => {
42
41
  const base = "/project";
43
42
 
44
43
  // Task-level
45
44
  const taskPath = resolveHookArtifactPath(base, "M001/S01/T01", "REVIEW-PASS.md");
46
- assertEq(
45
+ assert.deepStrictEqual(
47
46
  taskPath,
48
47
  join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-REVIEW-PASS.md"),
49
48
  "task-level artifact path",
@@ -51,7 +50,7 @@ console.log("\n=== resolveHookArtifactPath ===");
51
50
 
52
51
  // Slice-level
53
52
  const slicePath = resolveHookArtifactPath(base, "M001/S01", "REVIEW-PASS.md");
54
- assertEq(
53
+ assert.deepStrictEqual(
55
54
  slicePath,
56
55
  join(base, ".gsd", "milestones", "M001", "slices", "S01", "REVIEW-PASS.md"),
57
56
  "slice-level artifact path",
@@ -59,129 +58,106 @@ console.log("\n=== resolveHookArtifactPath ===");
59
58
 
60
59
  // Milestone-level
61
60
  const milestonePath = resolveHookArtifactPath(base, "M001", "REVIEW-PASS.md");
62
- assertEq(
61
+ assert.deepStrictEqual(
63
62
  milestonePath,
64
63
  join(base, ".gsd", "milestones", "M001", "REVIEW-PASS.md"),
65
64
  "milestone-level artifact path",
66
65
  );
67
- }
66
+ });
68
67
 
69
68
  // ─── resetHookState ────────────────────────────────────────────────────────
70
-
71
- console.log("\n=== resetHookState ===");
72
-
73
- {
69
+ test('resetHookState', () => {
74
70
  resetHookState();
75
- assertEq(getActiveHook(), null, "no active hook after reset");
76
- assertTrue(!isRetryPending(), "no retry pending after reset");
77
- assertEq(consumeRetryTrigger(), null, "no retry trigger after reset");
78
- }
71
+ assert.deepStrictEqual(getActiveHook(), null, "no active hook after reset");
72
+ assert.ok(!isRetryPending(), "no retry pending after reset");
73
+ assert.deepStrictEqual(consumeRetryTrigger(), null, "no retry trigger after reset");
74
+ });
79
75
 
80
76
  // ─── checkPostUnitHooks with no hooks configured ───────────────────────────
81
-
82
- console.log("\n=== No hooks configured ===");
83
-
84
- {
77
+ test('No hooks configured', () => {
85
78
  resetHookState();
86
79
  const base = createFixtureBase();
87
80
  try {
88
81
  const result = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
89
- assertEq(result, null, "returns null when no hooks configured");
82
+ assert.deepStrictEqual(result, null, "returns null when no hooks configured");
90
83
  } finally {
91
84
  rmSync(base, { recursive: true, force: true });
92
85
  }
93
- }
86
+ });
94
87
 
95
88
  // ─── Hook units don't trigger hooks (no hook-on-hook) ──────────────────────
96
-
97
- console.log("\n=== Hook-on-hook prevention ===");
98
-
99
- {
89
+ test('Hook-on-hook prevention', () => {
100
90
  resetHookState();
101
91
  const base = createFixtureBase();
102
92
  try {
103
93
  const result = checkPostUnitHooks("hook/code-review", "M001/S01/T01", base);
104
- assertEq(result, null, "hook units don't trigger other hooks");
94
+ assert.deepStrictEqual(result, null, "hook units don't trigger other hooks");
105
95
  } finally {
106
96
  rmSync(base, { recursive: true, force: true });
107
97
  }
108
- }
98
+ });
109
99
 
110
100
  // ─── consumeRetryTrigger clears state ──────────────────────────────────────
111
-
112
- console.log("\n=== consumeRetryTrigger clears state ===");
113
-
114
- {
101
+ test('consumeRetryTrigger clears state', () => {
115
102
  resetHookState();
116
- assertEq(consumeRetryTrigger(), null, "no trigger initially");
117
- assertTrue(!isRetryPending(), "no retry initially");
118
- }
103
+ assert.deepStrictEqual(consumeRetryTrigger(), null, "no trigger initially");
104
+ assert.ok(!isRetryPending(), "no retry initially");
105
+ });
119
106
 
120
107
  // ─── Variable substitution in prompts ──────────────────────────────────────
121
-
122
- console.log("\n=== Variable substitution ===");
123
-
124
- {
108
+ test('Variable substitution', () => {
125
109
  const base = "/project";
126
110
 
127
111
  // 3-part ID
128
112
  const path3 = resolveHookArtifactPath(base, "M002/S03/T05", "result.md");
129
- assertTrue(path3.includes("M002"), "3-part ID extracts milestoneId");
130
- assertTrue(path3.includes("S03"), "3-part ID extracts sliceId");
131
- assertTrue(path3.includes("T05"), "3-part ID extracts taskId");
132
- assertTrue(path3.includes("milestones"), "3-part ID includes milestones/ segment");
113
+ assert.ok(path3.includes("M002"), "3-part ID extracts milestoneId");
114
+ assert.ok(path3.includes("S03"), "3-part ID extracts sliceId");
115
+ assert.ok(path3.includes("T05"), "3-part ID extracts taskId");
116
+ assert.ok(path3.includes("milestones"), "3-part ID includes milestones/ segment");
133
117
 
134
118
  // 2-part ID
135
119
  const path2 = resolveHookArtifactPath(base, "M002/S03", "result.md");
136
- assertTrue(path2.includes("M002"), "2-part ID extracts milestoneId");
137
- assertTrue(path2.includes("S03"), "2-part ID extracts sliceId");
138
- assertTrue(path2.includes("milestones"), "2-part ID includes milestones/ segment");
120
+ assert.ok(path2.includes("M002"), "2-part ID extracts milestoneId");
121
+ assert.ok(path2.includes("S03"), "2-part ID extracts sliceId");
122
+ assert.ok(path2.includes("milestones"), "2-part ID includes milestones/ segment");
139
123
 
140
124
  // 1-part ID
141
125
  const path1 = resolveHookArtifactPath(base, "M002", "result.md");
142
- assertTrue(path1.includes("M002"), "1-part ID extracts milestoneId");
143
- assertTrue(path1.includes("milestones"), "1-part ID includes milestones/ segment");
144
- }
126
+ assert.ok(path1.includes("M002"), "1-part ID extracts milestoneId");
127
+ assert.ok(path1.includes("milestones"), "1-part ID includes milestones/ segment");
128
+ });
145
129
 
146
130
  // ═══════════════════════════════════════════════════════════════════════════
147
131
  // Phase 2: Pre-Dispatch Hook Tests
148
132
  // ═══════════════════════════════════════════════════════════════════════════
149
-
150
- console.log("\n=== Pre-dispatch: no hooks configured ===");
151
-
152
- {
133
+ test('Pre-dispatch: no hooks configured', () => {
153
134
  const base = createFixtureBase();
154
135
  try {
155
136
  const result = runPreDispatchHooks("execute-task", "M001/S01/T01", "original prompt", base);
156
- assertEq(result.action, "proceed", "proceeds when no hooks");
157
- assertEq(result.prompt, "original prompt", "prompt unchanged");
158
- assertEq(result.firedHooks.length, 0, "no hooks fired");
137
+ assert.deepStrictEqual(result.action, "proceed", "proceeds when no hooks");
138
+ assert.deepStrictEqual(result.prompt, "original prompt", "prompt unchanged");
139
+ assert.deepStrictEqual(result.firedHooks.length, 0, "no hooks fired");
159
140
  } finally {
160
141
  rmSync(base, { recursive: true, force: true });
161
142
  }
162
- }
163
-
164
- console.log("\n=== Pre-dispatch: hook units bypass ===");
143
+ });
165
144
 
166
- {
145
+ test('Pre-dispatch: hook units bypass', () => {
167
146
  const base = createFixtureBase();
168
147
  try {
169
148
  const result = runPreDispatchHooks("hook/review", "M001/S01/T01", "hook prompt", base);
170
- assertEq(result.action, "proceed", "hook units always proceed");
171
- assertEq(result.prompt, "hook prompt", "hook prompt unchanged");
172
- assertEq(result.firedHooks.length, 0, "no hooks fired for hook units");
149
+ assert.deepStrictEqual(result.action, "proceed", "hook units always proceed");
150
+ assert.deepStrictEqual(result.prompt, "hook prompt", "hook prompt unchanged");
151
+ assert.deepStrictEqual(result.firedHooks.length, 0, "no hooks fired for hook units");
173
152
  } finally {
174
153
  rmSync(base, { recursive: true, force: true });
175
154
  }
176
- }
155
+ });
177
156
 
178
157
  // ═══════════════════════════════════════════════════════════════════════════
179
158
  // Phase 3: State Persistence Tests
180
159
  // ═══════════════════════════════════════════════════════════════════════════
181
-
182
- console.log("\n=== State persistence: persist and restore ===");
183
-
184
- {
160
+ test('State persistence: persist and restore', () => {
185
161
  const base = createFixtureBase();
186
162
  try {
187
163
  resetHookState();
@@ -189,19 +165,17 @@ console.log("\n=== State persistence: persist and restore ===");
189
165
  // Persist empty state
190
166
  persistHookState(base);
191
167
  const filePath = join(base, ".gsd", "hook-state.json");
192
- assertTrue(existsSync(filePath), "hook-state.json created");
168
+ assert.ok(existsSync(filePath), "hook-state.json created");
193
169
 
194
170
  const content = JSON.parse(readFileSync(filePath, "utf-8"));
195
- assertEq(typeof content.savedAt, "string", "savedAt is a string");
196
- assertEq(Object.keys(content.cycleCounts).length, 0, "empty cycle counts");
171
+ assert.deepStrictEqual(typeof content.savedAt, "string", "savedAt is a string");
172
+ assert.deepStrictEqual(Object.keys(content.cycleCounts).length, 0, "empty cycle counts");
197
173
  } finally {
198
174
  rmSync(base, { recursive: true, force: true });
199
175
  }
200
- }
201
-
202
- console.log("\n=== State persistence: restore from disk ===");
176
+ });
203
177
 
204
- {
178
+ test('State persistence: restore from disk', () => {
205
179
  const base = createFixtureBase();
206
180
  try {
207
181
  resetHookState();
@@ -222,16 +196,14 @@ console.log("\n=== State persistence: restore from disk ===");
222
196
  // Verify by persisting and reading back
223
197
  persistHookState(base);
224
198
  const restored = JSON.parse(readFileSync(stateFile, "utf-8"));
225
- assertEq(restored.cycleCounts["review/execute-task/M001/S01/T01"], 2, "cycle count restored for review");
226
- assertEq(restored.cycleCounts["simplify/execute-task/M001/S01/T02"], 1, "cycle count restored for simplify");
199
+ assert.deepStrictEqual(restored.cycleCounts["review/execute-task/M001/S01/T01"], 2, "cycle count restored for review");
200
+ assert.deepStrictEqual(restored.cycleCounts["simplify/execute-task/M001/S01/T02"], 1, "cycle count restored for simplify");
227
201
  } finally {
228
202
  rmSync(base, { recursive: true, force: true });
229
203
  }
230
- }
231
-
232
- console.log("\n=== State persistence: clear ===");
204
+ });
233
205
 
234
- {
206
+ test('State persistence: clear', () => {
235
207
  const base = createFixtureBase();
236
208
  try {
237
209
  resetHookState();
@@ -246,77 +218,65 @@ console.log("\n=== State persistence: clear ===");
246
218
  clearPersistedHookState(base);
247
219
 
248
220
  const cleared = JSON.parse(readFileSync(stateFile, "utf-8"));
249
- assertEq(Object.keys(cleared.cycleCounts).length, 0, "cycle counts cleared");
221
+ assert.deepStrictEqual(Object.keys(cleared.cycleCounts).length, 0, "cycle counts cleared");
250
222
  } finally {
251
223
  rmSync(base, { recursive: true, force: true });
252
224
  }
253
- }
225
+ });
254
226
 
255
- console.log("\n=== State persistence: restore handles missing file ===");
256
-
257
- {
227
+ test('State persistence: restore handles missing file', () => {
258
228
  const base = createFixtureBase();
259
229
  try {
260
230
  resetHookState();
261
231
  // Should not throw
262
232
  restoreHookState(base);
263
- assertEq(getActiveHook(), null, "no active hook after restore from missing file");
233
+ assert.deepStrictEqual(getActiveHook(), null, "no active hook after restore from missing file");
264
234
  } finally {
265
235
  rmSync(base, { recursive: true, force: true });
266
236
  }
267
- }
237
+ });
268
238
 
269
- console.log("\n=== State persistence: restore handles corrupt file ===");
270
-
271
- {
239
+ test('State persistence: restore handles corrupt file', () => {
272
240
  const base = createFixtureBase();
273
241
  try {
274
242
  resetHookState();
275
243
  writeFileSync(join(base, ".gsd", "hook-state.json"), "not json", "utf-8");
276
244
  // Should not throw
277
245
  restoreHookState(base);
278
- assertEq(getActiveHook(), null, "no active hook after corrupt restore");
246
+ assert.deepStrictEqual(getActiveHook(), null, "no active hook after corrupt restore");
279
247
  } finally {
280
248
  rmSync(base, { recursive: true, force: true });
281
249
  }
282
- }
250
+ });
283
251
 
284
252
  // ═══════════════════════════════════════════════════════════════════════════
285
253
  // Phase 3: Hook Status Reporting Tests
286
254
  // ═══════════════════════════════════════════════════════════════════════════
287
-
288
- console.log("\n=== Hook status: no hooks ===");
289
-
290
- {
255
+ test('Hook status: no hooks', () => {
291
256
  resetHookState();
292
257
  const entries = getHookStatus();
293
258
  // No preferences file = no hooks
294
- assertEq(entries.length, 0, "no entries when no hooks configured");
259
+ assert.deepStrictEqual(entries.length, 0, "no entries when no hooks configured");
295
260
 
296
261
  const formatted = formatHookStatus();
297
- assertMatch(formatted, /No hooks configured/, "status message says no hooks");
298
- }
262
+ assert.match(formatted, /No hooks configured/, "status message says no hooks");
263
+ });
299
264
 
300
265
  // ═══════════════════════════════════════════════════════════════════════════
301
266
  // Phase 4: Manual Hook Trigger Tests
302
267
  // ═══════════════════════════════════════════════════════════════════════════
303
-
304
- console.log("\n=== triggerHookManually: hook not found ===");
305
-
306
- {
268
+ test('triggerHookManually: hook not found', () => {
307
269
  resetHookState();
308
270
  const base = createFixtureBase();
309
271
  try {
310
272
  const result = triggerHookManually("nonexistent-hook", "execute-task", "M001/S01/T01", base);
311
- assertEq(result, null, "returns null when hook not found");
273
+ assert.deepStrictEqual(result, null, "returns null when hook not found");
312
274
  } finally {
313
275
  rmSync(base, { recursive: true, force: true });
314
276
  }
315
- }
277
+ });
316
278
 
317
- console.log("\n=== triggerHookManually: with configured hook ===");
318
-
319
- {
279
+ test('triggerHookManually: with configured hook', () => {
320
280
  resetHookState();
321
281
  const base = createFixtureBase();
322
282
  try {
@@ -325,16 +285,16 @@ console.log("\n=== triggerHookManually: with configured hook ===");
325
285
  const result = triggerHookManually("code-review", "execute-task", "M001/S01/T01", base);
326
286
  // Result depends on whether code-review hook is configured in preferences
327
287
  // The function should either return null or a valid HookDispatchResult
328
- assertTrue(result === null || typeof result === "object", "returns null or object");
288
+ assert.ok(result === null || typeof result === "object", "returns null or object");
329
289
  if (result) {
330
- assertEq(result.hookName, "code-review", "hook name in result");
331
- assertEq(result.unitType, "hook/code-review", "unit type is hook-prefixed");
332
- assertEq(result.unitId, "M001/S01/T01", "unit ID preserved");
333
- assertTrue(typeof result.prompt === "string", "prompt is a string");
290
+ assert.deepStrictEqual(result.hookName, "code-review", "hook name in result");
291
+ assert.deepStrictEqual(result.unitType, "hook/code-review", "unit type is hook-prefixed");
292
+ assert.deepStrictEqual(result.unitId, "M001/S01/T01", "unit ID preserved");
293
+ assert.ok(typeof result.prompt === "string", "prompt is a string");
334
294
  }
335
295
  } finally {
336
296
  rmSync(base, { recursive: true, force: true });
337
297
  }
338
- }
298
+ });
339
299
 
340
- report();
300
+ });
@@ -15,6 +15,7 @@ import {
15
15
  applyModeDefaults,
16
16
  getIsolationMode,
17
17
  parsePreferencesMarkdown,
18
+ _resetParseWarningFlag,
18
19
  } from "../preferences.ts";
19
20
  import type { GSDPreferences, GSDModelConfigV2, GSDPhaseModelConfig } from "../preferences.ts";
20
21
 
@@ -352,3 +353,29 @@ test("handles empty models config", () => {
352
353
  assert.notEqual(prefs, null);
353
354
  assert.equal(prefs!.models, undefined);
354
355
  });
356
+
357
+ // ── Warn-once for unrecognized format (#2373) ────────────────────────────────
358
+
359
+ test("unrecognized format warning is emitted at most once (#2373)", () => {
360
+ const warnings: string[] = [];
361
+ const origWarn = console.warn;
362
+ console.warn = (...args: unknown[]) => warnings.push(args.join(" "));
363
+ try {
364
+ // Reset internal warned flag so the test starts clean
365
+ _resetParseWarningFlag();
366
+
367
+ const unrecognized = "This is just plain text with no frontmatter or headings.";
368
+
369
+ // Call multiple times — simulates repeated preference loads
370
+ parsePreferencesMarkdown(unrecognized);
371
+ parsePreferencesMarkdown(unrecognized);
372
+ parsePreferencesMarkdown(unrecognized);
373
+
374
+ const relevant = warnings.filter(w => w.includes("unrecognized format"));
375
+ assert.equal(relevant.length, 1, `expected exactly 1 warning, got ${relevant.length}: ${JSON.stringify(relevant)}`);
376
+ } finally {
377
+ console.warn = origWarn;
378
+ // Reset so other tests aren't affected by the flag state
379
+ _resetParseWarningFlag();
380
+ }
381
+ });
@@ -147,12 +147,12 @@ test("plan-slice prompt no longer frames direct PLAN writes as the source of tru
147
147
  assert.match(prompt, /Do \*\*not\*\* rely on direct `PLAN\.md` writes as the source of truth/i);
148
148
  });
149
149
 
150
- test("plan-slice prompt explicitly names gsd_plan_slice and gsd_plan_task as DB-backed planning tools", () => {
150
+ test("plan-slice prompt explicitly names gsd_plan_slice as DB-backed planning tool", () => {
151
151
  const prompt = readPrompt("plan-slice");
152
152
  assert.match(prompt, /gsd_plan_slice/);
153
153
  assert.match(prompt, /gsd_plan_task/);
154
- // The prompt should describe these as the canonical write path
155
- assert.match(prompt, /DB-backed tools are the canonical write path/i);
154
+ // The prompt should describe the DB-backed tool as the canonical write path
155
+ assert.match(prompt, /DB-backed tool is the canonical write path/i);
156
156
  });
157
157
 
158
158
  test("plan-slice prompt does not instruct direct file writes as a primary step", () => {
@@ -161,14 +161,18 @@ test("plan-slice prompt does not instruct direct file writes as a primary step",
161
161
  assert.doesNotMatch(prompt, /^\d+\.\s+Write `?\{\{outputPath\}\}`?\s*$/m);
162
162
  });
163
163
 
164
- test("plan-slice prompt instructs calling gsd_plan_task for each task", () => {
164
+ test("plan-slice prompt clarifies gsd_plan_slice handles task persistence", () => {
165
165
  const prompt = readPrompt("plan-slice");
166
- assert.match(prompt, /call `gsd_plan_task` for each task/i);
166
+ // gsd_plan_slice persists tasks in its transaction — no separate gsd_plan_task calls needed
167
+ assert.match(prompt, /gsd_plan_task/);
168
+ assert.match(prompt, /gsd_plan_slice` handles task persistence/i);
167
169
  });
168
170
 
169
- test("replan-slice prompt requires DB-backed planning state when available", () => {
171
+ test("replan-slice prompt uses gsd_replan_slice as canonical DB-backed tool", () => {
170
172
  const prompt = readPrompt("replan-slice");
171
- assert.match(prompt, /DB-backed planning tool exists for this phase, use it as the source of truth/i);
173
+ assert.match(prompt, /gsd_replan_slice/);
174
+ // Degraded fallback (direct file writes) was removed — DB tools are always available
175
+ assert.doesNotMatch(prompt, /Degraded fallback/i);
172
176
  });
173
177
 
174
178
  test("reassess-roadmap prompt references gsd_reassess_roadmap tool", () => {