@opengsd/gsd-pi 1.2.0-dev.84c56d87 → 1.2.0-dev.8e6112e9

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 (631) hide show
  1. package/dist/cli-model-override.d.ts +15 -0
  2. package/dist/cli-model-override.js +21 -0
  3. package/dist/cli.js +1 -18
  4. package/dist/headless-events.js +7 -5
  5. package/dist/loader.js +6 -4
  6. package/dist/mcp-server.js +2 -1
  7. package/dist/register-agent-bundles.d.ts +11 -2
  8. package/dist/register-agent-bundles.js +18 -4
  9. package/dist/resource-loader.d.ts +10 -5
  10. package/dist/resource-loader.js +121 -6
  11. package/dist/resources/.managed-resources-content-hash +1 -1
  12. package/dist/resources/GSD-WORKFLOW.md +5 -4
  13. package/dist/resources/extensions/ask-user-questions.js +3 -2
  14. package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
  15. package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
  16. package/dist/resources/extensions/async-jobs/index.js +65 -0
  17. package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
  18. package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
  19. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
  20. package/dist/resources/extensions/bg-shell/overlay.js +9 -6
  21. package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
  22. package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
  23. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
  24. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
  25. package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
  26. package/dist/resources/extensions/gsd/auto/custom-verify-retry-store.js +17 -2
  27. package/dist/resources/extensions/gsd/auto/detect-stuck.js +33 -13
  28. package/dist/resources/extensions/gsd/auto/dispatch-history.js +120 -0
  29. package/dist/resources/extensions/gsd/auto/dispatch-key.js +37 -0
  30. package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
  31. package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
  32. package/dist/resources/extensions/gsd/auto/loop.js +7 -1
  33. package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
  34. package/dist/resources/extensions/gsd/auto/orchestrator.js +174 -69
  35. package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
  36. package/dist/resources/extensions/gsd/auto/phases.js +17 -2329
  37. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
  38. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  39. package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
  40. package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
  41. package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
  42. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -34
  43. package/dist/resources/extensions/gsd/auto-dispatch.js +50 -58
  44. package/dist/resources/extensions/gsd/auto-model-selection.js +36 -13
  45. package/dist/resources/extensions/gsd/auto-post-unit.js +30 -12
  46. package/dist/resources/extensions/gsd/auto-prompts.js +78 -19
  47. package/dist/resources/extensions/gsd/auto-start.js +35 -15
  48. package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
  49. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +5 -4
  50. package/dist/resources/extensions/gsd/auto-verification.js +23 -30
  51. package/dist/resources/extensions/gsd/auto-worktree.js +15 -2
  52. package/dist/resources/extensions/gsd/auto.js +52 -2
  53. package/dist/resources/extensions/gsd/blocked-models.js +28 -0
  54. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +26 -6
  55. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +60 -13
  56. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -2
  57. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +145 -50
  58. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +302 -80
  59. package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
  60. package/dist/resources/extensions/gsd/closeout-wizard.js +92 -0
  61. package/dist/resources/extensions/gsd/commands/context.js +16 -2
  62. package/dist/resources/extensions/gsd/commands-handlers.js +46 -3
  63. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
  64. package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
  65. package/dist/resources/extensions/gsd/consent-question.js +353 -0
  66. package/dist/resources/extensions/gsd/consent-verdict.js +63 -0
  67. package/dist/resources/extensions/gsd/constants.js +0 -2
  68. package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
  69. package/dist/resources/extensions/gsd/db/engine.js +5 -3
  70. package/dist/resources/extensions/gsd/db/queries.js +56 -0
  71. package/dist/resources/extensions/gsd/db-writer.js +8 -17
  72. package/dist/resources/extensions/gsd/dispatch-guard.js +10 -35
  73. package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
  74. package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
  75. package/dist/resources/extensions/gsd/doctor-git-checks.js +2 -18
  76. package/dist/resources/extensions/gsd/engine-hook-contract.js +70 -0
  77. package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
  78. package/dist/resources/extensions/gsd/files.js +33 -19
  79. package/dist/resources/extensions/gsd/gsd-command-home.js +22 -12
  80. package/dist/resources/extensions/gsd/gsd-db.js +11 -8
  81. package/dist/resources/extensions/gsd/guidance.js +60 -0
  82. package/dist/resources/extensions/gsd/guided-flow.js +93 -4
  83. package/dist/resources/extensions/gsd/health-widget.js +87 -28
  84. package/dist/resources/extensions/gsd/markdown-renderer.js +10 -0
  85. package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
  86. package/dist/resources/extensions/gsd/memory-relations.js +1 -1
  87. package/dist/resources/extensions/gsd/milestone-closeout.js +85 -24
  88. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
  89. package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
  90. package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
  91. package/dist/resources/extensions/gsd/notifications.js +12 -7
  92. package/dist/resources/extensions/gsd/parsers-legacy.js +16 -4
  93. package/dist/resources/extensions/gsd/preferences-models.js +2 -2
  94. package/dist/resources/extensions/gsd/projection-flush.js +7 -0
  95. package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  96. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
  97. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  98. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  99. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
  100. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  101. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  102. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  103. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  104. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  105. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  106. package/dist/resources/extensions/gsd/prompts/run-uat.md +9 -5
  107. package/dist/resources/extensions/gsd/prompts/system.md +5 -2
  108. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  109. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  110. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  111. package/dist/resources/extensions/gsd/reactive-graph.js +8 -1
  112. package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
  113. package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
  114. package/dist/resources/extensions/gsd/session-lock.js +1 -1
  115. package/dist/resources/extensions/gsd/skill-activation.js +3 -6
  116. package/dist/resources/extensions/gsd/state.js +11 -2
  117. package/dist/resources/extensions/gsd/tool-contract.js +14 -3
  118. package/dist/resources/extensions/gsd/tool-presentation-plan.js +4 -4
  119. package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
  120. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  121. package/dist/resources/extensions/gsd/tools/complete-slice.js +22 -12
  122. package/dist/resources/extensions/gsd/tools/complete-task.js +65 -2
  123. package/dist/resources/extensions/gsd/tools/exec-tool.js +5 -0
  124. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
  125. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
  126. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
  127. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  128. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
  129. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
  130. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
  131. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +67 -2
  132. package/dist/resources/extensions/gsd/uat-policy.js +40 -15
  133. package/dist/resources/extensions/gsd/unit-context-composer.js +65 -0
  134. package/dist/resources/extensions/gsd/unit-registry.js +34 -4
  135. package/dist/resources/extensions/gsd/verdict-parser.js +1 -1
  136. package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
  137. package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
  138. package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
  139. package/dist/resources/extensions/gsd/workflow-events.js +6 -18
  140. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
  141. package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
  142. package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
  143. package/dist/resources/extensions/gsd/worktree-lifecycle.js +3 -2
  144. package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
  145. package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
  146. package/dist/resources/extensions/gsd/worktree.js +8 -1
  147. package/dist/resources/extensions/mcp-client/manager.js +6 -1
  148. package/dist/resources/extensions/shared/gsd-browser-cli.js +45 -3
  149. package/dist/resources/shared/gsd-browser-path-sync.js +214 -0
  150. package/dist/resources/shared/package-manager-detection.js +1 -1
  151. package/dist/resources/shared/package.json +3 -0
  152. package/dist/resources/skills/create-skill/SKILL.md +3 -0
  153. package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
  154. package/dist/runtime-checks.d.ts +10 -0
  155. package/dist/runtime-checks.js +27 -0
  156. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  157. package/dist/update-check.d.ts +2 -0
  158. package/dist/update-check.js +24 -1
  159. package/dist/update-cmd.js +20 -3
  160. package/dist/web/standalone/.next/BUILD_ID +1 -1
  161. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  162. package/dist/web/standalone/.next/build-manifest.json +3 -3
  163. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  164. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  165. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  166. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  168. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  169. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  170. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  171. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  172. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  173. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  174. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  176. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  178. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  179. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  180. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  181. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  183. package/dist/web/standalone/.next/server/app/index.html +1 -1
  184. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  185. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  186. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  187. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  188. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  190. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  191. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  192. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  195. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  196. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  197. package/dist/web/standalone/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
  198. package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
  199. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  200. package/dist/web/standalone/node_modules/postcss/lib/container.js +26 -18
  201. package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +47 -14
  202. package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
  203. package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
  204. package/dist/web/standalone/node_modules/postcss/lib/input.js +54 -29
  205. package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +47 -37
  206. package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +26 -9
  207. package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +57 -55
  208. package/dist/web/standalone/node_modules/postcss/lib/node.js +99 -31
  209. package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
  210. package/dist/web/standalone/node_modules/postcss/lib/parser.js +10 -9
  211. package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
  212. package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +30 -11
  213. package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
  214. package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
  215. package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
  216. package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +69 -28
  217. package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +6 -2
  218. package/dist/web/standalone/node_modules/postcss/package.json +48 -48
  219. package/package.json +3 -3
  220. package/packages/cloud-mcp-gateway/package.json +2 -2
  221. package/packages/contracts/dist/rpc.d.ts +1 -0
  222. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  223. package/packages/contracts/dist/rpc.js.map +1 -1
  224. package/packages/contracts/package.json +1 -1
  225. package/packages/daemon/package.json +4 -4
  226. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  227. package/packages/gsd-agent-core/dist/sdk.js +6 -4
  228. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  229. package/packages/gsd-agent-core/package.json +5 -5
  230. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  231. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  232. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  233. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  234. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +13 -0
  235. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  236. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +55 -6
  237. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  238. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
  239. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  240. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
  241. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  242. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  243. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +7 -0
  244. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  245. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  246. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
  247. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  248. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  249. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
  250. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  251. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
  252. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  253. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +12 -0
  254. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  255. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  256. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
  257. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  258. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  259. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
  260. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  261. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  262. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
  263. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  264. package/packages/gsd-agent-modes/package.json +7 -7
  265. package/packages/mcp-server/README.md +12 -3
  266. package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
  267. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
  268. package/packages/mcp-server/dist/cli-runner.js +137 -0
  269. package/packages/mcp-server/dist/cli-runner.js.map +1 -0
  270. package/packages/mcp-server/dist/cli.js +2 -53
  271. package/packages/mcp-server/dist/cli.js.map +1 -1
  272. package/packages/mcp-server/dist/moonshot-tool-schema.d.ts +29 -0
  273. package/packages/mcp-server/dist/moonshot-tool-schema.d.ts.map +1 -0
  274. package/packages/mcp-server/dist/moonshot-tool-schema.js +50 -0
  275. package/packages/mcp-server/dist/moonshot-tool-schema.js.map +1 -0
  276. package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
  277. package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
  278. package/packages/mcp-server/dist/pid-registry.js +452 -0
  279. package/packages/mcp-server/dist/pid-registry.js.map +1 -0
  280. package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
  281. package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
  282. package/packages/mcp-server/dist/probe-mode.js +10 -0
  283. package/packages/mcp-server/dist/probe-mode.js.map +1 -0
  284. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  285. package/packages/mcp-server/dist/server.js +4 -0
  286. package/packages/mcp-server/dist/server.js.map +1 -1
  287. package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
  288. package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
  289. package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
  290. package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
  291. package/packages/mcp-server/dist/workflow-tools.d.ts +18 -18
  292. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  293. package/packages/mcp-server/dist/workflow-tools.js +161 -81
  294. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  295. package/packages/mcp-server/package.json +5 -4
  296. package/packages/native/package.json +1 -1
  297. package/packages/pi-agent-core/dist/agent-loop.js +43 -2
  298. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  299. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
  300. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  301. package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
  302. package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  303. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  304. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  305. package/packages/pi-agent-core/dist/index.js +3 -0
  306. package/packages/pi-agent-core/dist/index.js.map +1 -1
  307. package/packages/pi-agent-core/package.json +1 -1
  308. package/packages/pi-ai/README.md +1 -0
  309. package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
  310. package/packages/pi-ai/dist/image-models.generated.js +6 -6
  311. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  312. package/packages/pi-ai/dist/index.d.ts +2 -0
  313. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  314. package/packages/pi-ai/dist/index.js +2 -0
  315. package/packages/pi-ai/dist/index.js.map +1 -1
  316. package/packages/pi-ai/dist/models.generated.d.ts +419 -221
  317. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  318. package/packages/pi-ai/dist/models.generated.js +460 -261
  319. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  320. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  321. package/packages/pi-ai/dist/providers/anthropic.js +12 -7
  322. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  323. package/packages/pi-ai/dist/providers/google-shared.d.ts +5 -0
  324. package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
  325. package/packages/pi-ai/dist/providers/google-shared.js +12 -3
  326. package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
  327. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  328. package/packages/pi-ai/dist/providers/openai-completions.js +7 -3
  329. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  330. package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts +9 -0
  331. package/packages/pi-ai/dist/utils/moonshot-tool-schema.d.ts.map +1 -0
  332. package/packages/pi-ai/dist/utils/moonshot-tool-schema.js +34 -0
  333. package/packages/pi-ai/dist/utils/moonshot-tool-schema.js.map +1 -0
  334. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  335. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +6 -2
  336. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  337. package/packages/pi-ai/package.json +3 -2
  338. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
  339. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  340. package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
  341. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  342. package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
  344. package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
  345. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  346. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  347. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  348. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  349. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
  350. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
  352. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  354. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  355. package/packages/pi-coding-agent/dist/index.js +1 -1
  356. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  357. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
  359. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
  361. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  362. package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
  363. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  364. package/packages/pi-coding-agent/package.json +7 -7
  365. package/packages/pi-tui/dist/index.d.ts +1 -1
  366. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  367. package/packages/pi-tui/dist/index.js +1 -1
  368. package/packages/pi-tui/dist/index.js.map +1 -1
  369. package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
  370. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
  371. package/packages/pi-tui/dist/terminal-image.js +54 -2
  372. package/packages/pi-tui/dist/terminal-image.js.map +1 -1
  373. package/packages/pi-tui/dist/tui.d.ts +8 -0
  374. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  375. package/packages/pi-tui/dist/tui.js +63 -18
  376. package/packages/pi-tui/dist/tui.js.map +1 -1
  377. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  378. package/packages/pi-tui/dist/utils.js +110 -36
  379. package/packages/pi-tui/dist/utils.js.map +1 -1
  380. package/packages/pi-tui/package.json +2 -2
  381. package/packages/rpc-client/package.json +2 -2
  382. package/pkg/dist/theme/theme.d.ts.map +1 -1
  383. package/pkg/dist/theme/theme.js +45 -17
  384. package/pkg/dist/theme/theme.js.map +1 -1
  385. package/pkg/package.json +1 -1
  386. package/src/resources/GSD-WORKFLOW.md +5 -4
  387. package/src/resources/extensions/ask-user-questions.ts +7 -2
  388. package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
  389. package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
  390. package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
  391. package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
  392. package/src/resources/extensions/async-jobs/index.ts +79 -0
  393. package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
  394. package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
  395. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
  396. package/src/resources/extensions/bg-shell/overlay.ts +9 -5
  397. package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
  398. package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
  399. package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
  400. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +40 -1
  401. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
  402. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
  403. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
  404. package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
  405. package/src/resources/extensions/gsd/auto/custom-verify-retry-store.ts +21 -3
  406. package/src/resources/extensions/gsd/auto/detect-stuck.ts +32 -9
  407. package/src/resources/extensions/gsd/auto/dispatch-history.ts +168 -0
  408. package/src/resources/extensions/gsd/auto/dispatch-key.ts +39 -0
  409. package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
  410. package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
  411. package/src/resources/extensions/gsd/auto/loop.ts +7 -1
  412. package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
  413. package/src/resources/extensions/gsd/auto/orchestrator.ts +193 -71
  414. package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
  415. package/src/resources/extensions/gsd/auto/phases.ts +58 -3022
  416. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
  417. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  418. package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
  419. package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
  420. package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
  421. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +18 -48
  422. package/src/resources/extensions/gsd/auto-dispatch.ts +48 -61
  423. package/src/resources/extensions/gsd/auto-model-selection.ts +41 -12
  424. package/src/resources/extensions/gsd/auto-post-unit.ts +33 -12
  425. package/src/resources/extensions/gsd/auto-prompts.ts +115 -35
  426. package/src/resources/extensions/gsd/auto-start.ts +36 -18
  427. package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
  428. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +4 -4
  429. package/src/resources/extensions/gsd/auto-verification.ts +26 -28
  430. package/src/resources/extensions/gsd/auto-worktree.ts +15 -2
  431. package/src/resources/extensions/gsd/auto.ts +64 -2
  432. package/src/resources/extensions/gsd/blocked-models.ts +49 -0
  433. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -5
  434. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +79 -12
  435. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -2
  436. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +163 -55
  437. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +350 -86
  438. package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
  439. package/src/resources/extensions/gsd/closeout-wizard.ts +102 -0
  440. package/src/resources/extensions/gsd/commands/context.ts +16 -2
  441. package/src/resources/extensions/gsd/commands-handlers.ts +46 -3
  442. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
  443. package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
  444. package/src/resources/extensions/gsd/consent-question.ts +431 -0
  445. package/src/resources/extensions/gsd/consent-verdict.ts +86 -0
  446. package/src/resources/extensions/gsd/constants.ts +0 -3
  447. package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
  448. package/src/resources/extensions/gsd/db/engine.ts +5 -3
  449. package/src/resources/extensions/gsd/db/queries.ts +66 -0
  450. package/src/resources/extensions/gsd/db-writer.ts +11 -19
  451. package/src/resources/extensions/gsd/dispatch-guard.ts +8 -31
  452. package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
  453. package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
  454. package/src/resources/extensions/gsd/doctor-git-checks.ts +2 -19
  455. package/src/resources/extensions/gsd/engine-hook-contract.ts +79 -0
  456. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  457. package/src/resources/extensions/gsd/files.ts +33 -12
  458. package/src/resources/extensions/gsd/gsd-command-home.ts +13 -3
  459. package/src/resources/extensions/gsd/gsd-db.ts +13 -10
  460. package/src/resources/extensions/gsd/guidance.ts +78 -0
  461. package/src/resources/extensions/gsd/guided-flow.ts +145 -24
  462. package/src/resources/extensions/gsd/health-widget.ts +91 -27
  463. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -0
  464. package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
  465. package/src/resources/extensions/gsd/memory-relations.ts +1 -1
  466. package/src/resources/extensions/gsd/milestone-closeout.ts +109 -24
  467. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
  468. package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
  469. package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
  470. package/src/resources/extensions/gsd/notifications.ts +13 -6
  471. package/src/resources/extensions/gsd/parsers-legacy.ts +16 -4
  472. package/src/resources/extensions/gsd/preferences-models.ts +2 -1
  473. package/src/resources/extensions/gsd/projection-flush.ts +20 -0
  474. package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  475. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
  476. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  477. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  478. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
  479. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  480. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  481. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  482. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  483. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  484. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  485. package/src/resources/extensions/gsd/prompts/run-uat.md +9 -5
  486. package/src/resources/extensions/gsd/prompts/system.md +5 -2
  487. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  488. package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  489. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  490. package/src/resources/extensions/gsd/reactive-graph.ts +11 -1
  491. package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
  492. package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
  493. package/src/resources/extensions/gsd/session-lock.ts +1 -1
  494. package/src/resources/extensions/gsd/skill-activation.ts +3 -6
  495. package/src/resources/extensions/gsd/state.ts +12 -1
  496. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
  497. package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
  498. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
  499. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +97 -1
  500. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +273 -37
  501. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
  502. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
  503. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
  504. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
  505. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +236 -0
  506. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
  507. package/src/resources/extensions/gsd/tests/blocked-models.test.ts +19 -0
  508. package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
  509. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  510. package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
  511. package/src/resources/extensions/gsd/tests/consent-question.test.ts +351 -0
  512. package/src/resources/extensions/gsd/tests/custom-verify-retry-store.test.ts +67 -0
  513. package/src/resources/extensions/gsd/tests/db-writer.test.ts +15 -4
  514. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +12 -11
  515. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +36 -0
  516. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
  517. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
  518. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +328 -0
  519. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
  520. package/src/resources/extensions/gsd/tests/doctor-git-checks-terminal.test.ts +73 -0
  521. package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
  522. package/src/resources/extensions/gsd/tests/engine-hook-contract.test.ts +148 -0
  523. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
  524. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
  525. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
  526. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
  527. package/src/resources/extensions/gsd/tests/gsd-command-home.test.ts +120 -0
  528. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +27 -0
  529. package/src/resources/extensions/gsd/tests/guidance.test.ts +23 -0
  530. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +18 -6
  531. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
  532. package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
  533. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +217 -0
  534. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
  535. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
  536. package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
  537. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
  538. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +95 -4
  539. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
  540. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
  541. package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
  542. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
  543. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
  544. package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
  545. package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
  546. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +18 -0
  547. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
  548. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +143 -0
  549. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
  550. package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
  551. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
  552. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
  553. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
  554. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +124 -6
  555. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
  556. package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
  557. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
  558. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +26 -2
  559. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +170 -48
  560. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
  561. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
  562. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
  563. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
  564. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +45 -2
  565. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
  566. package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
  567. package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
  568. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +88 -0
  569. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +44 -0
  570. package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
  571. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  572. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
  573. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
  574. package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
  575. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
  576. package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
  577. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
  578. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
  579. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +273 -38
  580. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
  581. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
  582. package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
  583. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
  584. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
  585. package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
  586. package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +358 -0
  587. package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -1
  588. package/src/resources/extensions/gsd/tool-contract.ts +38 -3
  589. package/src/resources/extensions/gsd/tool-presentation-plan.ts +4 -4
  590. package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
  591. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  592. package/src/resources/extensions/gsd/tools/complete-slice.ts +22 -12
  593. package/src/resources/extensions/gsd/tools/complete-task.ts +90 -2
  594. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -0
  595. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
  596. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
  597. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
  598. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  599. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
  600. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
  601. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
  602. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +81 -2
  603. package/src/resources/extensions/gsd/uat-policy.ts +60 -15
  604. package/src/resources/extensions/gsd/unit-context-composer.ts +99 -0
  605. package/src/resources/extensions/gsd/unit-registry.ts +34 -4
  606. package/src/resources/extensions/gsd/verdict-parser.ts +1 -1
  607. package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
  608. package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
  609. package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
  610. package/src/resources/extensions/gsd/workflow-events.ts +12 -20
  611. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
  612. package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
  613. package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
  614. package/src/resources/extensions/gsd/worktree-lifecycle.ts +3 -8
  615. package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
  616. package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
  617. package/src/resources/extensions/gsd/worktree.ts +7 -1
  618. package/src/resources/extensions/mcp-client/manager.ts +7 -1
  619. package/src/resources/extensions/shared/gsd-browser-cli.ts +54 -3
  620. package/src/resources/shared/gsd-browser-path-sync.ts +273 -0
  621. package/src/resources/shared/package-manager-detection.ts +1 -1
  622. package/src/resources/shared/package.json +3 -0
  623. package/src/resources/skills/create-skill/SKILL.md +3 -0
  624. package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
  625. package/dist/resources/extensions/gsd/user-input-boundary.js +0 -218
  626. package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
  627. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +0 -173
  628. package/src/resources/extensions/gsd/user-input-boundary.ts +0 -216
  629. package/src/resources/skills/gsd-browser/SKILL.md +0 -41
  630. /package/dist/web/standalone/.next/static/{AOpDeK_gJHU8OZjRo31gQ → pZbHa49xI-knmKlphIRq0}/_buildManifest.js +0 -0
  631. /package/dist/web/standalone/.next/static/{AOpDeK_gJHU8OZjRo31gQ → pZbHa49xI-knmKlphIRq0}/_ssgManifest.js +0 -0
