@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,447 @@
1
+ """Tests for GsdMcpClient milestone creation (subprocess-owned, stream-driven).
2
+
3
+ These tests pin the A1 design from issue #1162's grilling:
4
+ - create_milestone spawns `gsd headless --supervised new-milestone`
5
+ - a background stream-reader drives NotificationService on blocker/terminal events
6
+ - cancel_milestone SIGTERMs the held subprocess
7
+ - respond_to_milestone_blocker writes an extension_ui_response to stdin
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from io import BytesIO
13
+ import json
14
+ import subprocess
15
+ from unittest.mock import MagicMock, patch
16
+
17
+ import pytest
18
+
19
+ from open_gsd_hermes.config import GsdConfig
20
+ from open_gsd_hermes.gsd_client import GsdMcpClient
21
+ from open_gsd_hermes.types import ProgressSnapshot
22
+
23
+
24
+ def _config() -> GsdConfig:
25
+ return GsdConfig(cli_path="/usr/local/bin/gsd")
26
+
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # create_milestone — argument construction
30
+ # ---------------------------------------------------------------------------
31
+
32
+
33
+ class _FakeProc:
34
+ """Minimal stand-in for subprocess.Popen used by create_milestone."""
35
+
36
+ def __init__(self) -> None:
37
+ self.pid = 4242
38
+ self.stdin = MagicMock()
39
+ self.stdout = MagicMock()
40
+ self.stderr = BytesIO()
41
+ self.poll = MagicMock(return_value=None)
42
+ self.terminate = MagicMock()
43
+ self.wait = MagicMock(return_value=0)
44
+ self.kill = MagicMock()
45
+
46
+
47
+ def test_create_milestone_builds_supervised_new_milestone_command_with_context_text() -> None:
48
+ """create_milestone must invoke `gsd headless --supervised new-milestone --context-text <spec>`."""
49
+ client = GsdMcpClient(_config())
50
+ fake_proc = _FakeProc()
51
+
52
+ captured: dict[str, object] = {}
53
+
54
+ def fake_popen(args, **kwargs): # type: ignore[no-untyped-def]
55
+ captured["args"] = args
56
+ captured["cwd"] = kwargs.get("cwd")
57
+ captured["env"] = kwargs.get("env")
58
+ return fake_proc
59
+
60
+ with patch.object(client, "ensure_version"):
61
+ with patch("subprocess.Popen", side_effect=fake_popen):
62
+ with patch.object(client, "_milestone_stream_loop"):
63
+ client.create_milestone("/proj", context_text="Build a REST API")
64
+
65
+ args = captured["args"]
66
+ assert args[0] == "/usr/local/bin/gsd"
67
+ assert "headless" in args
68
+ assert "--supervised" in args
69
+ assert "--response-timeout" in args
70
+ timeout_idx = args.index("--response-timeout")
71
+ assert args[timeout_idx + 1] == str(GsdMcpClient._SUPERVISED_RESPONSE_TIMEOUT_MS)
72
+ assert "new-milestone" in args
73
+ assert "--context-text" in args
74
+ text_idx = args.index("--context-text")
75
+ assert args[text_idx + 1] == "Build a REST API"
76
+ assert captured["cwd"] == "/proj"
77
+
78
+
79
+ def test_create_milestone_builds_command_with_context_file() -> None:
80
+ """--file form passes --context <path> instead of --context-text."""
81
+ client = GsdMcpClient(_config())
82
+ fake_proc = _FakeProc()
83
+ captured: dict[str, object] = {}
84
+
85
+ def fake_popen(args, **kwargs): # type: ignore[no-untyped-def]
86
+ captured["args"] = args
87
+ return fake_proc
88
+
89
+ with patch.object(client, "ensure_version"):
90
+ with patch("subprocess.Popen", side_effect=fake_popen):
91
+ with patch.object(client, "_milestone_stream_loop"):
92
+ client.create_milestone("/proj", context_file="/path/to/spec.md")
93
+
94
+ args = captured["args"]
95
+ assert "--context" in args
96
+ ctx_idx = args.index("--context")
97
+ assert args[ctx_idx + 1] == "/path/to/spec.md"
98
+ assert "--context-text" not in args
99
+
100
+
101
+ def test_create_milestone_requires_either_context_text_or_file() -> None:
102
+ """No spec at all is a usage error, raised before any subprocess spawns."""
103
+ client = GsdMcpClient(_config())
104
+ with pytest.raises(ValueError, match="spec"):
105
+ client.create_milestone("/proj")
106
+
107
+
108
+ def test_create_milestone_rejects_both_context_text_and_file() -> None:
109
+ """Ambiguous input (both forms) is a usage error."""
110
+ client = GsdMcpClient(_config())
111
+ with pytest.raises(ValueError, match="spec"):
112
+ client.create_milestone(
113
+ "/proj", context_text="x", context_file="/p.md"
114
+ )
115
+
116
+
117
+ # ---------------------------------------------------------------------------
118
+ # Stream reader — sessionId capture, blocker, terminal
119
+ # ---------------------------------------------------------------------------
120
+
121
+
122
+ def test_create_milestone_returns_local_session_id_from_pid() -> None:
123
+ """create_milestone returns a local session id without waiting for init_result."""
124
+ client = GsdMcpClient(_config())
125
+ fake_proc = _FakeProc()
126
+
127
+ with patch.object(client, "ensure_version"):
128
+ with patch("subprocess.Popen", return_value=fake_proc):
129
+ with patch.object(client, "_milestone_stream_loop"):
130
+ session_id = client.create_milestone("/proj", context_text="spec")
131
+
132
+ assert session_id == "milestone-4242"
133
+
134
+
135
+ def test_stream_loop_captures_session_id_from_init_result() -> None:
136
+ """The reader must pull sessionId out of the init_result stream event."""
137
+ client = GsdMcpClient(_config())
138
+ client._handle_milestone_event({"type": "init_result", "sessionId": "S-789"})
139
+
140
+ assert client.milestone_session_id() == "S-789"
141
+
142
+
143
+ def test_stream_loop_notifies_blocker_on_supervised_select_request() -> None:
144
+ """Supervised planning prompts (select/input/confirm) must fire notify_blocker."""
145
+ client = GsdMcpClient(_config())
146
+ notifications = MagicMock()
147
+ blocker_event = {
148
+ "type": "extension_ui_request",
149
+ "method": "select",
150
+ "id": "sel-1",
151
+ "title": "Choose milestone scope",
152
+ "options": ["A", "B"],
153
+ }
154
+ with patch.object(client, "_milestone_notifications", notifications):
155
+ client._handle_milestone_event(blocker_event)
156
+
157
+ notifications.notify_blocker.assert_called_once()
158
+ assert client.milestone_pending_blocker_id() == "sel-1"
159
+
160
+
161
+ def test_stream_loop_notifies_blocker_on_extension_ui_request() -> None:
162
+ """Blocked-notice notify events are fire-and-forget; they must not prompt /gsd reply."""
163
+ client = GsdMcpClient(_config())
164
+ notifications = MagicMock()
165
+ # Realistic blocked-notice message — headless auto-acks notify in supervised mode.
166
+ blocker_event = {
167
+ "type": "extension_ui_request",
168
+ "method": "notify",
169
+ "id": "blk-1",
170
+ "message": "Auto-mode paused — blocked: choose an option",
171
+ }
172
+ with patch.object(client, "_milestone_notifications", notifications):
173
+ client._handle_milestone_event(blocker_event)
174
+
175
+ notifications.notify_blocker.assert_not_called()
176
+ assert client.milestone_pending_blocker_id() is None
177
+
178
+
179
+ def test_build_milestone_completion_message_uses_query_and_progress() -> None:
180
+ client = GsdMcpClient(_config())
181
+ with patch.object(
182
+ client,
183
+ "project_query",
184
+ return_value={"milestones": [{"id": "M002", "hasRoadmap": True}]},
185
+ ):
186
+ with patch.object(
187
+ client,
188
+ "progress",
189
+ return_value=ProgressSnapshot(
190
+ active_milestone={"id": "M002"},
191
+ slices={"total": 3},
192
+ tasks={"total": 9},
193
+ ),
194
+ ):
195
+ msg = client._build_milestone_completion_message("/proj")
196
+
197
+ assert msg == "✅ Milestone M002 ready — 3 slices, 9 tasks. Run `/gsd auto` to start."
198
+
199
+
200
+ def test_build_milestone_completion_message_falls_back_when_query_empty() -> None:
201
+ client = GsdMcpClient(_config())
202
+ with patch.object(client, "project_query", return_value={"milestones": []}):
203
+ msg = client._build_milestone_completion_message("/proj")
204
+
205
+ assert msg == "✅ Milestone created. Run `/gsd status` for details."
206
+
207
+
208
+ def test_stream_loop_notifies_terminal_on_process_exit() -> None:
209
+ """When the stream ends (process exited), a milestone completion notification fires."""
210
+ client = GsdMcpClient(_config())
211
+ notifications = MagicMock()
212
+ fake_proc = _FakeProc()
213
+ fake_proc.poll.return_value = 0
214
+ fake_proc.stdout.readline.side_effect = [b""]
215
+ client._milestone_session_id = "milestone-4242"
216
+ client._milestone_pending_blocker_id = "old-blocker"
217
+ client._milestone_pending_blocker_method = "input"
218
+
219
+ with patch.object(client, "_milestone_proc", fake_proc):
220
+ with patch.object(client, "_milestone_project_dir", "/proj"):
221
+ with patch.object(
222
+ client,
223
+ "_build_milestone_completion_message",
224
+ return_value="✅ Milestone M001 ready — 2 slices, 5 tasks. Run `/gsd auto` to start.",
225
+ ):
226
+ with patch.object(client, "_milestone_notifications", notifications):
227
+ client._milestone_stream_loop(fake_proc)
228
+
229
+ notifications.notify_milestone_complete.assert_called_once_with(
230
+ "✅ Milestone M001 ready — 2 slices, 5 tasks. Run `/gsd auto` to start."
231
+ )
232
+ notifications.notify_terminal.assert_not_called()
233
+ assert client.milestone_active() is False
234
+ assert client.milestone_session_id() is None
235
+ assert client.milestone_pending_blocker_id() is None
236
+ assert client._milestone_pending_blocker_method is None
237
+
238
+
239
+ def test_stream_loop_failure_reports_drained_stderr() -> None:
240
+ """Failure notifications must use stderr retained by the drain thread."""
241
+ client = GsdMcpClient(_config())
242
+ notifications = MagicMock()
243
+ fake_proc = _FakeProc()
244
+ fake_proc.poll.return_value = 2
245
+ fake_proc.stderr = BytesIO(b"fatal diagnostics\n")
246
+ fake_proc.stdout.readline.side_effect = [b""]
247
+
248
+ client._drain_milestone_stderr(fake_proc)
249
+ with patch.object(client, "_milestone_proc", fake_proc):
250
+ with patch.object(client, "_milestone_notifications", notifications):
251
+ client._milestone_stream_loop(fake_proc)
252
+
253
+ notifications.notify_terminal.assert_called_once_with(
254
+ "failed", "fatal diagnostics"
255
+ )
256
+
257
+
258
+ def test_stream_loop_releases_blocked_exit_with_pending_blocker() -> None:
259
+ """Exit 10 after stdout EOF is terminal even with a pending blocker id."""
260
+ client = GsdMcpClient(_config())
261
+ notifications = MagicMock()
262
+ fake_proc = _FakeProc()
263
+ fake_proc.poll.return_value = 10
264
+ fake_proc.stdout.readline.side_effect = [b""]
265
+ client._milestone_session_id = "milestone-4242"
266
+
267
+ with patch.object(client, "_milestone_proc", fake_proc):
268
+ with patch.object(client, "_milestone_pending_blocker_id", "sel-1"):
269
+ with patch.object(client, "_milestone_notifications", notifications):
270
+ client._milestone_stream_loop(fake_proc)
271
+
272
+ notifications.notify_terminal.assert_called_once_with("failed", "Planning blocked")
273
+ assert client.milestone_active() is False
274
+ assert client.milestone_session_id() is None
275
+
276
+
277
+ def test_stream_loop_releases_noninteractive_blocked_exit() -> None:
278
+ """Exit 10 without a pending blocker must not leave milestone ownership stuck."""
279
+ client = GsdMcpClient(_config())
280
+ notifications = MagicMock()
281
+ fake_proc = _FakeProc()
282
+ fake_proc.poll.return_value = 10
283
+ fake_proc.stdout.readline.side_effect = [b""]
284
+ client._milestone_session_id = "milestone-4242"
285
+
286
+ with patch.object(client, "_milestone_proc", fake_proc):
287
+ with patch.object(client, "_milestone_notifications", notifications):
288
+ client._milestone_stream_loop(fake_proc)
289
+
290
+ notifications.notify_terminal.assert_called_once_with("failed", "Planning blocked")
291
+ assert client.milestone_active() is False
292
+ assert client.milestone_session_id() is None
293
+
294
+
295
+ def test_stream_loop_retains_running_proc_on_stream_loss() -> None:
296
+ """If stdout closes while the child is alive, /gsd cancel must still work."""
297
+ client = GsdMcpClient(_config())
298
+ notifications = MagicMock()
299
+ on_terminal = MagicMock()
300
+ fake_proc = _FakeProc()
301
+ fake_proc.poll.return_value = None
302
+ fake_proc.wait.side_effect = subprocess.TimeoutExpired(cmd="gsd", timeout=5)
303
+ fake_proc.stdout.readline.side_effect = [b""]
304
+ client._milestone_session_id = "milestone-4242"
305
+
306
+ with patch.object(client, "_milestone_proc", fake_proc):
307
+ with patch.object(client, "_milestone_notifications", notifications):
308
+ with patch.object(client, "_milestone_on_terminal", on_terminal):
309
+ client._milestone_stream_loop(fake_proc)
310
+
311
+ notifications.notify_terminal.assert_called_once_with(
312
+ "failed", "stream lost — run /gsd cancel"
313
+ )
314
+ on_terminal.assert_called_once_with("failed")
315
+ assert client.milestone_active() is True
316
+ assert client.milestone_session_id() == "milestone-4242"
317
+
318
+ client.cancel_milestone()
319
+
320
+ fake_proc.terminate.assert_called_once()
321
+ assert client.milestone_active() is False
322
+
323
+
324
+ def test_stream_loop_handles_blocking_command_block() -> None:
325
+ """gsd-command-block failures must not be reported as milestone complete."""
326
+ client = GsdMcpClient(_config())
327
+ notifications = MagicMock()
328
+ fake_proc = _FakeProc()
329
+ client._milestone_session_id = "milestone-4242"
330
+ command_block = {
331
+ "type": "message_start",
332
+ "message": {
333
+ "role": "custom",
334
+ "customType": "gsd-command-block",
335
+ "content": (
336
+ "/gsd auto cannot run because the active milestone "
337
+ "is blocked by validation."
338
+ ),
339
+ },
340
+ }
341
+ fake_proc.poll.return_value = 0
342
+ fake_proc.stdout.readline.side_effect = [
343
+ (json.dumps(command_block) + "\n").encode(),
344
+ b"",
345
+ ]
346
+
347
+ with patch.object(client, "_milestone_proc", fake_proc):
348
+ with patch.object(client, "_milestone_notifications", notifications):
349
+ client._milestone_stream_loop(fake_proc)
350
+
351
+ notifications.notify_terminal.assert_called_with(
352
+ "failed",
353
+ "/gsd auto cannot run because the active milestone is blocked by validation.",
354
+ )
355
+ notifications.notify_milestone_complete.assert_not_called()
356
+ assert client.milestone_active() is False
357
+ assert client.milestone_session_id() is None
358
+
359
+
360
+ # ---------------------------------------------------------------------------
361
+ # cancel_milestone / respond_to_milestone_blocker / milestone_active
362
+ # ---------------------------------------------------------------------------
363
+
364
+
365
+ def test_cancel_milestone_terminates_held_subprocess() -> None:
366
+ client = GsdMcpClient(_config())
367
+ fake_proc = _FakeProc()
368
+
369
+ with patch.object(client, "_milestone_proc", fake_proc):
370
+ client.cancel_milestone()
371
+
372
+ fake_proc.terminate.assert_called_once()
373
+
374
+
375
+ def test_cancel_milestone_when_no_proc_is_a_noop() -> None:
376
+ """Calling cancel with no active milestone proc must not raise."""
377
+ client = GsdMcpClient(_config())
378
+ # should not raise
379
+ client.cancel_milestone()
380
+
381
+
382
+ def test_close_terminates_active_milestone_subprocess() -> None:
383
+ """close() must tear down the owned milestone subprocess as well as MCP."""
384
+ client = GsdMcpClient(_config())
385
+ fake_proc = _FakeProc()
386
+
387
+ with (
388
+ patch.object(client, "_terminate_process"),
389
+ patch.object(client, "_milestone_proc", fake_proc),
390
+ ):
391
+ client.close()
392
+
393
+ fake_proc.terminate.assert_called_once()
394
+ assert client.milestone_active() is False
395
+
396
+
397
+ def test_respond_to_milestone_blocker_writes_extension_ui_response_to_stdin() -> None:
398
+ """The reply must write the supervised-mode stdin JSONL protocol."""
399
+ client = GsdMcpClient(_config())
400
+ fake_proc = _FakeProc()
401
+
402
+ with patch.object(client, "_milestone_proc", fake_proc):
403
+ with patch.object(client, "_milestone_pending_blocker_id", "blk-7"):
404
+ with patch.object(client, "_milestone_pending_blocker_method", "input"):
405
+ client.respond_to_milestone_blocker("use option B")
406
+
407
+ assert fake_proc.stdin.write.called
408
+ written = fake_proc.stdin.write.call_args[0][0]
409
+ payload = json.loads(written.decode() if isinstance(written, bytes) else written)
410
+ assert payload["type"] == "extension_ui_response"
411
+ assert payload["id"] == "blk-7"
412
+ assert payload["value"] == "use option B"
413
+
414
+
415
+ def test_respond_to_milestone_blocker_writes_confirmed_for_confirm_prompt() -> None:
416
+ """Confirm blockers must answer with a confirmed boolean, not free text."""
417
+ client = GsdMcpClient(_config())
418
+ fake_proc = _FakeProc()
419
+
420
+ with patch.object(client, "_milestone_proc", fake_proc):
421
+ with patch.object(client, "_milestone_pending_blocker_id", "cfm-1"):
422
+ with patch.object(client, "_milestone_pending_blocker_method", "confirm"):
423
+ client.respond_to_milestone_blocker("yes")
424
+
425
+ written = fake_proc.stdin.write.call_args[0][0]
426
+ payload = json.loads(written.decode() if isinstance(written, bytes) else written)
427
+ assert payload["type"] == "extension_ui_response"
428
+ assert payload["id"] == "cfm-1"
429
+ assert payload["confirmed"] is True
430
+ assert "value" not in payload
431
+
432
+
433
+ def test_respond_to_milestone_blocker_without_pending_blocker_raises() -> None:
434
+ """No pending blocker id means nothing to reply to — fail loud, don't pretend."""
435
+ client = GsdMcpClient(_config())
436
+ fake_proc = _FakeProc()
437
+ with patch.object(client, "_milestone_proc", fake_proc):
438
+ with pytest.raises(RuntimeError, match="No pending blocker"):
439
+ client.respond_to_milestone_blocker("answer")
440
+
441
+
442
+ def test_milestone_active_reflects_held_proc() -> None:
443
+ client = GsdMcpClient(_config())
444
+ assert client.milestone_active() is False
445
+ with patch.object(client, "_milestone_proc", _FakeProc()):
446
+ assert client.milestone_active() is True
447
+ assert client.milestone_active() is False