@opengsd/gsd-pi 1.4.0 → 1.5.0

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 (329) 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/claude-code-cli/stream-adapter.js +118 -53
  6. package/dist/resources/extensions/cursor-cli/index.js +16 -0
  7. package/dist/resources/extensions/cursor-cli/models.js +63 -0
  8. package/dist/resources/extensions/cursor-cli/package.json +11 -0
  9. package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
  10. package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
  11. package/dist/resources/extensions/gsd/activity-log.js +4 -1
  12. package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
  13. package/dist/resources/extensions/gsd/auto/closeout.js +45 -42
  14. package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
  15. package/dist/resources/extensions/gsd/auto/orchestrator.js +1 -1
  16. package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
  17. package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
  18. package/dist/resources/extensions/gsd/auto-dispatch.js +15 -5
  19. package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
  20. package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
  21. package/dist/resources/extensions/gsd/auto-start.js +4 -3
  22. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
  23. package/dist/resources/extensions/gsd/auto-worktree.js +38 -11
  24. package/dist/resources/extensions/gsd/auto.js +23 -3
  25. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
  26. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +114 -5
  27. package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
  28. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +10 -2
  29. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
  30. package/dist/resources/extensions/gsd/codebase-generator.js +157 -19
  31. package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
  33. package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
  34. package/dist/resources/extensions/gsd/db/queries.js +39 -0
  35. package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  37. package/dist/resources/extensions/gsd/doctor-engine-checks.js +206 -14
  38. package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
  39. package/dist/resources/extensions/gsd/doctor.js +93 -3
  40. package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
  41. package/dist/resources/extensions/gsd/flat-phase-migration.js +74 -23
  42. package/dist/resources/extensions/gsd/git-service.js +14 -18
  43. package/dist/resources/extensions/gsd/gsd-db.js +52 -0
  44. package/dist/resources/extensions/gsd/guidance.js +1 -0
  45. package/dist/resources/extensions/gsd/guided-flow.js +40 -2
  46. package/dist/resources/extensions/gsd/key-manager.js +1 -0
  47. package/dist/resources/extensions/gsd/markdown-renderer.js +25 -8
  48. package/dist/resources/extensions/gsd/migrate-external.js +24 -7
  49. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +3 -3
  50. package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
  51. package/dist/resources/extensions/gsd/preferences-models.js +6 -4
  52. package/dist/resources/extensions/gsd/preferences-validation.js +25 -2
  53. package/dist/resources/extensions/gsd/preferences.js +29 -2
  54. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  55. package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
  56. package/dist/resources/extensions/gsd/repo-identity.js +16 -0
  57. package/dist/resources/extensions/gsd/repository-registry.js +6 -1
  58. package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
  59. package/dist/resources/extensions/gsd/safe-fs.js +23 -2
  60. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  61. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  62. package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
  63. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
  64. package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
  65. package/dist/resources/extensions/gsd/unit-registry.js +27 -2
  66. package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
  67. package/dist/resources/extensions/gsd/workspace.js +26 -8
  68. package/dist/resources/extensions/gsd/worktree-lifecycle.js +67 -5
  69. package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
  70. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  73. package/dist/web/standalone/.next/build-manifest.json +2 -2
  74. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/index.html +1 -1
  92. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  99. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  100. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  102. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  103. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  104. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  105. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  106. package/dist/wizard.d.ts +8 -0
  107. package/dist/wizard.js +8 -1
  108. package/integrations/hermes/CONTEXT.md +47 -0
  109. package/integrations/hermes/README.md +21 -0
  110. package/integrations/hermes/__init__.py +31 -0
  111. package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
  112. package/integrations/hermes/docs/setup.md +4 -0
  113. package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
  114. package/integrations/hermes/open_gsd_hermes/config.py +2 -0
  115. package/integrations/hermes/open_gsd_hermes/gsd_client.py +594 -31
  116. package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
  117. package/integrations/hermes/plugin.yaml +4 -0
  118. package/integrations/hermes/tests/test_config.py +6 -0
  119. package/integrations/hermes/tests/test_directory_loader.py +37 -0
  120. package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
  121. package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
  122. package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
  123. package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
  124. package/integrations/hermes/tests/test_milestone_client.py +447 -0
  125. package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
  126. package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
  127. package/package.json +8 -8
  128. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
  129. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
  130. package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
  131. package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
  132. package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
  133. package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
  134. package/packages/cloud-mcp-gateway/package.json +2 -2
  135. package/packages/contracts/package.json +1 -1
  136. package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
  137. package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
  138. package/packages/daemon/dist/cloud-runtime.js +25 -1
  139. package/packages/daemon/dist/cloud-runtime.js.map +1 -1
  140. package/packages/daemon/dist/cloud-runtime.test.js +51 -0
  141. package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
  142. package/packages/daemon/dist/message-batcher.d.ts +7 -0
  143. package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
  144. package/packages/daemon/dist/message-batcher.js +13 -0
  145. package/packages/daemon/dist/message-batcher.js.map +1 -1
  146. package/packages/daemon/dist/message-batcher.test.js +48 -0
  147. package/packages/daemon/dist/message-batcher.test.js.map +1 -1
  148. package/packages/daemon/dist/session-manager.js +1 -1
  149. package/packages/daemon/dist/session-manager.js.map +1 -1
  150. package/packages/daemon/package.json +5 -5
  151. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
  152. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  153. package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
  154. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  155. package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
  156. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
  157. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
  158. package/packages/gsd-agent-core/package.json +5 -5
  159. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
  160. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
  161. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
  162. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
  163. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  164. package/packages/gsd-agent-modes/package.json +7 -7
  165. package/packages/mcp-server/dist/server.d.ts +18 -5
  166. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  167. package/packages/mcp-server/dist/server.js +46 -5
  168. package/packages/mcp-server/dist/server.js.map +1 -1
  169. package/packages/mcp-server/dist/session-manager.js +1 -1
  170. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  171. package/packages/mcp-server/package.json +4 -4
  172. package/packages/native/package.json +1 -1
  173. package/packages/pi-agent-core/package.json +1 -1
  174. package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
  175. package/packages/pi-ai/dist/env-api-keys.js +3 -0
  176. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  177. package/packages/pi-ai/dist/types.d.ts +2 -2
  178. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  179. package/packages/pi-ai/dist/types.js.map +1 -1
  180. package/packages/pi-ai/package.json +1 -1
  181. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  183. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
  185. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
  189. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/package.json +2 -2
  192. package/packages/rpc-client/dist/rpc-client.js +1 -1
  193. package/packages/rpc-client/dist/rpc-client.js.map +1 -1
  194. package/packages/rpc-client/package.json +2 -2
  195. package/pkg/package.json +1 -1
  196. package/scripts/link-workspace-packages.cjs +14 -4
  197. package/src/resources/GSD-WORKFLOW.md +20 -25
  198. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
  199. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
  200. package/src/resources/extensions/cursor-cli/index.ts +18 -0
  201. package/src/resources/extensions/cursor-cli/models.ts +65 -0
  202. package/src/resources/extensions/cursor-cli/package.json +11 -0
  203. package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
  204. package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
  205. package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
  206. package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
  207. package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
  208. package/src/resources/extensions/gsd/activity-log.ts +4 -1
  209. package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
  210. package/src/resources/extensions/gsd/auto/closeout.ts +54 -56
  211. package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
  212. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  213. package/src/resources/extensions/gsd/auto/orchestrator.ts +1 -1
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
  215. package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
  216. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -5
  217. package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
  218. package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
  219. package/src/resources/extensions/gsd/auto-start.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
  221. package/src/resources/extensions/gsd/auto-worktree.ts +50 -11
  222. package/src/resources/extensions/gsd/auto.ts +22 -3
  223. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
  224. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +128 -5
  225. package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
  226. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +12 -2
  227. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
  228. package/src/resources/extensions/gsd/codebase-generator.ts +179 -18
  229. package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
  230. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
  231. package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
  232. package/src/resources/extensions/gsd/db/queries.ts +40 -0
  233. package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
  234. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  235. package/src/resources/extensions/gsd/doctor-engine-checks.ts +269 -18
  236. package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
  237. package/src/resources/extensions/gsd/doctor-types.ts +5 -1
  238. package/src/resources/extensions/gsd/doctor.ts +95 -3
  239. package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
  240. package/src/resources/extensions/gsd/flat-phase-migration.ts +98 -22
  241. package/src/resources/extensions/gsd/git-service.ts +14 -17
  242. package/src/resources/extensions/gsd/gsd-db.ts +72 -0
  243. package/src/resources/extensions/gsd/guidance.ts +2 -0
  244. package/src/resources/extensions/gsd/guided-flow.ts +45 -2
  245. package/src/resources/extensions/gsd/key-manager.ts +1 -0
  246. package/src/resources/extensions/gsd/markdown-renderer.ts +25 -7
  247. package/src/resources/extensions/gsd/migrate-external.ts +23 -6
  248. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +4 -4
  249. package/src/resources/extensions/gsd/model-router.ts +4 -5
  250. package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
  251. package/src/resources/extensions/gsd/preferences-models.ts +6 -3
  252. package/src/resources/extensions/gsd/preferences-validation.ts +28 -2
  253. package/src/resources/extensions/gsd/preferences.ts +38 -4
  254. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  255. package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
  256. package/src/resources/extensions/gsd/repo-identity.ts +17 -0
  257. package/src/resources/extensions/gsd/repository-registry.ts +7 -1
  258. package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
  259. package/src/resources/extensions/gsd/safe-fs.ts +22 -2
  260. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  261. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  262. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
  263. package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
  264. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
  265. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
  266. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  267. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
  268. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
  269. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
  270. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
  271. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
  272. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +8 -0
  273. package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
  274. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
  275. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +365 -0
  276. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
  277. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
  278. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +397 -5
  279. package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
  280. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +38 -1
  281. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
  282. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
  283. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
  284. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  285. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
  286. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
  287. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
  288. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +152 -22
  289. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +1 -1
  290. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
  291. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
  292. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
  293. package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
  294. package/src/resources/extensions/gsd/tests/preferences.test.ts +150 -0
  295. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +142 -0
  296. package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
  297. package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
  298. package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
  299. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
  300. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
  301. package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
  302. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
  303. package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
  304. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
  305. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
  306. package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
  307. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
  308. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +63 -0
  309. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
  310. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +56 -0
  311. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
  312. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
  313. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
  314. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
  315. package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
  316. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
  317. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
  318. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +31 -1
  319. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
  320. package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
  321. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
  322. package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
  323. package/src/resources/extensions/gsd/unit-registry.ts +34 -2
  324. package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
  325. package/src/resources/extensions/gsd/workspace.ts +42 -8
  326. package/src/resources/extensions/gsd/worktree-lifecycle.ts +135 -11
  327. package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
  328. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_buildManifest.js +0 -0
  329. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_ssgManifest.js +0 -0
