@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
@@ -4,14 +4,14 @@
4
4
  //
5
5
  // Exports: parseDecisionsTable, parseRequirementsSections, migrateFromMarkdown
6
6
  import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
7
- import { join, basename } from 'node:path';
7
+ import { join, basename, dirname } from 'node:path';
8
8
  import { upsertDecision, upsertRequirement, insertArtifact, insertMilestone, insertSlice, insertTask, transaction, updateMilestoneStatus, updateSliceStatus, _getAdapter, } from './gsd-db.js';
9
9
  import { openWorkflowDatabasePath } from './db-workspace.js';
10
- import { resolveGsdRootFile, resolveMilestoneFile, resolveSliceFile, resolveTasksDir, legacyMilestonesDir, gsdRoot, resolveTaskFiles, resolveMilestonePath, } from './paths.js';
10
+ import { resolveGsdRootFile, resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksDir, legacyMilestonesDir, gsdRoot, resolveTaskFiles, resolveMilestonePath, } from './paths.js';
11
11
  import { findMilestoneIds } from './guided-flow.js';
12
12
  import { milestoneIdToPhaseNum } from './layout-policy.js';
13
13
  import { parseRoadmap, parsePlan } from './parsers-legacy.js';
14
- import { parseContextDependsOn } from './files.js';
14
+ import { parseContextDependsOn, parseSummary } from './files.js';
15
15
  import { logWarning } from './workflow-logger.js';
16
16
  // ─── DECISIONS.md Parser ───────────────────────────────────────────────────
17
17
  const VALID_MADE_BY = new Set(['human', 'agent', 'collaborative']);