@@ -33,7 +33,7 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
33
33
  description:
34
34
  "Wait for background jobs to complete. Provide specific job IDs or omit to wait for the next job that finishes. Returns results of completed jobs.",
35
35
  parameters: schema,
36
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
36
+ async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
37
37
  const manager = getManager();
38
38
  const { jobs: jobIds, timeout } = params;
39
39
  const timeoutMs = ((timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1000);
@@ -66,41 +66,79 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
66
66
  }
67
67
  }
68
68
 
69
- // Suppress follow-up notifications for all watched jobs upfront.
69
+ // If all watched jobs are already done, suppress follow-up and return immediately.
70
70
  // suppressFollowUp() cancels the pending delivery timer (if any), which
71
71
  // handles both the within-turn case (job completes while we await) and
72
72
  // the cross-turn case (job already completed before await_job was called).
73
73
  // Previously this only set j.awaited = true, which missed the cross-turn
74
74
  // case because the queueMicrotask had already fired (#3787).
75
- for (const j of watched) manager.suppressFollowUp(j.id);
76
-
77
- // If all watched jobs are already done, return immediately
78
75
  const running = watched.filter((j) => j.status === "running");
79
76
  if (running.length === 0) {
80
- const result = formatResults(watched);
81
- return { content: [{ type: "text", text: result }], details: undefined };
77
+ for (const j of watched) manager.suppressFollowUp(j.id);
78
+ return { content: [{ type: "text", text: renderCompleted(watched) }], details: undefined };
82
79
  }
