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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (657) hide show
  1. package/README.md +11 -11
  2. package/dist/onboarding.js +11 -0
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/GSD-WORKFLOW.md +20 -25
  5. package/dist/resources/extensions/ask-user-questions.js +1 -1
  6. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +118 -53
  7. package/dist/resources/extensions/cursor-cli/index.js +16 -0
  8. package/dist/resources/extensions/cursor-cli/models.js +63 -0
  9. package/dist/resources/extensions/cursor-cli/package.json +11 -0
  10. package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
  11. package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
  12. package/dist/resources/extensions/gsd/activity-log.js +4 -1
  13. package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
  14. package/dist/resources/extensions/gsd/auto/closeout.js +137 -42
  15. package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
  16. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
  17. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  18. package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
  19. package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
  20. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  21. package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
  22. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  23. package/dist/resources/extensions/gsd/auto-dispatch.js +31 -7
  24. package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
  25. package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
  26. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  27. package/dist/resources/extensions/gsd/auto-start.js +8 -6
  28. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  36. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  37. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  38. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  39. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  40. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  41. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  42. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  43. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  44. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  45. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  46. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  47. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  48. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  49. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  50. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  51. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  52. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2046
  53. package/dist/resources/extensions/gsd/auto.js +31 -5
  54. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
  55. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  56. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +134 -7
  57. package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
  58. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  59. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +194 -28
  60. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
  61. package/dist/resources/extensions/gsd/codebase-generator.js +164 -20
  62. package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
  63. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  64. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
  65. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  66. package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
  67. package/dist/resources/extensions/gsd/db/queries.js +48 -9
  68. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  69. package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
  70. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  71. package/dist/resources/extensions/gsd/doctor-engine-checks.js +292 -18
  72. package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
  73. package/dist/resources/extensions/gsd/doctor.js +93 -3
  74. package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
  75. package/dist/resources/extensions/gsd/flat-phase-migration.js +106 -26
  76. package/dist/resources/extensions/gsd/forensics.js +1 -1
  77. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  78. package/dist/resources/extensions/gsd/git-service.js +59 -18
  79. package/dist/resources/extensions/gsd/gsd-db.js +75 -11
  80. package/dist/resources/extensions/gsd/guidance.js +1 -0
  81. package/dist/resources/extensions/gsd/guided-flow.js +62 -19
  82. package/dist/resources/extensions/gsd/key-manager.js +1 -0
  83. package/dist/resources/extensions/gsd/markdown-renderer.js +40 -11
  84. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  85. package/dist/resources/extensions/gsd/migrate-external.js +24 -7
  86. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  87. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +14 -3
  88. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  89. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  90. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  91. package/dist/resources/extensions/gsd/paths.js +17 -3
  92. package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
  93. package/dist/resources/extensions/gsd/preferences-models.js +6 -4
  94. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  95. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  96. package/dist/resources/extensions/gsd/preferences-validation.js +107 -2
  97. package/dist/resources/extensions/gsd/preferences.js +49 -2
  98. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  99. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  100. package/dist/resources/extensions/gsd/quick.js +12 -3
  101. package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
  102. package/dist/resources/extensions/gsd/repo-identity.js +16 -0
  103. package/dist/resources/extensions/gsd/repository-registry.js +6 -1
  104. package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
  105. package/dist/resources/extensions/gsd/safe-fs.js +23 -2
  106. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  107. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  108. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  109. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  110. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  111. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  112. package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
  113. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  114. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  115. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
  116. package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
  117. package/dist/resources/extensions/gsd/unit-registry.js +37 -2
  118. package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
  119. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  120. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  121. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  122. package/dist/resources/extensions/gsd/workspace.js +26 -8
  123. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  124. package/dist/resources/extensions/gsd/worktree-lifecycle.js +81 -14
  125. package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
  126. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  127. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  128. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  129. package/dist/web/standalone/.next/BUILD_ID +1 -1
  130. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  131. package/dist/web/standalone/.next/build-manifest.json +3 -3
  132. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  133. package/dist/web/standalone/.next/required-server-files.json +3 -3
  134. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  135. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  137. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  145. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  147. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  148. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  149. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  151. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  161. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  173. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  195. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  203. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  205. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  210. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  211. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  213. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  217. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  218. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  219. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  221. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  223. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  225. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  226. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  227. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  228. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  229. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  230. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  231. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  232. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  233. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  234. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  235. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  236. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  237. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  238. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  239. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  240. package/dist/web/standalone/.next/server/app/index.html +1 -1
  241. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  242. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  243. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  244. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  245. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  246. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  247. package/dist/web/standalone/.next/server/app/page.js +2 -2
  248. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  249. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  250. package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
  251. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  252. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  253. package/dist/web/standalone/.next/server/middleware.js +3 -3
  254. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  255. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  256. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  257. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +1 -0
  258. package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
  259. package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
  260. package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
  261. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
  262. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  263. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  264. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  265. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  266. package/dist/web/standalone/server.js +1 -1
  267. package/dist/wizard.d.ts +8 -0
  268. package/dist/wizard.js +8 -1
  269. package/integrations/hermes/CONTEXT.md +47 -0
  270. package/integrations/hermes/README.md +21 -0
  271. package/integrations/hermes/__init__.py +31 -0
  272. package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
  273. package/integrations/hermes/docs/setup.md +4 -0
  274. package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
  275. package/integrations/hermes/open_gsd_hermes/config.py +2 -0
  276. package/integrations/hermes/open_gsd_hermes/gsd_client.py +591 -39
  277. package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
  278. package/integrations/hermes/plugin.yaml +4 -0
  279. package/integrations/hermes/tests/test_config.py +6 -0
  280. package/integrations/hermes/tests/test_directory_loader.py +37 -0
  281. package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
  282. package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
  283. package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
  284. package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
  285. package/integrations/hermes/tests/test_milestone_client.py +447 -0
  286. package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
  287. package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
  288. package/package.json +8 -8
  289. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
  290. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
  291. package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
  292. package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
  293. package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
  294. package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
  295. package/packages/cloud-mcp-gateway/package.json +2 -2
  296. package/packages/contracts/package.json +1 -1
  297. package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
  298. package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
  299. package/packages/daemon/dist/cloud-runtime.js +25 -1
  300. package/packages/daemon/dist/cloud-runtime.js.map +1 -1
  301. package/packages/daemon/dist/cloud-runtime.test.js +51 -0
  302. package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
  303. package/packages/daemon/dist/message-batcher.d.ts +7 -0
  304. package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
  305. package/packages/daemon/dist/message-batcher.js +13 -0
  306. package/packages/daemon/dist/message-batcher.js.map +1 -1
  307. package/packages/daemon/dist/message-batcher.test.js +48 -0
  308. package/packages/daemon/dist/message-batcher.test.js.map +1 -1
  309. package/packages/daemon/dist/session-manager.js +1 -1
  310. package/packages/daemon/dist/session-manager.js.map +1 -1
  311. package/packages/daemon/package.json +5 -5
  312. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
  313. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  314. package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
  315. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  316. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  317. package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
  318. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
  319. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
  320. package/packages/gsd-agent-core/package.json +5 -5
  321. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  322. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  323. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  324. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
  325. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
  326. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
  327. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
  328. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  329. package/packages/gsd-agent-modes/package.json +7 -7
  330. package/packages/mcp-server/dist/server.d.ts +18 -5
  331. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  332. package/packages/mcp-server/dist/server.js +46 -5
  333. package/packages/mcp-server/dist/server.js.map +1 -1
  334. package/packages/mcp-server/dist/session-manager.js +1 -1
  335. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  336. package/packages/mcp-server/package.json +4 -4
  337. package/packages/native/package.json +1 -1
  338. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  339. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  340. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  341. package/packages/pi-agent-core/package.json +1 -1
  342. package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
  343. package/packages/pi-ai/dist/env-api-keys.js +3 -0
  344. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  345. package/packages/pi-ai/dist/index.d.ts +1 -0
  346. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  347. package/packages/pi-ai/dist/index.js +1 -0
  348. package/packages/pi-ai/dist/index.js.map +1 -1
  349. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  350. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  351. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  352. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  353. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  354. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  355. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  356. package/packages/pi-ai/dist/types.d.ts +10 -22
  357. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  358. package/packages/pi-ai/dist/types.js.map +1 -1
  359. package/packages/pi-ai/package.json +1 -1
  360. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  361. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  362. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  363. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  364. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  365. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  366. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  367. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  368. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  370. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
  372. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
  373. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
  374. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  375. package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
  376. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  377. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  378. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  379. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  380. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  381. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  382. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  383. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  384. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  385. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  386. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  387. package/packages/pi-coding-agent/dist/index.js +1 -0
  388. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  389. package/packages/pi-coding-agent/package.json +7 -7
  390. package/packages/pi-tui/package.json +2 -2
  391. package/packages/rpc-client/dist/rpc-client.js +1 -1
  392. package/packages/rpc-client/dist/rpc-client.js.map +1 -1
  393. package/packages/rpc-client/package.json +2 -2
  394. package/pkg/dist/core/export-html/template.js +53 -4
  395. package/pkg/package.json +1 -1
  396. package/scripts/link-workspace-packages.cjs +14 -4
  397. package/src/resources/GSD-WORKFLOW.md +20 -25
  398. package/src/resources/extensions/ask-user-questions.ts +1 -1
  399. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
  400. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
  401. package/src/resources/extensions/cursor-cli/index.ts +18 -0
  402. package/src/resources/extensions/cursor-cli/models.ts +65 -0
  403. package/src/resources/extensions/cursor-cli/package.json +11 -0
  404. package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
  405. package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
  406. package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
  407. package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
  408. package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
  409. package/src/resources/extensions/gsd/activity-log.ts +4 -1
  410. package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
  411. package/src/resources/extensions/gsd/auto/closeout.ts +163 -58
  412. package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
  413. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  414. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
  415. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  416. package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
  417. package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
  418. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  419. package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
  420. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  421. package/src/resources/extensions/gsd/auto-dispatch.ts +35 -7
  422. package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
  423. package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
  424. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  425. package/src/resources/extensions/gsd/auto-start.ts +9 -6
  426. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
  427. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  428. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  429. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  430. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  431. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  432. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  433. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  434. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  435. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  436. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  437. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  438. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  439. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  440. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  441. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  442. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  443. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  444. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  445. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  446. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  447. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  448. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  449. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  450. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2390
  451. package/src/resources/extensions/gsd/auto.ts +32 -19
  452. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
  453. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  454. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +148 -7
  455. package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
  456. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  457. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +229 -28
  458. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
  459. package/src/resources/extensions/gsd/codebase-generator.ts +186 -19
  460. package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
  461. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  462. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
  463. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  464. package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
  465. package/src/resources/extensions/gsd/db/queries.ts +49 -9
  466. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  467. package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
  468. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  469. package/src/resources/extensions/gsd/doctor-engine-checks.ts +372 -22
  470. package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
  471. package/src/resources/extensions/gsd/doctor-types.ts +5 -1
  472. package/src/resources/extensions/gsd/doctor.ts +95 -3
  473. package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
  474. package/src/resources/extensions/gsd/flat-phase-migration.ts +132 -25
  475. package/src/resources/extensions/gsd/forensics.ts +1 -1
  476. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  477. package/src/resources/extensions/gsd/git-service.ts +67 -17
  478. package/src/resources/extensions/gsd/gsd-db.ts +105 -11
  479. package/src/resources/extensions/gsd/guidance.ts +2 -0
  480. package/src/resources/extensions/gsd/guided-flow.ts +71 -20
  481. package/src/resources/extensions/gsd/key-manager.ts +1 -0
  482. package/src/resources/extensions/gsd/markdown-renderer.ts +40 -10
  483. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  484. package/src/resources/extensions/gsd/migrate-external.ts +23 -6
  485. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  486. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +19 -4
  487. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  488. package/src/resources/extensions/gsd/model-router.ts +4 -5
  489. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  490. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  491. package/src/resources/extensions/gsd/paths.ts +21 -3
  492. package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
  493. package/src/resources/extensions/gsd/preferences-models.ts +6 -3
  494. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  495. package/src/resources/extensions/gsd/preferences-types.ts +30 -0
  496. package/src/resources/extensions/gsd/preferences-validation.ts +97 -2
  497. package/src/resources/extensions/gsd/preferences.ts +58 -4
  498. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  499. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  500. package/src/resources/extensions/gsd/quick.ts +11 -3
  501. package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
  502. package/src/resources/extensions/gsd/repo-identity.ts +17 -0
  503. package/src/resources/extensions/gsd/repository-registry.ts +7 -1
  504. package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
  505. package/src/resources/extensions/gsd/safe-fs.ts +22 -2
  506. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  507. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  508. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  509. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  510. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  511. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  512. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  513. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
  514. package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
  515. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
  516. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
  517. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -0
  518. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
  519. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
  520. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
  521. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
  522. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  523. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
  524. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +10 -1
  525. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  526. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  527. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  528. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  529. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  530. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  531. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  532. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  533. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  534. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  535. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  536. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  537. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  538. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  539. package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
  540. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
  541. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +387 -0
  542. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
  543. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  544. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  545. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
  546. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +571 -5
  547. package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
  548. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  549. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +109 -2
  550. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
  551. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
  552. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
  553. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  554. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
  555. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  556. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  557. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  558. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  559. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  560. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  561. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  562. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  563. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  564. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  565. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  566. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  567. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  568. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  569. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
  570. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  571. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
  572. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  573. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  574. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
  575. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  576. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +592 -23
  577. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +25 -1
  578. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  579. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  580. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  581. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
  582. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
  583. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
  584. package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
  585. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  586. package/src/resources/extensions/gsd/tests/preferences.test.ts +169 -0
  587. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +147 -0
  588. package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
  589. package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
  590. package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
  591. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
  592. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  593. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
  594. package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
  595. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
  596. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  597. package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
  598. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  599. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  600. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
  601. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  602. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  603. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
  604. package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
  605. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  606. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  607. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  608. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  609. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  610. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  611. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
  612. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +259 -0
  613. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  614. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
  615. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +81 -0
  616. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
  617. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
  618. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
  619. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
  620. package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
  621. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  622. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  623. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  624. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  625. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  626. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
  627. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
  628. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  629. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  630. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  631. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +36 -3
  632. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  633. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  634. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
  635. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  636. package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
  637. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  638. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  639. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
  640. package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
  641. package/src/resources/extensions/gsd/unit-registry.ts +48 -2
  642. package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
  643. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  644. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  645. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  646. package/src/resources/extensions/gsd/workspace.ts +42 -8
  647. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  648. package/src/resources/extensions/gsd/worktree-lifecycle.ts +154 -26
  649. package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
  650. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  651. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  652. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  653. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
  654. package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
  655. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
  656. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
  657. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