@@ -662,21 +662,63 @@ export function migrateHierarchyToDb(basePath) {
662
662
  for (const taskEntry of plan.tasks) {
663
663
  // Per K002: use 'complete' not 'done'
664
664
  let taskStatus = taskEntry.done ? 'complete' : 'pending';
665
- // Pre-migration consistency (legacy layout only): if a task is marked
666
- // done but has no per-task SUMMARY.md, import it as pending so it gets
667
- // re-executed rather than silently entering the DB as complete.
668
- // Flat-phase has no per-task summary files by design the checkbox
669
- // state is the authoritative completion signal and is imported as-is.
670
- if (taskStatus === 'complete') {
671
- const tDir = resolveTasksDir(basePath, milestoneId, sliceEntry.id);
672
- if (tDir) {
673
- const summaryFile = join(tDir, `${taskEntry.id}-SUMMARY.md`);
674
- if (!existsSync(summaryFile)) {
675
- taskStatus = 'pending';
676
- process.stderr.write(`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} marked done but missing summary importing as pending\n`);
677
- }
665
+ // Resolve the per-task SUMMARY.md. Legacy layout keeps summaries under
666
+ // a tasks/ subdir; flat-phase writes TID-SUMMARY.md directly in the
667
+ // phase dir (execute-task's gsd_task_complete writes it there). A tasks/
668
+ // subdir may still exist in flat-phase for auxiliary artifactsits
669
+ // mere existence must NOT redirect summary resolution into tasks/ (#1208).
670
+ const slicePath = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
671
+ let taskSummaryFile = null;
672
+ let isLegacySlice = false;
673
+ // Legacy layout keeps per-task summaries under a tasks/ subdir. Its
674
+ // presence — not merely "is this a legacy slice" — is what makes a
675
+ // missing summary a downgrade signal. resolveTasksDir returns null when
676
+ // no such dir exists, so a legacy slice whose checkbox is the only
677
+ // completion signal (no tasks/ dir at all) is left complete on import.
678
+ let legacyTasksDir = null;
679
+ if (slicePath) {
680
+ isLegacySlice = basename(dirname(slicePath)) === 'slices';
681
+ legacyTasksDir = isLegacySlice
682
+ ? resolveTasksDir(basePath, milestoneId, sliceEntry.id)
683
+ : null;
684
+ const summaryDir = legacyTasksDir ?? slicePath;
685
+ taskSummaryFile = join(summaryDir, `${taskEntry.id}-SUMMARY.md`);
686
+ }
687
+ const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
688
+ // Pre-migration consistency (legacy tasks/ layout only): if a task is
689
+ // marked done but its tasks/ dir has no per-task SUMMARY.md, import it
690
+ // as pending so it gets re-executed rather than silently entering the DB
691
+ // as complete. A legacy slice with no tasks/ dir (checkbox-only
692
+ // completion) and flat-phase layout are both left as-is here.
693
+ if (taskStatus === 'complete' && legacyTasksDir !== null && !hasTaskSummary) {
694
+ taskStatus = 'pending';
695
+ process.stderr.write(`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} marked done but missing summary — importing as pending\n`);
696
+ }
697
+ // Lost-update guard (#1222): a per-task SUMMARY.md is the durable
698
+ // completion attestation written by gsd_task_complete. When the plan
699
+ // checkbox is stale-unchecked but the SUMMARY exists on disk, a
700
+ // re-import must NOT downgrade the completed task back to pending — that
701
+ // silent revert is the lost-update that hard-stopped auto-mode after the
702
+ // first execute-task. The surviving SUMMARY wins over a stale checkbox.
703
+ // reopen-task removes the SUMMARY, so this never re-completes an
704
+ // intentionally reopened task.
705
+ // Flat-phase summaries are phase-scoped (T01-SUMMARY.md); when a milestone
706
+ // has multiple slices, require the summary parent frontmatter to match so
707
+ // one slice's completion does not attest a sibling's same-named task.
708
+ let summaryAttestsCompletion = hasTaskSummary;
709
+ if (summaryAttestsCompletion && !isLegacySlice && roadmap.slices.length > 1 && taskSummaryFile) {
710
+ try {
711
+ const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
712
+ summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
713
+ }
714
+ catch {
715
+ summaryAttestsCompletion = false;
678
716
  }
679
717
  }
718
+ if (taskStatus === 'pending' && summaryAttestsCompletion) {
719
+ taskStatus = 'complete';
720
+ process.stderr.write(`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} unchecked in plan but SUMMARY.md present — importing as complete (#1222)\n`);
721
+ }
680
722
  insertTask({
681
723
  id: taskEntry.id,
682
724
  sliceId: sliceEntry.id,
@@ -687,6 +729,12 @@ export function migrateHierarchyToDb(basePath) {
687
729
  files: taskEntry.files ?? [],
688
730
  verify: taskEntry.verify ?? '',
689
731
  },
732
+ // #1222 (metadata half): re-import derives status from the plan/summary
733
+ // but has none of the execution prose gsd_task_complete wrote to the DB.
734
+ // Preserve the existing row's execution columns and completed_at so a
735
+ // re-import cannot blank a completed task's summary metadata or refresh
736
+ // its completion timestamp — the DB row is strictly richer than the plan.
737
+ preserveCompletionMetadata: true,
690
738
  });
691
739
  counts.tasks++;
692
740
  }
@@ -699,14 +747,32 @@ export function migrateHierarchyToDb(basePath) {
699
747
  if (!sliceEntry.done) {
700
748
  const sliceSummaryPath = resolveSliceFile(basePath, milestoneId, sliceEntry.id, 'SUMMARY');
701
749
  const hasSliceSummary = sliceSummaryPath !== null && existsSync(sliceSummaryPath);
750
+ const slicePathForTasks = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
751
+ const isLegacySliceLayout = slicePathForTasks !== null
752
+ && basename(dirname(slicePathForTasks)) === 'slices';
753
+ const taskSummaryDir = slicePathForTasks
754
+ ? (isLegacySliceLayout
755
+ ? (resolveTasksDir(basePath, milestoneId, sliceEntry.id) ?? slicePathForTasks)
756
+ : slicePathForTasks)
757
+ : null;
702
758
  const allTasksDone = plan.tasks.length > 0 && plan.tasks.every(t => {
703
- if (!t.done)
704
- return false;
705
- const tDir = resolveTasksDir(basePath, milestoneId, sliceEntry.id);
706
- // Flat-phase has no per-task summary files — checkbox state is authoritative.
707
- if (!tDir)
708
- return true;
709
- return existsSync(join(tDir, `${t.id}-SUMMARY.md`));
759
+ const taskSummaryFile = taskSummaryDir !== null
760
+ ? join(taskSummaryDir, `${t.id}-SUMMARY.md`)
761
+ : null;
762
+ const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
763
+ let summaryAttestsCompletion = hasTaskSummary;
764
+ if (summaryAttestsCompletion && !isLegacySliceLayout && roadmap.slices.length > 1 && taskSummaryFile) {
765
+ try {
766
+ const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
767
+ summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
768
+ }
769
+ catch {
770
+ summaryAttestsCompletion = false;
771
+ }
772
+ }
773
+ // Match per-task import rules above: SUMMARY attestation wins over a stale
774
+ // checkbox (#1222); flat-phase checkboxes are authoritative without summary.
775
+ return summaryAttestsCompletion || (t.done && !isLegacySliceLayout);
710
776
  });
711
777
  if (allTasksDone && hasSliceSummary) {
712
778
  if (_getAdapter()) {
@@ -56,14 +56,40 @@ function replaceSetPrefix(values, from, to) {
56
56
  values.add(`${to}${value.slice(from.length)}`);
57
57
  }
58
58
  }
59
+ /**
60
+ * Rewrite one milestone identity across EVERY markdown identity set. The
61
+ * roadmapless subset is a view of `milestones`, so it must speak the same
62
+ * aligned vocabulary; otherwise the discussion-phase exclusion below keys off
63
+ * the stale bare id, fails its `dbScan.milestones.has(...)` membership check,
64
+ * and decrements the milestone count even though the aligned identity remains
65
+ * in the set, reporting false drift for a milestone that is actually in sync.
66
+ * Centralising the set list here keeps every alignment path from silently
67
+ * missing a set (the defect that regressed the suffixed roadmapless case).
68
+ */
69
+ function realignMilestonePrefix(markdownScan, from, to) {
70
+ replaceSetPrefix(markdownScan.milestones, from, to);
71
+ replaceSetPrefix(markdownScan.slices, from, to);
72
+ replaceSetPrefix(markdownScan.tasks, from, to);
73
+ replaceSetPrefix(markdownScan.milestonesWithoutRoadmap, from, to);
74
+ }
59
75
  function alignNumericMarkdownIdsWithDb(markdownScan, dbScan) {
60
76
  for (const dbId of dbScan.milestones) {
61
77
  const paddedId = paddedMilestoneId(dbId);
62
78
  if (!paddedId || dbScan.milestones.has(paddedId) || !markdownScan.milestones.has(paddedId))
63
79
  continue;
64
- replaceSetPrefix(markdownScan.milestones, paddedId, dbId);
65
- replaceSetPrefix(markdownScan.slices, paddedId, dbId);
66
- replaceSetPrefix(markdownScan.tasks, paddedId, dbId);
80
+ realignMilestonePrefix(markdownScan, paddedId, dbId);
81
+ }
82
+ }
83
+ function bareMilestoneId(id) {
84
+ const match = id.match(/^(M\d{3})-[a-z0-9]{6}$/);
85
+ return match?.[1] ?? null;
86
+ }
87
+ function alignBareMarkdownIdsWithSuffixedDb(markdownScan, dbScan) {
88
+ for (const dbId of dbScan.milestones) {
89
+ const bareId = bareMilestoneId(dbId);
90
+ if (!bareId || dbScan.milestones.has(bareId) || !markdownScan.milestones.has(bareId))
91
+ continue;
92
+ realignMilestonePrefix(markdownScan, bareId, dbId);
67
93
  }
68
94
  }
69
95
  /**
@@ -148,6 +174,7 @@ export async function checkMarkdownHierarchyAgainstDb(basePath) {
148
174
  const dbScan = scanDbHierarchy();
149
175
  const beforeDb = dbScan.counts;
150
176
  alignNumericMarkdownIdsWithDb(markdownScan, dbScan);
177
+ alignBareMarkdownIdsWithSuffixedDb(markdownScan, dbScan);
151
178
  // Discussion-phase scratch: a milestone dir with no ROADMAP and no DB row is
152
179
  // a pre-registration discussion artifact (CONTEXT/CONTEXT-DRAFT only — the
153
180
  // queued DB row is inserted only at discussion handoff). Treating it as
@@ -1,5 +1,6 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Process-level transaction wrapper for milestone merge closeout.
3
+ import { mergeMilestoneToMain } from "./auto-worktree-merge.js";
3
4
  export function runMilestoneMergeTransaction(deps, input) {
4
5
  return deps.mergeMilestone(input.basePath, input.milestoneId, input.roadmapContent);
5
6
  }
@@ -8,3 +9,13 @@ export function createMilestoneMergeTransaction(mergeMilestone) {
8
9
  return runMilestoneMergeTransaction({ mergeMilestone }, { basePath, milestoneId, roadmapContent });
9
10
  };
10
11
  }
12
+ /**
13
+ * Production merge transaction adapter.
14
+ *
15
+ * This is the only production construction point that knows the legacy
16
+ * `auto-worktree.ts` merge primitive. Lifecycle callers depend on the
17
+ * transaction runner interface, not the legacy implementation module.
18
+ */
19
+ export function createDefaultMilestoneMergeTransaction(mergeMilestone = mergeMilestoneToMain) {
20
+ return createMilestoneMergeTransaction(mergeMilestone);
21
+ }
@@ -1,6 +1,6 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Milestone closeout settlement state across DB proof, artifacts, merge, and cleanup.
3
- import { isInAutoWorktree } from "./auto-worktree.js";
3
+ import { isInAutoWorktree } from "./auto-worktree-entry.js";
4
4
  import { formatCloseoutProofBlock, proveMilestoneCloseout, } from "./milestone-closeout-proof.js";
5
5
  import { resolveCloseoutArtifactProjection } from "./artifact-projection.js";
6
6
  function isActiveUnmergedWorktree(input) {
@@ -8,7 +8,7 @@ import { existsSync, readdirSync } from "node:fs";
8
8
  import { spawnSync } from "node:child_process";
9
9
  import { resolveGsdPathContract } from "./paths.js";
10
10
  import { worktreePathFor, worktreesDirs } from "./worktree-placement.js";
11
- import { getAutoWorktreePath } from "./auto-worktree.js";
11
+ import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
12
12
  import { buildWorktreeLifecycleDeps } from "./auto.js";
13
13
  import { mergeMilestoneStandalone, } from "./worktree-lifecycle.js";
14
14
  import { MergeConflictError } from "./git-service.js";
@@ -12,7 +12,9 @@ import { join, dirname } from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { gsdRoot } from "./paths.js";
14
14
  import { createWorktree, worktreePath } from "./worktree-manager.js";
15
- import { autoWorktreeBranch, fastForwardReusedMilestoneBranchIfSafe, runWorktreePostCreateHook, syncGsdStateToWorktreeByScope } from "./auto-worktree.js";
15
+ import { autoWorktreeBranch, fastForwardReusedMilestoneBranchIfSafe } from "./auto-worktree-branch-lifecycle.js";
16
+ import { syncGsdStateToWorktreeByScope } from "./auto-worktree-sync.js";
17
+ import { runWorktreePostCreateHook } from "./worktree-post-create-hook.js";
16
18
  import { nativeBranchExists } from "./native-git-bridge.js";
17
19
  import { readIntegrationBranch } from "./git-service.js";
18
20
  import { createWorkspace, scopeMilestone } from "./workspace.js";
@@ -669,7 +669,10 @@ function pickPreferredPhaseDir(phasesDir, matches) {
669
669
  }
670
670
  return best;
671
671
  }
672
- function phaseDirMatchesMilestoneId(dirName, milestoneId, phaseNum) {
672
+ function slugLooksLikeTeamSuffixProjection(slugPart) {
673
+ return /^[a-z0-9]{6}(-|$)/.test(slugPart);
674
+ }
675
+ function phaseDirMatchesMilestoneId(dirName, milestoneId, phaseNum, allowTeamSuffixSlugs = false) {
673
676
  const numMatch = dirName.match(/^(\d+)-(.*)$/);
674
677
  if (!numMatch || parseInt(numMatch[1], 10) !== phaseNum)
675
678
  return false;
@@ -678,8 +681,9 @@ function phaseDirMatchesMilestoneId(dirName, milestoneId, phaseNum) {
678
681
  if (suffix) {
679
682
  return slugPart === suffix || slugPart.startsWith(`${suffix}-`);
680
683
  }
681
- // Plain sequential IDs must not resolve to unique-suffix phase dirs.
682
- return !/^[a-z0-9]{6}(-|$)/.test(slugPart);
684
+ if (slugLooksLikeTeamSuffixProjection(slugPart) && !allowTeamSuffixSlugs)
685
+ return false;
686
+ return true;
683
687
  }
684
688
  function resolvePhaseDir(basePath, milestoneId) {
685
689
  // Try flat-phase layout first: phases/NN-slug/ (always scan phases/, even when
@@ -700,6 +704,16 @@ function resolvePhaseDir(basePath, milestoneId) {
700
704
  matches.push(entry.name);
701
705
  }
702
706
  }
707
+ // Bare milestone ids may only have a suffixed team-mode projection on disk.
708
+ if (matches.length === 0 && !milestoneIdUniqueSuffix(milestoneId)) {
709
+ for (const entry of readdirSync(phasesDir, { withFileTypes: true })) {
710
+ if (!entry.isDirectory())
711
+ continue;
712
+ if (phaseDirMatchesMilestoneId(entry.name, milestoneId, phaseNum, true)) {
713
+ matches.push(entry.name);
714
+ }
715
+ }
716
+ }
703
717
  }
704
718
  catch {
705
719
  // unreadable — fall through
@@ -4,35 +4,29 @@
4
4
  * Contains all logic for resolving skill references from preferences
5
5
  * to absolute filesystem paths, plus skill discovery and staleness config.
6
6
  */
7
- import { existsSync, readdirSync } from "node:fs";
7
+ import { existsSync, readdirSync, statSync } from "node:fs";
8
8
  import { homedir } from "node:os";
9
9
  import { isAbsolute, join } from "node:path";
10
+ import { getSkillDirectories } from "@gsd/pi-coding-agent";
10
11
  import { gsdHome } from "./gsd-home.js";
11
- import { statSync } from "node:fs";
12
12
  import { validatePreferences } from "./preferences-validation.js";
13
13
  /**
14
14
  * Known skill directories for **preference reference resolution** (bare skill names).
15
15
  *
16
- * Search order (first match wins):
17
- * 1. ~/.gsd/agent/skills/ (GSD bundled)
18
- * 2. ~/.agents/skills/ (ecosystem global)
19
- * 3. .agents/skills/ (project ecosystem)
20
- * 4. ~/.claude/skills/ and .claude/skills/ (Claude Code compatibility)
16
+ * Sourced from the shared taxonomy (`@gsd/pi-coding-agent` `getSkillDirectories`)
17
+ * so preference resolution, mid-session discovery, and the PackageManager
18
+ * catalog all agree on which directories exist. The `method` is derived from
19
+ * each entry's scope.
21
20
  *
22
21
  * Note: the ResourceLoader catalog uses PackageManager precedence instead —
23
22
  * project `.gsd/skills` and `.agents/skills` can override bundled GSD skills
24
23
  * on name collision. See docs/dev/what-is-pi/09-the-customization-stack.md.
25
24
  */
26
25
  export function getSkillSearchDirs(cwd) {
27
- const dirs = [
28
- { dir: join(gsdHome(), "agent", "skills"), method: "user-skill" },
29
- { dir: join(homedir(), ".agents", "skills"), method: "user-skill" },
30
- { dir: join(cwd, ".agents", "skills"), method: "project-skill" },
31
- // Claude Code official skill directories
32
- { dir: join(homedir(), ".claude", "skills"), method: "user-skill" },
33
- { dir: join(cwd, ".claude", "skills"), method: "project-skill" },
34
- ];
35
- return dirs;
26
+ return getSkillDirectories({ cwd, gsdHome: gsdHome() }).map((entry) => ({
27
+ dir: entry.path,
28
+ method: entry.scope === "project" ? "project-skill" : "user-skill",
29
+ }));
36
30
  }
37
31
  /**
38
32
  * Resolve a single skill reference to an absolute path.
@@ -86,6 +86,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set([
86
86
  "min_request_interval_ms",
87
87
  "stale_commit_threshold_minutes",
88
88
  "context_management",
89
+ "tool_call_loop_guard",
89
90
  "experimental",
90
91
  "codebase",
91
92
  "slice_parallel",
@@ -801,6 +801,88 @@ export function validatePreferences(preferences) {
801
801
  errors.push("context_management must be an object");
802
802
  }
803
803
  }
804
+ // ─── Tool-call loop guard (#1198) ────────────────────────────────────────
805
+ if (preferences.tool_call_loop_guard !== undefined) {
806
+ if (typeof preferences.tool_call_loop_guard === "object" && preferences.tool_call_loop_guard !== null) {
807
+ const g = preferences.tool_call_loop_guard;
808
+ const validGuard = {};
809
+ if (g.enabled !== undefined) {
810
+ if (typeof g.enabled === "boolean")
811
+ validGuard.enabled = g.enabled;
812
+ else
813
+ errors.push("tool_call_loop_guard.enabled must be a boolean");
814
+ }
815
+ if (g.identical_args !== undefined) {
816
+ if (typeof g.identical_args === "object" && g.identical_args !== null) {
817
+ const ia = g.identical_args;
818
+ const validIa = {};
819
+ if (ia.enabled !== undefined) {
820
+ if (typeof ia.enabled === "boolean")
821
+ validIa.enabled = ia.enabled;
822
+ else
823
+ errors.push("tool_call_loop_guard.identical_args.enabled must be a boolean");
824
+ }
825
+ if (ia.max_consecutive_calls !== undefined) {
826
+ const n = ia.max_consecutive_calls;
827
+ if (typeof n === "number" && Number.isInteger(n) && n >= 1)
828
+ validIa.max_consecutive_calls = n;
829
+ else
830
+ errors.push("tool_call_loop_guard.identical_args.max_consecutive_calls must be an integer >= 1");
831
+ }
832
+ if (Object.keys(validIa).length > 0)
833
+ validGuard.identical_args = validIa;
834
+ }
835
+ else {
836
+ errors.push("tool_call_loop_guard.identical_args must be an object");
837
+ }
838
+ }
839
+ if (g.repeated_tool !== undefined) {
840
+ if (typeof g.repeated_tool === "object" && g.repeated_tool !== null) {
841
+ const rt = g.repeated_tool;
842
+ const validRt = {};
843
+ if (rt.enabled !== undefined) {
844
+ if (typeof rt.enabled === "boolean")
845
+ validRt.enabled = rt.enabled;
846
+ else
847
+ errors.push("tool_call_loop_guard.repeated_tool.enabled must be a boolean");
848
+ }
849
+ if (rt.default_cap !== undefined) {
850
+ const n = rt.default_cap;
851
+ if (typeof n === "number" && Number.isInteger(n) && n >= 1)
852
+ validRt.default_cap = n;
853
+ else
854
+ errors.push("tool_call_loop_guard.repeated_tool.default_cap must be an integer >= 1");
855
+ }
856
+ if (rt.repeatable_cap !== undefined) {
857
+ const n = rt.repeatable_cap;
858
+ if (typeof n === "number" && Number.isInteger(n) && n >= 1)
859
+ validRt.repeatable_cap = n;
860
+ else
861
+ errors.push("tool_call_loop_guard.repeated_tool.repeatable_cap must be an integer >= 1");
862
+ }
863
+ if (rt.exempt_tools !== undefined) {
864
+ if (Array.isArray(rt.exempt_tools) && rt.exempt_tools.every((t) => typeof t === "string")) {
865
+ validRt.exempt_tools = rt.exempt_tools;
866
+ }
867
+ else {
868
+ errors.push("tool_call_loop_guard.repeated_tool.exempt_tools must be an array of strings");
869
+ }
870
+ }
871
+ if (Object.keys(validRt).length > 0)
872
+ validGuard.repeated_tool = validRt;
873
+ }
874
+ else {
875
+ errors.push("tool_call_loop_guard.repeated_tool must be an object");
876
+ }
877
+ }
878
+ if (Object.keys(validGuard).length > 0) {
879
+ validated.tool_call_loop_guard = validGuard;
880
+ }
881
+ }
882
+ else {
883
+ errors.push("tool_call_loop_guard must be an object");
884
+ }
885
+ }
804
886
  // ─── Context Mode (gsd_exec sandbox) ────────────────────────────────────
805
887
  if (preferences.context_mode !== undefined) {
806
888
  if (typeof preferences.context_mode === "object" && preferences.context_mode !== null) {
@@ -822,6 +822,26 @@ export function renderPreferencesForSystemPrompt(preferences, resolutions, optio
822
822
  }
823
823
  return lines.join("\n");
824
824
  }
825
+ /**
826
+ * Render a compact response-language directive for injection into dispatched
827
+ * prompt *content* (discuss/QA and auto-execution unit prompts).
828
+ *
829
+ * The main session's system prompt already carries the full GSD Skill
830
+ * Preferences block (including language) via `before_agent_start`, but
831
+ * discuss/QA turns and auto-execution units are dispatched as separate
832
+ * turns/sessions whose prompts are assembled here — without that block — so the
833
+ * configured language never reaches them (#1210). Injecting this directive into
834
+ * the dispatched prompt content makes the `language` preference effective across
835
+ * both guided and auto workflows.
836
+ *
837
+ * Returns "" when no language is configured.
838
+ */
839
+ export function renderLanguageDirectiveForPrompt(preferences) {
840
+ if (!preferences?.language)
841
+ return "";
842
+ const safeLang = preferences.language.replace(/[\r\n]/g, " ").slice(0, 50);
843
+ return `## Response Language\n\nAlways respond in ${safeLang} — questions, explanations, and summaries.`;
844
+ }
825
845
  // ─── Hook Resolution ──────────────────────────────────────────────────────────
826
846
  /**
827
847
  * Resolve enabled post-unit hooks from effective preferences.
@@ -38,7 +38,7 @@ If a `GSD Skill Preferences` block appears below, treat it as durable guidance f
38
38
 
39
39
  ### Naming Convention
40
40
 
41
- Directories use bare IDs. Files use ID-SUFFIX format. Milestones: `M001/` or `M{seq}-{rand6}/` when `unique_milestone_ids: true`; files like `M001-CONTEXT.md`, `M001-ROADMAP.md`, `M001-RESEARCH.md`. Slices: `S01/` with `S01-PLAN.md`, `S01-RESEARCH.md`, `S01-SUMMARY.md`, `S01-UAT.md`. Tasks: `T01-PLAN.md`, `T01-SUMMARY.md`. Titles live inside content, not names.
41
+ GSD projects use a flat-phase layout. Phase directories are `{MM}-{slug}` where `{MM}` is the zero-padded phase number and `{slug}` is derived from the phase title (e.g. `47-real-time-runtime-hardening`). Phase-level files are `{MM}-SUFFIX.md` (e.g. `47-CONTEXT.md`, `47-RESEARCH.md`, `47-ROADMAP.md`, `47-VALIDATION.md`, `47-SUMMARY.md`). Slice/plan files live directly in the phase directory as `{MM}-{SS}-SUFFIX.md` (e.g. `47-01-PLAN.md`, `47-01-SUMMARY.md`, `47-01-UAT.md`). Task plan content lives inside the slice plan file (`{MM}-{SS}-PLAN.md`) as checkboxes; there is no `tasks/` subdirectory and no `tasks/T##-PLAN.md`. Titles live inside content, not names. Prefer GSD-provided layout-aware paths/resolvers over hardcoding artifact paths, since phase slugs are title-derived.
42
42
 
43
43
  ### Directory Structure
44
44
 
@@ -46,13 +46,13 @@ Directories use bare IDs. Files use ID-SUFFIX format. Milestones: `M001/` or `M{
46
46
  .gsd/
47
47
  PROJECT.md, REQUIREMENTS.md, DECISIONS.md, KNOWLEDGE.md, CODEBASE.md, OVERRIDES.md, QUEUE.md, STATE.md
48
48
  runtime/, activity/, worktrees/
49
- milestones/M001/
50
- M001-CONTEXT.md, M001-RESEARCH.md, M001-ROADMAP.md, M001-SUMMARY.md
51
- slices/S01/
52
- S01-CONTEXT.md, S01-RESEARCH.md, S01-PLAN.md, S01-SUMMARY.md, S01-UAT.md
53
- tasks/T01-PLAN.md, tasks/T01-SUMMARY.md
49
+ phases/{MM}-{slug}/
50
+ {MM}-CONTEXT.md, {MM}-RESEARCH.md, {MM}-ROADMAP.md, {MM}-VALIDATION.md, {MM}-SUMMARY.md
51
+ {MM}-{SS}-PLAN.md, {MM}-{SS}-SUMMARY.md, {MM}-{SS}-UAT.md
54
52
  ```
55
53
 
54
+ Task plan content is embedded in `{MM}-{SS}-PLAN.md`; do not expect `tasks/T##-PLAN.md`.
55
+
56
56
  `runtime/`, `activity/`, `worktrees/`, and `STATE.md` are system-managed. `PROJECT.md` is current state; `REQUIREMENTS.md` is the active capability contract; `DECISIONS.md` and `KNOWLEDGE.md` are append-only; `CODEBASE.md` is an auto-refreshed codebase map.
57
57
 
58
58
  ### Isolation Model
@@ -13,7 +13,7 @@ import { isAbsolute, join, relative, resolve } from "node:path";
13
13
  import { QUICK_BRANCH_RE } from "./branch-patterns.js";
14
14
  import { loadPrompt } from "./prompt-loader.js";
15
15
  import { gsdRoot } from "./paths.js";
16
- import { GitServiceImpl, runGit } from "./git-service.js";
16
+ import { GitServiceImpl, runGit, taskBranchArgs } from "./git-service.js";
17
17
  import { loadEffectiveGSDPreferences } from "./preferences.js";
18
18
  import { nativeBranchExists, nativeDetectMainBranch, nativeDiffNumstat } from "./native-git-bridge.js";
19
19
  import { nativeHasStagedChanges } from "./native-git-bridge.js";
@@ -317,6 +317,7 @@ export async function handleQuick(args, ctx, pi) {
317
317
  const git = new GitServiceImpl(basePath, gitPrefs);
318
318
  const branchName = `gsd/quick/${taskNum}-${slug}`;
319
319
  let originalBranch = git.getCurrentBranch();
320
+ let returnBranch = originalBranch;
320
321
  const { getIsolationMode } = await import("./preferences.js");
321
322
  const usesBranch = getIsolationMode() !== "none" && isGitOpsEnabled();
322
323
  let branchCreated = false;
@@ -331,7 +332,15 @@ export async function handleQuick(args, ctx, pi) {
331
332
  }
332
333
  }
333
334
  catch { /* nothing to commit — fine */ }
334
- runGit(basePath, ["checkout", "-b", branchName]);
335
+ const branchArgs = taskBranchArgs(basePath, branchName, current, git.getMainBranch());
336
+ const startPoint = branchArgs[3];
337
+ // When the quick branch is redirected off a stale task branch, the
338
+ // squash-merge on closeout must return to the same base — merging a
339
+ // base-branch-rooted quick branch back into the stale task branch
340
+ // would drag the base's newer history into it.
341
+ if (startPoint)
342
+ returnBranch = startPoint;
343
+ runGit(basePath, branchArgs);
335
344
  branchCreated = true;
336
345
  }
337
346
  }
@@ -345,7 +354,7 @@ export async function handleQuick(args, ctx, pi) {
345
354
  if (actualBranch === branchName && originalBranch !== branchName) {
346
355
  persistPendingReturn({
347
356
  basePath,
348
- originalBranch,
357
+ originalBranch: returnBranch,
349
358
  quickBranch: branchName,
350
359
  taskNum,
351
360
  slug,