gsd-pi 2.44.0 → 2.45.0

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 (402) 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 +7 -7
  43. package/dist/web/standalone/.next/build-manifest.json +3 -3
  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/server/app/_global-error/page_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  48. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found/page.js +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  65. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/index.html +1 -1
  106. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  107. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  108. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  109. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  111. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  112. package/dist/web/standalone/.next/server/app/page.js +1 -1
  113. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  115. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  121. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  122. package/dist/web/standalone/.next/static/chunks/app/{page-7e9530a7122506c5.js → page-12dd5ece0df4badc.js} +1 -1
  123. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  124. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  125. package/package.json +1 -1
  126. package/packages/native/dist/stream-process/index.js +2 -2
  127. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  128. package/packages/native/src/stream-process/index.ts +2 -2
  129. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  130. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  132. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  134. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  136. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  138. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  140. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  141. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  142. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  144. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  146. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  148. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  150. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  152. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  154. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  156. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  158. package/packages/pi-coding-agent/dist/main.js +17 -0
  159. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  165. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  168. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  169. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  170. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  182. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  185. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  190. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  191. package/packages/pi-coding-agent/package.json +1 -1
  192. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  193. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  194. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  195. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  196. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  197. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  198. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  199. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  200. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  201. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  202. package/packages/pi-coding-agent/src/main.ts +19 -0
  203. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  204. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  205. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  206. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  207. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  208. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  210. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  211. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  212. package/pkg/package.json +1 -1
  213. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  214. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  215. package/src/resources/extensions/gsd/auto/phases.ts +46 -48
  216. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  217. package/src/resources/extensions/gsd/auto-start.ts +39 -2
  218. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  219. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  220. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  221. package/src/resources/extensions/gsd/auto.ts +37 -3
  222. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  223. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  224. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  225. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  226. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  227. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  228. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  229. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  230. package/src/resources/extensions/gsd/doctor.ts +7 -1
  231. package/src/resources/extensions/gsd/git-service.ts +6 -2
  232. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  233. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  234. package/src/resources/extensions/gsd/preferences.ts +11 -1
  235. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  236. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  237. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  238. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  239. package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
  240. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  241. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  242. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  243. package/src/resources/extensions/gsd/rethink.ts +154 -0
  244. package/src/resources/extensions/gsd/state.ts +41 -1
  245. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  246. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  247. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  248. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  249. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  250. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  251. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  252. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  253. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  254. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  255. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  256. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  257. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  258. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  259. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  260. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  261. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  262. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  263. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  264. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  265. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  266. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  267. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  268. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  269. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  270. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  271. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  272. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  273. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  274. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  275. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  276. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  277. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  278. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  279. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  280. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  281. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  282. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  283. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  284. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  285. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  286. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  287. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  288. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  289. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  290. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  291. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  292. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  293. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  294. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  295. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  296. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  297. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  298. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  299. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  300. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  301. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  302. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  303. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  304. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  305. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  306. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  307. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  308. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  309. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  310. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  311. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  312. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  313. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  314. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  315. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  316. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  317. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  318. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  319. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  320. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  321. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  322. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  323. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  324. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  325. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  326. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  327. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  328. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  329. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  330. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  331. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  332. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  333. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  334. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  335. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  336. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  337. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  338. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  339. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  340. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  341. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  342. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  343. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  344. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  345. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  346. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  347. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  348. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  349. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  350. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  351. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  352. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  353. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  354. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  355. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  356. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  357. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  358. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  359. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  360. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  361. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  362. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  363. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  364. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  365. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  366. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  367. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  368. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  369. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  370. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  371. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  372. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  373. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  374. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  375. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  376. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  377. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  378. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  379. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  380. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  381. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  382. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  383. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  384. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  385. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  386. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  387. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  388. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  389. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  390. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  391. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  392. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  393. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  394. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  395. package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
  396. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  397. package/src/resources/extensions/mcp-client/index.ts +20 -0
  398. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  399. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  400. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  401. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → wUzEX1U3CmFcMry2SUDJn}/_buildManifest.js +0 -0
  402. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → wUzEX1U3CmFcMry2SUDJn}/_ssgManifest.js +0 -0
@@ -39,61 +39,55 @@ function activeData(overrides: Partial<HealthWidgetData> = {}): HealthWidgetData
39
39
  };
40
40
  }
41
41
 
