gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.e51d2c88c

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 (434) hide show
  1. package/README.md +4 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +2 -2
  4. package/dist/resources/extensions/github-sync/templates.js +39 -8
  5. package/dist/resources/extensions/gsd/auto/loop.js +48 -10
  6. package/dist/resources/extensions/gsd/auto/phases.js +66 -45
  7. package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
  8. package/dist/resources/extensions/gsd/auto/run-unit.js +32 -16
  9. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  12. package/dist/resources/extensions/gsd/auto-prompts.js +124 -2
  13. package/dist/resources/extensions/gsd/auto-recovery.js +197 -9
  14. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  15. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  17. package/dist/resources/extensions/gsd/auto.js +75 -5
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +36 -3
  19. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +27 -20
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  21. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  22. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  23. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  24. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  25. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  26. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  27. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  28. package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
  29. package/dist/resources/extensions/gsd/db-base-schema.js +18 -2
  30. package/dist/resources/extensions/gsd/db-migration-steps.js +22 -0
  31. package/dist/resources/extensions/gsd/detection.js +106 -0
  32. package/dist/resources/extensions/gsd/graph.js +9 -3
  33. package/dist/resources/extensions/gsd/gsd-db.js +146 -13
  34. package/dist/resources/extensions/gsd/guided-flow.js +82 -16
  35. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  36. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  37. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  38. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  39. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  40. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  41. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  42. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  44. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  46. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  47. package/dist/resources/extensions/gsd/quick.js +34 -2
  48. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  49. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  50. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  51. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  52. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  53. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  54. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  55. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  56. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  57. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  58. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  59. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  60. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  61. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  62. package/dist/web/standalone/.next/BUILD_ID +1 -1
  63. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  64. package/dist/web/standalone/.next/build-manifest.json +3 -3
  65. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  66. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/index.html +1 -1
  85. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  92. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  93. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  96. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  97. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  98. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  99. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  100. package/package.json +12 -8
  101. package/packages/contracts/package.json +1 -1
  102. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  103. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  104. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  105. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  106. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  107. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  108. package/packages/native/tsconfig.tsbuildinfo +1 -1
  109. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  110. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  111. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  112. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  113. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  114. package/packages/pi-ai/dist/models/index.js +8 -0
  115. package/packages/pi-ai/dist/models/index.js.map +1 -1
  116. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  117. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  118. package/packages/pi-ai/dist/providers/fake.js +319 -0
  119. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  120. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  121. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  122. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  123. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  124. package/packages/pi-ai/src/models/index.ts +9 -0
  125. package/packages/pi-ai/src/providers/fake.ts +376 -0
  126. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  127. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  128. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  129. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -0
  132. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
  134. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  136. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  138. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  140. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  141. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  142. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  144. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  145. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  146. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  147. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  148. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  149. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  150. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  151. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
  152. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/extensions/runner.js +17 -1
  154. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +99 -0
  156. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  158. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  162. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  163. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +24 -0
  164. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  165. package/packages/pi-coding-agent/dist/core/settings-manager.js +33 -0
  166. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  167. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  169. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  171. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  180. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  182. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  186. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  190. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  192. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +17 -0
  194. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +109 -17
  196. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +69 -2
  199. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +93 -1
  201. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  203. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  205. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  208. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +26 -0
  210. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  214. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  215. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  216. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  217. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  218. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  227. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  228. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  230. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  235. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  237. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  238. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  239. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  240. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  241. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  243. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  244. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  245. package/packages/pi-coding-agent/src/core/agent-session.ts +8 -0
  246. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
  247. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  248. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  249. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  250. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  251. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +110 -0
  252. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -1
  253. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  254. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  255. package/packages/pi-coding-agent/src/core/settings-manager.ts +51 -1
  256. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  257. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  258. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  259. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  260. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  261. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  263. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +122 -17
  266. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +99 -1
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -3
  268. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  269. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  270. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -0
  271. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  272. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  273. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  274. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  275. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  276. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  277. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  278. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  279. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  280. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  281. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  282. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  283. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  284. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  285. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  286. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  287. package/packages/pi-tui/dist/index.d.ts +1 -0
  288. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  289. package/packages/pi-tui/dist/index.js +2 -0
  290. package/packages/pi-tui/dist/index.js.map +1 -1
  291. package/packages/pi-tui/dist/style.d.ts +41 -0
  292. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  293. package/packages/pi-tui/dist/style.js +158 -0
  294. package/packages/pi-tui/dist/style.js.map +1 -0
  295. package/packages/pi-tui/dist/tui.d.ts +0 -1
  296. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  297. package/packages/pi-tui/dist/tui.js +21 -16
  298. package/packages/pi-tui/dist/tui.js.map +1 -1
  299. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  300. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  301. package/packages/pi-tui/src/index.ts +9 -0
  302. package/packages/pi-tui/src/style.ts +225 -0
  303. package/packages/pi-tui/src/tui.ts +23 -16
  304. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  305. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  306. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  307. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  308. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  309. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  310. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  311. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  312. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  313. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  314. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  315. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  316. package/src/resources/GSD-WORKFLOW.md +2 -2
  317. package/src/resources/extensions/github-sync/templates.ts +38 -8
  318. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  319. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  320. package/src/resources/extensions/gsd/auto/loop.ts +67 -18
  321. package/src/resources/extensions/gsd/auto/phases.ts +77 -48
  322. package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
  323. package/src/resources/extensions/gsd/auto/run-unit.ts +42 -15
  324. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  325. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  326. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  327. package/src/resources/extensions/gsd/auto-prompts.ts +133 -2
  328. package/src/resources/extensions/gsd/auto-recovery.ts +207 -7
  329. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  330. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  331. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  332. package/src/resources/extensions/gsd/auto.ts +90 -4
  333. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +37 -2
  334. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +27 -19
  335. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -1
  336. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  337. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  338. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  339. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  340. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  341. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  342. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  343. package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
  344. package/src/resources/extensions/gsd/db-base-schema.ts +19 -2
  345. package/src/resources/extensions/gsd/db-migration-steps.ts +25 -0
  346. package/src/resources/extensions/gsd/detection.ts +128 -0
  347. package/src/resources/extensions/gsd/graph.ts +12 -5
  348. package/src/resources/extensions/gsd/gsd-db.ts +168 -13
  349. package/src/resources/extensions/gsd/guided-flow.ts +98 -16
  350. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  351. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  352. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  353. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  354. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  355. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  356. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  357. package/src/resources/extensions/gsd/prompts/complete-milestone.md +19 -19
  358. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  359. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  360. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  361. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  362. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  363. package/src/resources/extensions/gsd/quick.ts +37 -2
  364. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  365. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  366. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  367. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +155 -5
  368. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  369. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +184 -2
  370. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  371. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  372. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  373. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  374. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  375. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  376. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  377. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  378. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  379. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  380. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  381. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  382. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  383. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  384. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  385. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  386. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  387. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  388. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  389. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  390. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  391. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +234 -0
  392. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  393. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  394. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  395. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  396. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  397. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  398. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  399. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  400. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  401. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  402. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  403. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  404. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  405. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  406. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  407. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  408. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +101 -2
  409. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
  410. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  411. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
  412. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  413. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  414. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  415. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  416. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  417. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  418. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  419. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  420. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  421. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  422. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  423. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  424. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  425. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  426. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  427. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  428. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  429. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  430. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  431. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  432. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  433. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → 8F5YpnZNBaooIWGF4GBV3}/_buildManifest.js +0 -0
  434. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → 8F5YpnZNBaooIWGF4GBV3}/_ssgManifest.js +0 -0
