@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
package/README.md CHANGED
@@ -19,7 +19,7 @@ It combines a terminal agent, project workflow tools, worktree-aware Git automat
19
19
  - **Autonomous project workflow** — Break work into milestones, slices, and tasks, then let auto mode plan, implement, verify, and advance.
20
20
  - **Worktree-aware Git automation** — Keep implementation work isolated while preserving a reviewable main checkout.
21
21
  - **Local project memory** — Store project requirements, decisions, runtime notes, generated plans, summaries, and validation evidence under `.gsd/`.
22
- - **Multi-provider model routing** — Use the provider your team already has, with configurable defaults and per-phase model preferences.
22
+ - **Multi-provider model routing** — Use the provider your team already has, including API keys, OAuth providers, and external CLI providers such as Claude Code and Cursor Agent.
23
23
  - **Extension surface** — Add project-specific commands, tools, skills, and UI integrations through bundled or community extensions.
24
24
  - **Terminal and web surfaces** — Use the TUI by default, or launch `gsd --web` when a visual control plane fits the work better than a terminal.
25
25
 
@@ -28,16 +28,16 @@ See [CHANGELOG.md](./CHANGELOG.md) for release-by-release fixes and [Legacy Rele
28
28
  ## Latest Release Highlights
29
29
 
30
30
  <!-- release-highlights:start -->
31
- Latest release: **v1.4.0**
31
+ Latest release: **v1.5.0**
32
32
 
33
- - **models:** Add Claude Sonnet 5.
34
- - **Added:** Add Hermes plugin installer.
35
- - **compat:** Report .planning/ drift in /gsd sync and doctor.
36
- - **compat:** Project to .planning/ inside renderAllFromDb.
37
- - **compat:** Add external-planning-edit drift handler.
38
- - **compat:** Add DB .planning/ projection writer.
39
- - **compat:** Extract .planning/ layout detector.
40
- - **compat:** Extend marker schema 1→2 with planning field.
33
+ - **hermes:** Add /gsd new-milestone command for chat-driven milestone creation (#1162).
34
+ - **gsd:** Make CODEBASE.md workspace-aware in parent mode.
35
+ - **gsd:** Doctor probe + validation for parent-workspace repos.
36
+ - **gsd:** Surface per-repo git health in /gsd status + document limits.
37
+ - **gsd:** Wire repository assignment into slice/task planning.
38
+ - **gsd:** Add parent-workspace discovery UX + document nested-only layout.
39
+ - **provider:** Add Cursor Agent provider.
40
+ - **gsd:** Make workspace.mode parent behavioral (mode contract).
41
41
 
42
42
  <!-- release-highlights:end -->
43
43
 
@@ -158,7 +158,7 @@ Need help choosing settings? Use the [GSD Pi web configurator](https://pi.opengs
158
158
  gsd
159
159
  ```
160
160
 
161
- Run the setup flow, choose your preferred model provider, and open a project directory. GSD stores project planning and runtime state in `.gsd/`, with gitignored sibling runtime directories such as `.gsd-backups/` for migration snapshots. Stale `.gsd-backups/migrate-*` snapshots are pruned after 30 days once the flat-phase `.gsd/phases/` migration is complete.
161
+ Run the setup flow, choose your preferred model provider, and open a project directory. Cursor Agent users can choose the `cursor-agent` provider after installing and authenticating the local `cursor-agent` CLI; its default model is `composer-2.5`, and `CURSOR_API_KEY` is supported as an auth signal. GSD stores project planning and runtime state in `.gsd/`, with gitignored sibling runtime directories such as `.gsd-backups/` for migration snapshots. Stale `.gsd-backups/migrate-*` snapshots are pruned after 30 days once the flat-phase `.gsd/phases/` migration is complete.
162
162
 
163
163
  For a full first-run walkthrough, see [Getting Started With gsd-pi](./docs/user-docs/getting-started.md).
164
164
 
@@ -16,6 +16,7 @@ import { renderGsdPiLogo, GSD_PI_BRAND, GSD_WEBSITE } from './logo.js';
16
16
  import { agentDir } from './app-paths.js';
17
17
  import { isClaudeCliReady } from './claude-cli-check.js';
18
18
  import { isAntigravityCliReady, isGeminiCliReady } from './resources/extensions/google-cli/readiness.js';
19
+ import { isCursorAgentReady } from './resources/extensions/cursor-cli/readiness.js';
19
20
  import { markOnboardingComplete, markStepCompleted, markStepSkipped, isOnboardingComplete, } from './resources/extensions/gsd/onboarding-state.js';
20
21
  import { getLlmProviderIds } from './resources/extensions/gsd/setup-catalog.js';
21
22
  // ─── Constants ────────────────────────────────────────────────────────────────
@@ -359,6 +360,9 @@ export async function runLlmStep(p, pc, authStorage) {
359
360
  if (isClaudeCliReady()) {
360
361
  authOptions.push({ value: 'claude-cli', label: 'Use Claude Code CLI', hint: 'uses your existing Claude subscription' });
361
362
  }
363
+ if (isCursorAgentReady()) {
364
+ authOptions.push({ value: 'cursor-agent-cli', label: 'Use Cursor Agent', hint: 'uses your existing Cursor subscription' });
365
+ }
362
366
  if (isAntigravityCliReady()) {
363
367
  authOptions.push({ value: 'antigravity-cli', label: 'Use Antigravity CLI', hint: 'recommended — replaces Gemini CLI for individuals' });
364
368
  }
@@ -388,6 +392,13 @@ export async function runLlmStep(p, pc, authStorage) {
388
392
  persistDefaultProvider('claude-code');
389
393
  return true;
390
394
  }
395
+ if (method === 'cursor-agent-cli') {
396
+ p.log.success('Cursor Agent detected — routing through local CLI');
397
+ p.log.info('Your Cursor subscription will be used for inference. No API key needed.');
398
+ authStorage.set('cursor-agent', { type: 'api_key', key: 'cli' });
399
+ persistDefaultProvider('cursor-agent');
400
+ return true;
401
+ }
391
402
  if (method === 'gemini-cli') {
392
403
  p.log.warn('Google Gemini CLI is deprecated for individual users — install Antigravity: https://antigravity.google');
393
404
  p.log.success('Google Gemini CLI detected — routing through local CLI');
@@ -1 +1 @@
1
- 6cd0d33f2832351c
1
+ f30abebf080db301
@@ -5,7 +5,7 @@
5
5
  > **When to read this:** At the start of any session working on GSD-managed work, or when loaded by `/gsd`.
6
6
  >
7
7
  > **After reading this, always read `.gsd/STATE.md` to find out what's next.**
8
- > If the milestone has a `CONTEXT.md`, read that too. If the active slice has an `S##-CONTEXT.md`, read that as well — these files contain project-specific decisions, reference paths, and implementation guidance that this generic methodology doc does not.
8
+ > If the milestone has a `NN-CONTEXT.md`, read that too. If the active slice has an `NN-MM-CONTEXT.md`, read that as well — these files contain project-specific decisions, reference paths, and implementation guidance that this generic methodology doc does not.
9
9
 
10
10
  ---
11
11
 
@@ -14,10 +14,10 @@
14
14
  Read these files in order and act on what they say:
15
15
 
16
16
  1. **`.gsd/STATE.md`** — Where are we? What's the next action?
17
- 2. **`.gsd/phases/<active>/ROADMAP.md`** — What's the plan? Which slices are done? (`STATE.md` tells you which milestone is active)
18
- 3. **`.gsd/phases/<active>/CONTEXT.md`** — Milestone-level project decisions, reference paths, constraints. Read this before doing implementation work.
19
- 4. If a slice is active and has one, read **`S##-CONTEXT.md`** — Slice-specific decisions and constraints.
20
- 5. If a slice is active, read its **`S##-PLAN.md`** — Which tasks exist? Which are done?
17
+ 2. **`.gsd/phases/<NN-slug>/<NN>-ROADMAP.md`** — What's the plan? Which slices are done? (`STATE.md` tells you which milestone is active)
18
+ 3. **`.gsd/phases/<NN-slug>/<NN>-CONTEXT.md`** — Milestone-level project decisions, reference paths, constraints. Read this before doing implementation work.
19
+ 4. If a slice is active and has one, read **`<NN>-<MM>-CONTEXT.md`** — Slice-specific decisions and constraints.
20
+ 5. If a slice is active, read its **`<NN>-<MM>-PLAN.md`** — Which tasks exist? Which are done?
21
21
  6. If `.gsd/CODEBASE.md` exists, skim it for fast structural orientation before broad code exploration.
22
22
  7. If a task was interrupted, check for **`continue.md`** in the active slice directory — Resume from there.
23
23
 
@@ -47,22 +47,17 @@ All artifacts live in `.gsd/` at the project root:
47
47
  DECISIONS.md # Append-only decisions register
48
48
  CODEBASE.md # Generated codebase map cache (auto-refreshed by GSD)
49
49
  phases/
50
- M001/
51
- M001-ROADMAP.md # Milestone plan (checkboxes = state)
52
- M001-CONTEXT.md # Optional: user decisions from discuss phase
53
- M001-RESEARCH.md # Optional: codebase/tech research
54
- M001-SUMMARY.md # Milestone rollup (updated as slices complete)
55
- slices/
56
- S01/
57
- S01-PLAN.md # Task decomposition for this slice
58
- S01-CONTEXT.md # Optional: slice-level user decisions
59
- S01-RESEARCH.md # Optional: slice-level research
60
- S01-SUMMARY.md # Slice summary (written on completion)
61
- S01-UAT.md # Non-blocking human test script (written on completion)
62
- continue.md # Ephemeral: resume point if interrupted
63
- tasks/
64
- T01-PLAN.md # Individual task plan
65
- T01-SUMMARY.md # Task summary with frontmatter
50
+ 01-foundation/
51
+ 01-ROADMAP.md # Milestone plan (checkboxes = state)
52
+ 01-CONTEXT.md # Optional: user decisions from discuss phase
53
+ 01-RESEARCH.md # Optional: codebase/tech research
54
+ 01-SUMMARY.md # Milestone rollup (updated as slices complete)
55
+ 01-01-PLAN.md # Task decomposition for the first slice
56
+ 01-01-CONTEXT.md # Optional: slice-level user decisions
57
+ 01-01-RESEARCH.md # Optional: slice-level research
58
+ 01-01-SUMMARY.md # Slice summary (written on completion)
59
+ 01-01-UAT.md # Non-blocking human test script (written on completion)
60
+ 01-01-CONTINUE.md # Ephemeral: resume point if interrupted
66
61
  ```
67
62
 
68
63
  ---
@@ -424,7 +419,7 @@ key_decisions:
424
419
  patterns_established:
425
420
  - "Pattern name and where it lives"
426
421
  drill_down_paths:
427
- - .gsd/phases/M001/slices/S01/tasks/T01-PLAN.md
422
+ - .gsd/phases/01-foundation/01-01-PLAN.md
428
423
  duration: 15min
429
424
  verification_result: pass
430
425
  completed_at: 2026-03-07T16:00:00Z
@@ -635,10 +630,10 @@ These are soft caps — exceed them when genuinely needed, but don't let summari
635
630
 
636
631
  This methodology doc is generic. Project-specific guidance belongs in the milestone and slice context files:
637
632
 
638
- - **`.gsd/phases/<active>/CONTEXT.md`** — milestone-level architecture decisions, reference file paths, and implementation constraints
639
- - **`.gsd/phases/<active>/slices/S##/S##-CONTEXT.md`** — slice-level decisions, edge cases, and narrow implementation guidance when present
633
+ - **`.gsd/phases/<NN-slug>/<NN>-CONTEXT.md`** — milestone-level architecture decisions, reference file paths, and implementation constraints
634
+ - **`.gsd/phases/<NN-slug>/<NN>-<MM>-CONTEXT.md`** — slice-level decisions, edge cases, and narrow implementation guidance when present
640
635
 
641
- **Always read the active milestone's `CONTEXT.md` before starting implementation work.** If the active slice also has `S##-CONTEXT.md`, read that too. These files tell you what decisions are locked, what files to reference, and how to verify your work in this specific project.
636
+ **Always read the active milestone's `<NN>-CONTEXT.md` before starting implementation work.** If the active slice also has `<NN>-<MM>-CONTEXT.md`, read that too. These files tell you what decisions are locked, what files to reference, and how to verify your work in this specific project.
642
637
 
643
638
  ---
644
639
 
@@ -21,7 +21,7 @@ import { resolveWorkflowQuestionToolSurface } from "../gsd/question-transport.js
21
21
  import { buildProjectGsdMcpServers, ensureProjectWorkflowMcpConfig } from "../gsd/mcp-project-config.js";
22
22
  import { loadProjectGSDPreferences } from "../gsd/preferences.js";
23
23
  import { markToolStart, markToolEnd } from "../gsd/auto.js";
24
- import { markInteractiveElicitationStart, markInteractiveElicitationEnd, } from "../gsd/auto-tool-tracking.js";
24
+ import { markInteractiveElicitationStart, markInteractiveElicitationEnd, isInteractiveElicitationInFlight, } from "../gsd/auto-tool-tracking.js";
25
25
  import { discoverBrowserMcpServerName, discoverMcpServers, discoverMcpServerNames, discoverUserMcpServerNames, discoverWorkflowMcpServerName, computeMcpDisallowedTools, } from "../gsd/mcp-filter.js";
26
26
  import { RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES, RUN_UAT_FORBIDDEN_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES, resolveToolPresentationPlan } from "../gsd/tool-presentation-plan.js";
27
27
  import { getUnitToolSurfaceContract } from "../gsd/unit-tool-contracts.js";
@@ -51,6 +51,27 @@ export function resolveClaudeCodeCwd(options) {
51
51
  const OTHER_OPTION_LABEL = "None of the above";
52
52
  /** Regex pattern that identifies field names and descriptions that should be treated as sensitive/secure inputs. */
53
53
  const SENSITIVE_FIELD_PATTERN = /(password|passphrase|secret|token|api[_\s-]*key|private[_\s-]*key|credential)/i;
54
+ /** Mirrors @opengsd/mcp-server's ask_user_questions host elicitation timeout. */
55
+ const WORKFLOW_ELICIT_TIMEOUT_MS = 10 * 60 * 1000;
56
+ /** Close the local form just before the server drops the MCP elicitation request. */
57
+ const WORKFLOW_ELICIT_TIMEOUT_SKEW_MS = 1_000;
58
+ export const CLAUDE_CODE_INTERVIEW_FORM_TIMEOUT_MS = Math.max(1, WORKFLOW_ELICIT_TIMEOUT_MS - WORKFLOW_ELICIT_TIMEOUT_SKEW_MS);
59
+ const ELICITATION_EXPIRED_NOTICE = "Question expired before you answered - the form was closed and no answers were sent.";
60
+ const activeAskUserQuestionElicitationTimeoutAborters = new Set();
61
+ function isAskUserQuestionsToolName(toolName) {
62
+ const name = String(toolName ?? "");
63
+ return name === "ask_user_questions" || name.endsWith("__ask_user_questions");
64
+ }
65
+ function isAskUserQuestionsTimedOutResult(toolCall, result) {
66
+ return isAskUserQuestionsToolName(toolCall.name) && result.details?.timed_out === true;
67
+ }
68
+ function abortActiveAskUserQuestionElicitationsForTimeout() {
69
+ if (!isInteractiveElicitationInFlight())
70
+ return;
71
+ for (const abort of [...activeAskUserQuestionElicitationTimeoutAborters]) {
72
+ abort();
73
+ }
74
+ }
54
75
  // ---------------------------------------------------------------------------
55
76
  // Stream factory
56
77
  // ---------------------------------------------------------------------------
@@ -1238,17 +1259,40 @@ export function createClaudeCodeElicitationHandler(ui) {
1238
1259
  // it does not tear down the very elicitation that IS the human boundary
1239
1260
  // (#cc-elicitation-self-cancel). It clears in the finally on every exit.
1240
1261
  const elicId = "cc-elicit-" + (request.id ?? `${Date.now()}-${nextElicitationSeq()}`);
1262
+ const formAbortController = new AbortController();
1263
+ let expiryNoticeShown = false;
1264
+ const abortExpiredForm = () => {
1265
+ if (formAbortController.signal.aborted)
1266
+ return;
1267
+ formAbortController.abort();
1268
+ if (!expiryNoticeShown) {
1269
+ expiryNoticeShown = true;
1270
+ ui.notify(ELICITATION_EXPIRED_NOTICE, "warning");
1271
+ }
1272
+ };
1273
+ const forwardSdkAbort = () => {
1274
+ if (!formAbortController.signal.aborted)
1275
+ formAbortController.abort();
1276
+ };
1277
+ if (signal.aborted) {
1278
+ forwardSdkAbort();
1279
+ }
1280
+ else {
1281
+ signal.addEventListener("abort", forwardSdkAbort, { once: true });
1282
+ }
1283
+ const expiryTimer = setTimeout(abortExpiredForm, CLAUDE_CODE_INTERVIEW_FORM_TIMEOUT_MS);
1241
1284
  markInteractiveElicitationStart();
1242
1285
  markToolStart(elicId, "ask_user_questions");
1286
+ activeAskUserQuestionElicitationTimeoutAborters.add(abortExpiredForm);
1243
1287
  try {
1244
- const interviewResult = await showInterviewRound(questions, { signal, overlay: true }, { ui }).catch(() => undefined);
1288
+ const interviewResult = await showInterviewRound(questions, { signal: formAbortController.signal, overlay: true }, { ui }).catch(() => undefined);
1245
1289
  if (interviewResult === undefined) {
1246
1290
  // `await` so the dialog human-wait stays inside try/finally and the
1247
1291
  // in-flight guard is held until the dialog resolves. Without it,
1248
1292
  // `finally` runs the moment the promise is created and the fallback
1249
1293
  // wait runs with zero in-flight tools — reintroducing the
1250
1294
  // self-cancel on this path (Bugbot #1c00624d).
1251
- return await promptElicitationWithDialogs(request, questions, ui, signal);
1295
+ return await promptElicitationWithDialogs(request, questions, ui, formAbortController.signal);
1252
1296
  }
1253
1297
  if (Object.keys(interviewResult.answers).length === 0) {
1254
1298
  // A system/host teardown (compaction, session_switch, true
@@ -1265,6 +1309,9 @@ export function createClaudeCodeElicitationHandler(ui) {
1265
1309
  };
1266
1310
  }
1267
1311
  finally {
1312
+ clearTimeout(expiryTimer);
1313
+ signal.removeEventListener("abort", forwardSdkAbort);
1314
+ activeAskUserQuestionElicitationTimeoutAborters.delete(abortExpiredForm);
1268
1315
  markToolEnd(elicId);
1269
1316
  markInteractiveElicitationEnd();
1270
1317
  }
@@ -1785,6 +1832,8 @@ function createSdkAttemptMessageState() {
1785
1832
  builder: null,
1786
1833
  intermediateToolBlocks: [],
1787
1834
  toolResultsById: new Map(),
1835
+ toolCompletionTargetsById: new Map(),
1836
+ emittedExternalToolResultIds: new Set(),
1788
1837
  };
1789
1838
  }
1790
1839
  /** Async pump that drives the Claude Agent SDK's async-iterable message stream and pushes events into `stream`. */
@@ -1890,7 +1939,7 @@ async function pumpSdkMessages(model, context, options, stream) {
1890
1939
  }
1891
1940
  }
1892
1941
  sdkAttemptLoop: for (let readinessAttempt = 0;; readinessAttempt++) {
1893
- let { builder, intermediateToolBlocks, toolResultsById } = createSdkAttemptMessageState();
1942
+ let { builder, intermediateToolBlocks, toolResultsById, toolCompletionTargetsById, emittedExternalToolResultIds, } = createSdkAttemptMessageState();
1894
1943
  const controller = new AbortController();
1895
1944
  const forwardAbort = () => controller.abort();
1896
1945
  if (options?.signal) {
@@ -2009,7 +2058,7 @@ async function pumpSdkMessages(model, context, options, stream) {
2009
2058
  case "user": {
2010
2059
  // Capture content from the completed turn before resetting
2011
2060
  if (builder) {
2012
- for (const block of builder.message.content) {
2061
+ for (const [contentIndex, block] of builder.message.content.entries()) {
2013
2062
  if (block.type === "text" && block.text) {
2014
2063
  lastTextContent = block.text;
2015
2064
  }
@@ -2019,6 +2068,10 @@ async function pumpSdkMessages(model, context, options, stream) {
2019
2068
  else if (block.type === "toolCall" || block.type === "serverToolUse") {
2020
2069
  // Collect tool blocks for externalToolExecution rendering
2021
2070
  intermediateToolBlocks.push(block);
2071
+ toolCompletionTargetsById.set(block.id, {
2072
+ partial: builder.message,
2073
+ contentIndex,
2074
+ });
2022
2075
  }
2023
2076
  }
2024
2077
  }
@@ -2031,61 +2084,73 @@ async function pumpSdkMessages(model, context, options, stream) {
2031
2084
  // Push a synthetic toolcall_end for each tool call from this turn
2032
2085
  // so the TUI can render tool results in real-time during the SDK
2033
2086
  // session instead of waiting until the entire session completes.
2034
- if (builder) {
2035
- for (const block of builder.message.content) {
2036
- const extResult = block.externalResult;
2037
- if (!extResult)
2038
- continue;
2039
- const contentIndex = builder.message.content.indexOf(block);
2040
- if (contentIndex < 0)
2041
- continue;
2042
- const suppressDuplicateUnavailable = shouldSuppressDuplicateToolUnavailableBlock(block, builder.message.content);
2043
- // Push synthetic completion events with result attached so the
2044
- // chat-controller can update pending ToolExecutionComponents.
2045
- if (block.type === "toolCall") {
2046
- if (suppressDuplicateUnavailable) {
2047
- delete block.externalResult;
2048
- stream.push({
2049
- type: "toolcall_end",
2050
- contentIndex,
2051
- toolCall: block,
2052
- partial: builder.message,
2053
- });
2054
- block.externalResult = extResult;
2055
- continue;
2056
- }
2057
- try {
2058
- await onExternalToolResult?.({
2059
- toolCall: block,
2060
- result: extResult,
2061
- });
2062
- }
2063
- catch (error) {
2064
- console.warn("[claude-code] onExternalToolResult callback failed:", error);
2065
- }
2087
+ for (const block of intermediateToolBlocks) {
2088
+ if (block.type !== "toolCall" && block.type !== "serverToolUse")
2089
+ continue;
2090
+ if (emittedExternalToolResultIds.has(block.id))
2091
+ continue;
2092
+ const target = toolCompletionTargetsById.get(block.id);
2093
+ if (!target)
2094
+ continue;
2095
+ const extResult = block.externalResult;
2096
+ if (!extResult)
2097
+ continue;
2098
+ const suppressDuplicateUnavailable = shouldSuppressDuplicateToolUnavailableBlock(block, target.partial.content);
2099
+ // Push synthetic completion events with result attached so the
2100
+ // chat-controller can update pending ToolExecutionComponents.
2101
+ if (block.type === "toolCall") {
2102
+ if (isAskUserQuestionsTimedOutResult(block, extResult)) {
2103
+ abortActiveAskUserQuestionElicitationsForTimeout();
2104
+ }
2105
+ if (suppressDuplicateUnavailable) {
2106
+ delete block.externalResult;
2066
2107
  stream.push({
2067
2108
  type: "toolcall_end",
2068
- contentIndex,
2109
+ contentIndex: target.contentIndex,
2069
2110
  toolCall: block,
2070
- partial: builder.message,
2111
+ partial: target.partial,
2071
2112
  });
2113
+ block.externalResult = extResult;
2114
+ emittedExternalToolResultIds.add(block.id);
2115
+ continue;
2072
2116
  }
2073
- else if (block.type === "serverToolUse") {
2074
- try {
2075
- await onExternalToolResult?.({
2076
- toolCall: serverToolUseToToolCallLike(block),
2077
- result: extResult,
2078
- });
2079
- }
2080
- catch (error) {
2081
- console.warn("[claude-code] onExternalToolResult callback failed:", error);
2082
- }
2083
- stream.push({
2084
- type: "server_tool_use",
2085
- contentIndex,
2086
- partial: builder.message,
2117
+ try {
2118
+ await onExternalToolResult?.({
2119
+ toolCall: block,
2120
+ result: extResult,
2087
2121
  });
2088
2122
  }
2123
+ catch (error) {
2124
+ console.warn("[claude-code] onExternalToolResult callback failed:", error);
2125
+ }
2126
+ stream.push({
2127
+ type: "toolcall_end",
2128
+ contentIndex: target.contentIndex,
2129
+ toolCall: block,
2130
+ partial: target.partial,
2131
+ });
2132
+ emittedExternalToolResultIds.add(block.id);
2133
+ }
2134
+ else if (block.type === "serverToolUse") {
2135
+ const toolCall = serverToolUseToToolCallLike(block);
2136
+ if (isAskUserQuestionsTimedOutResult(toolCall, extResult)) {
2137
+ abortActiveAskUserQuestionElicitationsForTimeout();
2138
+ }
2139
+ try {
2140
+ await onExternalToolResult?.({
2141
+ toolCall,
2142
+ result: extResult,
2143
+ });
2144
+ }
2145
+ catch (error) {
2146
+ console.warn("[claude-code] onExternalToolResult callback failed:", error);
2147
+ }
2148
+ stream.push({
2149
+ type: "server_tool_use",
2150
+ contentIndex: target.contentIndex,
2151
+ partial: target.partial,
2152
+ });
2153
+ emittedExternalToolResultIds.add(block.id);
2089
2154
  }
2090
2155
  }
2091
2156
  builder = null;
@@ -0,0 +1,16 @@
1
+ import { CURSOR_AGENT_MODELS } from "./models.js";
2
+ import { isCursorAgentReady } from "./readiness.js";
3
+ import { streamViaCursorAgent } from "./stream-adapter.js";
4
+ export default function cursorCli(pi) {
5
+ if (process.env.GSD_CURSOR_DISABLE === "1")
6
+ return;
7
+ pi.registerProvider("cursor-agent", {
8
+ name: "Cursor Agent",
9
+ authMode: "externalCli",
10
+ api: "cursor-stream-json",
11
+ baseUrl: "local://cursor-agent",
12
+ isReady: isCursorAgentReady,
13
+ streamSimple: streamViaCursorAgent,
14
+ models: CURSOR_AGENT_MODELS,
15
+ });
16
+ }
@@ -0,0 +1,63 @@
1
+ const ZERO_COST = {
2
+ input: 0,
3
+ output: 0,
4
+ cacheRead: 0,
5
+ cacheWrite: 0,
6
+ };
7
+ const CURSOR_INPUT = ["text"];
8
+ export const CURSOR_AGENT_MODELS = [
9
+ {
10
+ id: "composer-2.5",
11
+ name: "Composer 2.5 (via Cursor)",
12
+ reasoning: true,
13
+ input: CURSOR_INPUT,
14
+ cost: ZERO_COST,
15
+ contextWindow: 1_000_000,
16
+ maxTokens: 64_000,
17
+ },
18
+ {
19
+ id: "claude-sonnet-4-6",
20
+ name: "Claude Sonnet 4.6 (via Cursor)",
21
+ reasoning: true,
22
+ input: CURSOR_INPUT,
23
+ cost: ZERO_COST,
24
+ contextWindow: 1_000_000,
25
+ maxTokens: 64_000,
26
+ },
27
+ {
28
+ id: "claude-opus-4-7",
29
+ name: "Claude Opus 4.7 (via Cursor)",
30
+ reasoning: true,
31
+ input: CURSOR_INPUT,
32
+ cost: ZERO_COST,
33
+ contextWindow: 1_000_000,
34
+ maxTokens: 128_000,
35
+ },
36
+ {
37
+ id: "gpt-5.5",
38
+ name: "GPT-5.5 (via Cursor)",
39
+ reasoning: true,
40
+ input: CURSOR_INPUT,
41
+ cost: ZERO_COST,
42
+ contextWindow: 1_000_000,
43
+ maxTokens: 128_000,
44
+ },
45
+ {
46
+ id: "gemini-2.5-pro",
47
+ name: "Gemini 2.5 Pro (via Cursor)",
48
+ reasoning: true,
49
+ input: CURSOR_INPUT,
50
+ cost: ZERO_COST,
51
+ contextWindow: 1_000_000,
52
+ maxTokens: 65_536,
53
+ },
54
+ {
55
+ id: "grok-4",
56
+ name: "Grok 4 (via Cursor)",
57
+ reasoning: true,
58
+ input: CURSOR_INPUT,
59
+ cost: ZERO_COST,
60
+ contextWindow: 256_000,
61
+ maxTokens: 64_000,
62
+ },
63
+ ];
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@gsd/cursor-cli",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "pi": {
7
+ "extensions": [
8
+ "./index.js"
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,128 @@
1
+ import { execFileSync } from "node:child_process";
2
+ const VERSION_TIMEOUT_MS = 5_000;
3
+ const STATUS_TIMEOUT_MS = 10_000;
4
+ const CHECK_INTERVAL_MS = 30_000;
5
+ let cachedBinaryPresent = null;
6
+ let cachedAuthed = null;
7
+ let lastCheckMs = 0;
8
+ export function getCursorAgentCommand(platform = process.platform) {
9
+ return platform === "win32" ? "cursor-agent.cmd" : "cursor-agent";
10
+ }
11
+ export function getCursorAgentCommandCandidates(platform = process.platform) {
12
+ const command = getCursorAgentCommand(platform);
13
+ return platform === "win32" ? [command, "cursor-agent.exe", "cursor-agent"] : [command];
14
+ }
15
+ export function buildCursorAgentSpawnInvocation(command, args, platform = process.platform) {
16
+ if (platform === "win32") {
17
+ return { command: "cmd", args: ["/c", command, ...args] };
18
+ }
19
+ return { command, args };
20
+ }
21
+ function debugLog(...parts) {
22
+ if (process.env.GSD_CURSOR_DEBUG) {
23
+ process.stderr.write(`[cursor-readiness] ${parts.map((part) => String(part)).join(" ")}\n`);
24
+ }
25
+ }
26
+ function spawnCursorAgent(command, args, timeout) {
27
+ const invocation = buildCursorAgentSpawnInvocation(command, args);
28
+ return execFileSync(invocation.command, invocation.args, { timeout, stdio: "pipe" });
29
+ }
30
+ function findWorkingCommand() {
31
+ for (const command of getCursorAgentCommandCandidates()) {
32
+ try {
33
+ spawnCursorAgent(command, ["--version"], VERSION_TIMEOUT_MS);
34
+ debugLog("version probe ok via", command);
35
+ return command;
36
+ }
37
+ catch (error) {
38
+ debugLog("version probe failed for", command, error.message?.slice(0, 200));
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ export function parseCursorAgentStatus(output) {
44
+ const trimmed = output.trim();
45
+ if (!trimmed)
46
+ return null;
47
+ if (trimmed.startsWith("{")) {
48
+ try {
49
+ const parsed = JSON.parse(trimmed);
50
+ for (const key of ["authenticated", "loggedIn", "logged_in", "isAuthenticated"]) {
51
+ if (typeof parsed[key] === "boolean")
52
+ return parsed[key];
53
+ }
54
+ }
55
+ catch {
56
+ // Fall through to text heuristics.
57
+ }
58
+ }
59
+ const lower = trimmed.toLowerCase();
60
+ if (/not authenticated|not logged in|no credentials|logged out|unauthenticated/.test(lower))
61
+ return false;
62
+ if (/authenticated|logged in|signed in|cursor account|subscription/.test(lower))
63
+ return true;
64
+ return null;
65
+ }
66
+ export function isCursorAgentApiKeyValue(value) {
67
+ const trimmed = value?.trim();
68
+ return Boolean(trimmed && trimmed !== "cli");
69
+ }
70
+ function hasCursorApiKey() {
71
+ return isCursorAgentApiKeyValue(process.env.CURSOR_API_KEY);
72
+ }
73
+ function probeAuth(command) {
74
+ if (hasCursorApiKey())
75
+ return true;
76
+ for (const args of [["agent", "status", "--json"], ["status", "--json"], ["agent", "status"], ["status"]]) {
77
+ try {
78
+ const out = spawnCursorAgent(command, args, STATUS_TIMEOUT_MS).toString();
79
+ debugLog("status output", args.join(" "), out.slice(0, 200));
80
+ const parsed = parseCursorAgentStatus(out);
81
+ if (parsed !== null)
82
+ return parsed;
83
+ }
84
+ catch (error) {
85
+ debugLog("status failed", args.join(" "), error.message?.slice(0, 200));
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+ export function isCursorAgentReadyUncached() {
91
+ const command = findWorkingCommand();
92
+ if (!command)
93
+ return false;
94
+ return probeAuth(command) === true;
95
+ }
96
+ function refreshCache() {
97
+ const now = Date.now();
98
+ if (cachedBinaryPresent !== null && now - lastCheckMs < CHECK_INTERVAL_MS)
99
+ return;
100
+ lastCheckMs = now;
101
+ const command = findWorkingCommand();
102
+ if (!command) {
103
+ cachedBinaryPresent = false;
104
+ cachedAuthed = false;
105
+ return;
106
+ }
107
+ cachedBinaryPresent = true;
108
+ const authed = probeAuth(command);
109
+ if (authed === null) {
110
+ if (cachedAuthed === null)
111
+ cachedAuthed = false;
112
+ return;
113
+ }
114
+ cachedAuthed = authed;
115
+ }
116
+ export function isCursorAgentBinaryPresent() {
117
+ refreshCache();
118
+ return cachedBinaryPresent ?? false;
119
+ }
120
+ export function isCursorAgentReady() {
121
+ refreshCache();
122
+ return (cachedBinaryPresent ?? false) && (cachedAuthed ?? false);
123
+ }
124
+ export function clearCursorAgentReadinessCache() {
125
+ cachedBinaryPresent = null;
126
+ cachedAuthed = null;
127
+ lastCheckMs = 0;
128
+ }