@opengsd/gsd-pi 1.4.0-dev.c18009cd → 1.5.0-dev.4fb2d460

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 (334) hide show
  1. package/README.md +10 -10
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/ask-user-questions.js +1 -1
  4. package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
  5. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
  6. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  7. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
  9. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -2
  11. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  12. package/dist/resources/extensions/gsd/auto-start.js +4 -3
  13. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  14. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  15. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  16. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  17. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  18. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  19. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  20. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  22. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  23. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  24. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  25. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  26. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  27. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  28. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  36. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
  37. package/dist/resources/extensions/gsd/auto.js +9 -3
  38. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  39. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
  40. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  41. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
  42. package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
  43. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  44. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  45. package/dist/resources/extensions/gsd/db/queries.js +9 -9
  46. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  47. package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
  48. package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
  49. package/dist/resources/extensions/gsd/forensics.js +1 -1
  50. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  51. package/dist/resources/extensions/gsd/git-service.js +45 -0
  52. package/dist/resources/extensions/gsd/gsd-db.js +23 -11
  53. package/dist/resources/extensions/gsd/guided-flow.js +22 -17
  54. package/dist/resources/extensions/gsd/markdown-renderer.js +15 -3
  55. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  56. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  57. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
  58. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  59. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  60. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  61. package/dist/resources/extensions/gsd/paths.js +17 -3
  62. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  63. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  64. package/dist/resources/extensions/gsd/preferences-validation.js +82 -0
  65. package/dist/resources/extensions/gsd/preferences.js +20 -0
  66. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  67. package/dist/resources/extensions/gsd/quick.js +12 -3
  68. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  69. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  70. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  71. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  72. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  73. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  74. package/dist/resources/extensions/gsd/unit-registry.js +12 -2
  75. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  76. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  77. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  78. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  79. package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
  80. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  81. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  82. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  83. package/dist/web/standalone/.next/BUILD_ID +1 -1
  84. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  85. package/dist/web/standalone/.next/build-manifest.json +2 -2
  86. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  87. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.html +1 -1
  104. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  111. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  113. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  114. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  115. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  116. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  117. package/integrations/hermes/open_gsd_hermes/gsd_client.py +24 -35
  118. package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
  119. package/package.json +6 -6
  120. package/packages/cloud-mcp-gateway/package.json +2 -2
  121. package/packages/contracts/package.json +1 -1
  122. package/packages/daemon/package.json +4 -4
  123. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  124. package/packages/gsd-agent-core/package.json +5 -5
  125. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  126. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  127. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  128. package/packages/gsd-agent-modes/package.json +7 -7
  129. package/packages/mcp-server/package.json +4 -4
  130. package/packages/native/package.json +1 -1
  131. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  132. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  133. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  134. package/packages/pi-agent-core/package.json +1 -1
  135. package/packages/pi-ai/dist/index.d.ts +1 -0
  136. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/index.js +1 -0
  138. package/packages/pi-ai/dist/index.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  140. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  141. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  142. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  143. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  144. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  145. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  146. package/packages/pi-ai/dist/types.d.ts +8 -20
  147. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/types.js.map +1 -1
  149. package/packages/pi-ai/package.json +1 -1
  150. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  151. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  153. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  155. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  157. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  159. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  160. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  161. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  162. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  163. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  165. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  167. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/index.js +1 -0
  169. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  170. package/packages/pi-coding-agent/package.json +7 -7
  171. package/packages/pi-tui/package.json +2 -2
  172. package/packages/rpc-client/package.json +2 -2
  173. package/pkg/dist/core/export-html/template.js +53 -4
  174. package/pkg/package.json +1 -1
  175. package/src/resources/extensions/ask-user-questions.ts +1 -1
  176. package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
  177. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
  178. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  179. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  180. package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
  181. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  182. package/src/resources/extensions/gsd/auto-dispatch.ts +18 -2
  183. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  184. package/src/resources/extensions/gsd/auto-start.ts +5 -3
  185. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  186. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  187. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  188. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  189. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  190. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  191. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  192. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  193. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  194. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  195. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  196. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  197. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  198. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  199. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  200. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  201. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  202. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  203. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  204. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  205. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  206. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  207. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  208. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
  209. package/src/resources/extensions/gsd/auto.ts +11 -17
  210. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  211. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
  212. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  213. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
  214. package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
  215. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  216. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  217. package/src/resources/extensions/gsd/db/queries.ts +9 -9
  218. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  219. package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
  220. package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
  221. package/src/resources/extensions/gsd/forensics.ts +1 -1
  222. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  223. package/src/resources/extensions/gsd/git-service.ts +53 -0
  224. package/src/resources/extensions/gsd/gsd-db.ts +33 -11
  225. package/src/resources/extensions/gsd/guided-flow.ts +26 -18
  226. package/src/resources/extensions/gsd/markdown-renderer.ts +15 -3
  227. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  228. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  229. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
  230. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  231. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  232. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  233. package/src/resources/extensions/gsd/paths.ts +21 -3
  234. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  235. package/src/resources/extensions/gsd/preferences-types.ts +30 -0
  236. package/src/resources/extensions/gsd/preferences-validation.ts +69 -0
  237. package/src/resources/extensions/gsd/preferences.ts +20 -0
  238. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  239. package/src/resources/extensions/gsd/quick.ts +11 -3
  240. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  241. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  242. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  243. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  244. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  245. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
  246. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  247. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
  248. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  249. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  250. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  251. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  252. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  253. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  254. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  255. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  256. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  257. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  258. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  259. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  260. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  261. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  262. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  263. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  264. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  265. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
  266. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  267. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
  268. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
  269. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  270. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  271. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  272. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  273. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  274. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  275. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  276. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  277. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  278. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  279. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  280. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  281. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  282. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  283. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  284. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  285. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  287. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
  288. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
  289. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  291. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  292. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  293. package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
  294. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
  295. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  296. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  297. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  298. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  299. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  300. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  301. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  302. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  303. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  304. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  305. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  306. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  307. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
  308. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  309. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
  310. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  311. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  312. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  313. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  314. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  315. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  316. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  317. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  318. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
  319. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  320. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  321. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  322. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  323. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  324. package/src/resources/extensions/gsd/unit-registry.ts +16 -2
  325. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  326. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  327. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  328. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  329. package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
  330. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  331. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  332. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  333. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
  334. /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