42
- test("detectHealthWidgetProjectState: no .gsd returns none", () => {
42
+ test("detectHealthWidgetProjectState: no .gsd returns none", (t) => {
43
43
  const dir = makeTempDir("none");
44
- try {
45
- assert.equal(detectHealthWidgetProjectState(dir), "none");
46
- } finally {
47
- cleanup(dir);
48
- }
44
+ t.after(() => { cleanup(dir); });
45
+
46
+ assert.equal(detectHealthWidgetProjectState(dir), "none");
49
47
  });
50
48
 
51
- test("detectHealthWidgetProjectState: bootstrapped .gsd without milestones returns initialized", () => {
49
+ test("detectHealthWidgetProjectState: bootstrapped .gsd without milestones returns initialized", (t) => {
52
50
  const dir = makeTempDir("initialized");
53
- try {
54
- mkdirSync(join(dir, ".gsd"), { recursive: true });
55
- assert.equal(detectHealthWidgetProjectState(dir), "initialized");
56
- } finally {
57
- cleanup(dir);
58
- }
51
+ t.after(() => { cleanup(dir); });
52
+
53
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
54
+ assert.equal(detectHealthWidgetProjectState(dir), "initialized");
59
55
  });
60
56
 
61
- test("detectHealthWidgetProjectState: milestone without metrics returns active", () => {
57
+ test("detectHealthWidgetProjectState: milestone without metrics returns active", (t) => {
62
58
  const dir = makeTempDir("active");
63
- try {
64
- mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
65
- assert.equal(detectHealthWidgetProjectState(dir), "active");
66
- } finally {
67
- cleanup(dir);
68
- }
59
+ t.after(() => { cleanup(dir); });
60
+
61
+ mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
62
+ assert.equal(detectHealthWidgetProjectState(dir), "active");
69
63
  });
70
64
 
71
- test("buildHealthLines: none state shows onboarding copy", () => {
65
+ test("buildHealthLines: none state shows onboarding copy", (t) => {
72
66
  assert.deepEqual(buildHealthLines(activeData({ projectState: "none" })), [
73
67
  " GSD No project loaded — run /gsd to start",
74
68
  ]);
75
69
  });
76
70
 
77
- test("buildHealthLines: initialized state shows continue setup copy", () => {
71
+ test("buildHealthLines: initialized state shows continue setup copy", (t) => {
78
72
  assert.deepEqual(buildHealthLines(activeData({ projectState: "initialized" })), [
79
73
  " GSD Project initialized — run /gsd to continue setup",
80
74
  ]);
81
75
  });
82
76
 
83
- test("buildHealthLines: active state with ledger-driven spend shows spent summary", () => {
77
+ test("buildHealthLines: active state with ledger-driven spend shows spent summary", (t) => {
84
78
  const lines = buildHealthLines(activeData({ budgetSpent: 0.42 }));
85
79
  assert.equal(lines.length, 1);
86
80
  assert.match(lines[0]!, /● System OK/);
87
81
  assert.match(lines[0]!, /Spent: 42\.0¢/);
88
82
  });
89
83
 
90
- test("buildHealthLines: active state with budget ceiling shows percent summary", () => {
84
+ test("buildHealthLines: active state with budget ceiling shows percent summary", (t) => {
91
85
  const lines = buildHealthLines(activeData({ budgetSpent: 2.5, budgetCeiling: 10 }));
92
86
  assert.equal(lines.length, 1);
93
87
  assert.match(lines[0]!, /Budget: \$2\.50\/\$10\.00 \(25%\)/);
94
88
  });
95
89
 
96
- test("buildHealthLines: active state with issues reports issue summary", () => {
90
+ test("buildHealthLines: active state with issues reports issue summary", (t) => {
97
91
  const lines = buildHealthLines(activeData({
98
92
  providerIssue: "✗ OpenAI key missing",
99
93
  environmentErrorCount: 1,
@@ -104,17 +98,15 @@ test("buildHealthLines: active state with issues reports issue summary", () => {
104
98
  assert.match(lines[0]!, /Env: 1 error/);
105
99
  });
106
100
 
107
- test("detectHealthWidgetProjectState: metrics file alone does not imply project", () => {
101
+ test("detectHealthWidgetProjectState: metrics file alone does not imply project", (t) => {
108
102
  const dir = makeTempDir("metrics-only");
109
- try {
110
- mkdirSync(join(dir, ".gsd"), { recursive: true });
111
- writeFileSync(
112
- join(dir, ".gsd", "metrics.json"),
113
- JSON.stringify({ version: 1, projectStartedAt: Date.now(), units: [] }),
114
- "utf-8",
115
- );
116
- assert.equal(detectHealthWidgetProjectState(dir), "initialized");
117
- } finally {
118
- cleanup(dir);
119
- }
103
+ t.after(() => { cleanup(dir); });
104
+
105
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
106
+ writeFileSync(
107
+ join(dir, ".gsd", "metrics.json"),
108
+ JSON.stringify({ version: 1, projectStartedAt: Date.now(), units: [] }),
109
+ "utf-8",
110
+ );
111
+ assert.equal(detectHealthWidgetProjectState(dir), "initialized");
120
112
  });
@@ -8,9 +8,9 @@ import {
8
8
  verifyExpectedArtifact,
9
9
  buildLoopRemediationSteps,
10
10
  } from "../auto.ts";
11
- import { createTestContext } from './test-helpers.ts';
11
+ import { describe, test, beforeEach, afterEach } from 'node:test';
12
+ import assert from 'node:assert/strict';
12
13
 
13
- const { assertEq, assertTrue, report } = createTestContext();
14
14
  function createFixtureBase(): string {
15
15
  const base = mkdtempSync(join(tmpdir(), "gsd-idle-recovery-test-"));
16
16
  mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), { recursive: true });
@@ -23,99 +23,91 @@ function cleanup(base: string): void {
23
23
 
24
24
  // ═══ resolveExpectedArtifactPath ═════════════════════════════════════════════
25
25
 
26
- {
27
- console.log("\n=== resolveExpectedArtifactPath: research-milestone ===");
26
+ test('resolveExpectedArtifactPath: research-milestone', () => {
28
27
  const base = createFixtureBase();
29
28
  try {
30
29
  const result = resolveExpectedArtifactPath("research-milestone", "M001", base);
31
- assertTrue(result !== null, "should resolve a path");
32
- assertTrue(result!.endsWith("M001-RESEARCH.md"), `path should end with M001-RESEARCH.md, got ${result}`);
30
+ assert.ok(result !== null, "should resolve a path");
31
+ assert.ok(result!.endsWith("M001-RESEARCH.md"), `path should end with M001-RESEARCH.md, got ${result}`);
33
32
  } finally {
34
33
  cleanup(base);
35
34
  }
36
- }
35
+ });
37
36
 
38
- {
39
- console.log("\n=== resolveExpectedArtifactPath: plan-milestone ===");
37
+ test('resolveExpectedArtifactPath: plan-milestone', () => {
40
38
  const base = createFixtureBase();
41
39
  try {
42
40
  const result = resolveExpectedArtifactPath("plan-milestone", "M001", base);
43
- assertTrue(result !== null, "should resolve a path");
44
- assertTrue(result!.endsWith("M001-ROADMAP.md"), `path should end with M001-ROADMAP.md, got ${result}`);
41
+ assert.ok(result !== null, "should resolve a path");
42
+ assert.ok(result!.endsWith("M001-ROADMAP.md"), `path should end with M001-ROADMAP.md, got ${result}`);
45
43
  } finally {
46
44
  cleanup(base);
47
45
  }
48
- }
46
+ });
49
47
 
50
- {
51
- console.log("\n=== resolveExpectedArtifactPath: research-slice ===");
48
+ test('resolveExpectedArtifactPath: research-slice', () => {
52
49
  const base = createFixtureBase();
53
50
  try {
54
51
  const result = resolveExpectedArtifactPath("research-slice", "M001/S01", base);
55
- assertTrue(result !== null, "should resolve a path");
56
- assertTrue(result!.endsWith("S01-RESEARCH.md"), `path should end with S01-RESEARCH.md, got ${result}`);
52
+ assert.ok(result !== null, "should resolve a path");
53
+ assert.ok(result!.endsWith("S01-RESEARCH.md"), `path should end with S01-RESEARCH.md, got ${result}`);
57
54
  } finally {
58
55
  cleanup(base);
59
56
  }
60
- }
57
+ });
61
58
 
62
- {
63
- console.log("\n=== resolveExpectedArtifactPath: plan-slice ===");
59
+ test('resolveExpectedArtifactPath: plan-slice', () => {
64
60
  const base = createFixtureBase();
65
61
  try {
66
62
  const result = resolveExpectedArtifactPath("plan-slice", "M001/S01", base);
67
- assertTrue(result !== null, "should resolve a path");
68
- assertTrue(result!.endsWith("S01-PLAN.md"), `path should end with S01-PLAN.md, got ${result}`);
63
+ assert.ok(result !== null, "should resolve a path");
64
+ assert.ok(result!.endsWith("S01-PLAN.md"), `path should end with S01-PLAN.md, got ${result}`);
69
65
  } finally {
70
66
  cleanup(base);
71
67
  }
72
- }
68
+ });
73
69
 
74
- {
75
- console.log("\n=== resolveExpectedArtifactPath: complete-milestone ===");
70
+ test('resolveExpectedArtifactPath: complete-milestone', () => {
76
71
  const base = createFixtureBase();
77
72
  try {
78
73
  const result = resolveExpectedArtifactPath("complete-milestone", "M001", base);
79
- assertTrue(result !== null, "should resolve a path");
80
- assertTrue(result!.endsWith("M001-SUMMARY.md"), `path should end with M001-SUMMARY.md, got ${result}`);
74
+ assert.ok(result !== null, "should resolve a path");
75
+ assert.ok(result!.endsWith("M001-SUMMARY.md"), `path should end with M001-SUMMARY.md, got ${result}`);
81
76
  } finally {
82
77
  cleanup(base);
83
78
  }
84
- }
79
+ });
85
80
 
86
- {
87
- console.log("\n=== resolveExpectedArtifactPath: unknown unit type → null ===");
81
+ test('resolveExpectedArtifactPath: unknown unit type → null', () => {
88
82
  const base = createFixtureBase();
89
83
  try {
90
84
  const result = resolveExpectedArtifactPath("unknown-type", "M001/S01", base);
91
- assertEq(result, null, "unknown type returns null");
85
+ assert.deepStrictEqual(result, null, "unknown type returns null");
92
86
  } finally {
93
87
  cleanup(base);
94
88
  }
95
- }
89
+ });
96
90
 
97
91
  // ═══ writeBlockerPlaceholder ═════════════════════════════════════════════════
98
92
 
99
- {
100
- console.log("\n=== writeBlockerPlaceholder: writes file for research-slice ===");
93
+ test('writeBlockerPlaceholder: writes file for research-slice', () => {
101
94
  const base = createFixtureBase();
102
95
  try {
103
96
  const result = writeBlockerPlaceholder("research-slice", "M001/S01", base, "idle recovery exhausted 2 attempts");
104
- assertTrue(result !== null, "should return relative path");
97
+ assert.ok(result !== null, "should return relative path");
105
98
  const absPath = resolveExpectedArtifactPath("research-slice", "M001/S01", base)!;
106
- assertTrue(existsSync(absPath), "file should exist on disk");
99
+ assert.ok(existsSync(absPath), "file should exist on disk");
107
100
  const content = readFileSync(absPath, "utf-8");
108
- assertTrue(content.includes("BLOCKER"), "should contain BLOCKER heading");
109
- assertTrue(content.includes("idle recovery exhausted 2 attempts"), "should contain the reason");
110
- assertTrue(content.includes("research-slice"), "should mention the unit type");
111
- assertTrue(content.includes("M001/S01"), "should mention the unit ID");
101
+ assert.ok(content.includes("BLOCKER"), "should contain BLOCKER heading");
102
+ assert.ok(content.includes("idle recovery exhausted 2 attempts"), "should contain the reason");
103
+ assert.ok(content.includes("research-slice"), "should mention the unit type");
104
+ assert.ok(content.includes("M001/S01"), "should mention the unit ID");
112
105
  } finally {
113
106
  cleanup(base);
114
107
  }
115
- }
108
+ });
116
109
 
117
- {
118
- console.log("\n=== writeBlockerPlaceholder: creates directory if missing ===");
110
+ test('writeBlockerPlaceholder: creates directory if missing', () => {
119
111
  const base = mkdtempSync(join(tmpdir(), "gsd-idle-recovery-test-"));
120
112
  try {
121
113
  // Only create milestone dir, not slice dir
@@ -123,38 +115,36 @@ function cleanup(base: string): void {
123
115
  // resolveSlicePath needs the slice dir to exist to resolve, so this should return null
124
116
  const result = writeBlockerPlaceholder("research-slice", "M001/S01", base, "test reason");
125
117
  // Since the slice dir doesn't exist, resolveExpectedArtifactPath returns null
126
- assertEq(result, null, "returns null when directory structure doesn't exist");
118
+ assert.deepStrictEqual(result, null, "returns null when directory structure doesn't exist");
127
119
  } finally {
128
120
  cleanup(base);
129
121
  }
130
- }
122
+ });
131
123
 
132
- {
133
- console.log("\n=== writeBlockerPlaceholder: writes file for research-milestone ===");
124
+ test('writeBlockerPlaceholder: writes file for research-milestone', () => {
134
125
  const base = createFixtureBase();
135
126
  try {
136
127
  const result = writeBlockerPlaceholder("research-milestone", "M001", base, "hard timeout");
137
- assertTrue(result !== null, "should return relative path");
128
+ assert.ok(result !== null, "should return relative path");
138
129
  const absPath = resolveExpectedArtifactPath("research-milestone", "M001", base)!;
139
- assertTrue(existsSync(absPath), "file should exist on disk");
130
+ assert.ok(existsSync(absPath), "file should exist on disk");
140
131
  const content = readFileSync(absPath, "utf-8");
141
- assertTrue(content.includes("BLOCKER"), "should contain BLOCKER heading");
142
- assertTrue(content.includes("hard timeout"), "should contain the reason");
132
+ assert.ok(content.includes("BLOCKER"), "should contain BLOCKER heading");
133
+ assert.ok(content.includes("hard timeout"), "should contain the reason");
143
134
  } finally {
144
135
  cleanup(base);
145
136
  }
146
- }
137
+ });
147
138
 
148
- {
149
- console.log("\n=== writeBlockerPlaceholder: unknown type → null ===");
139
+ test('writeBlockerPlaceholder: unknown type → null', () => {
150
140
  const base = createFixtureBase();
151
141
  try {
152
142
  const result = writeBlockerPlaceholder("unknown-type", "M001/S01", base, "test");
153
- assertEq(result, null, "unknown type returns null");
143
+ assert.deepStrictEqual(result, null, "unknown type returns null");
154
144
  } finally {
155
145
  cleanup(base);
156
146
  }
157
- }
147
+ });
158
148
 
159
149
  // ═══ verifyExpectedArtifact: complete-slice roadmap check ════════════════════
160
150
  // Regression for #indefinite-hang: complete-slice must verify roadmap [x] or
@@ -177,8 +167,7 @@ const ROADMAP_COMPLETE = `# M001: Test Milestone
177
167
  > After this: something works
178
168
  `;
179
169
 
180
- {
181
- console.log("\n=== verifyExpectedArtifact: complete-slice — all artifacts present + roadmap marked [x] returns true ===");
170
+ test('verifyExpectedArtifact: complete-slice — all artifacts present + roadmap marked [x] returns true', () => {
182
171
  const base = createFixtureBase();
183
172
  try {
184
173
  const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
@@ -186,14 +175,13 @@ const ROADMAP_COMPLETE = `# M001: Test Milestone
186
175
  writeFileSync(join(sliceDir, "S01-UAT.md"), "# UAT\n", "utf-8");
187
176
  writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), ROADMAP_COMPLETE, "utf-8");
188
177
  const result = verifyExpectedArtifact("complete-slice", "M001/S01", base);
189
- assertTrue(result === true, "SUMMARY + UAT + roadmap [x] should verify as true");
178
+ assert.ok(result === true, "SUMMARY + UAT + roadmap [x] should verify as true");
190
179
  } finally {
191
180
  cleanup(base);
192
181
  }
193
- }
182
+ });
194
183
 
195
- {
196
- console.log("\n=== verifyExpectedArtifact: complete-slice — SUMMARY + UAT present but roadmap NOT marked [x] returns false ===");
184
+ test('verifyExpectedArtifact: complete-slice — SUMMARY + UAT present but roadmap NOT marked [x] returns false', () => {
197
185
  const base = createFixtureBase();
198
186
  try {
199
187
  const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
@@ -201,14 +189,13 @@ const ROADMAP_COMPLETE = `# M001: Test Milestone
201
189
  writeFileSync(join(sliceDir, "S01-UAT.md"), "# UAT\n", "utf-8");
202
190
  writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), ROADMAP_INCOMPLETE, "utf-8");
203
191
  const result = verifyExpectedArtifact("complete-slice", "M001/S01", base);
204
- assertTrue(result === false, "roadmap not marked [x] should return false (crash recovery scenario)");
192
+ assert.ok(result === false, "roadmap not marked [x] should return false (crash recovery scenario)");
205
193
  } finally {
206
194
  cleanup(base);
207
195
  }
208
- }
196
+ });
209
197
 
210
- {
211
- console.log("\n=== verifyExpectedArtifact: complete-slice — SUMMARY present but UAT missing returns false ===");
198
+ test('verifyExpectedArtifact: complete-slice — SUMMARY present but UAT missing returns false', () => {
212
199
  const base = createFixtureBase();
213
200
  try {
214
201
  const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
@@ -216,14 +203,13 @@ const ROADMAP_COMPLETE = `# M001: Test Milestone
216
203
  // no UAT file
217
204
  writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), ROADMAP_COMPLETE, "utf-8");
218
205
  const result = verifyExpectedArtifact("complete-slice", "M001/S01", base);
219
- assertTrue(result === false, "missing UAT should return false");
206
+ assert.ok(result === false, "missing UAT should return false");
220
207
  } finally {
221
208
  cleanup(base);
222
209
  }
223
- }
210
+ });
224
211
 
225
- {
226
- console.log("\n=== verifyExpectedArtifact: complete-slice — no roadmap file present is lenient (returns true) ===");
212
+ test('verifyExpectedArtifact: complete-slice — no roadmap file present is lenient (returns true)', () => {
227
213
  const base = createFixtureBase();
228
214
  try {
229
215
  const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
@@ -231,87 +217,80 @@ const ROADMAP_COMPLETE = `# M001: Test Milestone
231
217
  writeFileSync(join(sliceDir, "S01-UAT.md"), "# UAT\n", "utf-8");
232
218
  // no roadmap file
233
219
  const result = verifyExpectedArtifact("complete-slice", "M001/S01", base);
234
- assertTrue(result === true, "missing roadmap file should be lenient and return true");
220
+ assert.ok(result === true, "missing roadmap file should be lenient and return true");
235
221
  } finally {
236
222
  cleanup(base);
237
223
  }
238
- }
224
+ });
239
225
 
240
226
  // ═══ buildLoopRemediationSteps ═══════════════════════════════════════════════
241
227
 
242
- {
243
- console.log("\n=== buildLoopRemediationSteps: execute-task returns concrete steps ===");
228
+ test('buildLoopRemediationSteps: execute-task returns concrete steps', () => {
244
229
  const base = mkdtempSync(join(tmpdir(), "gsd-loop-remediation-test-"));
245
230
  try {
246
231
  mkdirSync(join(base, ".gsd", "milestones", "M002", "slices", "S03", "tasks"), { recursive: true });
247
232
  const result = buildLoopRemediationSteps("execute-task", "M002/S03/T01", base);
248
- assertTrue(result !== null, "should return remediation steps");
249
- assertTrue(result!.includes("gsd undo-task"), "steps include undo-task command");
250
- assertTrue(result!.includes("T01"), "steps mention the task ID");
251
- assertTrue(result!.includes("gsd undo-task"), "steps include gsd undo-task command");
233
+ assert.ok(result !== null, "should return remediation steps");
234
+ assert.ok(result!.includes("gsd undo-task"), "steps include undo-task command");
235
+ assert.ok(result!.includes("T01"), "steps mention the task ID");
236
+ assert.ok(result!.includes("gsd undo-task"), "steps include gsd undo-task command");
252
237
  } finally {
253
238
  rmSync(base, { recursive: true, force: true });
254
239
  }
255
- }
240
+ });
256
241
 
257
- {
258
- console.log("\n=== buildLoopRemediationSteps: plan-slice returns concrete steps ===");
242
+ test('buildLoopRemediationSteps: plan-slice returns concrete steps', () => {
259
243
  const base = mkdtempSync(join(tmpdir(), "gsd-loop-remediation-test-"));
260
244
  try {
261
245
  mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
262
246
  const result = buildLoopRemediationSteps("plan-slice", "M001/S01", base);
263
- assertTrue(result !== null, "should return remediation steps for plan-slice");
264
- assertTrue(result!.includes("S01-PLAN.md"), "steps mention the slice plan file");
265
- assertTrue(result!.includes("gsd recover"), "steps include gsd recover command");
247
+ assert.ok(result !== null, "should return remediation steps for plan-slice");
248
+ assert.ok(result!.includes("S01-PLAN.md"), "steps mention the slice plan file");
249
+ assert.ok(result!.includes("gsd recover"), "steps include gsd recover command");
266
250
  } finally {
267
251
  rmSync(base, { recursive: true, force: true });
268
252
  }
269
- }
253
+ });
270
254
 
271
- {
272
- console.log("\n=== buildLoopRemediationSteps: research-slice returns concrete steps ===");
255
+ test('buildLoopRemediationSteps: research-slice returns concrete steps', () => {
273
256
  const base = mkdtempSync(join(tmpdir(), "gsd-loop-remediation-test-"));
274
257
  try {
275
258
  mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
276
259
  const result = buildLoopRemediationSteps("research-slice", "M001/S01", base);
277
- assertTrue(result !== null, "should return remediation steps for research-slice");
278
- assertTrue(result!.includes("S01-RESEARCH.md"), "steps mention the slice research file");
279
- assertTrue(result!.includes("gsd recover"), "steps include gsd recover command");
260
+ assert.ok(result !== null, "should return remediation steps for research-slice");
261
+ assert.ok(result!.includes("S01-RESEARCH.md"), "steps mention the slice research file");
262
+ assert.ok(result!.includes("gsd recover"), "steps include gsd recover command");
280
263
  } finally {
281
264
  rmSync(base, { recursive: true, force: true });
282
265
  }
283
- }
266
+ });
284
267
 
285
- {
286
- console.log("\n=== buildLoopRemediationSteps: unknown type returns null ===");
268
+ test('buildLoopRemediationSteps: unknown type returns null', () => {
287
269
  const base = mkdtempSync(join(tmpdir(), "gsd-loop-remediation-test-"));
288
270
  try {
289
271
  const result = buildLoopRemediationSteps("unknown-type", "M001/S01", base);
290
- assertEq(result, null, "unknown type returns null");
272
+ assert.deepStrictEqual(result, null, "unknown type returns null");
291
273
  } finally {
292
274
  rmSync(base, { recursive: true, force: true });
293
275
  }
294
- }
276
+ });
295
277
 
296
278
  // ═══ verifyExpectedArtifact: hook unit types ═════════════════════════════════
297
279
 
298
- console.log("\n=== verifyExpectedArtifact: hook types always return true ===");
299
-
300
- {
280
+ test('verifyExpectedArtifact: hook types always return true', () => {
301
281
  const base = createFixtureBase();
302
282
  try {
303
283
  // Hook units don't have standard artifacts — they should always pass
304
284
  const result1 = verifyExpectedArtifact("hook/code-review", "M001/S01/T01", base);
305
- assertTrue(result1, "hook/code-review should always return true");
285
+ assert.ok(result1, "hook/code-review should always return true");
306
286
 
307
287
  const result2 = verifyExpectedArtifact("hook/simplify", "M001/S01/T02", base);
308
- assertTrue(result2, "hook/simplify should always return true");
288
+ assert.ok(result2, "hook/simplify should always return true");
309
289
 
310
290
  const result3 = verifyExpectedArtifact("hook/custom-hook", "M001/S01", base);
311
- assertTrue(result3, "hook/custom-hook at slice level should return true");
291
+ assert.ok(result3, "hook/custom-hook at slice level should return true");
312
292
  } finally {
313
293
  rmSync(base, { recursive: true, force: true });
314
294
  }
315
- }
295
+ });
316
296
 
317
- report();
@@ -7,10 +7,13 @@ import { isInfrastructureError, INFRA_ERROR_CODES } from "../auto/infra-errors.j
7
7
  // ── INFRA_ERROR_CODES constant ───────────────────────────────────────────────
8
8
 
9
9
  test("INFRA_ERROR_CODES contains the expected codes", () => {
10
- for (const code of ["ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE", "ENFILE"]) {
10
+ for (const code of [
11
+ "ENOSPC", "ENOMEM", "EROFS", "EDQUOT", "EMFILE", "ENFILE",
12
+ "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH",
13
+ ]) {
11
14
  assert.ok(INFRA_ERROR_CODES.has(code), `missing ${code}`);
12
15
  }
13
- assert.equal(INFRA_ERROR_CODES.size, 6, "unexpected extra codes");
16
+ assert.equal(INFRA_ERROR_CODES.size, 9, "unexpected extra codes");
14
17
  });
15
18
 
16
19
  // ── isInfrastructureError: code property detection ───────────────────────────
@@ -45,6 +48,21 @@ test("detects ENFILE via code property", () => {
45
48
  assert.equal(isInfrastructureError(err), "ENFILE");
46
49
  });
47
50
 
51
+ test("detects ECONNREFUSED via code property", () => {
52
+ const err = Object.assign(new Error("connect ECONNREFUSED 127.0.0.1:3000"), { code: "ECONNREFUSED" });
53
+ assert.equal(isInfrastructureError(err), "ECONNREFUSED");
54
+ });
55
+
56
+ test("detects ENOTFOUND via code property", () => {
57
+ const err = Object.assign(new Error("getaddrinfo ENOTFOUND api.example.com"), { code: "ENOTFOUND" });
58
+ assert.equal(isInfrastructureError(err), "ENOTFOUND");
59
+ });
60
+
61
+ test("detects ENETUNREACH via code property", () => {
62
+ const err = Object.assign(new Error("connect ENETUNREACH 2607:f8b0:4004::"), { code: "ENETUNREACH" });
63
+ assert.equal(isInfrastructureError(err), "ENETUNREACH");
64
+ });
65
+
48
66
  // ── isInfrastructureError: message fallback ──────────────────────────────────
49
67
 
50
68
  test("falls back to message scanning when no code property", () => {
@@ -0,0 +1,121 @@
1
+ /**
2
+ * inherited-repo-home-dir.test.ts — Regression test for #2393.
3
+ *
4
+ * When the user's home directory IS a git repo (common with dotfile
5
+ * managers like yadm), isInheritedRepo() must not treat ~/.gsd (the
6
+ * global GSD state directory) as a project .gsd belonging to the home
7
+ * repo. Without the fix, isInheritedRepo() returns false for project
8
+ * subdirectories because it sees ~/.gsd and concludes the parent repo
9
+ * has already been initialised with GSD — causing the wrong project
10
+ * state to be loaded.
11
+ */
12
+
13
+ import { describe, test, beforeEach, afterEach } from "node:test";
14
+ import assert from "node:assert/strict";
15
+ import {
16
+ mkdtempSync,
17
+ mkdirSync,
18
+ rmSync,
19
+ writeFileSync,
20
+ realpathSync,
21
+ symlinkSync,
22
+ } from "node:fs";
23
+ import { join } from "node:path";
24
+ import { tmpdir } from "node:os";
25
+ import { execFileSync } from "node:child_process";
26
+
27
+ import { isInheritedRepo } from "../repo-identity.ts";
28
+
29
+ function run(cmd: string, args: string[], cwd: string): string {
30
+ return execFileSync(cmd, args, {
31
+ cwd,
32
+ stdio: ["ignore", "pipe", "pipe"],
33
+ encoding: "utf-8",
34
+ }).trim();
35
+ }
36
+
37
+ describe("isInheritedRepo when git root is HOME (#2393)", () => {
38
+ let fakeHome: string;
39
+ let stateDir: string;
40
+ let origGsdHome: string | undefined;
41
+ let origGsdStateDir: string | undefined;
42
+
43
+ beforeEach(() => {
44
+ // Create a fake HOME that is itself a git repo (dotfile manager scenario).
45
+ fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-home-repo-")));
46
+ run("git", ["init", "-b", "main"], fakeHome);
47
+ run("git", ["config", "user.name", "Test"], fakeHome);
48
+ run("git", ["config", "user.email", "test@example.com"], fakeHome);
49
+ writeFileSync(join(fakeHome, ".bashrc"), "# dotfiles\n", "utf-8");
50
+ run("git", ["add", ".bashrc"], fakeHome);
51
+ run("git", ["commit", "-m", "init dotfiles"], fakeHome);
52
+
53
+ // Create a plain ~/.gsd directory at fakeHome — this simulates the
54
+ // global GSD home directory, NOT a project .gsd.
55
+ mkdirSync(join(fakeHome, ".gsd", "projects"), { recursive: true });
56
+
57
+ // Save and override env. Point GSD_HOME at fakeHome/.gsd so the
58
+ // function recognizes it as the global state directory.
59
+ origGsdHome = process.env.GSD_HOME;
60
+ origGsdStateDir = process.env.GSD_STATE_DIR;
61
+ process.env.GSD_HOME = join(fakeHome, ".gsd");
62
+ stateDir = mkdtempSync(join(tmpdir(), "gsd-state-"));
63
+ process.env.GSD_STATE_DIR = stateDir;
64
+ });
65
+
66
+ afterEach(() => {
67
+ if (origGsdHome !== undefined) process.env.GSD_HOME = origGsdHome;
68
+ else delete process.env.GSD_HOME;
69
+ if (origGsdStateDir !== undefined) process.env.GSD_STATE_DIR = origGsdStateDir;
70
+ else delete process.env.GSD_STATE_DIR;
71
+
72
+ rmSync(fakeHome, { recursive: true, force: true });
73
+ rmSync(stateDir, { recursive: true, force: true });
74
+ });
75
+
76
+ test("subdirectory of home-as-git-root is detected as inherited even when ~/.gsd exists", () => {
77
+ // Create a project directory inside fake HOME
78
+ const projectDir = join(fakeHome, "projects", "my-app");
79
+ mkdirSync(projectDir, { recursive: true });
80
+
81
+ // The bug: isInheritedRepo sees ~/.gsd and returns false, thinking
82
+ // the home repo is a legitimate GSD project. It should return true
83
+ // because ~/.gsd is the global state dir, not a project .gsd.
84
+ assert.strictEqual(
85
+ isInheritedRepo(projectDir),
86
+ true,
87
+ "project inside home-as-git-root must be detected as inherited repo, " +
88
+ "even when ~/.gsd (global state dir) exists",
89
+ );
90
+ });
91
+
92
+ test("subdirectory with a real project .gsd symlink at git root is NOT inherited", () => {
93
+ // Simulate a legitimately initialised GSD project at the home repo root:
94
+ // .gsd is a symlink to an external state directory.
95
+ const externalState = join(stateDir, "projects", "home-project");
96
+ mkdirSync(externalState, { recursive: true });
97
+ const gsdDir = join(fakeHome, ".gsd");
98
+
99
+ // Remove the plain directory and replace with a symlink (real project .gsd)
100
+ rmSync(gsdDir, { recursive: true, force: true });
101
+ symlinkSync(externalState, gsdDir);
102
+
103
+ const projectDir = join(fakeHome, "projects", "my-app");
104
+ mkdirSync(projectDir, { recursive: true });
105
+
106
+ // When .gsd at root IS a project symlink, subdirectories are legitimate children
107
+ assert.strictEqual(
108
+ isInheritedRepo(projectDir),
109
+ false,
110
+ "subdirectory of a legitimately-initialised GSD project should NOT be inherited",
111
+ );
112
+ });
113
+
114
+ test("home-as-git-root itself is never inherited", () => {
115
+ assert.strictEqual(
116
+ isInheritedRepo(fakeHome),
117
+ false,
118
+ "the git root itself is never inherited",
119
+ );
120
+ });
121
+ });