gsd-pi 2.44.0 → 2.45.0-dev.e0ee972

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 (505) hide show
  1. package/README.md +30 -12
  2. package/dist/help-text.js +1 -1
  3. package/dist/loader.js +34 -0
  4. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  5. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  6. package/dist/resources/extensions/gsd/auto/phases.js +52 -45
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  8. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  9. package/dist/resources/extensions/gsd/auto-start.js +31 -2
  10. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  11. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  12. package/dist/resources/extensions/gsd/auto-worktree.js +14 -10
  13. package/dist/resources/extensions/gsd/auto.js +34 -8
  14. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +168 -11
  15. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  16. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  17. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  18. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  19. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  20. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  21. package/dist/resources/extensions/gsd/db-writer.js +40 -22
  22. package/dist/resources/extensions/gsd/doctor-checks.js +1 -1
  23. package/dist/resources/extensions/gsd/doctor.js +10 -2
  24. package/dist/resources/extensions/gsd/git-service.js +8 -3
  25. package/dist/resources/extensions/gsd/gsd-db.js +17 -2
  26. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  27. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  28. package/dist/resources/extensions/gsd/preferences.js +17 -5
  29. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  30. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  31. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  32. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  33. package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
  34. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  35. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  36. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  37. package/dist/resources/extensions/gsd/rethink.js +115 -0
  38. package/dist/resources/extensions/gsd/state.js +41 -3
  39. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  40. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  41. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  42. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  43. package/dist/resources/extensions/gsd/workflow-logger.js +138 -0
  44. package/dist/resources/extensions/gsd/worktree-manager.js +34 -3
  45. package/dist/resources/extensions/gsd/worktree-resolver.js +43 -0
  46. package/dist/resources/extensions/mcp-client/index.js +14 -0
  47. package/dist/resources/extensions/voice/index.js +11 -16
  48. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  49. package/dist/web/standalone/.next/BUILD_ID +1 -1
  50. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  51. package/dist/web/standalone/.next/build-manifest.json +4 -4
  52. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  53. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  54. package/dist/web/standalone/.next/required-server-files.json +3 -3
  55. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  56. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  58. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  66. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.rsc +5 -5
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  75. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  82. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  120. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  126. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  140. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  142. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  144. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  146. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/index.html +1 -1
  156. package/dist/web/standalone/.next/server/app/index.rsc +6 -6
  157. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  158. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +6 -6
  159. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  160. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  161. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  162. package/dist/web/standalone/.next/server/app/page.js +2 -2
  163. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  165. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  166. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  167. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/middleware.js +2 -2
  170. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  172. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  173. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  174. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  175. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  176. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  177. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  178. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  179. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  180. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  181. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  182. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  183. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  184. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  185. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  186. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  187. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  188. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  189. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  190. package/dist/web/standalone/server.js +1 -1
  191. package/package.json +1 -1
  192. package/packages/native/dist/stream-process/index.js +2 -2
  193. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  194. package/packages/native/src/stream-process/index.ts +2 -2
  195. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  196. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  198. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  200. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  201. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  203. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  205. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  206. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  207. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  208. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  210. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  212. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  214. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  216. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  217. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  218. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  219. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  220. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  221. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  222. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  223. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  224. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +13 -1
  226. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  227. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  228. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  229. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  230. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  231. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  232. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  233. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  234. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  236. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  237. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  238. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  239. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  240. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/main.js +17 -0
  243. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  245. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  246. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  247. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  248. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  249. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  250. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  251. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  253. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  254. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  255. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  256. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  257. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  258. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  259. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  260. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  261. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  262. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  264. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  265. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  266. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  267. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  269. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  271. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  272. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  273. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  274. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  275. package/packages/pi-coding-agent/package.json +1 -1
  276. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  277. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  278. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  279. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  280. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  281. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  282. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  283. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  284. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  285. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  286. package/packages/pi-coding-agent/src/core/model-registry.ts +51 -4
  287. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  288. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  289. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  290. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  291. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  292. package/packages/pi-coding-agent/src/main.ts +19 -0
  293. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  294. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  295. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  296. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  297. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  298. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  299. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  300. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  301. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  302. package/pkg/package.json +1 -1
  303. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  304. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  305. package/src/resources/extensions/gsd/auto/phases.ts +61 -59
  306. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  307. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  308. package/src/resources/extensions/gsd/auto-start.ts +39 -2
  309. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  310. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  311. package/src/resources/extensions/gsd/auto-worktree.ts +17 -11
  312. package/src/resources/extensions/gsd/auto.ts +40 -6
  313. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -11
  314. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  315. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  316. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  317. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  318. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  319. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  320. package/src/resources/extensions/gsd/db-writer.ts +41 -27
  321. package/src/resources/extensions/gsd/doctor-checks.ts +1 -1
  322. package/src/resources/extensions/gsd/doctor.ts +9 -3
  323. package/src/resources/extensions/gsd/git-service.ts +6 -2
  324. package/src/resources/extensions/gsd/gsd-db.ts +21 -2
  325. package/src/resources/extensions/gsd/journal.ts +6 -1
  326. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  327. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  328. package/src/resources/extensions/gsd/preferences.ts +18 -4
  329. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  330. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  331. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  332. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  333. package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
  334. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  335. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  336. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  337. package/src/resources/extensions/gsd/rethink.ts +154 -0
  338. package/src/resources/extensions/gsd/state.ts +41 -1
  339. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  340. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  341. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  342. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  343. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  344. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  345. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  346. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  347. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  348. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  349. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  350. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  351. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  352. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  353. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  354. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  355. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  356. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  357. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  358. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  359. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  360. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  361. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  362. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  363. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  364. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  365. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  366. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  367. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  368. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  369. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  370. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  371. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  372. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  373. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  374. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  375. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  376. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  377. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  378. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  379. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  380. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  381. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  382. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  383. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  384. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  385. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  386. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  387. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  388. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  389. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  390. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  391. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  392. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  393. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  394. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  395. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  396. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  397. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  398. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  399. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  400. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  401. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  402. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  403. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  404. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  405. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  406. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  407. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  408. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  409. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  410. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  411. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  412. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  413. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  414. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  415. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  416. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  417. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  418. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  419. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  420. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  421. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  422. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  423. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  424. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  425. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  426. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  427. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +75 -37
  428. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  429. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  430. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  431. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  432. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  433. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  434. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  435. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  436. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  437. package/src/resources/extensions/gsd/tests/preferences.test.ts +34 -9
  438. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  439. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  440. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  441. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  442. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  443. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  444. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  445. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  446. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  447. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  448. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  449. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  450. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  451. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  452. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  453. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  454. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  455. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  456. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  457. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  458. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  459. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  460. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  461. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  462. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  463. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  464. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  465. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  466. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  467. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  468. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  469. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  470. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  471. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  472. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  473. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  474. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  475. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  476. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  477. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  478. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  479. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  480. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  481. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  482. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  483. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  484. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  485. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  486. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  487. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  488. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  489. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  490. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  491. package/src/resources/extensions/gsd/workflow-logger.ts +193 -0
  492. package/src/resources/extensions/gsd/worktree-manager.ts +41 -5
  493. package/src/resources/extensions/gsd/worktree-resolver.ts +44 -0
  494. package/src/resources/extensions/mcp-client/index.ts +20 -0
  495. package/src/resources/extensions/voice/index.ts +11 -21
  496. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  497. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  498. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  499. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +0 -1
  500. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  501. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  502. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  503. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  504. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → dFMji9G1LZ-Tv36el9pRT}/_buildManifest.js +0 -0
  505. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → dFMji9G1LZ-Tv36el9pRT}/_ssgManifest.js +0 -0