@@ -4,11 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import os
7
+ import shutil
7
8
  import subprocess
8
9
  import threading
9
10
  import time
10
11
  from dataclasses import dataclass, field
11
- from typing import Any
12
+ from typing import Any, Callable
12
13
 
13
14
  from packaging import version
14
15
 
@@ -36,14 +37,38 @@ class GsdMcpClient:
36
37
  def __init__(self, config: GsdConfig) -> None:
37
38
  self._config = config
38
39
  self._proc: subprocess.Popen[bytes] | None = None
40
+ self._proc_cwd: str | None = None
41
+ self._stdout_buffer = b""
39
42
  self._lock = threading.Lock()
40
43
  self._request_id = 0
41
44
  self._cache: dict[str, _CacheEntry] = {}
42
45
  self._version_checked = False
46
+ # Milestone subprocess state (issue #1162). The plugin owns the
47
+ # `gsd headless --supervised new-milestone` process directly, so the
48
+ # MCP session manager cannot see or reach it. This client is the sole
49
+ # owner of its lifecycle (cancel, blocker replies, terminal events).
50
+ self._milestone_proc: subprocess.Popen[bytes] | None = None
51
+ self._milestone_session_id: str | None = None
52
+ self._milestone_pending_blocker_id: str | None = None
53
+ self._milestone_pending_blocker_method: str | None = None
54
+ self._milestone_command_block_failure: str | None = None
55
+ self._milestone_notifications: Any = None # NotificationService | None
56
+ self._milestone_project_dir: str | None = None
57
+ self._milestone_on_terminal: Callable[[str], None] | None = None
58
+ self._milestone_notified_terminal: bool = False
59
+ self._milestone_thread: threading.Thread | None = None
60
+ self._milestone_stderr_thread: threading.Thread | None = None
61
+ self._milestone_stderr_lock = threading.Lock()
62
+ self._milestone_stderr_tail = bytearray()
43
63
 