@@ -24,7 +24,7 @@ let _currentSigtermHandler = null;
24
24
  *
25
25
  * Returns the new handler so the caller can store and deregister it later.
26
26
  */
27
- export function registerSigtermHandler(currentBasePath, previousHandler) {
27
+ export function registerSigtermHandler(currentBasePath, previousHandler, onSignalCleanup) {
28
28
  // Remove the explicitly-passed previous handler
29
29
  if (previousHandler) {
30
30
  for (const sig of CLEANUP_SIGNALS)
@@ -37,6 +37,13 @@ export function registerSigtermHandler(currentBasePath, previousHandler) {
37
37
  process.off(sig, _currentSigtermHandler);
38
38
  }
39
39
  const handler = () => {
40
+ try {
41
+ onSignalCleanup?.();
42
+ }
43
+ catch {
44
+ void 0;
45
+ // Signal cleanup is best-effort; lock cleanup and process exit still run.
46
+ }
40
47
  clearLock(currentBasePath);
41
48
  releaseSessionLock(currentBasePath);
42
49
  process.exit(0);
@@ -35,13 +35,13 @@ export async function recoverTimedOutUnit(ctx, pi, unitType, unitId, reason, rct
35
35
  writeUnitRuntimeRecord(basePath, unitType, unitId, currentUnitStartedAt, {
36
36
  recovery: status,
37
37
  });
38
- const durableComplete = status.summaryExists && status.taskChecked && status.nextActionAdvanced;
38
+ const durableComplete = status.dbComplete || (status.summaryExists && status.taskChecked && status.nextActionAdvanced);
39
39
  if (durableComplete) {
40
40
  writeUnitRuntimeRecord(basePath, unitType, unitId, currentUnitStartedAt, {
41
41
  phase: "finalized",
42
42
  recovery: status,
43
43
  });
44
- ctx.ui.notify(`${reason === "idle" ? "Idle" : "Timeout"} recovery: ${unitType} ${unitId} already completed on disk. Continuing auto-mode. (attempt ${attemptNumber})`, "info");
44
+ ctx.ui.notify(`${reason === "idle" ? "Idle" : "Timeout"} recovery: ${unitType} ${unitId} already completed. Continuing auto-mode. (attempt ${attemptNumber})`, "info");
45
45
  unitRecoveryCount.delete(recoveryKey);
46
46
  bumpAndResolveSynthetic(`timeout-recovery:${reason}:${unitType}/${unitId}`);
47
47
  return "recovered";
@@ -20,7 +20,7 @@ import { gsdRoot, resolveMilestoneFile, resolveMilestonePath, resolveDir, milest
20
20
  import { invalidateAllCaches } from "./cache.js";
21
21
  import { clearActivityLogState } from "./activity-log.js";
22
22
  import { synthesizeCrashRecovery, getDeepDiagnostic, readActiveMilestoneId, } from "./session-forensics.js";
23
- import { writeLock, clearLock, readCrashLock, isLockProcessAlive, formatCrashInfo, emitCrashRecoveredUnitEnd, } from "./crash-recovery.js";
23
+ import { writeLock, clearLock, readCrashLock, isLockProcessAlive, formatCrashInfo, emitCrashRecoveredUnitEnd, emitOpenUnitEndForUnit, } from "./crash-recovery.js";
24
24
  import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
25
25
  import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
26
26
  import { sendDesktopNotification } from "./notifications.js";
@@ -61,6 +61,8 @@ import { isClosedStatus } from "./status-guards.js";
61
61
  import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, } from "./auto-dashboard.js";
62
62
  import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
63
63
  import { isDbAvailable, getMilestone } from "./gsd-db.js";
64
+ import { markLatestActiveForWorkerCanceled } from "./db/unit-dispatches.js";
65
+ import { writeUnitRuntimeRecord } from "./unit-runtime.js";
64
66
  import { countPendingCaptures } from "./captures.js";
65
67
  import { CMUX_CHANNELS } from "../shared/cmux-events.js";
66
68
  import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
@@ -85,6 +87,16 @@ import { validateDirectory } from "./validate-directory.js";
85
87
  import { createAutoOrchestrator } from "./auto/orchestrator.js";
86
88
  import { WorktreeResolver, } from "./worktree-resolver.js";
87
89
  import { reorderForCaching } from "./prompt-ordering.js";
90
+ import { initTokenCounter } from "./token-counter.js";
91
+ // Warm the tiktoken encoder at extension startup so context-budget computations
92
+ // can use accurate token counts via countTokensSync without paying the load
93
+ // cost mid-prompt-build. Fire-and-forget — failure falls back to the
94
+ // provider-aware char-ratio estimator already used by getCharsPerToken().
95
+ // Catch rejections explicitly: an unhandled rejection at module-import time
96
+ // can destabilize startup before the engine logger is configured.
97
+ void initTokenCounter().catch((err) => {
98
+ logWarning("engine", `token counter warm-up failed: ${err instanceof Error ? err.message : String(err)}`);
99
+ });
88
100
  export { STUB_RECOVERY_THRESHOLD, NEW_SESSION_TIMEOUT_MS, } from "./auto/session.js";
89
101
  import { autoSession as s } from "./auto-runtime-state.js";
90
102
  import { gsdHome } from "./gsd-home.js";
@@ -112,11 +124,12 @@ const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
112
124
  * the DB is unavailable (e.g. fresh project before init) we skip registration
113
125
  * silently rather than blocking session start.
114
126
  */
115
- function registerAutoWorkerForSession(session) {
127
+ function registerAutoWorkerForSession(session, projectRootOverride) {
116
128
  if (session.workerId)
117
129
  return; // already registered (e.g. resume re-runs)
118
130
  try {
119
- const projectRootRealpath = normalizeRealPath(session.scope?.workspace.projectRoot
131
+ const projectRootRealpath = normalizeRealPath(projectRootOverride
132
+ ?? session.scope?.workspace.projectRoot
120
133
  ?? (session.originalBasePath || session.basePath));
121
134
  session.workerId = registerAutoWorker({ projectRootRealpath });
122
135
  }
@@ -225,8 +238,16 @@ function synthesizePausedSessionRecovery(basePath, unitType, unitId, sessionFile
225
238
  export function _synthesizePausedSessionRecoveryForTest(basePath, unitType, unitId, sessionFile) {
226
239
  return synthesizePausedSessionRecovery(basePath, unitType, unitId, sessionFile);
227
240
  }
241
+ const DETACHED_AUTO_KEEPALIVE_INTERVAL_MS = 30_000;
242
+ function withDetachedAutoKeepalive(run) {
243
+ const keepAlive = setInterval(() => { }, DETACHED_AUTO_KEEPALIVE_INTERVAL_MS);
244
+ return run.finally(() => {
245
+ clearInterval(keepAlive);
246
+ });
247
+ }
248
+ export const _withDetachedAutoKeepaliveForTest = withDetachedAutoKeepalive;
228
249
  export function startAutoDetached(ctx, pi, base, verboseMode, options) {
229
- void startAuto(ctx, pi, base, verboseMode, options).catch((err) => {
250
+ void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch((err) => {
230
251
  const message = getErrorMessage(err);
231
252
  ctx.ui.notify(`Auto-start failed: ${message}`, "error");
232
253
  logWarning("engine", `auto start error: ${message}`, { file: "auto.ts" });
@@ -264,9 +285,50 @@ export function shouldUseWorktreeIsolation(basePath) {
264
285
  */
265
286
  // Re-export budget utilities for external consumers
266
287
  export { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, } from "./auto-budget.js";
288
+ function closeOutSignalInterruptedUnit(currentBasePath) {
289
+ const currentUnit = s.currentUnit;
290
+ if (!currentUnit)
291
+ return;
292
+ const reason = "Auto-mode process received a termination signal";
293
+ const errorContext = {
294
+ message: reason,
295
+ category: "aborted",
296
+ isTransient: false,
297
+ };
298
+ const basePath = s.basePath || currentBasePath;
299
+ try {
300
+ emitOpenUnitEndForUnit(basePath, currentUnit.type, currentUnit.id, "cancelled", errorContext);
301
+ }
302
+ catch (err) {
303
+ logWarning("engine", `signal unit-end cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
304
+ }
305
+ try {
306
+ writeUnitRuntimeRecord(basePath, currentUnit.type, currentUnit.id, currentUnit.startedAt, {
307
+ phase: "crashed",
308
+ lastProgressAt: Date.now(),
309
+ lastProgressKind: "signal",
310
+ });
311
+ }
312
+ catch (err) {
313
+ logWarning("engine", `signal runtime cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
314
+ }
315
+ try {
316
+ if (s.workerId)
317
+ markLatestActiveForWorkerCanceled(s.workerId, "signal-exit");
318
+ }
319
+ catch (err) {
320
+ logWarning("engine", `signal dispatch cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
321
+ }
322
+ try {
323
+ resolveAgentEndCancelled(errorContext);
324
+ }
325
+ catch (err) {
326
+ logWarning("engine", `signal resolve cleanup failed: ${getErrorMessage(err)}`, { file: "auto.ts" });
327
+ }
328
+ }
267
329
  /** Wrapper: register SIGTERM handler and store reference. */
268
330
  function registerSigtermHandler(currentBasePath) {
269
- s.sigtermHandler = _registerSigtermHandler(currentBasePath, s.sigtermHandler);
331
+ s.sigtermHandler = _registerSigtermHandler(currentBasePath, s.sigtermHandler, () => closeOutSignalInterruptedUnit(currentBasePath));
270
332
  }
271
333
  /** Wrapper: deregister SIGTERM handler and clear reference. */
272
334
  function deregisterSigtermHandler() {
@@ -1558,6 +1620,10 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1558
1620
  : new URL("../../../resource-loader.js", import.meta.url).href;
1559
1621
  const { initResources } = await import(resourceLoaderPath);
1560
1622
  initResources(agentDir);
1623
+ // initResources() uses synchronous fs APIs, so the prompt-template cache
1624
+ // can be primed immediately — no need for the legacy 1s setTimeout deferral.
1625
+ const { primeCache } = await import("./prompt-loader.js");
1626
+ primeCache();
1561
1627
  // Open the project DB before rebuild/derive so resume uses DB-backed
1562
1628
  // state instead of falling back to stale markdown parsing (#2940).
1563
1629
  await openProjectDbIfPresent(s.basePath);
@@ -1623,6 +1689,10 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1623
1689
  lockBase,
1624
1690
  buildResolver,
1625
1691
  };
1692
+ // Register the worker before bootstrap enters a milestone worktree.
1693
+ // This ensures enterMilestone can claim a lease and seed dispatch claims
1694
+ // for crash-recovery fidelity (#5405).
1695
+ registerAutoWorkerForSession(s, base);
1626
1696
  const ready = await bootstrapAutoSession(s, ctx, pi, base, verboseMode, requestedStepMode, bootstrapDeps, freshStartAssessment);
1627
1697
  if (!ready)
1628
1698
  return;
@@ -1,10 +1,11 @@
1
+ // GSD-2 + src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts - Handles provider and agent-end recovery for GSD auto-mode.
1
2
  import { logWarning } from "../workflow-logger.js";
2
3
  import { checkDeepProjectSetupAfterTurn, checkAutoStartAfterDiscuss, maybeHandleReadyPhraseWithoutFiles, maybeHandleEmptyIntentTurn, resetEmptyTurnCounter, } from "../guided-flow.js";
3
4
  import { clearPathCache } from "../paths.js";
4
5
  import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
5
6
  import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
6
7
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
7
- import { isSessionSwitchInFlight, resolveAgentEnd } from "../auto/resolve.js";
8
+ import { isSessionSwitchInFlight, resolveAgentEnd, resolveAgentEndCancelled } from "../auto/resolve.js";
8
9
  import { resolveModelId } from "../auto-model-selection.js";
9
10
  import { resolveProjectRoot } from "../worktree.js";
10
11
  import { clearDiscussionFlowState } from "./write-gate.js";
@@ -47,6 +48,11 @@ export function _buildAbortedPauseContext(lastMsg) {
47
48
  isTransient: true,
48
49
  };
49
50
  }
51
+ export function isUserInitiatedAbortMessage(message) {
52
+ if (!message)
53
+ return false;
54
+ return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user)\b/i.test(message);
55
+ }
50
56
  async function pauseTransientWithBackoff(cls, pi, ctx, errorDetail, isRateLimit) {
51
57
  retryState.consecutiveTransientCount += 1;
52
58
  const baseRetryAfterMs = "retryAfterMs" in cls ? cls.retryAfterMs : 15_000;
@@ -112,9 +118,28 @@ export async function handleAgentEnd(pi, event, ctx) {
112
118
  return;
113
119
  if (!isAutoActive())
114
120
  return;
115
- if (isSessionSwitchInFlight())
116
- return;
117
121
  const lastMsg = event.messages[event.messages.length - 1];
122
+ if (isSessionSwitchInFlight()) {
123
+ if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
124
+ const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
125
+ if (isUserInitiatedAbortMessage(rawErrorMsg)) {
126
+ resolveAgentEndCancelled({
127
+ message: rawErrorMsg,
128
+ category: "aborted",
129
+ isTransient: false,
130
+ });
131
+ }
132
+ }
133
+ else if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "aborted") {
134
+ const content = "content" in lastMsg ? lastMsg.content : undefined;
135
+ const hasEmptyContent = Array.isArray(content) && content.length === 0;
136
+ const hasErrorMessage = "errorMessage" in lastMsg && !!lastMsg.errorMessage;
137
+ if (!hasEmptyContent || hasErrorMessage) {
138
+ resolveAgentEndCancelled(_buildAbortedPauseContext(lastMsg));
139
+ }
140
+ }
141
+ return;
142
+ }
118
143
  if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "aborted") {
119
144
  // Empty content with aborted stopReason is a non-fatal agent stop (the LLM
120
145
  // chose to end without producing output). Only pause on genuine fatal aborts
@@ -150,6 +175,14 @@ export async function handleAgentEnd(pi, event, ctx) {
150
175
  // is in the assistant message text content. Fall back to content when
151
176
  // errorMessage looks uninformative.
152
177
  const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
178
+ if (isUserInitiatedAbortMessage(rawErrorMsg)) {
179
+ resolveAgentEndCancelled({
180
+ message: rawErrorMsg,
181
+ category: "aborted",
182
+ isTransient: false,
183
+ });
184
+ return;
185
+ }
153
186
  const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
154
187
  // #3588: When errorMessage is uninformative, extract the real error from
155
188
  // the assistant message text content for display purposes only.
@@ -3,21 +3,34 @@
3
3
  // Exposes the Context Mode runtime tools in-process. Default-on; opt out with
4
4
  // `context_mode.enabled: false` in preferences.
5
5
  import { Type } from "@sinclair/typebox";
6
+ async function loadContextModePreferences(baseDir) {
7
+ const [{ loadEffectiveGSDPreferences }, { logWarning }] = await Promise.all([
8
+ import("../preferences.js"),
9
+ import("../workflow-logger.js"),
10
+ ]);
11
+ try {
12
+ return loadEffectiveGSDPreferences(baseDir)?.preferences ?? null;
13
+ }
14
+ catch (err) {
15
+ logWarning("tool", `Context Mode tool could not load preferences: ${err instanceof Error ? err.message : String(err)}`);
16
+ return null;
17
+ }
18
+ }
6
19
  export function registerExecTools(pi) {
7
20
  pi.registerTool({
8
21
  name: "gsd_exec",
9
22
  label: "Exec (Sandboxed)",
10
- description: "Run a short script (bash/node/python) in a subprocess. Full stdout/stderr persist to " +
23
+ description: "Run a short script (bash/node/python) in a subprocess. Capped stdout/stderr and metadata persist to " +
11
24
  ".gsd/exec/<id>.{stdout,stderr,meta.json}; only a short digest returns in context. Use " +
12
25
  "this instead of reading many files or emitting large tool outputs — e.g. have the script " +
13
26
  "count/grep/summarize and log the finding. Enabled by default; opt out via " +
14
27
  "preferences.context_mode.enabled=false.",
15
- promptSnippet: "Run a bash/node/python script in a sandbox; full output is saved to disk and only a digest returns",
28
+ promptSnippet: "Run a bash/node/python script in a sandbox; capped output is saved to disk and only a digest returns",
16
29
  promptGuidelines: [
17
30
  "Prefer gsd_exec for analyses that would otherwise read >3 files or produce large tool output.",
18
31
  "Write scripts that log the finding (counts, matches, summaries) rather than raw dumps.",
19
32
  "The digest is the last ~300 chars of stdout — size your log output accordingly.",
20
- "Need the full output? Read the stdout_path returned in details (file on local disk).",
33
+ "Need persisted output? Read the stdout_path returned in details (file on local disk).",
21
34
  ],
22
35
  parameters: Type.Object({
23
36
  runtime: Type.Union([Type.Literal("bash"), Type.Literal("node"), Type.Literal("python")], { description: "Interpreter: bash (-c), node (-e), or python3 (-c)." }),
@@ -30,21 +43,11 @@ export function registerExecTools(pi) {
30
43
  })),
31
44
  }),
32
45
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
33
- const [{ executeGsdExec }, { loadEffectiveGSDPreferences }, { logWarning }] = await Promise.all([
34
- import("../tools/exec-tool.js"),
35
- import("../preferences.js"),
36
- import("../workflow-logger.js"),
37
- ]);
38
- let prefs = null;
39
- try {
40
- prefs = loadEffectiveGSDPreferences();
41
- }
42
- catch (err) {
43
- logWarning("tool", `gsd_exec could not load preferences: ${err instanceof Error ? err.message : String(err)}`);
44
- }
46
+ const { executeGsdExec } = await import("../tools/exec-tool.js");
47
+ const baseDir = process.cwd();
45
48
  return executeGsdExec(params, {
46
- baseDir: process.cwd(),
47
- preferences: prefs?.preferences ?? null,
49
+ baseDir,
50
+ preferences: await loadContextModePreferences(baseDir),
48
51
  });
49
52
  },
50
53
  });
@@ -56,7 +59,7 @@ export function registerExecTools(pi) {
56
59
  promptSnippet: "Search prior gsd_exec runs by substring, runtime, or failing-only filter",
57
60
  promptGuidelines: [
58
61
  "Use this before re-running an expensive analysis — the prior run's stdout file may still answer.",
59
- "The preview shows the trailing ~300 chars of stdout; read stdout_path for the full transcript.",
62
+ "The preview shows the trailing ~300 chars of stdout; read stdout_path for persisted output.",
60
63
  ],
61
64
  parameters: Type.Object({
62
65
  query: Type.Optional(Type.String({ description: "Substring matched against id and purpose (case-insensitive)." })),
@@ -68,8 +71,10 @@ export function registerExecTools(pi) {
68
71
  }),
69
72
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
70
73
  const { executeExecSearch } = await import("../tools/exec-search-tool.js");
74
+ const baseDir = process.cwd();
71
75
  return executeExecSearch(params, {
72
- baseDir: process.cwd(),
76
+ baseDir,
77
+ preferences: await loadContextModePreferences(baseDir),
73
78
  });
74
79
  },
75
80
  });
@@ -87,8 +92,10 @@ export function registerExecTools(pi) {
87
92
  parameters: Type.Object({}),
88
93
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
89
94
  const { executeResume } = await import("../tools/resume-tool.js");
95
+ const baseDir = process.cwd();
90
96
  return executeResume(params, {
91
- baseDir: process.cwd(),
97
+ baseDir,
98
+ preferences: await loadContextModePreferences(baseDir),
92
99
  });
93
100
  },
94
101
  });
@@ -2,7 +2,7 @@ import { join } from "node:path";
2
2
  import { isToolCallEventType } from "@gsd/pi-coding-agent";
3
3
  import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
4
4
  import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
5
- import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
5
+ import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, shouldBlockWorktreeWrite, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
6
6
  import { resolveManifest } from "../unit-context-manifest.js";
7
7
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
8
8
  import { loadFile, saveFile, formatContinue } from "../files.js";
@@ -52,6 +52,25 @@ async function applyDisabledModelProviderPolicy(ctx) {
52
52
  // Non-fatal: keep default provider visibility if preferences cannot be loaded.
53
53
  }
54
54
  }
55
+ /**
56
+ * Bridge `context_management.compaction_threshold_percent` from GSD preferences
57
+ * into the agent's runtime compaction settings (#5475). The preference is
58
+ * validated to (0.5, 0.95) at load time, but defense-in-depth normalization
59
+ * here protects against a stale or hand-edited prefs file. Calling with
60
+ * `undefined` clears any prior override so a removed preference does not leak.
61
+ */
62
+ async function applyCompactionThresholdOverride(ctx) {
63
+ try {
64
+ const { loadEffectiveGSDPreferences } = await import("../preferences.js");
65
+ const prefs = loadEffectiveGSDPreferences();
66
+ const raw = prefs?.preferences.context_management?.compaction_threshold_percent;
67
+ const value = typeof raw === "number" && Number.isFinite(raw) && raw > 0 && raw < 1 ? raw : undefined;
68
+ ctx.setCompactionThresholdOverride(value);
69
+ }
70
+ catch {
71
+ // Non-fatal: leave any existing override in place.
72
+ }
73
+ }
55
74
  export function resolveNotificationStoreBasePath(cwd = process.cwd()) {
56
75
  return resolveWorktreeProjectRoot(cwd);
57
76
  }
@@ -101,6 +120,7 @@ export function registerHooks(pi, ecosystemHandlers) {
101
120
  await resetAskUserQuestionsTurnCache();
102
121
  await syncServiceTierStatus(ctx);
103
122
  await applyDisabledModelProviderPolicy(ctx);
123
+ await applyCompactionThresholdOverride(ctx);
104
124
  // Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
105
125
  const { isInAutoWorktree } = await import("../auto-worktree.js");
106
126
  if (!isInAutoWorktree(process.cwd())) {
@@ -149,6 +169,7 @@ export function registerHooks(pi, ecosystemHandlers) {
149
169
  clearDiscussionFlowState(process.cwd());
150
170
  await syncServiceTierStatus(ctx);
151
171
  await applyDisabledModelProviderPolicy(ctx);
172
+ await applyCompactionThresholdOverride(ctx);
152
173
  // Skip MCP auto-prep when running inside an auto-worktree. The worktree
153
174
  // already has .mcp.json from createAutoWorktree, and re-running the writer
154
175
  // post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
@@ -432,6 +453,16 @@ export function registerHooks(pi, ecosystemHandlers) {
432
453
  return planningGuard;
433
454
  }
434
455
  }
456
+ // ── Worktree-isolation write gate (#5199) ────────────────────────────
457
+ // Block planning-write tools from landing code at the project root when
458
+ // git.isolation=worktree but auto-mode hasn't created the milestone
459
+ // worktree yet. Without this, writes silently orphan outside git history.
460
+ if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
461
+ const wtBasePath = resolveWorktreeProjectRoot(dash.basePath ?? discussionBasePath);
462
+ const wtGuard = shouldBlockWorktreeWrite(event.toolName, event.input.path, wtBasePath, isAutoActive(), dash.currentUnit?.type);
463
+ if (wtGuard.block)
464
+ return wtGuard;
465
+ }
435
466
  // ── Single-writer engine: block direct writes to STATE.md ──────────
436
467
  // Covers write, edit, and bash tools to prevent bypass vectors.
437
468
  if (isToolCallEventType("write", event)) {
@@ -1,7 +1,9 @@
1
- import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
3
  import { minimatch } from "minimatch";
4
+ import { getIsolationMode } from "../preferences.js";
4
5
  import { logWarning } from "../workflow-logger.js";
6
+ import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "../worktree-root.js";
5
7
  /**
6
8
  * Regex matching milestone CONTEXT.md file names in both legacy M001
7
9
  * and unique M001-abc123 formats. Exported so regex-hardening tests
@@ -728,3 +730,129 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
728
730
  // avoids breaking gsd_* MCP tools or future safe additions.
729
731
  return { block: false };
730
732
  }
733
+ // ─── Worktree isolation write gate (#5199) ────────────────────────────────
734
+ //
735
+ // When `git.isolation: worktree` is configured, the per-unit commit pipeline
736
+ // only runs inside the auto-mode loop (`auto-post-unit.ts`). If the LLM
737
+ // authors code at the project root before auto-mode is started, those writes
738
+ // land in the working tree but never reach a commit — they're silently
739
+ // orphaned outside git history. This guard blocks those writes at the
740
+ // tool_call seam so the agent receives a clear error instead.
741
+ const WORKTREE_GATE_BOOTSTRAP_UNITS = new Set([
742
+ "discuss-milestone",
743
+ "plan-milestone",
744
+ "init",
745
+ ]);
746
+ function realpathOrResolve(p) {
747
+ const abs = resolve(p);
748
+ try {
749
+ return realpathSync(abs);
750
+ }
751
+ catch {
752
+ // Path doesn't exist (yet) — realpath the deepest existing ancestor so
753
+ // platforms where /tmp -> /private/tmp don't break containment checks.
754
+ let dir = abs;
755
+ const tail = [];
756
+ while (dir && dir !== resolve(dir, "..")) {
757
+ try {
758
+ const real = realpathSync(dir);
759
+ return tail.length ? join(real, ...tail.reverse()) : real;
760
+ }
761
+ catch {
762
+ const idx = dir.lastIndexOf(sep);
763
+ if (idx <= 0)
764
+ break;
765
+ tail.push(dir.slice(idx + 1));
766
+ dir = dir.slice(0, idx) || sep;
767
+ }
768
+ }
769
+ return abs;
770
+ }
771
+ }
772
+ function isPathContained(target, container) {
773
+ if (target === container)
774
+ return true;
775
+ return target.startsWith(container.endsWith(sep) ? container : container + sep);
776
+ }
777
+ /**
778
+ * Block planning-write tool calls that would land code at the project root
779
+ * while `git.isolation: worktree` is in effect and auto-mode hasn't created
780
+ * (or flipped cwd into) the milestone worktree.
781
+ *
782
+ * Pure / unit-testable. Callers in `register-hooks.ts` supply the resolved
783
+ * project root and current auto liveness; this function does no I/O beyond
784
+ * realpath resolution.
785
+ *
786
+ * Allow rules (in order):
787
+ * 1. Tool isn't a planning-write (write/edit/multi_edit/notebook_edit).
788
+ * 2. `GSD_DISABLE_WORKTREE_WRITE_GUARD=1` self-hosting bypass.
789
+ * 3. Isolation mode is not "worktree".
790
+ * 4. Active unit is a bootstrap unit (discuss-milestone/plan-milestone/init).
791
+ * 5. Target is inside `<projectRoot>/.gsd/worktrees/` (a real worktree).
792
+ * 6. Target is inside `<projectRoot>/.gsd/` and isn't masquerading as a
793
+ * worktrees sibling (rejects the `.gsd/worktrees-extra/…` prefix trick).
794
+ * 7. Auto is live AND `effectiveBasePath` is itself a `.gsd/worktrees/…` path.
795
+ *
796
+ * Otherwise: block with a message that points the agent at `/gsd` to start
797
+ * auto-mode.
798
+ */
799
+ export function shouldBlockWorktreeWrite(toolName, targetPath, effectiveBasePath, isAutoLive, currentUnitType) {
800
+ const tool = canonicalToolName(toolName);
801
+ if (!PLANNING_WRITE_TOOLS.has(tool))
802
+ return { block: false };
803
+ if (process.env.GSD_DISABLE_WORKTREE_WRITE_GUARD === "1")
804
+ return { block: false };
805
+ if (getIsolationMode(effectiveBasePath) !== "worktree")
806
+ return { block: false };
807
+ if (currentUnitType && WORKTREE_GATE_BOOTSTRAP_UNITS.has(currentUnitType))
808
+ return { block: false };
809
+ if (!targetPath) {
810
+ return {
811
+ block: true,
812
+ reason: [
813
+ `HARD BLOCK: ${tool} called with empty path while \`git.isolation: worktree\` is configured`,
814
+ `and auto-mode is not active. Refusing to allow writes that cannot be located.`,
815
+ ].join(" "),
816
+ };
817
+ }
818
+ // Resolve the target relative to the project root, then realpath to defeat
819
+ // symlink-based escapes and prefix tricks (e.g. .gsd/worktrees-extra/).
820
+ const projectRoot = resolveWorktreeProjectRoot(effectiveBasePath);
821
+ const absTarget = isAbsolute(targetPath) ? targetPath : resolve(projectRoot, targetPath);
822
+ const realTarget = realpathOrResolve(absTarget);
823
+ const realRoot = realpathOrResolve(projectRoot);
824
+ const realGsd = realpathOrResolve(join(projectRoot, ".gsd"));
825
+ const realWorktreesDir = realpathOrResolve(join(projectRoot, ".gsd", "worktrees"));
826
+ // Allow writes inside the legitimate worktrees subtree.
827
+ if (isPathContained(realTarget, realWorktreesDir))
828
+ return { block: false };
829
+ // Allow writes to .gsd/ planning artifacts, but reject siblings whose name
830
+ // starts with "worktrees" (the worktrees-extra prefix trick — case 4).
831
+ if (isPathContained(realTarget, realGsd)) {
832
+ const rel = relative(realGsd, realTarget);
833
+ const firstSeg = rel.split(/[\/\\]/)[0] ?? "";
834
+ if (!firstSeg.startsWith("worktrees"))
835
+ return { block: false };
836
+ // fall through: looks like worktrees<something> sibling — block
837
+ }
838
+ // Auto is live and the caller is operating inside a worktree path —
839
+ // host tool's write happens in worktree context; let it through.
840
+ if (isAutoLive && isGsdWorktreePath(effectiveBasePath))
841
+ return { block: false };
842
+ // Block. Provide enough context that the agent can self-correct.
843
+ const displayTarget = isPathContained(realTarget, realRoot)
844
+ ? relative(realRoot, realTarget) || "."
845
+ : realTarget;
846
+ return {
847
+ block: true,
848
+ reason: [
849
+ `HARD BLOCK: Worktree isolation is configured (\`git.isolation: worktree\`) but auto-mode is`,
850
+ `not running and the target "${displayTarget}" is not inside \`.gsd/worktrees/<MID>/\`.`,
851
+ `Code edits at the project root would be lost — only the auto-mode commit pipeline`,
852
+ `(auto-post-unit) commits work, and it never runs outside the loop.`,
853
+ `Required action: start auto-mode with \`/gsd\` so the milestone worktree is created,`,
854
+ `then write inside it. To disable this guard for self-hosting development, set`,
855
+ `GSD_DISABLE_WORKTREE_WRITE_GUARD=1.`,
856
+ ].join(" "),
857
+ };
858
+ }
@@ -16,6 +16,31 @@ import { execFileSync } from "node:child_process";
16
16
  import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
17
17
  import { logWarning } from "./workflow-logger.js";
18
18
  import { nativeHasChanges } from "./native-git-bridge.js";
19
+ function findPreflightStashRef(basePath, milestoneId, stashMarker) {
20
+ const markerPrefix = `gsd-preflight-stash:${milestoneId}:`;
21
+ let fallbackRef = null;
22
+ try {
23
+ const list = execFileSync("git", ["stash", "list", "--format=%gd%x00%s"], {
24
+ cwd: basePath,
25
+ stdio: ["ignore", "pipe", "pipe"],
26
+ encoding: "utf-8",
27
+ env: GIT_NO_PROMPT_ENV,
28
+ });
29
+ for (const line of list.split("\n")) {
30
+ const [ref, subject] = line.split("\x00");
31
+ if (!ref || !subject)
32
+ continue;
33
+ if (stashMarker && subject.includes(stashMarker))
34
+ return ref;
35
+ if (!fallbackRef && subject.includes(markerPrefix))
36
+ fallbackRef = ref;
37
+ }
38
+ }
39
+ catch (err) {
40
+ logWarning("preflight", `stash list failed before restore: ${err instanceof Error ? err.message : String(err)}`);
41
+ }
42
+ return fallbackRef;
43
+ }
19
44
  /**
20
45
  * Check the working tree for dirty files before a milestone merge.
21
46
  *
@@ -47,7 +72,8 @@ export function preflightCleanRoot(basePath, milestoneId, notify) {
47
72
  notify(warnMsg, "warning");
48
73
  // Push the stash
49
74
  try {
50
- execFileSync("git", ["stash", "push", "--include-untracked", "-m", "gsd-preflight-stash"], {
75
+ const stashMarker = `gsd-preflight-stash:${milestoneId}:${process.pid}:${Date.now()}:${process.hrtime.bigint().toString(36)}`;
76
+ execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd-preflight-stash [${stashMarker}]`], {
51
77
  cwd: basePath,
52
78
  stdio: ["ignore", "pipe", "pipe"],
53
79
  encoding: "utf-8",
@@ -55,6 +81,7 @@ export function preflightCleanRoot(basePath, milestoneId, notify) {
55
81
  });
56
82
  return {
57
83
  stashPushed: true,
84
+ stashMarker,
58
85
  summary: `Stashed uncommitted changes before merge (milestone ${milestoneId}).`,
59
86
  };
60
87
  }
@@ -73,9 +100,17 @@ export function preflightCleanRoot(basePath, milestoneId, notify) {
73
100
  * Any pop error (e.g. conflict) is logged and notified but does NOT throw —
74
101
  * the merge already completed successfully.
75
102
  */
76
- export function postflightPopStash(basePath, milestoneId, notify) {
103
+ export function postflightPopStash(basePath, milestoneId, stashMarker, notify) {
104
+ let stashRef = null;
77
105
  try {
78
- execFileSync("git", ["stash", "pop"], {
106
+ stashRef = findPreflightStashRef(basePath, milestoneId, stashMarker);
107
+ if (!stashRef) {
108
+ const msg = `No matching GSD preflight stash found for milestone ${milestoneId}; leaving stash list untouched.`;
109
+ logWarning("preflight", msg);
110
+ notify(msg, "warning");
111
+ return;
112
+ }
113
+ execFileSync("git", ["stash", "pop", stashRef], {
79
114
  cwd: basePath,
80
115
  stdio: ["ignore", "pipe", "pipe"],
81
116
  encoding: "utf-8",
@@ -86,7 +121,10 @@ export function postflightPopStash(basePath, milestoneId, notify) {
86
121
  catch (err) {
87
122
  // Pop conflicts mean the merged code collides with the stashed changes.
88
123
  // Log a warning — the user needs to resolve manually, but the merge succeeded.
89
- const msg = `git stash pop failed after merge of milestone ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. Run "git stash pop" manually to restore your changes.`;
124
+ const restoreHint = stashRef
125
+ ? `Run "git stash pop ${stashRef}" or "git stash apply ${stashRef}" manually to restore the correct stash.`
126
+ : `Run "git stash list" to find the matching GSD preflight stash before restoring manually.`;
127
+ const msg = `git stash pop ${stashRef ?? ""}`.trim() + ` failed after merge of milestone ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. ${restoreHint}`;
90
128
  logWarning("preflight", msg);
91
129
  notify(msg, "warning");
92
130
  }
@@ -33,5 +33,10 @@ export async function handleGSDCommand(args, ctx, pi) {
33
33
  }
34
34
  if (handled)
35
35
  return;
36
+ if (trimmed.includes(" ")) {
37
+ const { handleDo } = await import("../commands-do.js");
38
+ await handleDo(trimmed, ctx, pi);
39
+ return;
40
+ }
36
41
  ctx.ui.notify(`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`, "warning");
37
42
  }