@@ -17,9 +17,9 @@ import {
17
17
  } from "../worktree.ts";
18
18
  import { readIntegrationBranch } from "../git-service.ts";
19
19
  import { _resetHasChangesCache } from "../native-git-bridge.ts";
20
- import { createTestContext } from './test-helpers.ts';
20
+ import { describe, test } from 'node:test';
21
+ import assert from 'node:assert/strict';
21
22
 
22
- const { assertEq, assertTrue, report } = createTestContext();
23
23
 
24
24
  /**
25
25
  * Normalize a path for reliable comparison on Windows CI runners.
@@ -47,56 +47,56 @@ writeFileSync(join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLA
47
47
  run("git add .", base);
48
48
  run('git commit -m "chore: init"', base);
49
49
 
50
- async function main(): Promise<void> {
50
+ describe('worktree', async () => {
51
51
 
52
52
  console.log("\n=== autoCommitCurrentBranch ===");
53
53
  // Clean — should return null
54
54
  const cleanResult = autoCommitCurrentBranch(base, "execute-task", "M001/S01/T01");
55
- assertEq(cleanResult, null, "returns null for clean repo");
55
+ assert.deepStrictEqual(cleanResult, null, "returns null for clean repo");
56
56
 
57
57
  // Make dirty — reset the nativeHasChanges cache so the fresh dirt is detected
58
58
  _resetHasChangesCache();
59
59
  writeFileSync(join(base, "dirty.txt"), "uncommitted\n", "utf-8");
60
60
  const dirtyResult = autoCommitCurrentBranch(base, "execute-task", "M001/S01/T01");
61
- assertTrue(dirtyResult !== null, "returns commit message for dirty repo");
62
- assertTrue(dirtyResult!.includes("M001/S01/T01"), "commit message includes unit id");
63
- assertEq(run("git status --short", base), "", "repo is clean after auto-commit");
61
+ assert.ok(dirtyResult !== null, "returns commit message for dirty repo");
62
+ assert.ok(dirtyResult!.includes("M001/S01/T01"), "commit message includes unit id");
63
+ assert.deepStrictEqual(run("git status --short", base), "", "repo is clean after auto-commit");
64
64
 
65
65
  console.log("\n=== getSliceBranchName ===");
66
- assertEq(getSliceBranchName("M001", "S01"), "gsd/M001/S01", "branch name format correct");
67
- assertEq(getSliceBranchName("M001", "S01", null), "gsd/M001/S01", "null worktree = plain branch");
68
- assertEq(getSliceBranchName("M001", "S01", "my-wt"), "gsd/my-wt/M001/S01", "worktree-namespaced branch");
66
+ assert.deepStrictEqual(getSliceBranchName("M001", "S01"), "gsd/M001/S01", "branch name format correct");
67
+ assert.deepStrictEqual(getSliceBranchName("M001", "S01", null), "gsd/M001/S01", "null worktree = plain branch");
68
+ assert.deepStrictEqual(getSliceBranchName("M001", "S01", "my-wt"), "gsd/my-wt/M001/S01", "worktree-namespaced branch");
69
69
 
70
70
  console.log("\n=== parseSliceBranch ===");
71
71
  const plain = parseSliceBranch("gsd/M001/S01");
72
- assertTrue(plain !== null, "parses plain branch");
73
- assertEq(plain!.worktreeName, null, "plain branch has no worktree name");
74
- assertEq(plain!.milestoneId, "M001", "plain branch milestone");
75
- assertEq(plain!.sliceId, "S01", "plain branch slice");
72
+ assert.ok(plain !== null, "parses plain branch");
73
+ assert.deepStrictEqual(plain!.worktreeName, null, "plain branch has no worktree name");
74
+ assert.deepStrictEqual(plain!.milestoneId, "M001", "plain branch milestone");
75
+ assert.deepStrictEqual(plain!.sliceId, "S01", "plain branch slice");
76
76
 
77
77
  const namespaced = parseSliceBranch("gsd/feature-auth/M001/S01");
78
- assertTrue(namespaced !== null, "parses worktree-namespaced branch");
79
- assertEq(namespaced!.worktreeName, "feature-auth", "worktree name extracted");
80
- assertEq(namespaced!.milestoneId, "M001", "namespaced branch milestone");
81
- assertEq(namespaced!.sliceId, "S01", "namespaced branch slice");
78
+ assert.ok(namespaced !== null, "parses worktree-namespaced branch");
79
+ assert.deepStrictEqual(namespaced!.worktreeName, "feature-auth", "worktree name extracted");
80
+ assert.deepStrictEqual(namespaced!.milestoneId, "M001", "namespaced branch milestone");
81
+ assert.deepStrictEqual(namespaced!.sliceId, "S01", "namespaced branch slice");
82
82
 
83
83
  const invalid = parseSliceBranch("main");
84
- assertEq(invalid, null, "non-slice branch returns null");
84
+ assert.deepStrictEqual(invalid, null, "non-slice branch returns null");
85
85
 
86
86
  const worktreeBranch = parseSliceBranch("worktree/foo");
87
- assertEq(worktreeBranch, null, "worktree/ prefix is not a slice branch");
87
+ assert.deepStrictEqual(worktreeBranch, null, "worktree/ prefix is not a slice branch");
88
88
 
89
89
  console.log("\n=== SLICE_BRANCH_RE ===");
90
- assertTrue(SLICE_BRANCH_RE.test("gsd/M001/S01"), "regex matches plain branch");
91
- assertTrue(SLICE_BRANCH_RE.test("gsd/my-wt/M001/S01"), "regex matches worktree branch");
92
- assertTrue(!SLICE_BRANCH_RE.test("main"), "regex rejects main");
93
- assertTrue(!SLICE_BRANCH_RE.test("gsd/"), "regex rejects bare gsd/");
94
- assertTrue(!SLICE_BRANCH_RE.test("worktree/foo"), "regex rejects worktree/foo");
90
+ assert.ok(SLICE_BRANCH_RE.test("gsd/M001/S01"), "regex matches plain branch");
91
+ assert.ok(SLICE_BRANCH_RE.test("gsd/my-wt/M001/S01"), "regex matches worktree branch");
92
+ assert.ok(!SLICE_BRANCH_RE.test("main"), "regex rejects main");
93
+ assert.ok(!SLICE_BRANCH_RE.test("gsd/"), "regex rejects bare gsd/");
94
+ assert.ok(!SLICE_BRANCH_RE.test("worktree/foo"), "regex rejects worktree/foo");
95
95
 
96
96
  console.log("\n=== detectWorktreeName ===");
97
- assertEq(detectWorktreeName("/projects/myapp"), null, "no worktree in plain path");
98
- assertEq(detectWorktreeName("/projects/myapp/.gsd/worktrees/feature-auth"), "feature-auth", "detects worktree name");
99
- assertEq(detectWorktreeName("/projects/myapp/.gsd/worktrees/my-wt/subdir"), "my-wt", "detects worktree with subdir");
97
+ assert.deepStrictEqual(detectWorktreeName("/projects/myapp"), null, "no worktree in plain path");
98
+ assert.deepStrictEqual(detectWorktreeName("/projects/myapp/.gsd/worktrees/feature-auth"), "feature-auth", "detects worktree name");
99
+ assert.deepStrictEqual(detectWorktreeName("/projects/myapp/.gsd/worktrees/my-wt/subdir"), "my-wt", "detects worktree with subdir");
100
100
 
101
101
  // ═══════════════════════════════════════════════════════════════════════
102
102
  // Integration branch — facade-level tests
@@ -115,16 +115,16 @@ async function main(): Promise<void> {
115
115
  run("git add -A && git commit -m init", repo);
116
116
 
117
117
  run("git checkout -b f-123-thing", repo);
118
- assertEq(getCurrentBranch(repo), "f-123-thing", "on feature branch");
118
+ assert.deepStrictEqual(getCurrentBranch(repo), "f-123-thing", "on feature branch");
119
119
 
120
120
  const commitsBefore = run("git rev-list --count HEAD", repo);
121
121
  captureIntegrationBranch(repo, "M001");
122
- assertEq(readIntegrationBranch(repo, "M001"), "f-123-thing",
122
+ assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), "f-123-thing",
123
123
  "captureIntegrationBranch records the current branch");
124
124
 
125
125
  // Metadata is stored in external state, not committed to git.
126
126
  const commitsAfter = run("git rev-list --count HEAD", repo);
127
- assertEq(commitsAfter, commitsBefore, "captureIntegrationBranch does not create a git commit");
127
+ assert.deepStrictEqual(commitsAfter, commitsBefore, "captureIntegrationBranch does not create a git commit");
128
128
 
129
129
  rmSync(repo, { recursive: true, force: true });
130
130
  }
@@ -144,7 +144,7 @@ async function main(): Promise<void> {
144
144
  run("git checkout -b gsd/M001/S01", repo);
145
145
  captureIntegrationBranch(repo, "M001");
146
146
 
147
- assertEq(readIntegrationBranch(repo, "M001"), null,
147
+ assert.deepStrictEqual(readIntegrationBranch(repo, "M001"), null,
148
148
  "capture from slice branch is a no-op");
149
149
 
150
150
  rmSync(repo, { recursive: true, force: true });
@@ -167,12 +167,12 @@ async function main(): Promise<void> {
167
167
 
168
168
  // Without milestone set, getMainBranch returns "main"
169
169
  setActiveMilestoneId(repo, null);
170
- assertEq(getMainBranch(repo), "main",
170
+ assert.deepStrictEqual(getMainBranch(repo), "main",
171
171
  "getMainBranch returns main without milestone set");
172
172
 
173
173
  // With milestone set, getMainBranch returns feature branch
174
174
  setActiveMilestoneId(repo, "M001");
175
- assertEq(getMainBranch(repo), "my-feature",
175
+ assert.deepStrictEqual(getMainBranch(repo), "my-feature",
176
176
  "getMainBranch returns integration branch with milestone set");
177
177
 
178
178
  rmSync(repo, { recursive: true, force: true });
@@ -180,22 +180,22 @@ async function main(): Promise<void> {
180
180
 
181
181
  // ── detectWorktreeName: symlink-resolved paths ───────────────────────────
182
182
  console.log("\n=== detectWorktreeName (symlink-resolved paths) ===");
183
- assertEq(
183
+ assert.deepStrictEqual(
184
184
  detectWorktreeName("/Users/fran/.gsd/projects/89e1c9ad49bf/worktrees/M001"),
185
185
  "M001",
186
186
  "detects milestone in symlink-resolved path",
187
187
  );
188
- assertEq(
188
+ assert.deepStrictEqual(
189
189
  detectWorktreeName("/Users/fran/.gsd/projects/abc123/worktrees/M002/subdir"),
190
190
  "M002",
191
191
  "detects milestone with trailing subdir in symlink-resolved path",
192
192
  );
193
- assertEq(
193
+ assert.deepStrictEqual(
194
194
  detectWorktreeName("/Users/fran/.gsd/projects/abc123"),
195
195
  null,
196
196
  "returns null for project root without worktrees segment",
197
197
  );
198
- assertEq(
198
+ assert.deepStrictEqual(
199
199
  detectWorktreeName("/foo/.gsd/worktrees/M001"),
200
200
  "M001",
201
201
  "still detects direct layout path",
@@ -211,7 +211,7 @@ async function main(): Promise<void> {
211
211
 
212
212
  // With GSD_PROJECT_ROOT env var set (layer 1 — coordinator passes it)
213
213
  process.env.GSD_PROJECT_ROOT = "/real/project";
214
- assertEq(
214
+ assert.deepStrictEqual(
215
215
  resolveProjectRoot("/Users/fran/.gsd/projects/89e1c9ad49bf/worktrees/M001"),
216
216
  "/real/project",
217
217
  "uses GSD_PROJECT_ROOT when set",
@@ -219,7 +219,7 @@ async function main(): Promise<void> {
219
219
  delete process.env.GSD_PROJECT_ROOT;
220
220
 
221
221
  // Without GSD_PROJECT_ROOT, direct layout still works (no ~/.gsd collision)
222
- assertEq(
222
+ assert.deepStrictEqual(
223
223
  resolveProjectRoot("/some/repo"),
224
224
  "/some/repo",
225
225
  "ignores GSD_PROJECT_ROOT override for non-worktree paths",
@@ -227,19 +227,19 @@ async function main(): Promise<void> {
227
227
  delete process.env.GSD_PROJECT_ROOT;
228
228
 
229
229
  // Without GSD_PROJECT_ROOT, direct layout still works (no ~/.gsd collision)
230
- assertEq(
230
+ assert.deepStrictEqual(
231
231
  resolveProjectRoot("/foo/.gsd/worktrees/M001"),
232
232
  "/foo",
233
233
  "still resolves direct layout path",
234
234
  );
235
- assertEq(
235
+ assert.deepStrictEqual(
236
236
  resolveProjectRoot("/some/repo"),
237
237
  "/some/repo",
238
238
  "returns unchanged for non-worktree path",
239
239
  );
240
240
 
241
241
  // Without GSD_PROJECT_ROOT, direct layout with nested subdirs
242
- assertEq(
242
+ assert.deepStrictEqual(
243
243
  resolveProjectRoot("/data/.gsd/worktrees/M003/nested"),
244
244
  "/data",
245
245
  "resolves correctly with nested subdirs after worktree name (direct layout)",
@@ -264,7 +264,7 @@ async function main(): Promise<void> {
264
264
  mkdirSync(deep, { recursive: true });
265
265
 
266
266
  process.env.GSD_HOME = join(fakeHome, ".gsd");
267
- assertEq(
267
+ assert.deepStrictEqual(
268
268
  normalizePath(resolveProjectRoot(realpathSync(deep))),
269
269
  normalizePath(project),
270
270
  "resolves to real project root from deep symlink-resolved worktree path",
@@ -276,10 +276,4 @@ async function main(): Promise<void> {
276
276
  }
277
277
 
278
278
  rmSync(base, { recursive: true, force: true });
279
- report();
280
- }
281
-
282
- main().catch((error) => {
283
- console.error(error);
284
- process.exit(1);
285
279
  });
@@ -20,6 +20,7 @@ export interface PlanSliceTaskInput {
20
20
  inputs: string[];
21
21
  expectedOutput: string[];
22
22
  observabilityImpact?: string;
23
+ fullPlanMd?: string;
23
24
  }
24
25
 
25
26
  export interface PlanSliceParams {
@@ -167,6 +168,7 @@ export async function handlePlanSlice(
167
168
  inputs: task.inputs,
168
169
  expectedOutput: task.expectedOutput,
169
170
  observabilityImpact: task.observabilityImpact ?? "",
171
+ fullPlanMd: task.fullPlanMd,
170
172
  });
171
173
  }
172
174
  });
@@ -15,6 +15,7 @@ export interface PlanTaskParams {
15
15
  inputs: string[];
16
16
  expectedOutput: string[];
17
17
  observabilityImpact?: string;
18
+ fullPlanMd?: string;
18
19
  }
19
20
 
20
21
  export interface PlanTaskResult {
@@ -94,6 +95,7 @@ export async function handlePlanTask(
94
95
  inputs: params.inputs,
95
96
  expectedOutput: params.expectedOutput,
96
97
  observabilityImpact: params.observabilityImpact ?? "",
98
+ fullPlanMd: params.fullPlanMd,
97
99
  });
98
100
  });
99
101
  } catch (err) {
@@ -21,6 +21,7 @@ export interface ReplanSliceTaskInput {
21
21
  verify: string;
22
22
  inputs: string[];
23
23
  expectedOutput: string[];
24
+ fullPlanMd?: string;
24
25
  }
25
26
 
26
27
  export interface ReplanSliceParams {
@@ -136,6 +137,7 @@ export async function handleReplanSlice(
136
137
  verify: updatedTask.verify || "",
137
138
  inputs: updatedTask.inputs || [],
138
139
  expectedOutput: updatedTask.expectedOutput || [],
140
+ fullPlanMd: updatedTask.fullPlanMd,
139
141
  });
140
142
  } else {
141
143
  // Insert new task then set planning fields
@@ -154,6 +156,7 @@ export async function handleReplanSlice(
154
156
  verify: updatedTask.verify || "",
155
157
  inputs: updatedTask.inputs || [],
156
158
  expectedOutput: updatedTask.expectedOutput || [],
159
+ fullPlanMd: updatedTask.fullPlanMd,
157
160
  });
158
161
  }
159
162
  }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * validate-milestone handler — the core operation behind gsd_validate_milestone.
3
+ *
4
+ * Persists milestone validation results to the assessments table,
5
+ * renders VALIDATION.md to disk, and invalidates caches.
6
+ */
7
+
8
+ import { join } from "node:path";
9
+
10
+ import {
11
+ transaction,
12
+ _getAdapter,
13
+ } from "../gsd-db.js";
14
+ import { resolveMilestonePath, clearPathCache } from "../paths.js";
15
+ import { saveFile, clearParseCache } from "../files.js";
16
+ import { invalidateStateCache } from "../state.js";
17
+
18
+ export interface ValidateMilestoneParams {
19
+ milestoneId: string;
20
+ verdict: "pass" | "needs-attention" | "needs-remediation";
21
+ remediationRound: number;
22
+ successCriteriaChecklist: string;
23
+ sliceDeliveryAudit: string;
24
+ crossSliceIntegration: string;
25
+ requirementCoverage: string;
26
+ verdictRationale: string;
27
+ remediationPlan?: string;
28
+ }
29
+
30
+ export interface ValidateMilestoneResult {
31
+ milestoneId: string;
32
+ verdict: string;
33
+ validationPath: string;
34
+ }
35
+
36
+ function renderValidationMarkdown(params: ValidateMilestoneParams): string {
37
+ let md = `---
38
+ verdict: ${params.verdict}
39
+ remediation_round: ${params.remediationRound}
40
+ ---
41
+
42
+ # Milestone Validation: ${params.milestoneId}
43
+
44
+ ## Success Criteria Checklist
45
+ ${params.successCriteriaChecklist}
46
+
47
+ ## Slice Delivery Audit
48
+ ${params.sliceDeliveryAudit}
49
+
50
+ ## Cross-Slice Integration
51
+ ${params.crossSliceIntegration}
52
+
53
+ ## Requirement Coverage
54
+ ${params.requirementCoverage}
55
+
56
+ ## Verdict Rationale
57
+ ${params.verdictRationale}
58
+ `;
59
+
60
+ if (params.verdict === "needs-remediation" && params.remediationPlan) {
61
+ md += `\n## Remediation Plan\n${params.remediationPlan}\n`;
62
+ }
63
+
64
+ return md;
65
+ }
66
+
67
+ export async function handleValidateMilestone(
68
+ params: ValidateMilestoneParams,
69
+ basePath: string,
70
+ ): Promise<ValidateMilestoneResult | { error: string }> {
71
+ if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
72
+ return { error: "milestoneId is required and must be a non-empty string" };
73
+ }
74
+ const validVerdicts = ["pass", "needs-attention", "needs-remediation"];
75
+ if (!validVerdicts.includes(params.verdict)) {
76
+ return { error: `verdict must be one of: ${validVerdicts.join(", ")}` };
77
+ }
78
+
79
+ // ── Filesystem render ──────────────────────────────────────────────────
80
+ const validationMd = renderValidationMarkdown(params);
81
+
82
+ let validationPath: string;
83
+ const milestoneDir = resolveMilestonePath(basePath, params.milestoneId);
84
+ if (milestoneDir) {
85
+ validationPath = join(milestoneDir, `${params.milestoneId}-VALIDATION.md`);
86
+ } else {
87
+ const gsdDir = join(basePath, ".gsd");
88
+ const manualDir = join(gsdDir, "milestones", params.milestoneId);
89
+ validationPath = join(manualDir, `${params.milestoneId}-VALIDATION.md`);
90
+ }
91
+
92
+ try {
93
+ await saveFile(validationPath, validationMd);
94
+ } catch (renderErr) {
95
+ process.stderr.write(
96
+ `gsd-db: validate_milestone — disk render failed: ${(renderErr as Error).message}\n`,
97
+ );
98
+ return { error: `disk render failed: ${(renderErr as Error).message}` };
99
+ }
100
+
101
+ // ── DB write — store in assessments table ──────────────────────────────
102
+ const validatedAt = new Date().toISOString();
103
+
104
+ transaction(() => {
105
+ const adapter = _getAdapter()!;
106
+ adapter.prepare(
107
+ `INSERT OR REPLACE INTO assessments (path, milestone_id, slice_id, task_id, status, scope, full_content, created_at)
108
+ VALUES (:path, :mid, NULL, NULL, :verdict, 'milestone-validation', :content, :created_at)`,
109
+ ).run({
110
+ ":path": validationPath,
111
+ ":mid": params.milestoneId,
112
+ ":verdict": params.verdict,
113
+ ":content": validationMd,
114
+ ":created_at": validatedAt,
115
+ });
116
+ });
117
+
118
+ invalidateStateCache();
119
+ clearPathCache();
120
+ clearParseCache();
121
+
122
+ return {
123
+ milestoneId: params.milestoneId,
124
+ verdict: params.verdict,
125
+ validationPath,
126
+ };
127
+ }
@@ -0,0 +1,193 @@
1
+ // GSD Extension — Workflow Logger
2
+ // Centralized warning/error accumulator for the workflow engine pipeline.
3
+ // Captures structured entries that the auto-loop can drain after each unit
4
+ // to surface root causes for stuck loops, silent degradation, and blocked writes.
5
+ //
6
+ // Stderr policy: every logWarning/logError call writes immediately to stderr
7
+ // for terminal visibility. This is intentional — unlike debug-logger (which is
8
+ // opt-in and zero-overhead when disabled), workflow-logger covers operational
9
+ // warnings/errors that should always be visible. There is no disable flag.
10
+ //
11
+ // Singleton safety: _buffer is module-level and shared across all calls within
12
+ // a process. The auto-loop must call _resetLogs() (or drainAndSummarize()) at
13
+ // the start of each unit to prevent log bleed between units running in the same
14
+ // Node process.
15
+
16
+ // ─── Types ──────────────────────────────────────────────────────────────
17
+
18
+ export type LogSeverity = "warn" | "error";
19
+
20
+ export type LogComponent =
21
+ | "engine" // WorkflowEngine afterCommand side effects
22
+ | "projection" // Projection rendering
23
+ | "manifest" // Manifest write
24
+ | "event-log" // Event append
25
+ | "intercept" // Write intercept / tool-call blocks
26
+ | "migration" // Auto-migration from markdown
27
+ | "state" // deriveState fallback/degradation
28
+ | "tool" // Tool handler errors
29
+ | "compaction" // Event compaction
30
+ | "reconcile"; // Worktree reconciliation
31
+
32
+ export interface LogEntry {
33
+ ts: string;
34
+ severity: LogSeverity;
35
+ component: LogComponent;
36
+ message: string;
37
+ /** Optional structured context (file path, command name, etc.) */
38
+ context?: Record<string, string>;
39
+ }
40
+
41
+ // ─── Buffer ─────────────────────────────────────────────────────────────
42
+
43
+ const MAX_BUFFER = 100;
44
+ let _buffer: LogEntry[] = [];
45
+
46
+ // ─── Public API ─────────────────────────────────────────────────────────
47
+
48
+ /**
49
+ * Record a warning. Also writes to stderr for terminal visibility.
50
+ */
51
+ export function logWarning(
52
+ component: LogComponent,
53
+ message: string,
54
+ context?: Record<string, string>,
55
+ ): void {
56
+ _push("warn", component, message, context);
57
+ }
58
+
59
+ /**
60
+ * Record an error. Also writes to stderr for terminal visibility.
61
+ */
62
+ export function logError(
63
+ component: LogComponent,
64
+ message: string,
65
+ context?: Record<string, string>,
66
+ ): void {
67
+ _push("error", component, message, context);
68
+ }
69
+
70
+ /**
71
+ * Drain all accumulated entries and clear the buffer.
72
+ * Returns entries oldest-first.
73
+ *
74
+ * WARNING: Call summarizeLogs() or drainAndSummarize() BEFORE calling this
75
+ * if you need a summary — drainLogs() clears the buffer immediately.
76
+ */
77
+ export function drainLogs(): LogEntry[] {
78
+ const entries = _buffer;
79
+ _buffer = [];
80
+ return entries;
81
+ }
82
+
83
+ /**
84
+ * Atomically summarize then drain — the safe way to consume logs.
85
+ * Use this in the auto-loop instead of calling summarizeLogs() + drainLogs()
86
+ * separately to avoid the ordering footgun.
87
+ */
88
+ export function drainAndSummarize(): { logs: LogEntry[]; summary: string | null } {
89
+ const summary = summarizeLogs();
90
+ const logs = drainLogs();
91
+ return { logs, summary };
92
+ }
93
+
94
+ /**
95
+ * Peek at current entries without clearing.
96
+ */
97
+ export function peekLogs(): readonly LogEntry[] {
98
+ return _buffer;
99
+ }
100
+
101
+ /**
102
+ * Returns true if the buffer contains any error-severity entries.
103
+ */
104
+ export function hasErrors(): boolean {
105
+ return _buffer.some((e) => e.severity === "error");
106
+ }
107
+
108
+ /**
109
+ * Returns true if the buffer contains any warn-severity entries.
110
+ * Use hasAnyIssues() if you want to check for either severity.
111
+ */
112
+ export function hasWarnings(): boolean {
113
+ return _buffer.some((e) => e.severity === "warn");
114
+ }
115
+
116
+ /**
117
+ * Returns true if the buffer contains any entries (warn or error).
118
+ */
119
+ export function hasAnyIssues(): boolean {
120
+ return _buffer.length > 0;
121
+ }
122
+
123
+ /**
124
+ * Get a one-line summary of accumulated issues for stuck detection messages.
125
+ * Returns null if no entries.
126
+ *
127
+ * Must be called BEFORE drainLogs() — use drainAndSummarize() for safe ordering.
128
+ */
129
+ export function summarizeLogs(): string | null {
130
+ if (_buffer.length === 0) return null;
131
+ const errors = _buffer.filter((e) => e.severity === "error");
132
+ const warns = _buffer.filter((e) => e.severity === "warn");
133
+
134
+ const parts: string[] = [];
135
+ if (errors.length > 0) {
136
+ parts.push(`${errors.length} error(s): ${errors.map((e) => e.message).join("; ")}`);
137
+ }
138
+ if (warns.length > 0) {
139
+ parts.push(`${warns.length} warning(s): ${warns.map((e) => e.message).join("; ")}`);
140
+ }
141
+ return parts.join(" | ");
142
+ }
143
+
144
+ /**
145
+ * Format entries for display (used by auto-loop post-unit notification).
146
+ * Note: context fields are not included in the formatted output.
147
+ */
148
+ export function formatForNotification(entries: readonly LogEntry[]): string {
149
+ if (entries.length === 0) return "";
150
+ if (entries.length === 1) {
151
+ const e = entries[0];
152
+ return `[${e.component}] ${e.message}`;
153
+ }
154
+ return entries
155
+ .map((e) => `[${e.component}] ${e.message}`)
156
+ .join("\n");
157
+ }
158
+
159
+ /**
160
+ * Reset buffer. Call at the start of each auto-loop unit to prevent log bleed
161
+ * between units running in the same process. Also used in tests via _resetLogs().
162
+ */
163
+ export function _resetLogs(): void {
164
+ _buffer = [];
165
+ }
166
+
167
+ // ─── Internal ───────────────────────────────────────────────────────────
168
+
169
+ function _push(
170
+ severity: LogSeverity,
171
+ component: LogComponent,
172
+ message: string,
173
+ context?: Record<string, string>,
174
+ ): void {
175
+ const entry: LogEntry = {
176
+ ts: new Date().toISOString(),
177
+ severity,
178
+ component,
179
+ message,
180
+ ...(context ? { context } : {}),
181
+ };
182
+
183
+ // Always forward to stderr so terminal watchers see it (see module header for policy)
184
+ const prefix = severity === "error" ? "ERROR" : "WARN";
185
+ const ctxStr = context ? ` ${JSON.stringify(context)}` : "";
186
+ process.stderr.write(`[gsd:${component}] ${prefix}: ${message}${ctxStr}\n`);
187
+
188
+ // Buffer for auto-loop to drain
189
+ _buffer.push(entry);
190
+ if (_buffer.length > MAX_BUFFER) {
191
+ _buffer.shift();
192
+ }
193
+ }
@@ -16,8 +16,10 @@
16
16
  */