44
64
  def _env(self) -> dict[str, str]:
45
65
  env = os.environ.copy()
46
- env["GSD_CLI_PATH"] = self._config.cli_path
66
+ # Resolve cli_path to an absolute path so the MCP server's
67
+ # resolveCLIPath() does not resolve a bare command name (e.g. "gsd")
68
+ # against the project directory, producing a non-existent
69
+ # project-relative path that hangs gsd_execute until timeout.
70
+ resolved = shutil.which(self._config.cli_path) or self._config.cli_path
71
+ env["GSD_CLI_PATH"] = resolved
47
72
  return env
48
73
 
49
74
  def ensure_version(self) -> None:
@@ -72,43 +97,113 @@ class GsdMcpClient:
72
97
  )
73
98
  self._version_checked = True
74
99
 
75
- def _ensure_process(self) -> subprocess.Popen[bytes]:
100
+ def _ensure_process(self, project_dir: str | None = None) -> subprocess.Popen[bytes]:
101
+ cwd = (
102
+ os.path.abspath(project_dir)
103
+ if project_dir is not None
104
+ else self._proc_cwd
105
+ )
76
106
  if self._proc is not None and self._proc.poll() is None:
77
- return self._proc
107
+ if cwd is None or self._proc_cwd == cwd:
108
+ return self._proc
109
+ self._terminate_process()
110
+ self._stdout_buffer = b""
111
+ self._proc_cwd = cwd
78
112
  self._proc = subprocess.Popen(
79
113
  [self._config.mcp_server_path],
80
114
  stdin=subprocess.PIPE,
81
115
  stdout=subprocess.PIPE,
82
116
  stderr=subprocess.PIPE,
83
117
  env=self._env(),
118
+ cwd=cwd,
84
119
  )