83
80
 
84
- // Wait for at least one to complete, or timeout
81
+ // Wait for at least one to complete, timeout, or abort signal
85
82
  const TIMEOUT_SENTINEL = Symbol("timeout");
83
+ const ABORT_SENTINEL = Symbol("abort");
84
+
85
+ // The race timer and abort listener are explicitly torn down once the race
86
+ // settles (below) so a completion- or timeout-won race never leaks a pending
87
+ // timer or a lingering abort listener — the listener holds a closure over the
88
+ // race resolver, so { once: true } alone (which only detaches on fire) is not
89
+ // enough when ESC never happens.
90
+ let raceTimer: ReturnType<typeof setTimeout> | undefined;
91
+ let abortListener: (() => void) | undefined;
86
92
  const timeoutPromise = new Promise<typeof TIMEOUT_SENTINEL>((resolve) => {
87
- const timer = setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs);
93
+ raceTimer = setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs);
88
94
  // Allow the process to exit even if the timer is pending
89
- if (typeof timer === "object" && "unref" in timer) timer.unref();
95
+ if (typeof raceTimer === "object" && "unref" in raceTimer) raceTimer.unref();
96
+ });
97
+
98
+ const abortPromise = new Promise<typeof ABORT_SENTINEL>((resolve) => {
99
+ if (!signal || signal.aborted) {
100
+ resolve(ABORT_SENTINEL);
101
+ } else {
102
+ abortListener = () => resolve(ABORT_SENTINEL);
103
+ signal.addEventListener("abort", abortListener, { once: true });
104
+ }
90
105
  });
