@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,154 @@
1
+ // gsd-pi — Auto-worktree entry and presence detection module.
2
+ //
3
+ // Owns the small interface for finding, entering, and identifying milestone
4
+ // worktrees. The legacy auto-worktree barrel re-exports these functions for
5
+ // compatibility while lifecycle callers can depend on this focused module.
6
+
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ import { GSDError, GSD_GIT_ERROR, GSD_IO_ERROR } from "./errors.js";
11
+ import { nativeGetCurrentBranch } from "./native-git-bridge.js";
12
+ import { worktreePath } from "./worktree-manager.js";
13
+ import { worktreePathFor } from "./worktree-placement.js";
14
+ import { nudgeGitBranchCache } from "./worktree.js";
15
+ import {
16
+ isGsdWorktreePath,
17
+ normalizeWorktreePathForCompare,
18
+ resolveWorktreeProjectRoot,
19
+ } from "./worktree-root.js";
20
+ import {
21
+ getAutoWorktreeOriginalBase,
22
+ setActiveWorkspace,
23
+ } from "./auto-worktree-session-registry.js";
24
+ import { createWorkspace } from "./workspace.js";
25
+ import { logWarning } from "./workflow-logger.js";
26
+
27
+ function safeCwd(fallback: string): string {
28
+ try {
29
+ return process.cwd();
30
+ } catch {
31
+ return fallback;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Detect if the process is currently inside an auto-worktree.
37
+ * Uses the current directory structure plus git branch prefix so detection
38
+ * still works after process restart when module state has been reset.
39
+ */
40
+ export function isInAutoWorktree(basePath: string): boolean {
41
+ const targetPath = isGsdWorktreePath(basePath) ? basePath : safeCwd("");
42
+ if (!isGsdWorktreePath(targetPath)) return false;
43
+
44
+ const storedBase = getAutoWorktreeOriginalBase();
45
+ const projectRoot = resolveWorktreeProjectRoot(basePath, storedBase);
46
+ const targetProjectRoot = resolveWorktreeProjectRoot(targetPath, storedBase);
47
+ if (
48
+ normalizeWorktreePathForCompare(projectRoot) !==
49
+ normalizeWorktreePathForCompare(targetProjectRoot)
50
+ ) {
51
+ return false;
52
+ }
53
+
54
+ try {
55
+ const branch = nativeGetCurrentBranch(targetPath);
56
+ return branch.startsWith("milestone/");
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get the filesystem path for an auto-worktree, or null if it doesn't exist
64
+ * or is not a valid git worktree.
65
+ *
66
+ * Validates that the path is a real git worktree (has a .git file with a
67
+ * gitdir: pointer) rather than just a stray directory. This prevents
68
+ * mis-detection of leftover directories as active worktrees (#695).
69
+ */
70
+ export function getAutoWorktreePath(
71
+ basePath: string,
72
+ milestoneId: string,
73
+ ): string | null {
74
+ basePath = resolveWorktreeProjectRoot(basePath);
75
+
76
+ // basePath is already the resolved project root — go straight to placement
77
+ // instead of worktreePath(), which would re-resolve the root.
78
+ const p = worktreePathFor(basePath, milestoneId);
79
+ if (!existsSync(p)) return null;
80
+
81
+ // Validate this is a real git worktree, not a stray directory.
82
+ // A git worktree has a .git *file* (not directory) containing "gitdir: <path>".
83
+ const gitPath = join(p, ".git");
84
+ if (!existsSync(gitPath)) return null;
85
+ try {
86
+ const content = readFileSync(gitPath, "utf8").trim();
87
+ if (!content.startsWith("gitdir: ")) return null;
88
+ } catch (e) {
89
+ logWarning("worktree", `getAutoWorktreePath .git read failed: ${(e as Error).message}`);
90
+ return null;
91
+ }
92
+
93
+ return p;
94
+ }
95
+
96
+ /**
97
+ * Enter an existing auto-worktree (chdir into it, store originalBase).
98
+ * Use for resume -- the worktree already exists from a prior create.
99
+ *
100
+ * Atomic: chdir + originalBase update in same try block.
101
+ */
102
+ export function enterAutoWorktree(
103
+ basePath: string,
104
+ milestoneId: string,
105
+ ): string {
106
+ basePath = resolveWorktreeProjectRoot(basePath);
107
+
108
+ const p = worktreePath(basePath, milestoneId);
109
+ if (!existsSync(p)) {
110
+ throw new GSDError(
111
+ GSD_IO_ERROR,
112
+ `Auto-worktree for ${milestoneId} does not exist at ${p}`,
113
+ );
114
+ }
115
+
116
+ // Validate this is a real git worktree, not a stray directory (#695)
117
+ const gitPath = join(p, ".git");
118
+ if (!existsSync(gitPath)) {
119
+ throw new GSDError(
120
+ GSD_GIT_ERROR,
121
+ `Auto-worktree path ${p} exists but is not a git worktree (no .git)`,
122
+ );
123
+ }
124
+ try {
125
+ const content = readFileSync(gitPath, "utf8").trim();
126
+ if (!content.startsWith("gitdir: ")) {
127
+ throw new GSDError(
128
+ GSD_GIT_ERROR,
129
+ `Auto-worktree path ${p} has a .git but it is not a worktree gitdir pointer`,
130
+ );
131
+ }
132
+ } catch (err) {
133
+ if (err instanceof Error && err.message.includes("worktree")) throw err;
134
+ throw new GSDError(
135
+ GSD_IO_ERROR,
136
+ `Auto-worktree path ${p} exists but .git is unreadable`,
137
+ );
138
+ }
139
+
140
+ const previousCwd = process.cwd();
141
+
142
+ try {
143
+ process.chdir(p);
144
+ setActiveWorkspace(createWorkspace(basePath));
145
+ } catch (err) {
146
+ throw new GSDError(
147
+ GSD_IO_ERROR,
148
+ `Failed to enter auto-worktree at ${p}: ${err instanceof Error ? err.message : String(err)}`,
149
+ );
150
+ }
151
+
152
+ nudgeGitBranchCache(previousCwd);
153
+ return p;
154
+ }
@@ -0,0 +1,161 @@
1
+ // gsd-pi — Already-merged milestone merge fast path.
2
+ //
3
+ // Owns the safety invariant for milestone branches already reachable from the
4
+ // integration branch: skip the squash merge only when milestone-touched code is
5
+ // still anchored on the current integration branch, then run normal cleanup.
6
+
7
+ import { execFileSync } from "node:child_process";
8
+
9
+ import { debugLog } from "./debug-logger.js";
10
+ import { GSDError, GSD_GIT_ERROR } from "./errors.js";
11
+ import {
12
+ nativeDiffNumstat,
13
+ nativeIsAncestor,
14
+ } from "./native-git-bridge.js";
15
+ import { cleanupMergedMilestoneWorktree } from "./auto-worktree-merge-cleanup.js";
16
+ import { logWarning } from "./workflow-logger.js";
17
+
18
+ export interface AlreadyMergedMilestoneRequest {
19
+ projectRoot: string;
20
+ milestoneId: string;
21
+ milestoneBranch: string;
22
+ mainBranch: string;
23
+ previousCwd: string;
24
+ commitMessage: string;
25
+ }
26
+
27
+ export interface AlreadyMergedMilestoneResult {
28
+ commitMessage: string;
29
+ pushed: false;
30
+ prCreated: false;
31
+ codeFilesChanged: true;
32
+ }
33
+
34
+ export function finalizeAlreadyMergedMilestoneIfReachable(
35
+ request: AlreadyMergedMilestoneRequest,
36
+ ): AlreadyMergedMilestoneResult | null {
37
+ const {
38
+ projectRoot,
39
+ milestoneId,
40
+ milestoneBranch,
41
+ mainBranch,
42
+ previousCwd,
43
+ commitMessage,
44
+ } = request;
45
+
46
+ if (!nativeIsAncestor(projectRoot, milestoneBranch, mainBranch)) {
47
+ return null;
48
+ }
49
+
50
+ assertNoUnanchoredRegularMergeCodeChanges({
51
+ projectRoot,
52
+ milestoneBranch,
53
+ mainBranch,
54
+ previousCwd,
55
+ });
56
+
57
+ debugLog("mergeMilestoneToMain", {
58
+ action: "skip-squash-already-merged",
59
+ milestoneId,
60
+ milestoneBranch,
61
+ mainBranch,
62
+ });
63
+ cleanupMergedMilestoneWorktree({
64
+ projectRoot,
65
+ milestoneId,
66
+ milestoneBranch,
67
+ previousCwd,
68
+ clearProjectRootState: true,
69
+ chdirWarningContext: "after already-merged cleanup",
70
+ });
71
+ return { commitMessage, pushed: false, prCreated: false, codeFilesChanged: true };
72
+ }
73
+
74
+ function assertNoUnanchoredRegularMergeCodeChanges(request: {
75
+ projectRoot: string;
76
+ milestoneBranch: string;
77
+ mainBranch: string;
78
+ previousCwd: string;
79
+ }): void {
80
+ const { projectRoot, milestoneBranch, mainBranch, previousCwd } = request;
81
+ const codeChanges = nativeDiffNumstat(
82
+ projectRoot,
83
+ mainBranch,
84
+ milestoneBranch,
85
+ ).filter((entry) => !entry.path.startsWith(".gsd/"));
86
+ if (codeChanges.length === 0) return;
87
+
88
+ const regularMergeChangedPaths = findRegularMergeChangedPaths(
89
+ projectRoot,
90
+ milestoneBranch,
91
+ mainBranch,
92
+ );
93
+ const unanchoredCodeChanges = codeChanges.filter((entry) =>
94
+ regularMergeChangedPaths.has(entry.path)
95
+ );
96
+ if (unanchoredCodeChanges.length === 0) return;
97
+
98
+ process.chdir(previousCwd);
99
+ throw new GSDError(
100
+ GSD_GIT_ERROR,
101
+ `Milestone branch "${milestoneBranch}" is reachable from "${mainBranch}" ` +
102
+ `but has ${unanchoredCodeChanges.length} milestone-touched code file(s) not on current "${mainBranch}". ` +
103
+ `Aborting worktree teardown to prevent data loss.`,
104
+ );
105
+ }
106
+
107
+ function findRegularMergeChangedPaths(
108
+ basePath: string,
109
+ milestoneBranch: string,
110
+ mainBranch: string,
111
+ ): Set<string> {
112
+ const changedPaths = new Set<string>();
113
+ let mergeLog = "";
114
+ try {
115
+ mergeLog = execFileSync(
116
+ "git",
117
+ ["rev-list", "--merges", "--parents", mainBranch],
118
+ {
119
+ cwd: basePath,
120
+ stdio: ["ignore", "pipe", "pipe"],
121
+ encoding: "utf-8",
122
+ },
123
+ ).trim();
124
+ } catch (err) {
125
+ logWarning("worktree", `regular merge lookup failed: ${err instanceof Error ? err.message : String(err)}`);
126
+ return changedPaths;
127
+ }
128
+
129
+ for (const line of mergeLog.split("\n").filter(Boolean)) {
130
+ const [mergeCommit, firstParent, ...otherParents] = line.split(" ");
131
+ if (!mergeCommit || !firstParent || otherParents.length === 0) continue;
132
+ const mergedMilestone = otherParents.some((parent) => {
133
+ try {
134
+ return nativeIsAncestor(basePath, milestoneBranch, parent);
135
+ } catch {
136
+ return false;
137
+ }
138
+ });
139
+ if (!mergedMilestone) continue;
140
+
141
+ try {
142
+ const output = execFileSync(
143
+ "git",
144
+ ["diff", "--name-only", firstParent, mergeCommit],
145
+ {
146
+ cwd: basePath,
147
+ stdio: ["ignore", "pipe", "pipe"],
148
+ encoding: "utf-8",
149
+ },
150
+ ).trim();
151
+ for (const path of output.split("\n").filter(Boolean)) {
152
+ if (!path.startsWith(".gsd/")) changedPaths.add(path);
153
+ }
154
+ } catch (err) {
155
+ logWarning("worktree", `regular merge diff lookup failed: ${err instanceof Error ? err.message : String(err)}`);
156
+ }
157
+ return changedPaths;
158
+ }
159
+
160
+ return changedPaths;
161
+ }
@@ -0,0 +1,83 @@
1
+ // gsd-pi — Milestone branch/head reconciliation before merge.
2
+ //
3
+ // Owns the data-loss guard that keeps the named milestone branch aligned with
4
+ // the actual worktree HEAD. Detached worktree commits can otherwise sit beyond
5
+ // the branch ref and be silently omitted from the integration merge.
6
+
7
+ import { execFileSync } from "node:child_process";
8
+
9
+ import { debugLog } from "./debug-logger.js";
10
+ import { GSDError, GSD_GIT_ERROR } from "./errors.js";
11
+ import {
12
+ nativeIsAncestor,
13
+ nativeUpdateRef,
14
+ } from "./native-git-bridge.js";
15
+
16
+ export type MilestoneBranchHeadReconciliationResult =
17
+ | { checked: false; updated: false; reason: "not-worktree" | "lookup-failed" }
18
+ | { checked: true; updated: false; reason: "already-current" }
19
+ | { checked: true; updated: true; reason: "fast-forwarded" };
20
+
21
+ export interface MilestoneBranchHeadReconciliationRequest {
22
+ projectRoot: string;
23
+ worktreeCwd: string;
24
+ milestoneBranch: string;
25
+ previousCwd: string;
26
+ }
27
+
28
+ export function reconcileMilestoneBranchHead(
29
+ request: MilestoneBranchHeadReconciliationRequest,
30
+ ): MilestoneBranchHeadReconciliationResult {
31
+ const { projectRoot, worktreeCwd, milestoneBranch, previousCwd } = request;
32
+
33
+ if (worktreeCwd === projectRoot) {
34
+ return { checked: false, updated: false, reason: "not-worktree" };
35
+ }
36
+
37
+ try {
38
+ const worktreeHead = gitRevParse(worktreeCwd, "HEAD");
39
+ const branchHead = gitRevParse(projectRoot, milestoneBranch);
40
+
41
+ if (!worktreeHead || !branchHead || worktreeHead === branchHead) {
42
+ return { checked: true, updated: false, reason: "already-current" };
43
+ }
44
+
45
+ if (nativeIsAncestor(projectRoot, branchHead, worktreeHead)) {
46
+ nativeUpdateRef(
47
+ projectRoot,
48
+ `refs/heads/${milestoneBranch}`,
49
+ worktreeHead,
50
+ );
51
+ debugLog("mergeMilestoneToMain", {
52
+ action: "fast-forward-branch-ref",
53
+ milestoneBranch,
54
+ oldRef: branchHead.slice(0, 8),
55
+ newRef: worktreeHead.slice(0, 8),
56
+ });
57
+ return { checked: true, updated: true, reason: "fast-forwarded" };
58
+ }
59
+
60
+ process.chdir(previousCwd);
61
+ throw new GSDError(
62
+ GSD_GIT_ERROR,
63
+ `Worktree HEAD (${worktreeHead.slice(0, 8)}) diverged from ` +
64
+ `${milestoneBranch} (${branchHead.slice(0, 8)}). ` +
65
+ `Manual reconciliation required before merge.`,
66
+ );
67
+ } catch (err) {
68
+ if (err instanceof GSDError) throw err;
69
+ debugLog("mergeMilestoneToMain", {
70
+ action: "reconcile-skipped",
71
+ reason: String(err),
72
+ });
73
+ return { checked: false, updated: false, reason: "lookup-failed" };
74
+ }
75
+ }
76
+
77
+ function gitRevParse(cwd: string, ref: string): string {
78
+ return execFileSync("git", ["rev-parse", ref], {
79
+ cwd,
80
+ stdio: ["ignore", "pipe", "pipe"],
81
+ encoding: "utf-8",
82
+ }).trim();
83
+ }
@@ -0,0 +1,132 @@
1
+ // gsd-pi — Milestone merge cleanup module.
2
+ //
3
+ // Owns the teardown ordering after a milestone has safely reached the
4
+ // integration branch: clear transient state, remove the worktree, delete the
5
+ // milestone branch only after removal succeeds, clear process-local workspace
6
+ // state, and anchor cwd back at the project root.
7
+
8
+ import { nativeBranchDelete } from "./native-git-bridge.js";
9
+ import { clearProjectRootStateFiles } from "./auto-worktree-cleanup.js";
10
+ import { setActiveWorkspace } from "./auto-worktree-session-registry.js";
11
+ import { removeWorktree } from "./worktree-manager.js";
12
+ import { nudgeGitBranchCache } from "./worktree.js";
13
+ import { debugLog } from "./debug-logger.js";
14
+ import { logWarning } from "./workflow-logger.js";
15
+
16
+ export interface MilestoneMergeCleanupRequest {
17
+ projectRoot: string;
18
+ milestoneId: string;
19
+ milestoneBranch: string;
20
+ previousCwd: string;
21
+ clearProjectRootState?: boolean;
22
+ chdirWarningContext?: string;
23
+ }
24
+
25
+ export interface MilestoneMergeCleanupResult {
26
+ worktreeRemoved: boolean;
27
+ branchDeleteAttempted: boolean;
28
+ activeWorkspaceCleared: boolean;
29
+ }
30
+
31
+ interface MilestoneMergeCleanupDeps {
32
+ clearProjectRootStateFiles: typeof clearProjectRootStateFiles;
33
+ removeWorktree: typeof removeWorktree;
34
+ nativeBranchDelete: typeof nativeBranchDelete;
35
+ setActiveWorkspace: typeof setActiveWorkspace;
36
+ nudgeGitBranchCache: typeof nudgeGitBranchCache;
37
+ chdir: (path: string) => void;
38
+ }
39
+
40
+ const defaultDeps: MilestoneMergeCleanupDeps = {
41
+ clearProjectRootStateFiles,
42
+ removeWorktree,
43
+ nativeBranchDelete,
44
+ setActiveWorkspace,
45
+ nudgeGitBranchCache,
46
+ chdir: process.chdir.bind(process),
47
+ };
48
+
49
+ let deps: MilestoneMergeCleanupDeps = defaultDeps;
50
+
51
+ export function _setMilestoneCleanupDepsForTests(overrides: Partial<MilestoneMergeCleanupDeps>): void {
52
+ deps = { ...defaultDeps, ...overrides };
53
+ }
54
+
55
+ export function _resetMilestoneCleanupDepsForTests(): void {
56
+ deps = defaultDeps;
57
+ }
58
+
59
+ export function cleanupMergedMilestoneWorktree(
60
+ request: MilestoneMergeCleanupRequest,
61
+ ): MilestoneMergeCleanupResult {
62
+ const {
63
+ projectRoot,
64
+ milestoneId,
65
+ milestoneBranch,
66
+ previousCwd,
67
+ clearProjectRootState = false,
68
+ chdirWarningContext = "after milestone cleanup",
69
+ } = request;
70
+
71
+ if (clearProjectRootState) {
72
+ try {
73
+ deps.clearProjectRootStateFiles(projectRoot, milestoneId);
74
+ } catch (err) {
75
+ logWarning("worktree", `clearProjectRootStateFiles failed during milestone cleanup: ${err instanceof Error ? err.message : String(err)}`);
76
+ }
77
+ }
78
+
79
+ let worktreeRemoved = true;
80
+ try {
81
+ worktreeRemoved = deps.removeWorktree(projectRoot, milestoneId, {
82
+ branch: milestoneBranch,
83
+ deleteBranch: false,
84
+ });
85
+ } catch (err) {
86
+ worktreeRemoved = false;
87
+ logWarning("worktree", `worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);
88
+ }
89
+
90
+ if (!worktreeRemoved) {
91
+ logWarning(
92
+ "worktree",
93
+ `Skipping milestone branch deletion for ${milestoneBranch}; worktree removal was aborted to preserve uncommitted milestone work.`,
94
+ );
95
+ deps.nudgeGitBranchCache(previousCwd);
96
+ chdirToProjectRoot(projectRoot, chdirWarningContext);
97
+ return {
98
+ worktreeRemoved: false,
99
+ branchDeleteAttempted: false,
100
+ activeWorkspaceCleared: false,
101
+ };
102
+ }
103
+
104
+ try {
105
+ deps.nativeBranchDelete(projectRoot, milestoneBranch);
106
+ } catch (err) {
107
+ logWarning("worktree", `git branch-delete failed: ${err instanceof Error ? err.message : String(err)}`);
108
+ }
109
+
110
+ deps.setActiveWorkspace(null);
111
+ deps.nudgeGitBranchCache(previousCwd);
112
+ chdirToProjectRoot(projectRoot, chdirWarningContext);
113
+
114
+ return {
115
+ worktreeRemoved: true,
116
+ branchDeleteAttempted: true,
117
+ activeWorkspaceCleared: true,
118
+ };
119
+ }
120
+
121
+ function chdirToProjectRoot(projectRoot: string, context: string): void {
122
+ try {
123
+ deps.chdir(projectRoot);
124
+ } catch (err) {
125
+ logWarning("worktree", `chdir to project root ${context} failed: ${err instanceof Error ? err.message : String(err)}`);
126
+ debugLog("mergeMilestoneToMain", {
127
+ phase: "post-merge-chdir-failed",
128
+ target: projectRoot,
129
+ error: err instanceof Error ? err.message : String(err),
130
+ });
131
+ }
132
+ }
@@ -0,0 +1,71 @@
1
+ // gsd-pi — Code-change safety checks for auto-worktree merge closeout.
2
+ //
3
+ // Owns the git diff policy that decides whether a completed milestone actually
4
+ // changed user code, and whether an empty merge result is safe to clean up.
5
+
6
+ import { execFileSync } from "node:child_process";
7
+
8
+ import { GSDError, GSD_GIT_ERROR } from "./errors.js";
9
+ import { nativeDiffNumstat } from "./native-git-bridge.js";
10
+ import { logWarning } from "./workflow-logger.js";
11
+
12
+ const GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
13
+
14
+ export function assertNoUnanchoredCodeChangesAfterEmptyMerge(
15
+ basePath: string,
16
+ mainBranch: string,
17
+ milestoneBranch: string,
18
+ effectiveStrategy: "merge" | "squash",
19
+ nothingToCommit: boolean,
20
+ ): void {
21
+ if (!nothingToCommit) return;
22
+
23
+ const codeChanges = nativeDiffNumstat(basePath, mainBranch, milestoneBranch)
24
+ .filter((entry) => !entry.path.startsWith(".gsd/"));
25
+ if (codeChanges.length === 0) return;
26
+
27
+ throw new GSDError(
28
+ GSD_GIT_ERROR,
29
+ `${effectiveStrategy === "merge" ? "Merge" : "Squash merge"} produced nothing to commit but milestone branch "${milestoneBranch}" ` +
30
+ `has ${codeChanges.length} code file(s) not on "${mainBranch}". ` +
31
+ `Aborting worktree teardown to prevent data loss.`,
32
+ );
33
+ }
34
+
35
+ export function detectMergedCodeFilesChanged(basePath: string, nothingToCommit: boolean): boolean {
36
+ if (nothingToCommit) return false;
37
+
38
+ try {
39
+ const diffTreeOutput = execFileSync(
40
+ "git",
41
+ ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"],
42
+ { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
43
+ ).trim();
44
+ return containsCodeFile(diffTreeOutput);
45
+ } catch (err) {
46
+ return detectCodeFilesChangedFromEmptyTree(basePath, err);
47
+ }
48
+ }
49
+
50
+ function detectCodeFilesChangedFromEmptyTree(basePath: string, originalError: unknown): boolean {
51
+ try {
52
+ const fallbackOutput = execFileSync(
53
+ "git",
54
+ ["diff", "--name-only", GIT_EMPTY_TREE, "HEAD"],
55
+ { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
56
+ ).trim();
57
+ return containsCodeFile(fallbackOutput);
58
+ } catch {
59
+ // Truly unable to determine — assume code was changed to avoid silent data loss.
60
+ logWarning(
61
+ "worktree",
62
+ `diff-tree and empty-tree fallback both failed (assuming code changed): ${originalError instanceof Error ? originalError.message : String(originalError)}`,
63
+ );
64
+ return true;
65
+ }
66
+ }
67
+
68
+ function containsCodeFile(output: string): boolean {
69
+ const files = output ? output.split("\n").filter(Boolean) : [];
70
+ return files.some((file) => !file.startsWith(".gsd/"));
71
+ }