85
- self._initialize()
120
+ try:
121
+ self._initialize()
122
+ except Exception:
123
+ self._terminate_process()
124
+ raise
86
125
  return self._proc
87
126
 
88
- def _send(self, payload: dict[str, Any]) -> None:
89
- proc = self._ensure_process()
127
+ def _terminate_process(self) -> None:
128
+ proc = self._proc
129
+ self._proc = None
130
+ self._proc_cwd = None
131
+ self._stdout_buffer = b""
132
+ if proc is None or proc.poll() is not None:
133
+ return
134
+ proc.terminate()
135
+ try:
136
+ proc.wait(timeout=5)
137
+ except subprocess.TimeoutExpired:
138
+ proc.kill()
139
+
140
+ def _send(self, payload: dict[str, Any], *, project_dir: str | None = None) -> None:
141
+ proc = self._ensure_process(project_dir)
90
142
  assert proc.stdin is not None
91
- body = json.dumps(payload).encode("utf-8")
92
- header = f"Content-Length: {len(body)}\r\n\r\n".encode("ascii")
93
- proc.stdin.write(header + body)
143
+ # gsd-mcp-server uses the @modelcontextprotocol/sdk stdio transport,
144
+ # which is newline-delimited JSON (NDJSON), not the Content-Length
145
+ # framing from the LSP-style MCP spec. Emit one JSON object per line.
146
+ data = json.dumps(payload, ensure_ascii=False).encode("utf-8") + b"\n"
147
+ proc.stdin.write(data)
94
148
  proc.stdin.flush()
95
149
 
96
- def _read_message(self) -> dict[str, Any]:
97
- proc = self._ensure_process()
150
+ def _read_stdout_chunk(self, deadline: float) -> None:
151
+ proc = self._proc
152
+ if proc is None or proc.poll() is not None:
153
+ self._terminate_process()
154
+ raise McpProtocolError("MCP server closed stdout")
98
155
  assert proc.stdout is not None
99
- headers: dict[str, str] = {}
156
+ remaining = deadline - time.monotonic()
157
+ if remaining <= 0:
158
+ self._raise_read_timeout()
159
+
160
+ chunks: list[bytes] = []
161
+ errors: list[BaseException] = []
162
+
163
+ def read_chunk() -> None:
164
+ try:
165
+ chunks.append(os.read(proc.stdout.fileno(), 65536))
166
+ except BaseException as e:
167
+ errors.append(e)
168
+
169
+ reader = threading.Thread(target=read_chunk, daemon=True)
170
+ reader.start()
171
+ reader.join(remaining)
172
+ if reader.is_alive():
173
+ self._raise_read_timeout()
174
+ if errors:
175
+ self._terminate_process()
176
+ raise McpProtocolError(f"MCP server stdout read failed: {errors[0]}")
177
+ chunk = chunks[0] if chunks else b""
178
+ if not chunk:
179
+ self._terminate_process()
180
+ raise McpProtocolError("MCP server closed stdout")
181
+ self._stdout_buffer += chunk
182
+
183
+ def _raise_read_timeout(self) -> None:
184
+ self._terminate_process()
185
+ raise McpProtocolError(
186
+ f"MCP server response timed out after "
187
+ f"{self._config.mcp_read_timeout_seconds:g}s"
188
+ )
189
+
190
+ def _read_line(self, deadline: float) -> bytes:
191
+ while True:
192
+ idx = self._stdout_buffer.find(b"\n")
193
+ if idx >= 0:
194
+ raw = self._stdout_buffer[:idx]
195
+ self._stdout_buffer = self._stdout_buffer[idx + 1 :]
196
+ return raw.rstrip(b"\r")
197
+ self._read_stdout_chunk(deadline)
198
+
199
+ def _read_message(self) -> dict[str, Any]:
200
+ deadline = time.monotonic() + self._config.mcp_read_timeout_seconds
201
+ # gsd-mcp-server emits newline-delimited JSON (NDJSON); each line is a
202
+ # complete JSON-RPC message. Skip blank keep-alive lines.
100
203
  while True:
101
- line = proc.stdout.readline()
102
- if not line:
103
- raise McpProtocolError("MCP server closed stdout")
104
- decoded = line.decode("utf-8").strip()
105
- if decoded == "":
106
- break
107
- key, _, val = decoded.partition(":")
108
- headers[key.strip().lower()] = val.strip()
109
- length = int(headers.get("content-length", "0"))
110
- body = proc.stdout.read(length)
111
- return json.loads(body.decode("utf-8"))
204
+ line = self._read_line(deadline)
205
+ if line.strip():
206
+ return json.loads(line.decode("utf-8"))
112
207
 
113
208
  def _initialize(self) -> None:
114
209
  self._request_id += 1
@@ -128,7 +223,12 @@ class GsdMcpClient:
128
223
  self._send({"jsonrpc": "2.0", "method": "notifications/initialized"})
129
224
 
130
225
  def _call_tool(
131
- self, name: str, arguments: dict[str, Any], *, check_version: bool = True
226
+ self,
227
+ name: str,
228
+ arguments: dict[str, Any],
229
+ *,
230
+ check_version: bool = True,
231
+ project_dir: str | None = None,
132
232
  ) -> dict[str, Any]:
133
233
  with self._lock:
134
234
  if check_version:
@@ -141,7 +241,8 @@ class GsdMcpClient:
141
241
  "id": req_id,
142
242
  "method": "tools/call",
143
243
  "params": {"name": name, "arguments": arguments},
144
- }
244
+ },
245
+ project_dir=project_dir,
145
246
  )
146
247
  while True:
147
248
  msg = self._read_message()
@@ -219,6 +320,7 @@ class GsdMcpClient:
219
320
  "gsd_progress",
220
321
  {"projectDir": project_dir},
221
322
  check_version=False,
323
+ project_dir=project_dir,
222
324
  )
223
325
  return ProgressSnapshot.from_mcp(data)
224
326
 
@@ -231,21 +333,36 @@ class GsdMcpClient:
231
333
 
232
334
  def execute(self, project_dir: str, *, command: str = "/gsd auto") -> dict[str, Any]:
233
335
  self.invalidate_cache(project_dir)
234
- return self._call_tool(
235
- "gsd_execute",
236
- {"projectDir": project_dir, "command": command},
237
- )
336
+ args = {"projectDir": project_dir, "command": command}
337
+ try:
338
+ return self._call_tool("gsd_execute", args, project_dir=project_dir)
339
+ except GsdVersionError:
340
+ return self._call_tool(
341
+ "gsd_execute",
342
+ args,
343
+ check_version=False,
344
+ project_dir=project_dir,
345
+ )
238
346
 
239
- def cancel(self, *, session_id: str | None = None, project_dir: str | None = None) -> dict[str, Any]:
347
+ def cancel(
348
+ self,
349
+ *,
350
+ session_id: str | None = None,
351
+ project_dir: str | None = None,
352
+ ) -> dict[str, Any]:
240
353
  args: dict[str, Any] = {}
241
354
  if session_id:
242
355
  args["sessionId"] = session_id
243
356
  if project_dir:
244
357
  args["projectDir"] = project_dir
245
- return self._call_tool("gsd_cancel", args)
358
+ return self._call_tool("gsd_cancel", args, project_dir=project_dir)
246
359
 
247
360
  def cancel_by_project(self, project_dir: str) -> dict[str, Any]:
248
- return self._call_tool("gsd_cancel_by_project", {"projectDir": project_dir})
361
+ return self._call_tool(
362
+ "gsd_cancel_by_project",
363
+ {"projectDir": project_dir},
364
+ project_dir=project_dir,
365
+ )
249
366
 
250
367
  def resolve_blocker(self, session_id: str, response: str) -> dict[str, Any]:
251
368
  return self._call_tool(
@@ -257,13 +374,448 @@ class GsdMcpClient:
257
374
  return self._call_tool(
258
375
  "gsd_memory_query",
259
376
  {"projectDir": project_dir, "query": query},
377
+ project_dir=project_dir,
260
378
  )
261
379
 
262
- def close(self) -> None:
263
- if self._proc and self._proc.poll() is None:
264
- self._proc.terminate()
380
+ def project_query(self, project_dir: str, query: str) -> dict[str, Any]:
381
+ return self._call_tool(
382
+ "gsd_query",
383
+ {"projectDir": project_dir, "query": query},
384
+ )
385
+
386
+ # ------------------------------------------------------------------
387
+ # Milestone creation (issue #1162)
388
+ #
389
+ # The plugin spawns `gsd headless --supervised new-milestone` directly
390
+ # and owns the subprocess. The MCP session manager cannot see it (it
391
+ # only knows sessions it started via gsd_execute), and there is no
392
+ # auto.lock pid backstop during the planning phase. So this client is
393
+ # the sole owner of the subprocess's lifecycle: cancel (SIGTERM),
394
+ # blocker replies (stdin), and transition/terminal notifications
395
+ # (stdout stream). See integrations/hermes/docs/issue-1162-grilling.md.
396
+ # ------------------------------------------------------------------
397
+
398
+ # Stop-notice vocabulary mirrored from src/resources/extensions/gsd/stop-notice.ts.
399
+ # Keep both sides in lockstep; the canonical prefixes the headless event
400
+ # loop emits in notify messages. Terminal detection is handled by process
401
+ # exit (the stream reader emits notify_terminal on stdout EOF), so only the
402
+ # blocked-notice classifier is needed here.
403
+ _PAUSED_NOTICE_PREFIXES = ("auto-mode paused", "step-mode paused")
404
+ _FIRE_AND_FORGET_UI_METHODS = frozenset(
405
+ {"notify", "setStatus", "setWidget", "setTitle", "set_editor_text"}
406
+ )
407
+ _EXIT_BLOCKED = 10
408
+ _STDERR_TAIL_LIMIT = 64 * 1024
409
+ # Supervised headless defaults to 30s; chat /gsd reply needs much longer.
410
+ _SUPERVISED_RESPONSE_TIMEOUT_MS = 3_600_000
411
+
412
+ def _is_blocked_notice(self, message: str) -> bool:
413
+ """Mirror of stop-notice.ts isBlockedNoticeMessage (lowercased input)."""
414
+ if "blocked:" in message:
415
+ return True
416
+ if any(message.startswith(p) for p in self._PAUSED_NOTICE_PREFIXES):
417
+ if "idempotent advance: unit already active" not in message:
418
+ return True
419
+ return (
420
+ "resolve manually and re-run /gsd auto" in message
421
+ or "resolve conflicts manually and run /gsd auto to resume" in message
422
+ or "resolve and run /gsd auto to resume" in message
423
+ )
424
+
425
+ def _is_milestone_blocker_event(self, event: dict[str, Any]) -> bool:
426
+ """True for supervised interactive UI requests (select/input/confirm/editor)."""
427
+ method = str(event.get("method") or "")
428
+ if method in self._FIRE_AND_FORGET_UI_METHODS:
429
+ return False
430
+ return True
431
+
432
+ def _get_command_block_content(self, event: dict[str, Any]) -> str | None:
433
+ """Extract gsd-command-block message content from stream events."""
434
+ etype = event.get("type")
435
+ if etype not in ("message_start", "message_end"):
436
+ return None
437
+ message = event.get("message")
438
+ if not isinstance(message, dict):
439
+ return None
440
+ if message.get("customType") != "gsd-command-block":
441
+ return None
442
+ return str(message.get("content") or "")
443
+
444
+ def _is_blocking_command_block(self, event: dict[str, Any]) -> bool:
445
+ """Mirror headless-events isBlockingCommandBlock."""
446
+ content = self._get_command_block_content(event)
447
+ if not content:
448
+ return False
449
+ lowered = content.lower()
450
+ return (
451
+ (
452
+ "cannot start new workflow work" in lowered
453
+ and "complete but not merged" in lowered
454
+ )
455
+ or "cannot run because the active milestone is blocked by validation"
456
+ in lowered
457
+ )
458
+
459
+ def _parse_confirm_response(self, response: str) -> bool:
460
+ normalized = response.strip().lower()
461
+ if normalized in ("no", "n", "false", "0", "cancel", "decline"):
462
+ return False
463
+ return True
464
+
465
+ def create_milestone(
466
+ self,
467
+ project_dir: str,
468
+ *,
469
+ context_text: str | None = None,
470
+ context_file: str | None = None,
471
+ notifications: Any = None,
472
+ on_terminal: Callable[[str], None] | None = None,
473
+ ) -> str:
474
+ """Spawn `gsd headless --supervised new-milestone` and return a session id.
475
+
476
+ The subprocess is started detached; a background thread reads its
477
+ stream-json stdout and drives the supplied NotificationService on
478
+ blocker/terminal events. Returns a local session id immediately
479
+ (headless does not emit init_result on stdout).
480
+
481
+ Raises ValueError if neither (or both) of context_text/context_file
482
+ is supplied.
483
+ """
484
+ if bool(context_text) == bool(context_file):
485
+ raise ValueError(
486
+ "Provide exactly one milestone spec: context_text or context_file"
487
+ )
488
+ self.ensure_version()
489
+ if self._milestone_proc is not None and self._milestone_proc.poll() is None:
490
+ raise RuntimeError("Milestone creation already in progress.")
491
+
492
+ args: list[str] = [
493
+ self._config.cli_path,
494
+ "headless",
495
+ "--supervised",
496
+ "--response-timeout",
497
+ str(self._SUPERVISED_RESPONSE_TIMEOUT_MS),
498
+ "--output-format",
499
+ "stream-json",
500
+ ]
501
+ if context_text:
502
+ args += ["--context-text", context_text]
503
+ else:
504
+ assert context_file is not None
505
+ args += ["--context", context_file]
506
+ args.append("new-milestone")
507
+
508
+ proc = subprocess.Popen( # noqa: S603 - args list, not shell
509
+ args,
510
+ stdin=subprocess.PIPE,
511
+ stdout=subprocess.PIPE,
512
+ stderr=subprocess.PIPE,
513
+ cwd=project_dir,
514
+ env=self._env(),
515
+ )
516
+ self._milestone_proc = proc
517
+ self._milestone_session_id = None
518
+ self._milestone_pending_blocker_id = None
519
+ self._milestone_pending_blocker_method = None
520
+ self._milestone_command_block_failure = None
521
+ self._milestone_notifications = notifications
522
+ self._milestone_project_dir = project_dir
523
+ self._milestone_on_terminal = on_terminal
524
+ self._milestone_notified_terminal = False
525
+ self._clear_milestone_stderr()
526
+ self._milestone_stderr_thread = threading.Thread(
527
+ target=self._drain_milestone_stderr,
528
+ args=(proc,),
529
+ daemon=True,
530
+ )
531
+ self._milestone_stderr_thread.start()
532
+
533
+ # Headless stream-json does not emit init_result on stdout (init is an
534
+ # internal RPC handshake). Use a local session id for ack/status only;
535
+ # milestone lifecycle is keyed on the held subprocess, not MCP.
536
+ session_id = f"milestone-{proc.pid}"
537
+ self._milestone_session_id = session_id
538
+
539
+ self._milestone_thread = threading.Thread(
540
+ target=self._milestone_stream_loop,
541
+ args=(proc,),
542
+ daemon=True,
543
+ )
544
+ self._milestone_thread.start()
545
+ return session_id
546
+
547
+ def _milestone_stream_loop(self, proc: subprocess.Popen[bytes]) -> None:
548
+ """Background reader: capture blocker/terminal events and notify.
549
+
550
+ Best-effort. On stream loss we surface FAILED via notify_terminal so
551
+ the user can `/gsd cancel` to clean up (no watchdog; the cancel
552
+ escape hatch is the safety net).
553
+ """
554
+ assert proc.stdout is not None
555
+ try:
556
+ while True:
557
+ line = proc.stdout.readline()
558
+ if not line:
559
+ break
560
+ try:
561
+ event = json.loads(line.decode("utf-8"))
562
+ except (json.JSONDecodeError, UnicodeDecodeError):
563
+ continue
564
+ if self._milestone_proc is not proc:
565
+ return
566
+ self._handle_milestone_event(event)
567
+ except Exception:
568
+ pass
569
+ if self._milestone_proc is not proc:
570
+ return
571
+ if self._milestone_notified_terminal:
572
+ if proc.poll() is not None:
573
+ self._clear_milestone_state()
574
+ return
575
+ exit_code = proc.poll()
576
+ if exit_code is None:
265
577
  try:
266
- self._proc.wait(timeout=5)
578
+ exit_code = proc.wait(timeout=5)
267
579
  except subprocess.TimeoutExpired:
268
- self._proc.kill()
269
- self._proc = None
580
+ exit_code = None
581
+ if exit_code == self._EXIT_BLOCKED:
582
+ if not self._milestone_notified_terminal:
583
+ if self._milestone_notifications is not None:
584
+ self._milestone_notifications.notify_terminal(
585
+ "failed", "Planning blocked"
586
+ )
587
+ if self._milestone_on_terminal is not None:
588
+ self._milestone_on_terminal("failed")
589
+ self._milestone_notified_terminal = True
590
+ self._clear_milestone_state()
591
+ return
592
+ if exit_code == 0:
593
+ if self._milestone_command_block_failure:
594
+ status = "failed"
595
+ error = self._milestone_command_block_failure
596
+ else:
597
+ status = "complete"
598
+ error = None
599
+ elif exit_code is not None:
600
+ status = "failed"
601
+ self._join_milestone_stderr_thread()
602
+ error = self._milestone_stderr_text(proc) or f"exit code {exit_code}"
603
+ else:
604
+ status = "failed"
605
+ error = "stream lost — run /gsd cancel"
606
+ if self._milestone_notifications is not None:
607
+ if status == "complete":
608
+ summary = self._build_milestone_completion_message(
609
+ self._milestone_project_dir or ""
610
+ )
611
+ self._milestone_notifications.notify_milestone_complete(summary)
612
+ else:
613
+ self._milestone_notifications.notify_terminal(status, error)
614
+ if self._milestone_on_terminal is not None:
615
+ self._milestone_on_terminal(status)
616
+ self._milestone_notified_terminal = True
617
+ if exit_code is not None:
618
+ self._clear_milestone_state()
619
+ else:
620
+ self._milestone_pending_blocker_id = None
621
+ self._milestone_pending_blocker_method = None
622
+
623
+ def _drain_milestone_stderr(self, proc: subprocess.Popen[bytes]) -> None:
624
+ """Drain stderr so a full pipe cannot stall the child process."""
625
+ if proc.stderr is None:
626
+ return
627
+ try:
628
+ while True:
629
+ chunk = proc.stderr.read(4096)
630
+ if not chunk:
631
+ break
632
+ self._append_milestone_stderr(chunk)
633
+ except Exception:
634
+ pass
635
+
636
+ def _append_milestone_stderr(self, chunk: bytes) -> None:
637
+ with self._milestone_stderr_lock:
638
+ self._milestone_stderr_tail.extend(chunk)
639
+ overflow = len(self._milestone_stderr_tail) - self._STDERR_TAIL_LIMIT
640
+ if overflow > 0:
641
+ del self._milestone_stderr_tail[:overflow]
642
+
643
+ def _clear_milestone_stderr(self) -> None:
644
+ with self._milestone_stderr_lock:
645
+ self._milestone_stderr_tail.clear()
646
+
647
+ def _join_milestone_stderr_thread(self) -> None:
648
+ thread = self._milestone_stderr_thread
649
+ if thread and thread is not threading.current_thread():
650
+ thread.join(timeout=1)
651
+
652
+ def _milestone_stderr_text(self, proc: subprocess.Popen[bytes]) -> str:
653
+ with self._milestone_stderr_lock:
654
+ err_bytes = bytes(self._milestone_stderr_tail)
655
+ if not err_bytes and proc.stderr is not None:
656
+ try:
657
+ err_bytes = proc.stderr.read()
658
+ except Exception:
659
+ err_bytes = b""
660
+ return err_bytes.decode("utf-8", errors="replace").strip()
661
+
662
+ def _handle_milestone_event(self, event: dict[str, Any]) -> None:
663
+ etype = event.get("type")
664
+ if etype == "init_result":
665
+ sid = event.get("sessionId")
666
+ if sid:
667
+ self._milestone_session_id = str(sid)
668
+ return
669
+ if self._is_blocking_command_block(event):
670
+ failure = self._get_command_block_content(event) or "Planning blocked"
671
+ self._milestone_command_block_failure = failure
672
+ if not self._milestone_notified_terminal:
673
+ if self._milestone_notifications is not None:
674
+ self._milestone_notifications.notify_terminal("failed", failure)
675
+ if self._milestone_on_terminal is not None:
676
+ self._milestone_on_terminal("failed")
677
+ self._milestone_notified_terminal = True
678
+ return
679
+ if etype != "extension_ui_request":
680
+ return
681
+ if not self._is_milestone_blocker_event(event):
682
+ return
683
+ event_id = str(event.get("id") or "")
684
+ self._milestone_pending_blocker_id = event_id or None
685
+ self._milestone_pending_blocker_method = str(event.get("method") or "") or None
686
+ if self._milestone_notifications is not None:
687
+ self._milestone_notifications.notify_blocker(
688
+ SessionStatus(
689
+ status="blocked",
690
+ pending_blocker=event,
691
+ session_id=self._milestone_session_id,
692
+ )
693
+ )
694
+
695
+ def cancel_milestone(self) -> None:
696
+ """SIGTERM the held milestone subprocess and clear state."""
697
+ proc = self._milestone_proc
698
+ if proc is None:
699
+ return
700
+ self._milestone_notified_terminal = True
701
+ try:
702
+ proc.terminate()
703
+ except Exception:
704
+ pass
705
+ try:
706
+ proc.wait(timeout=5)
707
+ except subprocess.TimeoutExpired:
708
+ try:
709
+ proc.kill()
710
+ except Exception:
711
+ pass
712
+ if self._milestone_proc is proc:
713
+ self._clear_milestone_state()
714
+
715
+ def _release_milestone_proc(self) -> None:
716
+ self._milestone_proc = None
717
+ self._milestone_notifications = None
718
+ self._milestone_on_terminal = None
719
+ self._milestone_stderr_thread = None
720
+ self._clear_milestone_stderr()
721
+
722
+ def _build_milestone_completion_message(self, project_dir: str) -> str:
723
+ fallback = "✅ Milestone created. Run `/gsd status` for details."
724
+ if not project_dir:
725
+ return fallback
726
+ try:
727
+ query_data = self.project_query(project_dir, "milestones")
728
+ except Exception:
729
+ return fallback
730
+ milestones = query_data.get("milestones")
731
+ if not milestones:
732
+ return fallback
733
+ try:
734
+ self.invalidate_cache(project_dir)
735
+ snap = self.progress(project_dir)
736
+ except Exception:
737
+ snap = None
738
+ milestone_id: str | None = None
739
+ if snap and snap.active_milestone:
740
+ milestone_id = snap.active_milestone.get("id")
741
+ if not milestone_id:
742
+ last = milestones[-1]
743
+ if isinstance(last, dict):
744
+ milestone_id = last.get("id")
745
+ if not milestone_id:
746
+ return fallback
747
+ slice_total = snap.slices.get("total", 0) if snap else 0
748
+ task_total = snap.tasks.get("total", 0) if snap else 0
749
+ msg = f"✅ Milestone {milestone_id} ready"
750
+ counts: list[str] = []
751
+ if slice_total:
752
+ counts.append(f"{slice_total} slice{'s' if slice_total != 1 else ''}")
753
+ if task_total:
754
+ counts.append(f"{task_total} task{'s' if task_total != 1 else ''}")
755
+ if counts:
756
+ msg += f" — {', '.join(counts)}"
757
+ return f"{msg}. Run `/gsd auto` to start."
758
+
759
+ def _clear_milestone_state(self) -> None:
760
+ self._release_milestone_proc()
761
+ self._milestone_session_id = None
762
+ self._milestone_project_dir = None
763
+ self._milestone_pending_blocker_id = None
764
+ self._milestone_pending_blocker_method = None
765
+ self._milestone_command_block_failure = None
766
+
767
+ def respond_to_milestone_blocker(self, response: str) -> None:
768
+ """Write an extension_ui_response to the milestone subprocess stdin.
769
+
770
+ Uses the supervised-mode JSONL protocol (see startSupervisedStdinReader
771
+ in src/headless-ui.ts): {"type":"extension_ui_response","id":...,"value":...}
772
+ or {"type":"extension_ui_response","id":...,"confirmed":...} for confirm prompts.
773
+ """
774
+ proc = self._milestone_proc
775
+ blocker_id = self._milestone_pending_blocker_id
776
+ if self._milestone_notified_terminal:
777
+ raise RuntimeError(
778
+ "Milestone session is no longer accepting replies. "
779
+ "Run `/gsd cancel` to clean up."
780
+ )
781
+ if proc is None or not blocker_id:
782
+ raise RuntimeError(
783
+ "No pending blocker for the active milestone session."
784
+ )
785
+ assert proc.stdin is not None
786
+ response_payload: dict[str, Any] = {
787
+ "type": "extension_ui_response",
788
+ "id": blocker_id,
789
+ }
790
+ if self._milestone_pending_blocker_method == "confirm":
791
+ response_payload["confirmed"] = self._parse_confirm_response(response)
792
+ else:
793
+ response_payload["value"] = response
794
+ payload = json.dumps(response_payload).encode("utf-8")
795
+ proc.stdin.write(payload + b"\n")
796
+ proc.stdin.flush()
797
+ self._milestone_pending_blocker_id = None
798
+ self._milestone_pending_blocker_method = None
799
+
800
+ def milestone_active(self) -> bool:
801
+ proc = self._milestone_proc
802
+ if proc is None:
803
+ return False
804
+ thread = self._milestone_thread
805
+ if thread is not None and thread.is_alive():
806
+ return True
807
+ if proc.poll() is None:
808
+ return True
809
+ self._clear_milestone_state()
810
+ return False
811
+
812
+ def milestone_session_id(self) -> str | None:
813
+ return self._milestone_session_id
814
+
815
+ def milestone_pending_blocker_id(self) -> str | None:
816
+ return self._milestone_pending_blocker_id
817
+
818
+ def close(self) -> None:
819
+ self._terminate_process()
820
+ if self._milestone_proc is not None:
821
+ self.cancel_milestone()