91
106
 
92
107
  const raceResult = await Promise.race([
93
108
  Promise.race(running.map((j) => j.promise)).then(() => "completed" as const),
94
109
  timeoutPromise,
110
+ abortPromise,
95
111
  ]);
96
112
 
113
+ // Tear down race resources now that a winner is decided.
114
+ if (raceTimer) clearTimeout(raceTimer);
115
+ if (abortListener && signal) signal.removeEventListener("abort", abortListener);
116
+
117
+ const aborted = raceResult === ABORT_SENTINEL;
97
118
  const timedOut = raceResult === TIMEOUT_SENTINEL;
98
119
 
99
120
  // Collect all completed results (more may have finished while waiting)
100
121
  const completed = watched.filter((j) => j.status !== "running");
101
-
102
122
  const stillRunning = watched.filter((j) => j.status === "running");
103
- let result = formatResults(completed);
123
+
124
+ // Suppress follow-up ONLY for completed jobs — leave stillRunning unsuppressed
125
+ // so deliverResult/onJobComplete can resurface their results later.
126
+ for (const j of completed) manager.suppressFollowUp(j.id);
127
+
128
+ if (aborted) {
129
+ // ESC ended the wait, not the jobs: still-running jobs keep going and
130
+ // resurface via onJobComplete (they were deliberately not suppressed above).
131
+ const runningDesc = stillRunning.map((j) => `${j.id} (${j.label})`).join(", ");
132
+ const interrupt = stillRunning.length > 0
133
+ ? `Wait interrupted. Still running: ${runningDesc} — results will surface when complete.`
134
+ : "Wait interrupted.";
135
+ const text = completed.length > 0
136
+ ? `${renderCompleted(completed)}\n\n${interrupt}`
137
+ : interrupt;
138
+ return { content: [{ type: "text", text }], details: undefined };
139
+ }
140
+
141
+ let result = renderCompleted(completed);
104
142
  if (stillRunning.length > 0) {
105
143
  result += `\n\n**Still running:** ${stillRunning.map((j) => `${j.id} (${j.label})`).join(", ")}`;
106
144
  }