17
17
 
18
18
  import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync } from "node:fs";
19
+ import { execFileSync } from "node:child_process";
19
20
  import { join, resolve, sep } from "node:path";
20
21
  import { GSDError, GSD_PARSE_ERROR, GSD_STALE_STATE, GSD_LOCK_HELD, GSD_GIT_ERROR, GSD_MERGE_CONFLICT } from "./errors.js";
22
+ import { logWarning } from "./workflow-logger.js";
21
23
  import {
22
24
  nativeBranchDelete,
23
25
  nativeBranchExists,
@@ -135,9 +137,7 @@ export function createWorktree(basePath: string, name: string, opts: { branch?:
135
137
  // worktree can be created in its place.
136
138
  const gitFilePath = join(wtPath, ".git");
137
139
  if (!existsSync(gitFilePath)) {
138
- console.error(
139
- `[GSD] Removing stale worktree directory (no .git file): ${wtPath}`,
140
- );
140
+ logWarning("reconcile", `Removing stale worktree directory (no .git file): ${wtPath}`, { worktree: name });
141
141
  rmSync(wtPath, { recursive: true, force: true });
142
142
  } else {
143
143
  throw new GSDError(GSD_STALE_STATE, `Worktree "${name}" already exists at ${wtPath}`);
@@ -321,8 +321,44 @@ export function removeWorktree(
321
321
  return;
322
322
  }
323
323
 
324
- // Remove worktree using the resolved path (force if requested, to handle dirty worktrees)
325
- try { nativeWorktreeRemove(basePath, resolvedWtPath, force); } catch { /* may fail */ }
324
+ // Submodule safety (#2337): detect submodules with uncommitted changes
325
+ // before force-removing the worktree. Force removal destroys all uncommitted
326
+ // state, which is especially destructive for submodule directories.
327
+ let hasSubmoduleChanges = false;
328
+ const gitmodulesPath = join(resolvedWtPath, ".gitmodules");
329
+ if (existsSync(gitmodulesPath)) {
330
+ try {
331
+ const submoduleStatus = execFileSync(
332
+ "git", ["submodule", "status"],
333
+ { cwd: resolvedWtPath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
334
+ ).trim();
335
+ // Lines starting with '+' indicate uncommitted submodule changes
336
+ hasSubmoduleChanges = submoduleStatus.split("\n").some(
337
+ (line: string) => line.startsWith("+") || line.startsWith("-"),
338
+ );
339
+ if (hasSubmoduleChanges) {
340
+ // Stash submodule changes so they are not lost during force removal.
341
+ // The stash is created in the worktree before it's torn down.
342
+ try {
343
+ execFileSync(
344
+ "git", ["stash", "push", "-m", "gsd: auto-stash submodule changes before worktree teardown"],
345
+ { cwd: resolvedWtPath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
346
+ );
347
+ logWarning("reconcile", `Stashed uncommitted submodule changes before worktree teardown`, { worktree: name, path: resolvedWtPath });
348
+ } catch {
349
+ // Stash failed — warn the user that submodule changes may be lost
350
+ logWarning("reconcile", `Submodule changes detected — stash failed, changes may be lost during force removal`, { worktree: name, path: resolvedWtPath });
351
+ }
352
+ }
353
+ } catch {
354
+ // submodule status failed — proceed with normal removal
355
+ }
356
+ }
357
+
358
+ // Remove worktree: try non-force first when submodules have changes,
359
+ // falling back to force only after submodule state has been preserved.
360
+ const useForce = hasSubmoduleChanges ? false : force;
361
+ try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch { /* may fail */ }
326
362
 
327
363
  // If the directory is still there (e.g. locked), try harder with force
328
364
  if (existsSync(resolvedWtPath)) {