@@ -0,0 +1,31 @@
1
+ """open-gsd-hermes — Hermes Agent plugin directory-loader bridge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ import sys
7
+ from pathlib import Path
8
+ from types import ModuleType
9
+
10
+
11
+ def _load_package() -> ModuleType:
12
+ package_dir = Path(__file__).resolve().parent / "open_gsd_hermes"
13
+ init_file = package_dir / "__init__.py"
14
+ spec = importlib.util.spec_from_file_location(
15
+ "open_gsd_hermes",
16
+ init_file,
17
+ submodule_search_locations=[str(package_dir)],
18
+ )
19
+ if spec is None or spec.loader is None:
20
+ raise ImportError(f"Cannot load open_gsd_hermes from {init_file}")
21
+
22
+ module = importlib.util.module_from_spec(spec)
23
+ sys.modules["open_gsd_hermes"] = module
24
+ spec.loader.exec_module(module)
25
+ return module
26
+
27
+
28
+ _package = _load_package()
29
+ register = _package.register
30
+
31
+ __all__ = ["register"]
@@ -0,0 +1,197 @@
1
+ # Issue #1162 — Grilling summary: `/gsd new-milestone` via Hermes
2
+
3
+ Source: `grill-with-docs` + `grilling` + `domain-modeling` skills on issue #1162.
4
+ This is the shared understanding the interview reached — the design tree walked
5
+ branch-by-branch with a decision at each node. Implementation should conform to
6
+ this; deviations should re-open the corresponding question, not silently diverge.
7
+
8
+ Glossary for every term below lives in [`../CONTEXT.md`](../CONTEXT.md).
9
+
10
+ ---
11
+
12
+ ## The one-line design
13
+
14
+ Add a `/gsd new-milestone <spec>` slash command that spawns
15
+ `gsd headless new-milestone` as a **plugin-owned subprocess**, fires-and-forgets
16
+ an ack, drives lifecycle (blocker/terminal) notifications from the subprocess's
17
+ stream-json stdout, and hands execution off to the **existing, untouched**
18
+ `/gsd auto` path. No `--auto` chaining, no new MCP tool, no TS-side changes.
19
+
20
+ ---
21
+
22
+ ## Decision tree (Q1–Q9)
23
+
24
+ ### Q1 — Mechanism & transport → **(A) Slash command → CLI subprocess**
25
+ - New `_cmd_new_milestone` in `GsdCommandRouter`. Shells out to
26
+ `gsd headless new-milestone --context-text <spec>`.
27
+ - **Not** a Hermes skill (B), **not** a new MCP `gsd_create_milestone` tool (C).
28
+ - Rationale: the plugin already has a proven direct-CLI pattern (`progress()`
29
+ calls `gsd read progress --json`); `new-milestone` is even more naturally a
30
+ CLI call (it bootstraps `.gsd/`, has a 10-min timeout, manages its own RPC
31
+ child). An MCP tool would re-implement what the CLI already does, and the
32
+ upstream lease-layer co-run issue (CHANGELOG:509, open) makes advertising
33
+ `gsd_create_milestone` over MCP actively unsafe to every MCP client.
34
+ - A later Hermes skill (B) can wrap the same client method with zero rework.
35
+
36
+ ### Q2 — Co-run safety gate → **(A) Local supervisor state only**
37
+ - Reject if `SupervisorContext.state in (RUNNING, BLOCKED)` before spawning.
38
+ - **Not** a server-side cross-binding check (B). Documented limitation: a
39
+ session started by *another* plugin instance / a headless run outside this
40
+ plugin is invisible to the local guard; the CLI's own lease check is the
41
+ backstop.
42
+ - Rationale: the plugin can only authoritatively own its own state; a
43
+ server-side "is anything running?" check is itself racy and adds a new query
44
+ path to design and test.
45
+
46
+ ### Q3 — Run model → **(A) Fire-and-track via the existing async-push model**
47
+ - Return "🚀 Milestone creation started (session X)" immediately; push
48
+ blocker/terminal notifications from a background stream-reader.
49
+ - **Not** a synchronous blocking call (rejected after discovering the plugin is
50
+ fundamentally async-push — `_cmd_auto` already works this way; the target
51
+ chat platforms don't hold request/response open for minutes).
52
+ - *(Note: "reuse `SupervisorFsm` unchanged" was initially proposed under this
53
+ label but does NOT work — see A1 below.)*
54
+
55
+ ### Q3 follow-up — How to drive the fire-and-track → **(A1) Client-owned subprocess, direct stream→notification**
56
+ - `GsdMcpClient` spawns `gsd headless new-milestone --output-format stream-json`
57
+ on a background thread, reads stdout line-by-line, captures
58
+ `init_result.sessionId`, and on terminal/blocker events calls
59
+ `NotificationService` directly.
60
+ - `SupervisorContext` is **shared state** (written by the stream-reader, read by
61
+ `/gsd status`, `/gsd cancel`, the co-run gate); the `SupervisorFsm` poll loop
62
+ is **not** reused for the milestone path (its `_tick` calls `gsd_status`,
63
+ which can't see our subprocess).
64
+ - Rationale: the stream *is* the lifecycle signal; polling would be a lossy
65
+ proxy for events we already get deterministically. Reuses
66
+ `NotificationService` and `SupervisorContext` unchanged.
67
+
68
+ ### Q4 — Cancellation → **(A) Local PID ownership + direct SIGTERM**
69
+ - `GsdMcpClient` holds the `subprocess.Popen` handle; `cancel_milestone()` calls
70
+ `proc.terminate()` directly. `_cmd_cancel` routes to it when a milestone proc
71
+ is active.
72
+ - **Not** a new `.gsd/milestone.lock` pid file for the MCP backstop (B) — that
73
+ crosses the Python/TS boundary and inverts ownership. **Not** "no cancel during
74
+ planning" (C) — a stuck planning run needs an escape hatch.
75
+ - Discovered: MCP `cancelSessionByDir`'s `auto.lock` pid backstop does NOT find
76
+ our planning subprocess (no `auto.lock` exists during planning — it's an
77
+ auto-loop concept). So local ownership is required, not optional.
78
+ - Plus explicit supervisor stop on cancel (immediate confirmation, don't wait
79
+ for next poll tick).
80
+
81
+ ### Q5 — Scope: `--auto` (chained execution)? → **No. Planning-only.**
82
+ - The plugin creates the milestone and hands off; execution is `/gsd auto`'s job.
83
+ - `/gsd auto` already has a robust path (MCP `gsd_execute` → self-healing poll
84
+ supervisor). Chaining `--auto` would duplicate that with a stateful
85
+ stream-reader over hours (fragile) instead of the proven poll loop.
86
+ - **Boundary clarification:** this plugin (`gsd-pi/integrations/hermes/`) is the
87
+ chat gateway. One-shot `new-milestone --auto` is gsd-core's terminal surface,
88
+ not the gateway's.
89
+ - `/gsd new-milestone --auto` is **rejected at the command level** with a message
90
+ pointing to the two-step flow.
91
+
92
+ ### Q6 — Input shape → **Bare positional, with `--file` escape**
93
+ - `/gsd new-milestone <spec text>` → `--context-text` (matches `/gsd reply`).
94
+ - `/gsd new-milestone --file <path>` → `--context <file>` (explicit opt-in).
95
+ - **Not** auto-detect path-vs-text (heuristic misroutes on collision — rule #5).
96
+ - **Not** `--description "..."` flag form (issue's example used it, but the bare
97
+ positional matches the sibling command users already know).
98
+
99
+ ### Q7 — Output → **(A) Bounded summary via `gsd_query` at completion, with (B) as graceful fallback**
100
+ - Immediate ack: "🚀 Milestone creation started for `<project>`."
101
+ - Completion push: bounded summary — milestone id, slice count, task count,
102
+ one-line next step ("Run `/gsd auto` to start."). Built from
103
+ `gsd_query {projectDir, query: "milestones"}` after exit-0.
104
+ - Fallback: if the query comes back empty/missing after exit-0, degrade to
105
+ "✅ Milestone created. Run `/gsd status` for details."
106
+ - **Trust exit-0; no defensive re-querying.** If exit-0 lies about flush state,
107
+ that's a gsd-core bug to fix at the source, not a leak for the plugin to patch
108
+ with a racy retry loop.
109
+ - Error/blocked cases route through existing `notify_terminal`/`notify_blocker`
110
+ unchanged.
111
+
112
+ ### Q8 — Failure modes
113
+ | # | Mode | Disposition |
114
+ |---|---|---|
115
+ | 1 | Active auto session (co-run) | Reject before spawn; fail closed |
116
+ | 2 | No project bound | Reuse `BindingError` path (zero new code) |
117
+ | 3 | Empty spec | Usage message (matches `_cmd_reply`/`_cmd_bind`) |
118
+ | 4 | Spec too long for `--context-text` | Surface `E2BIG`/`OSError`; point to `--file` |
119
+ | 5 | Planning errors (non-zero exit) | `notify_terminal(status, error)` via existing path |
120
+ | 6 | Planning hits a blocker | `notify_blocker`; reply via **client-owned stdin** (see below) |
121
+ | 7 | `--file` missing/unreadable | Validate before spawn |
122
+ | 8 | Stream-reader thread/pipe dies | Best-effort: set FAILED, `notify_terminal("failed", "stream lost — run /gsd cancel")`. **No watchdog.** Cancel is the escape hatch. |
123
+ | 9 | Plugin restart mid-planning | **Documented limitation.** Orphaned subprocess survives; invisible after restart (same as existing supervisor's restart-blindness). |
124
+ | 10 | User passes `--auto` | Reject at command level (see Q5) |
125
+
126
+ **Blocker replies (#6) — client-owned, not MCP-routed.** `gsd_resolve_blocker`
127
+ calls `sessionManager.resolveBlocker` which looks up the MCP server's *own*
128
+ sessions map — our subprocess isn't there, so it throws "Session not found".
129
+ Because we own the subprocess's stdin (A1), `/gsd reply` for a planning blocker
130
+ writes the response to *our* subprocess's stdin via a new
131
+ `respond_to_milestone_blocker(response)` method. `_cmd_reply` gets one branch:
132
+ if a milestone subprocess with a pending blocker is active, route locally; else
133
+ fall through to the existing MCP `resolve_blocker` path.
134
+
135
+ ### Q9 — Cross-effects on existing commands
136
+ - **`_cmd_cancel`** — MUST change: add milestone-subprocess branch routing to
137
+ local `cancel_milestone()` (SIGTERM) when one is active. *Required.*
138
+ - **`_cmd_auto`** — MUST change: add symmetric co-run guard (reject if
139
+ `SupervisorContext` shows an active milestone run, point to `/gsd cancel`).
140
+ *Required* — without it, `/gsd auto` during milestone planning silently
141
+ re-exposes the unsafe co-run (the MCP server's "Session already active"
142
+ rejection does NOT fire for our planning subprocess, since it's not registered
143
+ and writes no `auto.lock`). Rule #12 (fail loud) over rule #3 (surgical) here.
144
+ - **`/gsd status`** — leave alone. Reads disk honestly; the Q3-accepted planning
145
+ progress gap means it may show stale/partial state early on. The completion
146
+ notification is the real "done" signal.
147
+ - **`/gsd reply`** — additive branch only (Q8 #6); core MCP path untouched.
148
+ - **`SupervisorFsm`** — untouched (A1 uses a separate stream-reader driver).
149
+ - **TS MCP server** — untouched (no new tools).
150
+
151
+ ---
152
+
153
+ ## Implementation surface (additive unless noted)
154
+
155
+ **`gsd_client.py`** — new methods + state:
156
+ - `self._milestone_proc: subprocess.Popen | None`
157
+ - `create_milestone(project_dir, *, context_text=None, context_file=None) -> str` —
158
+ spawns `gsd headless new-milestone --output-format stream-json`, starts the
159
+ stream-reader thread, returns `sessionId` (captured from `init_result`). Throws
160
+ on co-run guard / missing spec.
161
+ - `_milestone_stream_loop(proc)` — reads stdout JSONL; on `init_result` captures
162
+ `sessionId`; on blocker event → `notify_blocker` + sets
163
+ `SupervisorContext.pending_blocker_id`; on terminal/exit → `notify_terminal` +
164
+ sets `SupervisorContext.state`.
165
+ - `cancel_milestone()` — `proc.terminate()` + cleanup.
166
+ - `respond_to_milestone_blocker(response)` — writes UI-response to proc stdin.
167
+ - `milestone_active()` predicate for the co-run gate.
168
+
169
+ **`commands.py`** — new handler + two modifications:
170
+ - `_cmd_new_milestone(rest)` — the new command (guard, parse, spawn, ack).
171
+ - `_cmd_cancel` (modify) — milestone-subprocess branch.
172
+ - `_cmd_auto` (modify) — symmetric co-run guard.
173
+ - `_cmd_reply` (modify) — milestone-blocker branch (additive).
174
+ - `_cmd_help` (modify) — list `new-milestone`.
175
+
176
+ **`__init__.py`** — wire the new client methods / supervisor notifications if
177
+ the constructor surface changes (likely no change — `GsdMcpClient` already has
178
+ `config`, and `NotificationService` is already injected).
179
+
180
+ **Tests** (new, matching existing `tests/test_*.py` patterns):
181
+ - `test_new_milestone_command.py` — guard (co-run both ways), input parsing
182
+ (bare text, `--file`, empty, `--auto` rejected), spawn + ack.
183
+ - extend `test_gsd_client_cache.py` or new `test_milestone_client.py` —
184
+ stream-reader captures sessionId from `init_result`; blocker event →
185
+ `notify_blocker`; terminal → `notify_terminal`; cancel SIGTERMs the proc;
186
+ blocker reply writes stdin.
187
+ - extend cancel/reply tests for the new branches.
188
+
189
+ ---
190
+
191
+ ## Explicitly OUT of scope (v1)
192
+
193
+ - `--auto` chaining (execution stays with `/gsd auto`).
194
+ - Cross-instance / server-side co-run coordination (Q2 limitation).
195
+ - Plugin-restart recovery of an orphaned planning subprocess (Q8 #9).
196
+ - A Hermes skill (issue's Option B) — additive follow-on, wraps the same client.
197
+ - Any TS-side / MCP-server change.
@@ -79,6 +79,7 @@ gsd:
79
79
  default_project: ~/code/myapp # optional fallback
80
80
  poll_interval_seconds: 12
81
81
  cache_ttl_seconds: 45
82
+ mcp_read_timeout_seconds: 60 # per-response MCP read timeout
82
83
  notification_level: normal # quiet | normal | verbose
83
84
  bindings:
84
85
  slack:
@@ -89,6 +90,8 @@ gsd:
89
90
 
90
91
  **Binding tiers (first match wins):** cron explicit → slash argument → `/gsd bind` session binding → channel map → `default_project` → cwd heuristic.
91
92
 
93
+ **MCP read timeout:** `gsd.mcp_read_timeout_seconds` defaults to `60` seconds and bounds each response read from the `gsd-mcp-server` sidecar. Increase it for very slow projects, machines, or network-backed filesystems where Hermes reports MCP response timeouts but the same GSD command eventually completes locally; lower it only when you prefer faster failure detection. When the timeout fires, the plugin terminates the stuck sidecar and the next project-scoped call starts a fresh server in the bound project directory.
94
+
92
95
  **Credentials:** With `credential_source: gsd`, ensure GSD’s normal provider keys are configured (`~/.gsd/` or env). Hermes passthrough (`credential_source: hermes`) is 6b+.
93
96
 
94
97
  ### Verify plugin loaded
@@ -224,6 +227,7 @@ Verify on **one** platform (Slack or Telegram):
224
227
  | `GsdVersionError` on start | `gsd` outside semver range | Set `gsd_version_min` in `gsd.yaml` or upgrade gsd |
225
228
  | MCP spawn fails | Wrong `mcp_server_path` | `which gsd-mcp-server`; set absolute path |
226
229
  | `/gsd status` hangs | MCP sidecar stuck | Check `GSD_CLI_PATH`; restart gateway; kill orphaned `gsd-mcp-server` |
230
+ | MCP response times out after 60s | Slow project or filesystem, or wedged sidecar | Confirm the command locally; tune `mcp_read_timeout_seconds` if it completes after 60s |
227
231
  | No push notifications | Invalid session key / target | Key must match `agent:main:{platform}:{chat_type}:{chat_id}`; check Hermes gateway logs |
228
232
  | Notifications in CLI but not Slack | `DeliveryTarget.from_session_key` returned None | Run checklist in **gateway** mode, not CLI-only |
229
233
  | Cancel ignored | Stale session id | `/gsd cancel` uses `gsd_cancel_by_project` fallback when no session id |
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  import shlex
6
7
  from typing import Any, Callable
7
8
 
@@ -58,6 +59,7 @@ class GsdCommandRouter:
58
59
  "auto": self._cmd_auto,
59
60
  "cancel": self._cmd_cancel,
60
61
  "reply": self._cmd_reply,
62
+ "new-milestone": self._cmd_new_milestone,
61
63
  "help": self._cmd_help,
62
64
  }
63
65
  handler = handlers.get(sub, self._cmd_help)
@@ -69,6 +71,7 @@ class GsdCommandRouter:
69
71
  "- `/gsd bind <path>` — bind this chat to a project\n"
70
72
  "- `/gsd status` — show progress snapshot\n"
71
73
  "- `/gsd auto` — start auto mode (MCP)\n"
74
+ "- `/gsd new-milestone <spec>` or `--file <path>` — create a milestone (then `/gsd auto`)\n"
72
75
  "- `/gsd cancel` — cancel running session\n"
73
76
  "- `/gsd reply <text>` — resolve blocker"
74
77
  )
@@ -112,6 +115,15 @@ class GsdCommandRouter:
112
115
  return f"GSD status unavailable: {e}"
113
116
 
114
117
  async def _cmd_auto(self, _rest: list[str]) -> str:
118
+ # Symmetric co-run guard (issue #1162): refuse if a milestone run is
119
+ # in progress. The MCP server's "Session already active" rejection
120
+ # does NOT fire for a milestone subprocess (it's not registered and
121
+ # writes no auto.lock during planning), so this local gate is the
122
+ # only thing preventing an unsafe co-run.
123
+ if self._client.milestone_active():
124
+ return (
125
+ "Milestone creation is in progress. Run `/gsd cancel` first."
126
+ )
115
127
  try:
116
128
  project_dir = resolve_project_dir(
117
129
  self._config, self._binding_ctx()
@@ -140,6 +152,18 @@ class GsdCommandRouter:
140
152
 
141
153
  async def _cmd_cancel(self, _rest: list[str]) -> str:
142
154
  ctx = self._get_supervisor_ctx()
155
+ # Milestone subprocess is locally owned (issue #1162); route to the
156
+ # local SIGTERM path instead of MCP cancel, which cannot see it.
157
+ if self._client.milestone_active():
158
+ self._client.cancel_milestone()
159
+ ctx.session_id = None
160
+ ctx.state = SupervisorState.CANCELLED
161
+ ctx.notified_terminal = True
162
+ self._set_supervisor_ctx(ctx)
163
+ self._supervisor.stop()
164
+ if self._notifications is not None:
165
+ self._notifications.notify_terminal("cancelled")
166
+ return "Cancel requested."
143
167
  try:
144
168
  try:
145
169
  if ctx.session_id and ctx.project_dir:
@@ -175,6 +199,17 @@ class GsdCommandRouter:
175
199
  if not rest:
176
200
  return "Usage: `/gsd reply <your answer>`"
177
201
  text = " ".join(rest)
202
+ # Milestone planning blocker (issue #1162): the subprocess is locally
203
+ # owned, so its blocker replies must go to its stdin, not MCP
204
+ # gsd_resolve_blocker (which can't see our session).
205
+ if self._client.milestone_active():
206
+ if not self._client.milestone_pending_blocker_id():
207
+ return "No pending blocker for the active milestone session."
208
+ try:
209
+ self._client.respond_to_milestone_blocker(text)
210
+ except Exception as e:
211
+ return f"Could not send blocker response: {e}"
212
+ return "Blocker response sent."
178
213
  ctx = self._get_supervisor_ctx()
179
214
  if not ctx.session_id:
180
215
  return "No active GSD session. Run `/gsd auto` first."
@@ -184,3 +219,109 @@ class GsdCommandRouter:
184
219
  except Exception as e:
185
220
  return f"Could not send blocker response: {e}"
186
221
  return "Blocker response sent."
222
+
223
+ async def _cmd_new_milestone(self, rest: list[str]) -> str:
224
+ """Create a milestone via `gsd headless --supervised new-milestone`.
225
+
226
+ Planning-only: the plugin spawns the subprocess, owns its lifecycle,
227
+ and notifies on blocker/terminal events. Execution stays with
228
+ `/gsd auto`. See integrations/hermes/docs/issue-1162-grilling.md.
229
+ """
230
+ if not rest:
231
+ return (
232
+ "Usage: `/gsd new-milestone <spec>` "
233
+ "(or `--file <path>`)"
234
+ )
235
+ # --auto is intentionally rejected (Q5): execution is /gsd auto's job.
236
+ if rest[0] == "--auto":
237
+ return (
238
+ "Execution isn't chained from `/gsd new-milestone`. "
239
+ "Create the milestone, then run `/gsd auto`."
240
+ )
241
+ # Co-run guard (Q2): refuse if a milestone or auto session is active.
242
+ if self._client.milestone_active():
243
+ return (
244
+ "Milestone creation is in progress. Run `/gsd cancel` first."
245
+ )
246
+ ctx = self._get_supervisor_ctx()
247
+ if ctx.state in (SupervisorState.RUNNING, SupervisorState.BLOCKED):
248
+ return (
249
+ "GSD auto is running. Run `/gsd cancel` first."
250
+ )
251
+ # Resolve the bound project.
252
+ try:
253
+ project_dir = resolve_project_dir(
254
+ self._config, self._binding_ctx()
255
+ )
256
+ except BindingError as e:
257
+ return str(e)
258
+
259
+ # Input shape (Q6): bare positional → context_text, `--file <path>`
260
+ # → context_file.
261
+ context_text: str | None = None
262
+ context_file: str | None = None
263
+ if rest[0] == "--file":
264
+ if len(rest) < 2:
265
+ return "Usage: `/gsd new-milestone --file <path>`"
266
+ context_file = rest[1]
267
+ resolved_file = (
268
+ context_file
269
+ if os.path.isabs(context_file)
270
+ else os.path.join(project_dir, context_file)
271
+ )
272
+ if not os.path.isfile(resolved_file):
273
+ return f"Context file not found: `{context_file}`"
274
+ if not os.access(resolved_file, os.R_OK):
275
+ return f"Context file is not readable: `{context_file}`"
276
+ context_file = resolved_file
277
+ else:
278
+ context_text = " ".join(rest)
279
+
280
+ def _on_milestone_terminal(status: str) -> None:
281
+ milestone_ctx = self._get_supervisor_ctx()
282
+ if milestone_ctx.session_id != session_id:
283
+ return
284
+ normalized = status.lower()
285
+ if normalized in ("complete", "completed", "done"):
286
+ milestone_ctx.state = SupervisorState.COMPLETE
287
+ elif normalized == "cancelled":
288
+ milestone_ctx.state = SupervisorState.CANCELLED
289
+ else:
290
+ milestone_ctx.state = SupervisorState.FAILED
291
+ milestone_ctx.session_id = None
292
+ milestone_ctx.pending_blocker_id = None
293
+ milestone_ctx.notified_terminal = True
294
+ self._set_supervisor_ctx(milestone_ctx)
295
+
296
+ # Spawn the supervised subprocess; create_milestone returns a local
297
+ # session id immediately and continues the stream reader in the
298
+ # background.
299
+ try:
300
+ session_id = self._client.create_milestone(
301
+ project_dir,
302
+ context_text=context_text,
303
+ context_file=context_file,
304
+ notifications=self._notifications,
305
+ on_terminal=_on_milestone_terminal,
306
+ )
307
+ except ValueError:
308
+ return "Usage: `/gsd new-milestone <spec>` (or `--file <path>`)"
309
+ except Exception as e:
310
+ return f"Could not start milestone creation: {e}"
311
+
312
+ # Record the active milestone run so /gsd status, /gsd cancel, and the
313
+ # co-run gate (in _cmd_auto) see it. The supervisor poll loop is NOT
314
+ # used (the stream reader drives notifications); we only carry state.
315
+ self._supervisor.stop()
316
+ ctx.session_id = session_id
317
+ ctx.project_dir = project_dir
318
+ ctx.state = SupervisorState.RUNNING
319
+ ctx.last_progress = None
320
+ ctx.last_status = None
321
+ ctx.pending_blocker_id = None
322
+ ctx.notified_terminal = False
323
+ self._set_supervisor_ctx(ctx)
324
+ return (
325
+ f"🚀 Milestone creation started (session `{session_id}`). "
326
+ "I'll notify you when it's ready; run `/gsd auto` to execute."
327
+ )
@@ -19,6 +19,7 @@ class GsdConfig:
19
19
  bindings: dict[str, dict[str, str]] = field(default_factory=dict)
20
20
  poll_interval_seconds: int = 12
21
21
  cache_ttl_seconds: int = 45
22
+ mcp_read_timeout_seconds: float = 60.0
22
23
  notification_level: str = "normal"
23
24
  gsd_version_min: str = "2.53"
24
25
  gsd_version_max: str = "3.0"
@@ -35,6 +36,7 @@ class GsdConfig:
35
36
  bindings=dict(gsd.get("bindings") or {}),
36
37
  poll_interval_seconds=int(gsd.get("poll_interval_seconds", 12)),
37
38
  cache_ttl_seconds=int(gsd.get("cache_ttl_seconds", 45)),
39
+ mcp_read_timeout_seconds=float(gsd.get("mcp_read_timeout_seconds", 60.0)),
38
40
  notification_level=str(gsd.get("notification_level", "normal")),
39
41
  gsd_version_min=str(gsd.get("gsd_version_min", "2.53")),
40
42
  gsd_version_max=str(gsd.get("gsd_version_max", "3.0")),