@@ -115,6 +153,38 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
115
153
  };
116
154
  }
117
155
 
156
+ /**
157
+ * Render completed jobs for the await_job result, de-duplicating against
158
+ * follow-ups that have already been delivered to context.
159
+ *
160
+ * A job's follow-up fires ~immediately (setTimeout(0)) once it settles, so when
161
+ * await_job runs in a LATER turn the result is already in context. Reprinting it
162
+ * inline produces the same output twice. Jobs already `delivered` are therefore
163
+ * acknowledged on a single line instead of having their full output reprinted;
164
+ * not-yet-delivered jobs (the within-turn case, where suppressFollowUp won the
165
+ * race) are rendered in full as before.
166
+ */
167
+ function renderCompleted(jobs: Job[]): string {
168
+ if (jobs.length === 0) return "No completed jobs.";
169
+
170
+ const fresh = jobs.filter((j) => !j.delivered);
171
+ const alreadyDelivered = jobs.filter((j) => j.delivered);
172
+
173
+ const sections: string[] = [];
174
+ if (fresh.length > 0) sections.push(formatResults(fresh));
175
+ if (alreadyDelivered.length > 0) {
176
+ const names = alreadyDelivered
177
+ .map((j) => `${j.id} (${j.label})`)
178
+ .join(", ");
179
+ const sentence =
180
+ alreadyDelivered.length === 1
181
+ ? `This job already finished and its result was shown above when it completed, so there is nothing new to report: ${names}.`
182
+ : `These jobs already finished and their results were shown above when they completed, so there is nothing new to report: ${names}.`;
183
+ sections.push(sentence);
184
+ }
185
+ return sections.join("\n\n");
186
+ }
187
+
118
188
  function formatResults(jobs: Job[]): string {
119
189
  if (jobs.length === 0) return "No completed jobs.";
120
190
 
@@ -117,10 +117,89 @@ export default function AsyncJobs(pi: ExtensionAPI) {
117
117
  return;
118
118
  }
119
119
 
120
+ const ctx = _ctx;
120
121
  const running = manager.getRunningJobs();
121
122
  const recent = manager.getRecentJobs(10);
122
123
  const completed = recent.filter((j) => j.status !== "running");
123
124
 
125
+ // Interactive kill-picker when there are running jobs and a UI is available
126
+ if (running.length > 0 && ctx.hasUI) {
127
+ // Kill-picker loop: each iteration shows live running jobs.
128
+ // Step 1 picks a job (neutral label — selecting does NOT cancel yet);
129
+ // step 2 confirms before the destructive cancel. Labels deliberately
130
+ // omit a live elapsed time: ctx.ui.select renders the option strings
131
+ // once and never refreshes them, so a "(24s)" baked into the label
132
+ // would freeze and mislead. Accurate elapsed times are shown in the
133
+ // post-picker summary (rebuilt fresh) instead.
134
+ const DONE = "Close";
135
+ while (true) {
136
+ const liveJobs = manager.getRunningJobs();
137
+ if (liveJobs.length === 0) break;
138
+
139
+ // Map display label -> job id so we never parse ids back out of
140
+ // free-form label text.
141
+ const labelToId = new Map<string, string>();
142
+ for (const j of liveJobs) {
143
+ labelToId.set(`${j.id} — ${j.label} (running)`, j.id);
144
+ }
145
+ const options = [...labelToId.keys(), DONE];
146
+
147
+ const choice = await ctx.ui.select(
148
+ "Background jobs — pick one to cancel, Escape to close",
149
+ options,
150
+ );
151
+
152
+ // ESC returns undefined; headless may return string[]; DONE closes
153
+ if (!choice || typeof choice !== "string" || choice === DONE) break;
154
+
155
+ const id = labelToId.get(choice);
156
+ if (!id) break;
157
+
158
+ const job = liveJobs.find((j) => j.id === id);
159
+ const confirmed = await ctx.ui.confirm(
160
+ "Cancel background job?",
161
+ `This will stop ${id}${job ? ` — ${job.label}` : ""}. Other jobs keep running.`,
162
+ );
163
+ if (!confirmed) continue;
164
+
165
+ const r = manager.cancel(id);
166
+ ctx.ui.notify(
167
+ r === "cancelled" ? `Job ${id} cancelled.` : `Job ${id}: ${r}`,
168
+ r === "cancelled" ? "success" : "warning",
169
+ );
170
+ }
171
+
172
+ // After picker, send a summary of any still-running jobs
173
+ const stillRunning = manager.getRunningJobs();
174
+ const summaryLines: string[] = ["## Background Jobs"];
175
+ if (stillRunning.length > 0) {
176
+ summaryLines.push("", "### Running");
177
+ for (const job of stillRunning) {
178
+ const elapsed = ((Date.now() - job.startTime) / 1000).toFixed(0);
179
+ summaryLines.push(`- **${job.id}** — ${job.label} (${elapsed}s)`);
180
+ }
181
+ }
182
+ const recentCompleted = manager.getRecentJobs(10).filter((j) => j.status !== "running");
183
+ if (recentCompleted.length > 0) {
184
+ summaryLines.push("", "### Recent");
185
+ for (const job of recentCompleted) {
186
+ const elapsed = ((Date.now() - job.startTime) / 1000).toFixed(1);
187
+ summaryLines.push(`- **${job.id}** — ${job.label} (${job.status}, ${elapsed}s)`);
188
+ }
189
+ }
190
+ if (stillRunning.length === 0 && recentCompleted.length === 0) {
191
+ summaryLines.push("", "No background jobs.");
192
+ }
193
+
194
+ pi.sendMessage({
195
+ customType: "async_jobs_list",
196
+ content: summaryLines.join("\n"),
197
+ display: true,
198
+ });
199
+ return;
200
+ }
201
+
202
+ // Text-only display: headless/RPC mode, or no running jobs
124
203
  const lines: string[] = ["## Background Jobs"];
125
204
 
126
205
  if (running.length === 0 && completed.length === 0) {
@@ -24,6 +24,14 @@ export interface Job {
24
24
  errorText?: string;
25
25
  /** Set by await_job when results are consumed. Suppresses follow-up delivery. */
26
26
  awaited?: boolean;
27
+ /**
28
+ * Set true once the follow-up notification has actually been delivered via
29
+ * onJobComplete. The delivery timer fires ~immediately (setTimeout(0)) after a
30
+ * job settles, so by a later LLM turn the follow-up is already in context.
31
+ * await_job reads this to avoid rendering the same result inline a second time
32
+ * (duplicate-in-context bug): a delivered job is acknowledged tersely, not reprinted.
33
+ */
34
+ delivered?: boolean;
27
35
  /**
28
36
  * Handle for the pending follow-up delivery timer (set by deliverResult).
29
37
  * Stored so suppressFollowUp() can cancel it before the notification fires,
@@ -102,6 +110,15 @@ export class AsyncJobManager {
102
110
 
103
111
  job.promise = runFn(abortController.signal)
104
112
  .then((resultText) => {
113
+ if (job.status === "cancelled") {
114
+ // Already cancelled by cancel(). The runFn resolves (not rejects) even
115
+ // when aborted — async_bash's safeResolve returns "Command aborted"
116
+ // rather than throwing — so without this guard the status would be
117
+ // clobbered back to "completed", mislabeling a user-cancelled job.
118
+ // Mirrors the symmetric guard in the .catch branch below.
119
+ this.scheduleEviction(id);
120
+ return;
121
+ }
105
122
  job.status = "completed";
106
123
  job.resultText = resultText;
107
124
  this.scheduleEviction(id);
@@ -200,7 +217,10 @@ export class AsyncJobManager {
200
217
  const cb = this.onJobComplete;
201
218
  job.deliveryTimer = setTimeout(() => {
202
219
  job.deliveryTimer = undefined;
203
- if (!job.awaited) cb(job);
220
+ if (!job.awaited) {
221
+ job.delivered = true;
222
+ cb(job);
223
+ }
204
224
  }, 0);
205
225
  // Allow process to exit even if timer is pending
206
226
  if (typeof job.deliveryTimer === "object" && "unref" in job.deliveryTimer) {
@@ -8,7 +8,7 @@ import { shortcutDesc } from "../shared/terminal.js";
8
8
 
9
9
  import {
10
10
  processes,
11
- killProcess,
11
+ terminateProcess,
12
12
  getGroupStatus,
13
13
  cleanupAll,
14
14
  } from "./process-manager.js";
@@ -150,11 +150,11 @@ export function registerBgShellCommand(pi: ExtensionAPI, state: BgShellSharedSta
150
150
  ctx.ui.notify(`No process with id '${id}'`, "error");
151
151
  return;
152
152
  }
153
- killProcess(id, "SIGTERM");
154
- await new Promise(r => setTimeout(r, 300));
155
- if (bg.alive) {
156
- killProcess(id, "SIGKILL");
157
- await new Promise(r => setTimeout(r, 200));
153
+ // Graceful ladder (SIGTERM → grace → SIGKILL) via killProcessTree.
154
+ terminateProcess(id);
155
+ const deadline = Date.now() + 6_000; // grace (5s) + slack
156
+ while (bg.alive && Date.now() < deadline) {
157
+ await new Promise(r => setTimeout(r, 100));
158
158
  }
159
159
  if (!bg.alive) processes.delete(id);
160
160
  ctx.ui.notify(`Killed process ${id} (${bg.label})`, "info");
@@ -12,6 +12,7 @@ import {
12
12
  processes,
13
13
  startProcess,
14
14
  killProcess,
15
+ terminateProcess,
15
16
  restartProcess,
16
17
  getInfo,
17
18
  getGroupStatus,
@@ -44,7 +45,8 @@ export function registerBgShellTool(pi: ExtensionAPI, state: BgShellSharedState)
44
45
  "group_status (health of a process group), highlights (significant output lines only).",
45
46
 
46
47
  promptGuidelines: [
47
- "Use bg_shell to start long-running processes (servers, watchers, builds) that should not block the agent.",
48
+ "Use bg_shell for processes that STAY ALIVE and you interact with over time: servers, watchers, daemons, REPLs. For a command that runs to completion and exits (terraform apply, migrations, builds, tests, installs), use async_bash or sync bash instead — NOT bg_shell.",
49
+ "'wait_for_ready' is for long-lived processes that signal readiness (open a port / print a pattern). Never use it on a run-to-completion command: that command exits instead of becoming ready, so a clean exit-0 is reported as 'not ready'. If you see that, switch to async_bash.",
48
50
  "After starting a server, use 'wait_for_ready' to efficiently block until it's listening — avoids polling loops entirely.",
49
51
  "Use 'digest' instead of 'output' when you just need status — it returns a structured ~30-token summary instead of ~2000 tokens of raw output.",
50
52
  "Use 'highlights' to see only significant output (errors, URLs, results) — typically 5-15 lines instead of hundreds.",
@@ -615,11 +617,13 @@ export function registerBgShellTool(pi: ExtensionAPI, state: BgShellSharedState)
615
617
  };
616
618
  }
617
619
 
618
- const killed = killProcess(params.id, "SIGTERM");
619
- await new Promise(r => setTimeout(r, 300));
620
- if (bg.alive) {
621
- killProcess(params.id, "SIGKILL");
622
- await new Promise(r => setTimeout(r, 200));
620
+ // Graceful termination: SIGTERM → grace → SIGKILL via the shared
621
+ // killProcessTree ladder (same path bash/async_bash/exec use), so a
622
+ // stateful process gets a clean-shutdown window instead of a bare kill.
623
+ const killed = terminateProcess(params.id);
624
+ const deadline = Date.now() + 6_000; // grace (5s) + slack
625
+ while (bg.alive && Date.now() < deadline) {
626
+ await new Promise(r => setTimeout(r, 100));
623
627
  }
624
628
 
625
629
  const info = getInfo(bg);
@@ -9,7 +9,7 @@ import { ERROR_PATTERNS, WARNING_PATTERNS } from "./types.js";
9
9
  import { formatUptime, formatTimeAgo } from "./utilities.js";
10
10
  import {
11
11
  processes,
12
- killProcess,
12
+ terminateProcess,
13
13
  cleanupAll,
14
14
  restartProcess,
15
15
  } from "./process-manager.js";
@@ -134,16 +134,20 @@ export class BgManagerOverlay {
134
134
  return;
135
135
  }
136
136
 
137
- // x or d = kill selected
137
+ // x or d = kill selected (graceful ladder via killProcessTree).
138
+ // Use a short interactive grace: the user explicitly asked to kill, so give the
139
+ // process a brief clean-shutdown window then force, and re-render AFTER that grace
140
+ // plus slack so the row reflects the SIGKILL escalation (a 300ms re-render against
141
+ // the default 5s grace would show the process still alive).
138
142
  if (data === "x" || data === "d") {
139
143
  const proc = procs[this.selected];
140
144
  if (proc && proc.alive) {
141
- killProcess(proc.id, "SIGTERM");
145
+ const OVERLAY_KILL_GRACE_MS = 500;
146
+ terminateProcess(proc.id, OVERLAY_KILL_GRACE_MS);
142
147
  setTimeout(() => {
143
- if (proc.alive) killProcess(proc.id, "SIGKILL");
144
148
  this.invalidate();
145
149
  this.tui.requestRender();
146
- }, 300);
150
+ }, OVERLAY_KILL_GRACE_MS + 400);
147
151
  }
148
152
  return;
149
153
  }
@@ -7,7 +7,7 @@ import { spawn, spawnSync } from "node:child_process";
7
7
  import { randomUUID } from "node:crypto";
8
8
  import { writeFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
9
9
  import { join } from "node:path";
10
- import { getShellConfig, sanitizeCommand } from "@gsd/pi-coding-agent";
10
+ import { getShellConfig, sanitizeCommand, killProcessTree } from "@gsd/pi-coding-agent";
11
11
  import { rewriteCommandWithRtk } from "../shared/rtk.js";
12
12
  import type {
13
13
  BgProcess,
@@ -259,6 +259,34 @@ export function startProcess(opts: StartOptions): BgProcess {
259
259
 
260
260
  // ── Process Kill ───────────────────────────────────────────────────────────
261
261
 
262
+ /**
263
+ * Gracefully terminate a process and its tree using the shared killProcessTree
264
+ * ladder (SIGTERM → grace window → SIGKILL), the same path bash/async_bash/exec
265
+ * use. This is the "I want it dead, cleanly" intent — use it for the `kill`
266
+ * action, `restart`, and session cleanup. For sending a SPECIFIC signal the
267
+ * agent chose on purpose (SIGINT, SIGHUP, …) use killProcess(), which delivers
268
+ * that exact signal once and does not escalate.
269
+ *
270
+ * `graceMs` overrides the SIGTERM→SIGKILL window (default: killProcessTree's 5s);
271
+ * session cleanup passes a shorter grace so it stays snappy between units.
272
+ *
273
+ * Returns false only when the process is unknown/already dead; the actual
274
+ * SIGKILL escalation fires asynchronously after the grace window, so callers
275
+ * should not assume the process is dead the instant this returns.
276
+ */
277
+ export function terminateProcess(id: string, graceMs?: number): boolean {
278
+ const bg = processes.get(id);
279
+ if (!bg) return false;
280
+ if (!bg.alive) return true;
281
+ if (!bg.proc.pid) {
282
+ // No pid to target a tree; fall back to a direct graceful signal.
283
+ try { bg.proc.kill("SIGTERM"); } catch { /* already gone */ }
284
+ return true;
285
+ }
286
+ killProcessTree(bg.proc.pid, graceMs !== undefined ? { graceMs } : undefined);
287
+ return true;
288
+ }
289
+
262
290
  export function killProcess(id: string, sig: NodeJS.Signals = "SIGTERM"): boolean {
263
291
  const bg = processes.get(id);
264
292
  if (!bg) return false;
@@ -306,13 +334,14 @@ export async function restartProcess(id: string): Promise<BgProcess | null> {
306
334
  const config = old.startConfig;
307
335
  const restartCount = old.restartCount + 1;
308
336
 
309
- // Kill old process
337
+ // Kill old process via the graceful ladder, then wait for it to actually die.
338
+ // killProcessTree escalates SIGTERM → grace → SIGKILL asynchronously, so poll
339
+ // for death rather than assuming a fixed sleep is enough.
310
340
  if (old.alive) {
311
- killProcess(id, "SIGTERM");
312
- await new Promise(r => setTimeout(r, 300));
313
- if (old.alive) {
314
- killProcess(id, "SIGKILL");
315
- await new Promise(r => setTimeout(r, 200));
341
+ terminateProcess(id);
342
+ const deadline = Date.now() + 6_000; // grace (5s) + slack
343
+ while (old.alive && Date.now() < deadline) {
344
+ await new Promise(r => setTimeout(r, 100));
316
345
  }
317
346
  }
318
347
  processes.delete(id);
@@ -374,25 +403,16 @@ export function pruneDeadProcesses(): void {
374
403
  }
375
404
 
376
405
  export function cleanupAll(): void {
406
+ // Deliberately a bare, synchronous SIGKILL — not the graceful ladder. This runs
407
+ // from process 'exit'/signal handlers where timers no longer fire, so a deferred
408
+ // SIGKILL would never be delivered and children would be orphaned when we vanish.
409
+ // Immediate force-kill is the correct teardown semantics here.
377
410
  for (const [id, bg] of processes) {
378
411
  if (bg.alive) killProcess(id, "SIGKILL");
379
412
  }
380
413
  processes.clear();
381
414
  }
382
415
 
383
- /**
384
- * Kill all alive, non-persistent bg processes.
385
- * Called between auto-mode units to prevent orphaned servers from
386
- * keeping ports bound across task boundaries (#1209).
387
- */
388
- export function killSessionProcesses(): void {
389
- for (const [id, bg] of processes) {
390
- if (bg.alive && !bg.persistAcrossSessions) {
391
- killProcess(id, "SIGTERM");
392
- }
393
- }
394
- }
395
-
396
416
  async function waitForProcessExit(bg: BgProcess, timeoutMs: number): Promise<boolean> {
397
417
  if (!bg.alive) return true;
398
418
  await new Promise<void>((resolve) => {
@@ -406,6 +426,13 @@ async function waitForProcessExit(bg: BgProcess, timeoutMs: number): Promise<boo
406
426
  return !bg.alive;
407
427
  }
408
428
 
429
+ /**
430
+ * Terminate the alive, non-persistent processes owned by a session, gracefully.
431
+ * Routes through the shared killProcessTree ladder (SIGTERM → grace → SIGKILL)
432
+ * via terminateProcess, with a short grace (default 300ms) so cleanup between
433
+ * units stays snappy; killProcessTree handles the SIGKILL escalation itself, so
434
+ * there is no separate force-kill pass here.
435
+ */
409
436
  export async function cleanupSessionProcesses(
410
437
  sessionFile: string,
411
438
  options?: { graceMs?: number },
@@ -417,13 +444,11 @@ export async function cleanupSessionProcesses(
417
444
  if (matches.length === 0) return [];
418
445
 
419
446
  for (const bg of matches) {
420
- killProcess(bg.id, "SIGTERM");
447
+ terminateProcess(bg.id, graceMs);
421
448
  }
422
449
  if (graceMs > 0) {
423
- await Promise.all(matches.map((bg) => waitForProcessExit(bg, graceMs)));
424
- }
425
- for (const bg of matches) {
426
- if (bg.alive) killProcess(bg.id, "SIGKILL");
450
+ // Wait past the grace so the SIGKILL escalation has fired and exits are observed.
451
+ await Promise.all(matches.map((bg) => waitForProcessExit(bg, graceMs + 200)));
427
452
  }
428
453
  return matches.map((bg) => bg.id);
429
454
  }
@@ -87,6 +87,18 @@ export async function waitForReady(bg: BgProcess, timeout: number, signal?: Abor
87
87
  return { ready: false, detail: "Cancelled" };
88
88
  }
89
89
  if (!bg.alive) {
90
+ // A clean exit-0 means the command ran to completion — it was a batch
91
+ // command (e.g. terraform apply, a migration, a build), not a long-lived
92
+ // server. That is success, not a readiness failure; say so plainly and
93
+ // point at the right tool so the agent stops using wait_for_ready here.
94
+ if (bg.exitCode === 0) {
95
+ return {
96
+ ready: false,
97
+ detail:
98
+ "Process completed (exit 0) before signaling readiness — it ran to completion rather than staying alive. " +
99
+ "wait_for_ready is for long-lived servers/watchers; for a run-to-completion command use async_bash (or bg_shell 'run').",
100
+ };
101
+ }
90
102
  const stderrLines = bg.output.filter(l => l.stream === "stderr").slice(-5).map(l => l.line);
91
103
  const stderrContext = stderrLines.length > 0 ? `\nstderr:\n${stderrLines.join("\n").slice(0, 500)}` : "";
92
104
  return {
@@ -1,7 +1,7 @@
1
1
  import { describe, test } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import type { BgProcess } from "../types.ts";
4
- import { detectProcessType } from "../process-manager.ts";
4
+ import { detectProcessType, startProcess, terminateProcess, processes } from "../process-manager.ts";
5
5
  import { waitForReady } from "../readiness-detector.ts";
6
6
  import {
7
7
  formatTimeAgo,
@@ -108,6 +108,18 @@ describe("bg-shell waitForReady", () => {
108
108
  assert.match(result.detail, /137/);
109
109
  });
110
110
 
111
+ test("a clean exit-0 reads as completion, not a crash, and points at the right tool", async () => {
112
+ // Regression: a run-to-completion batch command (e.g. terraform apply) put
113
+ // under wait_for_ready exits 0 on success. It must NOT be framed as a crash;
114
+ // it should say it completed and steer toward async_bash.
115
+ const bg = makeBg({ alive: false, exitCode: 0 });
116
+ const result = await waitForReady(bg, 1000);
117
+ assert.equal(result.ready, false);
118
+ assert.match(result.detail, /completed \(exit 0\)/);
119
+ assert.match(result.detail, /async_bash/);
120
+ assert.doesNotMatch(result.detail, /exited before becoming ready/);
121
+ });
122
+
111
123
  test("reports failure when the process entered an error state", async () => {
112
124
  const bg = makeBg({ status: "error", readyPort: 5173 });
113
125
  const result = await waitForReady(bg, 1000);
@@ -204,3 +216,38 @@ describe("bg-shell resolveBgShellPersistenceCwd", () => {
204
216
  assert.equal(result, "/proj/.gsd/worktrees/feature-b");
205
217
  });
206
218
  });
219
+
220
+ describe("bg-shell terminateProcess (graceful kill ladder)", () => {
221
+ test(
222
+ "force-kills a SIGTERM-immune process via the shared killProcessTree ladder",
223
+ { skip: process.platform === "win32" ? "Unix-primary graceful semantics" : false, timeout: 15_000 },
224
+ async (t) => {
225
+ // A process that ignores SIGTERM must still die — terminateProcess routes
226
+ // through killProcessTree, which escalates SIGTERM → grace → SIGKILL. A bare
227
+ // single-signal kill (the old behavior) would have left this running.
228
+ const bg = startProcess({
229
+ command: "trap '' TERM; while true; do sleep 1; done",
230
+ cwd: "/tmp",
231
+ label: "sigterm-immune",
232
+ type: "generic",
233
+ });
234
+ t.after(() => {
235
+ try { if (bg.proc.pid) process.kill(-bg.proc.pid, "SIGKILL"); } catch { /* gone */ }
236
+ processes.delete(bg.id);
237
+ });
238
+
239
+ // Let the trap install.
240
+ await new Promise((r) => setTimeout(r, 300));
241
+ assert.equal(bg.alive, true, "process should be alive before terminate");
242
+
243
+ terminateProcess(bg.id);
244
+
245
+ // SIGKILL fires after the 5s grace; poll up to grace + slack.
246
+ const deadline = Date.now() + 9_000;
247
+ while (bg.alive && Date.now() < deadline) {
248
+ await new Promise((r) => setTimeout(r, 100));
249
+ }
250
+ assert.equal(bg.alive, false, "SIGTERM-immune process must be SIGKILLed via the ladder, not left running");
251
+ },
252
+ );
253
+ });
@@ -1,7 +1,10 @@
1
1
  import { describe, it } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
 
4
- const { resolveGsdBrowserMcpLaunchConfig } = await import("../../shared/gsd-browser-cli.ts");
4
+ const {
5
+ resolveGsdBrowserDaemonStartInvocation,
6
+ resolveGsdBrowserMcpLaunchConfig,
7
+ } = await import("../../shared/gsd-browser-cli.ts");
5
8
 
6
9
  describe("resolveGsdBrowserMcpLaunchConfig identity flags", () => {
7
10
  it("emits a non-empty --identity-key alongside --identity-scope", () => {
@@ -34,4 +37,40 @@ describe("resolveGsdBrowserMcpLaunchConfig identity flags", () => {
34
37
  });
35
38
  assert.equal(args[args.indexOf("--identity-key") + 1], "custom-key");
36
39
  });
40
+
41
+ it("splits GSD_BROWSER_MCP_COMMAND command lines before spawning", () => {
42
+ const commandLine = '"C:\\Program Files\\nodejs\\node.exe" "C:\\Users\\Test User\\AppData\\Roaming\\npm\\node_modules\\@opengsd\\gsd-browser\\bin\\gsd-browser"';
43
+ const { command, args } = resolveGsdBrowserMcpLaunchConfig("C:\\Users\\Test User\\project", {
44
+ GSD_BROWSER_MCP_COMMAND: commandLine,
45
+ });
46
+
47
+ assert.equal(command, "C:\\Program Files\\nodejs\\node.exe");
48
+ assert.equal(args[0], "C:\\Users\\Test User\\AppData\\Roaming\\npm\\node_modules\\@opengsd\\gsd-browser\\bin\\gsd-browser");
49
+ assert.equal(args[1], "mcp");
50
+ });
51
+
52
+ it("uses a path-safe identity-project identifier", () => {
53
+ const { args } = resolveGsdBrowserMcpLaunchConfig("/tmp/example/project", {});
54
+ const projectId = args[args.indexOf("--identity-project") + 1];
55
+ assert.equal(typeof projectId, "string");
56
+ assert.doesNotMatch(projectId, /[\\/]/);
57
+ });
58
+ });
59
+
60
+ describe("resolveGsdBrowserDaemonStartInvocation", () => {
61
+ it("mirrors MCP session and identity flags with daemon start", () => {
62
+ const launch = resolveGsdBrowserMcpLaunchConfig("/tmp/example-project", {});
63
+ const daemon = resolveGsdBrowserDaemonStartInvocation("/tmp/example-project", {});
64
+
65
+ assert.equal(daemon.command, launch.command);
66
+ assert.equal(daemon.cwd, launch.cwd);
67
+ assert.deepEqual(
68
+ daemon.args.slice(daemon.args.indexOf("--session")),
69
+ launch.args.slice(launch.args.indexOf("--session")),
70
+ );
71
+ assert.deepEqual(
72
+ daemon.args.slice(0, daemon.args.indexOf("--session")),
73
+ [...launch.args.slice(0, launch.args.indexOf("mcp")), "daemon", "start"],
74
+ );
75
+ });
37
76
  });