@@ -0,0 +1,391 @@
1
+ // gsd-pi — Auto-worktree merge module.
2
+ //
3
+ // Owns milestone merge/closeout for auto-worktrees: dirty-state capture, DB
4
+ // reconciliation, integration-branch merge, stash/shelter recovery, publication,
5
+ // and safe teardown.
6
+
7
+ import { atomicWriteSync } from "./atomic-write.js";
8
+ import { debugLog } from "./debug-logger.js";
9
+ import { GSDError, GSD_GIT_ERROR } from "./errors.js";
10
+ import { autoResolveSafeConflictPaths } from "./git-conflict-resolve.js";
11
+ import {
12
+ MergeConflictError,
13
+ RUNTIME_EXCLUSION_PATHS,
14
+ } from "./git-service.js";
15
+ import {
16
+ getMilestone,
17
+ isDbAvailable,
18
+ } from "./gsd-db.js";
19
+ import {
20
+ nativeAddAllWithExclusions,
21
+ nativeCommit,
22
+ nativeConflictFiles,
23
+ nativeGetCurrentBranch,
24
+ nativeMergeRegular,
25
+ nativeMergeSquash,
26
+ nativeWorkingTreeStatus,
27
+ } from "./native-git-bridge.js";
28
+ import { loadEffectiveGSDPreferences } from "./preferences.js";
29
+ import { publishMilestone } from "./publication.js";
30
+ import {
31
+ autoWorktreeBranch,
32
+ } from "./auto-worktree-branch-lifecycle.js";
33
+ import { createMilestoneDirectoryShelter } from "./auto-worktree-milestone-shelter.js";
34
+ import { getActiveWorkspace } from "./auto-worktree-session-registry.js";
35
+ import {
36
+ assertNoUnanchoredCodeChangesAfterEmptyMerge,
37
+ detectMergedCodeFilesChanged,
38
+ } from "./auto-worktree-merge-code-changes.js";
39
+ import { reconcileMilestoneBranchHead } from "./auto-worktree-merge-branch-head.js";
40
+ import { finalizeAlreadyMergedMilestoneIfReachable } from "./auto-worktree-merge-already-merged.js";
41
+ import { cleanupMergedMilestoneWorktree } from "./auto-worktree-merge-cleanup.js";
42
+ import { assertMilestoneDbReadyForMerge } from "./auto-worktree-merge-db-ready.js";
43
+ import { prepareIntegrationBranchForMilestoneMerge } from "./auto-worktree-merge-integration-branch.js";
44
+ import { buildMilestoneMergeMessage } from "./auto-worktree-merge-message.js";
45
+ import { assertMilestoneWorktreeCleanBeforeTeardown } from "./auto-worktree-merge-pre-teardown.js";
46
+ import { createPreMergeStash } from "./auto-worktree-merge-stash.js";
47
+ import {
48
+ cleanupConflictState,
49
+ removeMergeStateFiles,
50
+ } from "./worktree-git-recovery.js";
51
+ import { logError, logWarning } from "./workflow-logger.js";
52
+
53
+ export { _setRestoreEntryFnForTests } from "./auto-worktree-milestone-shelter.js";
54
+
55
+
56
+ // ─── Merge Milestone -> Main ───────────────────────────────────────────────
57
+
58
+ /**
59
+ * Auto-commit any dirty (uncommitted) state in the given directory.
60
+ * Returns true if a commit was made, false if working tree was clean.
61
+ */
62
+ function autoCommitDirtyState(cwd: string): boolean {
63
+ try {
64
+ const status = nativeWorkingTreeStatus(cwd);
65
+ if (!status) return false;
66
+ nativeAddAllWithExclusions(cwd, RUNTIME_EXCLUSION_PATHS);
67
+ const result = nativeCommit(
68
+ cwd,
69
+ "chore: auto-commit before milestone merge",
70
+ );
71
+ return result !== null;
72
+ } catch (e) {
73
+ debugLog("autoCommitDirtyState", { error: String(e) });
74
+ throw new GSDError(
75
+ GSD_GIT_ERROR,
76
+ `Failed to auto-commit dirty worktree state before milestone merge: ${e instanceof Error ? e.message : String(e)}`,
77
+ );
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Squash-merge the milestone branch into main with a rich commit message
83
+ * listing all completed slices, then tear down the worktree.
84
+ *
85
+ * Sequence:
86
+ * 1. Auto-commit dirty worktree state
87
+ * 2. chdir to originalBasePath
88
+ * 3. git checkout main
89
+ * 4. git merge --squash milestone/<MID>
90
+ * 5. git commit with rich message
91
+ * 6. Auto-push if enabled
92
+ * 7. Delete milestone branch
93
+ * 8. Remove worktree directory
94
+ * 9. Clear originalBase
95
+ *
96
+ * On merge conflict: throws MergeConflictError.
97
+ * On "nothing to commit" after squash: safe only if milestone work is already
98
+ * on the integration branch. Throws if unanchored code changes would be lost.
99
+ *
100
+ * @internal **Do not call directly.** This is the inner squash-merge primitive
101
+ * for the Worktree Lifecycle Module (ADR-016 phase 2 / A3, issue #5619).
102
+ * Production callers must go through `WorktreeLifecycle.mergeMilestoneStandalone`
103
+ * or `WorktreeLifecycle.exitMilestone({ merge: true })`. The export keyword
104
+ * is preserved for legacy tests and the default transaction adapter; production
105
+ * wiring depends on `createDefaultMilestoneMergeTransaction()` instead.
106
+ */
107
+ export function mergeMilestoneToMain(
108
+ originalBasePath_: string,
109
+ milestoneId: string,
110
+ roadmapContent: string,
111
+ ): { commitMessage: string; pushed: boolean; prCreated: boolean; codeFilesChanged: boolean } {
112
+ const worktreeCwd = process.cwd();
113
+ const milestoneBranch = autoWorktreeBranch(milestoneId);
114
+
115
+ // 1. Auto-commit dirty state before leaving.
116
+ // Guard: when we entered through an auto-worktree (originalBase is set),
117
+ // only auto-commit when cwd is on the milestone branch. In parallel mode,
118
+ // cwd may be on the integration branch after a prior merge's
119
+ // MergeConflictError left cwd unrestored. Auto-committing on the
120
+ // integration branch captures dirty files from OTHER milestones under a
121
+ // misleading commit message, contaminating the main branch (#2929).
122
+ //
123
+ // When activeWorkspace is null (branch mode, no worktree), autoCommitDirtyState
124
+ // runs unconditionally — the caller is responsible for cwd placement.
125
+ {
126
+ let shouldAutoCommit = true;
127
+ if (getActiveWorkspace() !== null) {
128
+ try {
129
+ const currentBranch = nativeGetCurrentBranch(worktreeCwd);
130
+ shouldAutoCommit = currentBranch === milestoneBranch;
131
+ } catch {
132
+ // If we can't determine the branch, skip the auto-commit to be safe
133
+ shouldAutoCommit = false;
134
+ }
135
+ }
136
+ if (shouldAutoCommit) {
137
+ autoCommitDirtyState(worktreeCwd);
138
+ }
139
+ }
140
+
141
+ // Reconcile DB state and prove closeout before leaving worktree context.
142
+ assertMilestoneDbReadyForMerge({
143
+ milestoneId,
144
+ projectRoot: originalBasePath_,
145
+ worktreeCwd,
146
+ });
147
+
148
+ // 2. Build completed-slice summaries and rich commit message.
149
+ const { commitMessage, milestoneTitle, sliceSummaries } = buildMilestoneMergeMessage({
150
+ milestoneId,
151
+ milestoneBranch,
152
+ roadmapContent,
153
+ });
154
+
155
+ // 3. chdir to original base
156
+ // Note: previousCwd captures the cwd at this point — i.e. the worktree cwd
157
+ // entering the function. Subsequent throws restore to previousCwd, leaving
158
+ // the caller in worktree-cwd; callers (worktree-resolver) are responsible
159
+ // for any further cwd movement on the error path.
160
+ const previousCwd = process.cwd();
161
+ process.chdir(originalBasePath_);
162
+
163
+ // 4/5. Resolve and prepare the integration branch. The integration-branch
164
+ // module owns stale metadata fallback, self-merge fail-closed behavior,
165
+ // detached HEAD protection, transient state cleanup, and checkout.
166
+ const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
167
+ const { mainBranch } = prepareIntegrationBranchForMilestoneMerge({
168
+ milestoneBranch,
169
+ milestoneId,
170
+ prefs,
171
+ previousCwd,
172
+ projectRoot: originalBasePath_,
173
+ });
174
+
175
+ // 6b. Reconcile worktree HEAD with milestone branch ref (#1846).
176
+ // The branch-head module owns the data-loss guard: fast-forward stale
177
+ // branch refs to detached worktree commits, and fail loudly on divergence.
178
+ reconcileMilestoneBranchHead({
179
+ projectRoot: originalBasePath_,
180
+ worktreeCwd,
181
+ milestoneBranch,
182
+ previousCwd,
183
+ });
184
+
185
+ // Already regular-merged milestones can skip the squash path and proceed to cleanup (#5831).
186
+ const alreadyMerged = finalizeAlreadyMergedMilestoneIfReachable({
187
+ projectRoot: originalBasePath_,
188
+ milestoneId,
189
+ milestoneBranch,
190
+ mainBranch,
191
+ previousCwd,
192
+ commitMessage,
193
+ });
194
+ if (alreadyMerged) return alreadyMerged;
195
+
196
+ // 7. Shelter queued milestone directories before the squash merge (#2505).
197
+ // MUST run before the pre-merge stash so queued CONTEXT files are not swept
198
+ // into a stash that may later fail to pop and strand user work.
199
+ const milestoneDirectoryShelter = createMilestoneDirectoryShelter(
200
+ originalBasePath_,
201
+ milestoneId,
202
+ getMilestone(milestoneId)?.title,
203
+ );
204
+
205
+ // 7a. Stash pre-existing dirty files so the squash merge is not blocked by
206
+ // unrelated local changes (#2151). The stash module also owns Windows DB
207
+ // handle cycling and later stash-pop conflict recovery.
208
+ const preMergeStash = createPreMergeStash(
209
+ originalBasePath_,
210
+ milestoneId,
211
+ process.platform === "win32" && isDbAvailable(),
212
+ );
213
+ preMergeStash.stash();
214
+
215
+ // 7b. Clean up stale merge state before attempting the merge (#2912).
216
+ // A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
217
+ // or interrupted operation) causes git merge to refuse with
218
+ // "fatal: You have not concluded your merge (MERGE_HEAD exists)".
219
+ // Defensively remove merge artifacts before starting.
220
+ removeMergeStateFiles(originalBasePath_, "pre-merge");
221
+
222
+ // 8. Merge — respect merge_strategy preference (#549).
223
+ // "squash" (default): stages changes without a merge commit; caller commits.
224
+ // "merge": --no-ff --no-commit so caller can supply the commit message while
225
+ // git records a real merge commit (MERGE_HEAD present when nativeCommit runs).
226
+ const effectiveStrategy = prefs.merge_strategy === "merge" ? "merge" : "squash";
227
+ const mergeResult = effectiveStrategy === "merge"
228
+ ? nativeMergeRegular(originalBasePath_, milestoneBranch)
229
+ : nativeMergeSquash(originalBasePath_, milestoneBranch);
230
+ preMergeStash.reopenDbAfterMerge();
231
+
232
+ if (!mergeResult.success) {
233
+ // Dirty working tree — the merge was rejected before it started (e.g.
234
+ // untracked .gsd/ files left by syncStateToProjectRoot). Preserve the
235
+ // milestone branch so commits are not lost.
236
+ if (mergeResult.conflicts.includes("__dirty_working_tree__")) {
237
+ // Defensively clean merge state — the native path may leave MERGE_HEAD
238
+ // even when the merge is rejected (#2912).
239
+ removeMergeStateFiles(originalBasePath_, "dirty-tree rejection");
240
+
241
+ // Pop stash before throwing so local work is not lost.
242
+ preMergeStash.restoreForMergeFailure();
243
+ milestoneDirectoryShelter.restore();
244
+ // Restore cwd so the caller is not stranded on the integration branch
245
+ process.chdir(previousCwd);
246
+ // Surface the actual dirty filenames from git stderr instead of
247
+ // generically blaming .gsd/ (#2151).
248
+ const fileList = mergeResult.dirtyFiles?.length
249
+ ? `Dirty files:\n${mergeResult.dirtyFiles.map((f) => ` ${f}`).join("\n")}`
250
+ : `Check \`git status\` in the project root for details.`;
251
+ throw new GSDError(
252
+ GSD_GIT_ERROR,
253
+ `${effectiveStrategy === "merge" ? "Merge" : "Squash merge"} of ${milestoneBranch} rejected: working tree has dirty or untracked files ` +
254
+ `that conflict with the merge. ${fileList}`,
255
+ );
256
+ }
257
+
258
+ // Check for conflicts — use merge result first, fall back to nativeConflictFiles
259
+ const conflictedFiles =
260
+ mergeResult.conflicts.length > 0
261
+ ? mergeResult.conflicts
262
+ : nativeConflictFiles(originalBasePath_);
263
+
264
+ if (conflictedFiles.length > 0) {
265
+ // Separate auto-resolvable conflicts (GSD state files + build artifacts)
266
+ // from real code conflicts. GSD state files diverge between branches
267
+ // during normal operation. Build artifacts are machine-generated and
268
+ // regenerable. Both are safe to accept from the milestone branch.
269
+ const { resolved: autoResolved, remaining: codeConflicts } = autoResolveSafeConflictPaths(
270
+ originalBasePath_,
271
+ conflictedFiles,
272
+ );
273
+ if (autoResolved.length > 0) {
274
+ logWarning("worktree", `auto-resolved safe merge conflicts: ${autoResolved.join(", ")}`);
275
+ }
276
+
277
+ // If there are still real code conflicts, escalate
278
+ if (codeConflicts.length > 0) {
279
+ cleanupConflictState(originalBasePath_);
280
+
281
+ // Pop stash before throwing so local work is not lost (#2151).
282
+ preMergeStash.restoreForMergeFailure();
283
+ milestoneDirectoryShelter.restore();
284
+ // Restore cwd so the caller is not stranded on the integration branch.
285
+ // Without this, the next mergeMilestoneToMain call in a parallel merge
286
+ // sequence uses process.cwd() (now the project root) as worktreeCwd,
287
+ // causing autoCommitDirtyState to commit unrelated milestone files to
288
+ // the integration branch (#2929).
289
+ process.chdir(previousCwd);
290
+ throw new MergeConflictError(
291
+ codeConflicts,
292
+ effectiveStrategy,
293
+ milestoneBranch,
294
+ mainBranch,
295
+ );
296
+ }
297
+ }
298
+ // No conflicts detected — possibly "already up to date", fall through to commit
299
+ }
300
+
301
+ // 9. Commit (handle nothing-to-commit gracefully)
302
+ const commitResult = nativeCommit(originalBasePath_, commitMessage);
303
+ const nothingToCommit = commitResult === null;
304
+
305
+ // 9a. Clean up merge state files left by git merge --squash (#1853, #2912).
306
+ // git only removes SQUASH_MSG when the commit reads it directly (plain
307
+ // `git commit`). nativeCommit uses `-F -` (stdin) or libgit2, neither
308
+ // of which trigger git's SQUASH_MSG cleanup. MERGE_HEAD is created by
309
+ // libgit2's merge even in squash mode and is not removed by nativeCommit.
310
+ // If left on disk, doctor reports `corrupt_merge_state` on every subsequent run.
311
+ removeMergeStateFiles(originalBasePath_, "post-commit");
312
+
313
+ // 9a-ii. Restore stashed files now that the merge+commit is complete (#2151).
314
+ preMergeStash.restoreAfterCommit();
315
+
316
+ // 9a-iii. Restore sheltered queued milestone directories (#2505).
317
+ milestoneDirectoryShelter.restore();
318
+
319
+ // 9b. Safety check (#1792): if nothing was committed, verify the milestone
320
+ // work is already on the integration branch before allowing teardown.
321
+ try {
322
+ assertNoUnanchoredCodeChangesAfterEmptyMerge(
323
+ originalBasePath_,
324
+ mainBranch,
325
+ milestoneBranch,
326
+ effectiveStrategy,
327
+ nothingToCommit,
328
+ );
329
+ } catch (err) {
330
+ process.chdir(previousCwd);
331
+ throw err;
332
+ }
333
+
334
+ // 9c. Detect whether any non-.gsd/ code files were actually merged (#1906).
335
+ const codeFilesChanged = detectMergedCodeFilesChanged(originalBasePath_, nothingToCommit);
336
+
337
+ const finalizeMilestoneCleanup = (): void => {
338
+ cleanupMergedMilestoneWorktree({
339
+ projectRoot: originalBasePath_,
340
+ milestoneId,
341
+ milestoneBranch,
342
+ previousCwd,
343
+ chdirWarningContext: "after merge",
344
+ });
345
+ };
346
+
347
+ let shouldCleanup = false;
348
+ try {
349
+ // 10/9b. Publication (auto-push / draft PR) — Publication module seam (ADR-034).
350
+ const publication = publishMilestone({
351
+ basePath: originalBasePath_,
352
+ milestoneId,
353
+ milestoneTitle,
354
+ integrationBranch: mainBranch,
355
+ milestoneBranch,
356
+ sliceSummaries,
357
+ nothingToCommit,
358
+ prefs: {
359
+ autoPush: prefs.auto_push === true,
360
+ autoPr: prefs.auto_pr === true,
361
+ remote: prefs.remote,
362
+ prTargetBranch: prefs.pr_target_branch,
363
+ },
364
+ });
365
+ const { pushed, prCreated } = publication;
366
+
367
+ // 11. Guard removed — step 9b (#1792) now handles this with a smarter check:
368
+ // throws only when the milestone has unanchored code changes, passes
369
+ // through when the code is genuinely already on the integration branch.
370
+
371
+ // 11a. Pre-teardown safety net (#1853): preserve the milestone branch if
372
+ // the source worktree still has uncommitted changes after the merge commit.
373
+ assertMilestoneWorktreeCleanBeforeTeardown({
374
+ milestoneBranch,
375
+ previousCwd,
376
+ worktreeCwd,
377
+ });
378
+
379
+ shouldCleanup = true;
380
+ return { commitMessage, pushed, prCreated, codeFilesChanged };
381
+ } finally {
382
+ if (shouldCleanup) {
383
+ finalizeMilestoneCleanup();
384
+ } else {
385
+ logWarning(
386
+ "worktree",
387
+ `Skipping worktree cleanup for ${milestoneBranch}; merge did not reach safe-cleanup point and milestone work is preserved for manual recovery.`,
388
+ );
389
+ }
390
+ }
391
+ }
@@ -0,0 +1,159 @@
1
+ // gsd-pi — Milestone directory shelter for auto-worktree merge closeout.
2
+ //
3
+ // Owns the #2505 queued-milestone directory protection step that runs before
4
+ // merge stash handling. This keeps queued CONTEXT files out of the stash and
5
+ // restores them after merge closeout without hiding restore failures.
6
+
7
+ import {
8
+ cpSync,
9
+ existsSync,
10
+ mkdirSync,
11
+ readdirSync,
12
+ rmSync,
13
+ } from "node:fs";
14
+ import { basename, join } from "node:path";
15
+
16
+ import {
17
+ canonicalPhaseDirName,
18
+ gsdRoot,
19
+ milestonesDir,
20
+ resolveMilestonePath,
21
+ } from "./paths.js";
22
+ import { logError, logWarning } from "./workflow-logger.js";
23
+
24
+ export interface MilestoneDirectoryShelter {
25
+ restore(): void;
26
+ }
27
+
28
+ /**
29
+ * Optional override for the shelter restore copy step (milestone merge #2505).
30
+ * Production leaves this null so restore uses the real cpSync; tests inject a
31
+ * throwing function to deterministically exercise the best-effort failure path
32
+ * and the shelter-retention guarantee.
33
+ * @internal
34
+ */
35
+ let restoreEntryFn: ((src: string, dest: string) => void) | null = null;
36
+
37
+ /** @internal */
38
+ export function _setRestoreEntryFnForTests(
39
+ fn: ((src: string, dest: string) => void) | null,
40
+ ): () => void {
41
+ restoreEntryFn = fn;
42
+ return () => { restoreEntryFn = null; };
43
+ }
44
+
45
+ export function createMilestoneDirectoryShelter(
46
+ originalBasePath: string,
47
+ milestoneId: string,
48
+ milestoneTitle: string | undefined,
49
+ ): MilestoneDirectoryShelter {
50
+ const planningDir = milestonesDir(originalBasePath);
51
+ const shelterDir = join(gsdRoot(originalBasePath), ".milestone-shelter");
52
+ const shelteredDirs: string[] = [];
53
+ const mergeDirNames = milestoneDirectoryNames(originalBasePath, milestoneId, milestoneTitle);
54
+ let restored = false;
55
+
56
+ try {
57
+ if (existsSync(planningDir)) {
58
+ const entries = readdirSync(planningDir, { withFileTypes: true });
59
+ for (const entry of entries) {
60
+ if (!entry.isDirectory()) continue;
61
+ if (mergeDirNames.has(entry.name)) continue;
62
+ shelterMilestoneDirectory(planningDir, shelterDir, entry.name, shelteredDirs);
63
+ }
64
+ }
65
+ } catch (err) {
66
+ // Non-fatal — proceed with merge; untracked files may block it.
67
+ logWarning("worktree", `milestone shelter operation failed: ${err instanceof Error ? err.message : String(err)}`);
68
+ }
69
+
70
+ return {
71
+ restore(): void {
72
+ if (restored) return;
73
+ restored = true;
74
+ restoreShelteredDirectories(planningDir, shelterDir, shelteredDirs);
75
+ },
76
+ };
77
+ }
78
+
79
+ function milestoneDirectoryNames(
80
+ originalBasePath: string,
81
+ milestoneId: string,
82
+ milestoneTitle: string | undefined,
83
+ ): Set<string> {
84
+ const mergeDirNames = new Set<string>([milestoneId]);
85
+ const resolvedMergeDir = resolveMilestonePath(originalBasePath, milestoneId);
86
+ if (resolvedMergeDir) {
87
+ mergeDirNames.add(basename(resolvedMergeDir));
88
+ } else {
89
+ mergeDirNames.add(canonicalPhaseDirName(milestoneId, milestoneTitle));
90
+ }
91
+ return mergeDirNames;
92
+ }
93
+
94
+ function shelterMilestoneDirectory(
95
+ planningDir: string,
96
+ shelterDir: string,
97
+ dirName: string,
98
+ shelteredDirs: string[],
99
+ ): void {
100
+ const srcDir = join(planningDir, dirName);
101
+ const dstDir = join(shelterDir, dirName);
102
+ try {
103
+ mkdirSync(shelterDir, { recursive: true });
104
+ cpSync(srcDir, dstDir, { recursive: true, force: true });
105
+ rmSync(srcDir, { recursive: true, force: true });
106
+ shelteredDirs.push(dirName);
107
+ } catch (err) {
108
+ // Non-fatal — if shelter fails, the merge may still succeed.
109
+ logWarning("worktree", `milestone shelter failed (${dirName}): ${err instanceof Error ? err.message : String(err)}`);
110
+ }
111
+ }
112
+
113
+ function restoreShelteredDirectories(
114
+ planningDir: string,
115
+ shelterDir: string,
116
+ shelteredDirs: string[],
117
+ ): void {
118
+ if (shelteredDirs.length === 0) return;
119
+
120
+ let restoreFailed = false;
121
+ for (const dirName of shelteredDirs) {
122
+ const src = join(shelterDir, dirName);
123
+ if (!existsSync(src)) {
124
+ logWarning(
125
+ "worktree",
126
+ `shelter source missing for ${dirName}; skipping restore (shelter already cleaned or entry never staged)`,
127
+ );
128
+ continue;
129
+ }
130
+ try {
131
+ mkdirSync(planningDir, { recursive: true });
132
+ const dest = join(planningDir, dirName);
133
+ if (restoreEntryFn) {
134
+ restoreEntryFn(src, dest);
135
+ } else {
136
+ cpSync(src, dest, { recursive: true, force: true });
137
+ }
138
+ } catch (err) {
139
+ restoreFailed = true;
140
+ logError("worktree", `shelter restore failed (${dirName}): ${err instanceof Error ? err.message : String(err)}`);
141
+ }
142
+ }
143
+
144
+ // Preserve the shelter if any per-entry restore failed — it is the only
145
+ // surviving copy of the queued milestone dirs (sources were deleted during
146
+ // shelter). Deleting it here would permanently lose those files (#2505).
147
+ if (restoreFailed) {
148
+ logWarning("worktree", `shelter retained at ${shelterDir} — manual recovery required for unrestored entries`);
149
+ return;
150
+ }
151
+
152
+ if (existsSync(shelterDir)) {
153
+ try {
154
+ rmSync(shelterDir, { recursive: true, force: true });
155
+ } catch (err) {
156
+ logWarning("worktree", `shelter cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
157
+ }
158
+ }
159
+ }
@@ -0,0 +1,6 @@
1
+ // gsd-pi — Auto-worktree path resolution seam.
2
+ //
3
+ // Keeps callers that only resolve existing auto-worktree paths on the focused
4
+ // entry/path module instead of the legacy auto-worktree compatibility barrel.
5
+
6
+ export { getAutoWorktreePath } from "./auto-worktree-entry.js";
@@ -0,0 +1,10 @@
1
+ // gsd-pi — Narrow auto-worktree path utility seam.
2
+ //
3
+ // Keeps path comparison, pathspec, and expected-warning callers off the legacy
4
+ // auto-worktree compatibility barrel while extraction continues.
5
+
6
+ export {
7
+ _gitPathspecForWorktreePath,
8
+ _isExpectedWorktreeUnlinkError,
9
+ _isSamePath,
10
+ } from "./auto-worktree-cleanup.js";
@@ -0,0 +1,44 @@
1
+ // gsd-pi — Auto-worktree resource version module.
2
+ //
3
+ // Owns managed-resource freshness checks for long-running auto sessions.
4
+
5
+ import { readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+
8
+ import { gsdHome } from "./gsd-home.js";
9
+ import { logWarning } from "./workflow-logger.js";
10
+
11
+ /**
12
+ * Read the resource version (semver) from the managed-resources manifest.
13
+ * Uses gsdVersion instead of syncedAt so that launching a second session
14
+ * doesn't falsely trigger staleness (#804).
15
+ */
16
+ export function readResourceVersion(): string | null {
17
+ const agentDir = process.env.GSD_CODING_AGENT_DIR || join(gsdHome(), "agent");
18
+ const manifestPath = join(agentDir, "managed-resources.json");
19
+ try {
20
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
21
+ return typeof manifest?.gsdVersion === "string"
22
+ ? manifest.gsdVersion
23
+ : null;
24
+ } catch (e) {
25
+ logWarning("worktree", `readResourceVersion failed: ${(e as Error).message}`);
26
+ return null;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Check if managed resources have been updated since session start.
32
+ * Returns a warning message if stale, null otherwise.
33
+ */
34
+ export function checkResourcesStale(
35
+ versionOnStart: string | null,
36
+ ): string | null {
37
+ if (versionOnStart === null) return null;
38
+ const current = readResourceVersion();
39
+ if (current === null) return null;
40
+ if (current !== versionOnStart) {
41
+ return "GSD resources were updated since this session started. Restart gsd to load the new code.";
42
+ }
43
+ return null;
44
+ }