gsd-pi 2.41.0 → 2.42.0-dev.1df898f

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 (494) hide show
  1. package/README.md +92 -29
  2. package/dist/cli-web-branch.d.ts +6 -0
  3. package/dist/cli-web-branch.js +17 -0
  4. package/dist/cli.js +18 -3
  5. package/dist/loader.js +3 -1
  6. package/dist/onboarding.js +2 -1
  7. package/dist/resource-loader.js +39 -6
  8. package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
  9. package/dist/resources/extensions/async-jobs/await-tool.js +5 -0
  10. package/dist/resources/extensions/async-jobs/index.js +2 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +89 -1
  12. package/dist/resources/extensions/gsd/auto/phases.js +29 -13
  13. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  14. package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
  16. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
  17. package/dist/resources/extensions/gsd/auto-prompts.js +3 -16
  18. package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
  19. package/dist/resources/extensions/gsd/auto-start.js +16 -14
  20. package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
  21. package/dist/resources/extensions/gsd/auto.js +64 -2
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
  23. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +25 -3
  26. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
  27. package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  29. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  30. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
  31. package/dist/resources/extensions/gsd/context-injector.js +74 -0
  32. package/dist/resources/extensions/gsd/context-store.js +4 -3
  33. package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
  34. package/dist/resources/extensions/gsd/custom-verification.js +145 -0
  35. package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
  36. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
  37. package/dist/resources/extensions/gsd/db-writer.js +5 -2
  38. package/dist/resources/extensions/gsd/definition-loader.js +352 -0
  39. package/dist/resources/extensions/gsd/detection.js +20 -1
  40. package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
  41. package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
  42. package/dist/resources/extensions/gsd/doctor-checks.js +31 -1
  43. package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
  44. package/dist/resources/extensions/gsd/doctor.js +11 -1
  45. package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
  46. package/dist/resources/extensions/gsd/engine-types.js +8 -0
  47. package/dist/resources/extensions/gsd/execution-policy.js +8 -0
  48. package/dist/resources/extensions/gsd/exit-command.js +12 -2
  49. package/dist/resources/extensions/gsd/export.js +9 -13
  50. package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
  51. package/dist/resources/extensions/gsd/files.js +28 -11
  52. package/dist/resources/extensions/gsd/forensics.js +94 -3
  53. package/dist/resources/extensions/gsd/git-constants.js +1 -0
  54. package/dist/resources/extensions/gsd/git-service.js +6 -2
  55. package/dist/resources/extensions/gsd/graph.js +225 -0
  56. package/dist/resources/extensions/gsd/gsd-db.js +25 -8
  57. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  58. package/dist/resources/extensions/gsd/guided-flow.js +7 -3
  59. package/dist/resources/extensions/gsd/journal.js +85 -0
  60. package/dist/resources/extensions/gsd/md-importer.js +5 -0
  61. package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
  62. package/dist/resources/extensions/gsd/native-git-bridge.js +3 -2
  63. package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
  64. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  65. package/dist/resources/extensions/gsd/preferences.js +60 -8
  66. package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
  67. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  68. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  69. package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
  71. package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
  72. package/dist/resources/extensions/gsd/repo-identity.js +92 -7
  73. package/dist/resources/extensions/gsd/rule-registry.js +489 -0
  74. package/dist/resources/extensions/gsd/rule-types.js +6 -0
  75. package/dist/resources/extensions/gsd/run-manager.js +134 -0
  76. package/dist/resources/extensions/gsd/service-tier.js +147 -0
  77. package/dist/resources/extensions/gsd/session-lock.js +2 -2
  78. package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
  79. package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
  80. package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
  81. package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
  82. package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
  83. package/dist/resources/extensions/gsd/worktree-resolver.js +21 -4
  84. package/dist/resources/extensions/gsd/worktree.js +2 -2
  85. package/dist/resources/extensions/mcp-client/index.js +2 -1
  86. package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
  87. package/dist/resources/extensions/subagent/index.js +7 -3
  88. package/dist/resources/extensions/voice/index.js +4 -4
  89. package/dist/resources/skills/create-workflow/SKILL.md +103 -0
  90. package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  91. package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
  92. package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  93. package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  94. package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  95. package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  96. package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  97. package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  98. package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  99. package/dist/web/standalone/.next/BUILD_ID +1 -1
  100. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  101. package/dist/web/standalone/.next/build-manifest.json +4 -4
  102. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  103. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  104. package/dist/web/standalone/.next/required-server-files.json +3 -3
  105. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  106. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  108. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  116. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  119. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  170. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  176. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  190. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  192. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  194. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  200. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  203. package/dist/web/standalone/.next/server/app/index.html +1 -1
  204. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  205. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  206. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  207. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  208. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  209. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  210. package/dist/web/standalone/.next/server/app/page.js +2 -2
  211. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  213. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  214. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  215. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  217. package/dist/web/standalone/.next/server/middleware.js +2 -2
  218. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  219. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  220. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  221. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  222. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  223. package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
  224. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
  226. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
  227. package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
  228. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  229. package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
  230. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  231. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  232. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  233. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  234. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  235. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  236. package/dist/web/standalone/server.js +1 -1
  237. package/dist/web-mode.d.ts +4 -0
  238. package/dist/web-mode.js +69 -11
  239. package/package.json +1 -1
  240. package/packages/native/src/__tests__/text.test.mjs +33 -0
  241. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  242. package/packages/pi-agent-core/dist/agent.js +2 -0
  243. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  244. package/packages/pi-agent-core/dist/types.d.ts +6 -0
  245. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  246. package/packages/pi-agent-core/dist/types.js.map +1 -1
  247. package/packages/pi-agent-core/src/agent.test.ts +53 -0
  248. package/packages/pi-agent-core/src/agent.ts +3 -0
  249. package/packages/pi-agent-core/src/types.ts +6 -0
  250. package/packages/pi-agent-core/tsconfig.json +1 -1
  251. package/packages/pi-ai/dist/models.d.ts +5 -3
  252. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  253. package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
  254. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  255. package/packages/pi-ai/dist/models.generated.js +1135 -1588
  256. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  257. package/packages/pi-ai/dist/models.js.map +1 -1
  258. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  259. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
  260. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  261. package/packages/pi-ai/scripts/generate-models.ts +1543 -0
  262. package/packages/pi-ai/src/models.generated.ts +1140 -1593
  263. package/packages/pi-ai/src/models.ts +7 -4
  264. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
  265. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -1
  267. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
  269. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
  271. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  272. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
  273. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  274. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
  275. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  276. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  277. package/packages/pi-coding-agent/dist/core/extensions/loader.js +18 -0
  278. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  280. package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
  281. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  282. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -0
  284. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
  286. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
  288. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
  290. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  291. package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
  292. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  294. package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
  295. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
  298. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
  299. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  300. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
  301. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  303. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
  304. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  305. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  306. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +34 -10
  307. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  308. package/packages/pi-coding-agent/package.json +1 -1
  309. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -1
  310. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
  311. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
  312. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
  313. package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
  314. package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
  315. package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
  316. package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
  317. package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
  318. package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
  319. package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
  320. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
  321. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
  322. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +36 -11
  323. package/pkg/package.json +1 -1
  324. package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
  325. package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
  326. package/src/resources/extensions/async-jobs/await-tool.test.ts +47 -0
  327. package/src/resources/extensions/async-jobs/await-tool.ts +5 -0
  328. package/src/resources/extensions/async-jobs/index.ts +1 -0
  329. package/src/resources/extensions/async-jobs/job-manager.ts +2 -0
  330. package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -2
  331. package/src/resources/extensions/gsd/auto/loop.ts +101 -1
  332. package/src/resources/extensions/gsd/auto/phases.ts +31 -13
  333. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  334. package/src/resources/extensions/gsd/auto/types.ts +4 -0
  335. package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
  336. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
  337. package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
  338. package/src/resources/extensions/gsd/auto-prompts.ts +2 -18
  339. package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
  340. package/src/resources/extensions/gsd/auto-start.ts +15 -13
  341. package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
  342. package/src/resources/extensions/gsd/auto.ts +71 -2
  343. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
  344. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
  345. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  346. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +25 -4
  347. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
  348. package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
  349. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  350. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  351. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
  352. package/src/resources/extensions/gsd/context-injector.ts +100 -0
  353. package/src/resources/extensions/gsd/context-store.ts +4 -3
  354. package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
  355. package/src/resources/extensions/gsd/custom-verification.ts +180 -0
  356. package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
  357. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
  358. package/src/resources/extensions/gsd/db-writer.ts +6 -2
  359. package/src/resources/extensions/gsd/definition-loader.ts +462 -0
  360. package/src/resources/extensions/gsd/detection.ts +20 -1
  361. package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
  362. package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
  363. package/src/resources/extensions/gsd/doctor-checks.ts +32 -1
  364. package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
  365. package/src/resources/extensions/gsd/doctor-types.ts +1 -0
  366. package/src/resources/extensions/gsd/doctor.ts +12 -1
  367. package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
  368. package/src/resources/extensions/gsd/engine-types.ts +71 -0
  369. package/src/resources/extensions/gsd/execution-policy.ts +43 -0
  370. package/src/resources/extensions/gsd/exit-command.ts +14 -2
  371. package/src/resources/extensions/gsd/export.ts +8 -15
  372. package/src/resources/extensions/gsd/extension-manifest.json +2 -2
  373. package/src/resources/extensions/gsd/files.ts +29 -12
  374. package/src/resources/extensions/gsd/forensics.ts +101 -3
  375. package/src/resources/extensions/gsd/git-constants.ts +1 -0
  376. package/src/resources/extensions/gsd/git-service.ts +5 -5
  377. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  378. package/src/resources/extensions/gsd/graph.ts +312 -0
  379. package/src/resources/extensions/gsd/gsd-db.ts +37 -8
  380. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  381. package/src/resources/extensions/gsd/guided-flow.ts +7 -3
  382. package/src/resources/extensions/gsd/journal.ts +134 -0
  383. package/src/resources/extensions/gsd/md-importer.ts +6 -0
  384. package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
  385. package/src/resources/extensions/gsd/native-git-bridge.ts +3 -2
  386. package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
  387. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  388. package/src/resources/extensions/gsd/preferences.ts +63 -6
  389. package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
  390. package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  391. package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  392. package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
  393. package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
  394. package/src/resources/extensions/gsd/prompts/queue.md +1 -1
  395. package/src/resources/extensions/gsd/repo-identity.ts +95 -7
  396. package/src/resources/extensions/gsd/rule-registry.ts +599 -0
  397. package/src/resources/extensions/gsd/rule-types.ts +68 -0
  398. package/src/resources/extensions/gsd/run-manager.ts +180 -0
  399. package/src/resources/extensions/gsd/service-tier.ts +184 -0
  400. package/src/resources/extensions/gsd/session-lock.ts +2 -2
  401. package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
  402. package/src/resources/extensions/gsd/templates/decisions.md +2 -2
  403. package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
  404. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
  405. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
  406. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
  407. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
  408. package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
  409. package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
  410. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
  411. package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
  412. package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
  413. package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
  414. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
  415. package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
  416. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
  417. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
  418. package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
  419. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
  420. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
  421. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
  422. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
  423. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
  424. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
  425. package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
  426. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
  427. package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
  428. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +48 -0
  429. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
  430. package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
  431. package/src/resources/extensions/gsd/tests/git-service.test.ts +44 -0
  432. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
  433. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
  434. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
  435. package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
  436. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
  437. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
  438. package/src/resources/extensions/gsd/tests/journal.test.ts +341 -0
  439. package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
  440. package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
  441. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  442. package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
  443. package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
  444. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
  445. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
  446. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
  447. package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
  448. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
  449. package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
  450. package/src/resources/extensions/gsd/tests/service-tier.test.ts +127 -0
  451. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +56 -3
  452. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
  453. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
  454. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
  455. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
  456. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
  457. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
  458. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
  459. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
  460. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
  461. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
  462. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
  463. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +135 -0
  464. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +203 -106
  465. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +79 -5
  466. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
  467. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
  468. package/src/resources/extensions/gsd/types.ts +3 -0
  469. package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
  470. package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
  471. package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
  472. package/src/resources/extensions/gsd/worktree-resolver.ts +32 -12
  473. package/src/resources/extensions/gsd/worktree.ts +2 -2
  474. package/src/resources/extensions/mcp-client/index.ts +5 -1
  475. package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
  476. package/src/resources/extensions/subagent/index.ts +7 -3
  477. package/src/resources/extensions/voice/index.ts +4 -4
  478. package/src/resources/skills/create-workflow/SKILL.md +103 -0
  479. package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  480. package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
  481. package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  482. package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  483. package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  484. package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  485. package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  486. package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  487. package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  488. package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
  489. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
  490. package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
  491. package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
  492. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
  493. /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → qw8qDHXOTLUXBq1vEknSz}/_buildManifest.js +0 -0
  494. /package/dist/web/standalone/.next/static/{Ute3pMouVczQyT15qrBBO → qw8qDHXOTLUXBq1vEknSz}/_ssgManifest.js +0 -0
@@ -1,7 +1,7 @@
1
1
  // GSD Login Dialog Component — OAuth login flow UI
2
2
  // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
3
  import { getOAuthProviders } from "@gsd/pi-ai/oauth";
4
- import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@gsd/pi-tui";
4
+ import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, truncateToWidth, type TUI } from "@gsd/pi-tui";
5
5
  import { execFile } from "child_process";
6
6
  import { theme } from "../theme/theme.js";
7
7
  import { DynamicBorder } from "./dynamic-border.js";
@@ -121,21 +121,25 @@ export class LoginDialogComponent extends Container implements Focusable {
121
121
  showAuth(url: string, instructions?: string): void {
122
122
  this.contentContainer.clear();
123
123
  this.contentContainer.addChild(new Spacer(1));
124
- this.contentContainer.addChild(new Text(theme.fg("accent", url), 1, 0));
124
+
125
+ // Truncate the visible URL text so it never wraps (which would break
126
+ // the OSC 8 hyperlink). The full URL is still the link target.
127
+ const maxUrlWidth = Math.max(20, this.tui.terminal.columns - 4);
128
+ const displayUrl = truncateToWidth(url, maxUrlWidth);
129
+ const urlLink = `\x1b]8;;${url}\x07${theme.fg("accent", displayUrl)}\x1b]8;;\x07`;
130
+ this.contentContainer.addChild(new Text(urlLink, 1, 0));
125
131
 
126
132
  const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
127
- const hyperlink = `\x1b]8;;${url}\x07${clickHint}\x1b]8;;\x07`;
128
- this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
133
+ this.contentContainer.addChild(new Text(theme.fg("dim", clickHint), 1, 0));
129
134
 
130
135
  if (instructions) {
131
136
  this.contentContainer.addChild(new Spacer(1));
132
137
  this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
133
138
  }
134
139
 
135
- // Try to open browser on Windows, `start` needs an empty title arg
136
- // so it treats the URL as a target, not a window title
140
+ // PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
137
141
  if (process.platform === "win32") {
138
- execFile("cmd", ["/c", "start", "", url], () => {});
142
+ execFile("powershell", ["-c", `Start-Process '${url.replace(/'/g, "''")}'`], () => {});
139
143
  } else {
140
144
  const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
141
145
  execFile(openCmd, [url], () => {});
@@ -1519,6 +1519,13 @@ export class InteractiveMode {
1519
1519
  options: string[],
1520
1520
  opts?: ExtensionUIDialogOptions,
1521
1521
  ): Promise<string | undefined> {
1522
+ // If a previous selector is still active, dispose it before creating a
1523
+ // new one. This avoids leaking the previous promise and DOM state when
1524
+ // showExtensionSelector is called rapidly.
1525
+ if (this.extensionSelector) {
1526
+ this.hideExtensionSelector();
1527
+ }
1528
+
1522
1529
  return new Promise((resolve) => {
1523
1530
  if (opts?.signal?.aborted) {
1524
1531
  resolve(undefined);
@@ -2321,23 +2328,34 @@ export class InteractiveMode {
2321
2328
  }
2322
2329
 
2323
2330
  private handleCtrlZ(): void {
2331
+ // On Windows, SIGTSTP doesn't exist - Ctrl+Z is not supported
2332
+ if (process.platform === "win32") {
2333
+ return;
2334
+ }
2335
+
2324
2336
  // Ignore SIGINT while suspended so Ctrl+C in the terminal does not
2325
2337
  // kill the backgrounded process. The handler is removed on resume.
2326
2338
  const ignoreSigint = () => {};
2327
2339
  process.on("SIGINT", ignoreSigint);
2328
2340
 
2329
- // Set up handler to restore TUI when resumed
2330
- process.once("SIGCONT", () => {
2331
- process.removeListener("SIGINT", ignoreSigint);
2332
- this.ui.start();
2333
- this.ui.requestRender(true);
2334
- });
2341
+ try {
2342
+ // Set up handler to restore TUI when resumed
2343
+ process.once("SIGCONT", () => {
2344
+ process.removeListener("SIGINT", ignoreSigint);
2345
+ this.ui.start();
2346
+ this.ui.requestRender(true);
2347
+ });
2335
2348
 
2336
- // Stop the TUI (restore terminal to normal mode)
2337
- this.ui.stop();
2349
+ // Stop the TUI (restore terminal to normal mode)
2350
+ this.ui.stop();
2338
2351
 
2339
- // Send SIGTSTP to process group (pid=0 means all processes in group)
2340
- process.kill(0, "SIGTSTP");
2352
+ // Send SIGTSTP to process group (pid=0 means all processes in group)
2353
+ process.kill(0, "SIGTSTP");
2354
+ } catch {
2355
+ // If suspend fails (e.g. SIGTSTP not supported), ensure the
2356
+ // SIGINT listener doesn't leak.
2357
+ process.removeListener("SIGINT", ignoreSigint);
2358
+ }
2341
2359
  }
2342
2360
 
2343
2361
  private async handleFollowUp(): Promise<void> {
@@ -2455,7 +2473,14 @@ export class InteractiveMode {
2455
2473
  // Determine editor (respect $VISUAL, then $EDITOR)
2456
2474
  const editorCmd = process.env.VISUAL || process.env.EDITOR;
2457
2475
  if (!editorCmd) {
2458
- this.showWarning("No editor configured. Set $VISUAL or $EDITOR environment variable.");
2476
+ let msg = "No editor configured. Set $VISUAL or $EDITOR environment variable.";
2477
+ if (process.env.TERM_PROGRAM === "iTerm.app") {
2478
+ msg +=
2479
+ "\n\nTip: If you meant to open the GSD dashboard (Ctrl+Alt+G), set Left Option Key to" +
2480
+ " \"Esc+\" in iTerm2 → Profiles → Keys. With the default \"Normal\" setting," +
2481
+ " Ctrl+Alt+G sends Ctrl+G instead.";
2482
+ }
2483
+ this.showWarning(msg);
2459
2484
  return;
2460
2485
  }
2461
2486
 
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "2.41.0",
3
+ "version": "2.42.0",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -0,0 +1,122 @@
1
+ /**
2
+ * async-bash-timeout.test.ts — Tests for async_bash timeout behavior.
3
+ *
4
+ * Reproduces issue #2186: when an async bash job exceeds its timeout and
5
+ * the child process ignores SIGTERM, the promise hangs indefinitely.
6
+ * The fix adds a SIGKILL fallback and a hard deadline that force-resolves
7
+ * the promise so execution can continue.
8
+ */
9
+
10
+ import test from "node:test";
11
+ import assert from "node:assert/strict";
12
+ import { createAsyncBashTool } from "./async-bash-tool.ts";
13
+ import { AsyncJobManager } from "./job-manager.ts";
14
+
15
+ function getTextFromResult(result: { content: Array<{ type: string; text?: string }> }): string {
16
+ return result.content.map((c) => c.text ?? "").join("\n");
17
+ }
18
+
19
+ const noopSignal = new AbortController().signal;
20
+
21
+ test("async_bash with timeout resolves even if process ignores SIGTERM", async () => {
22
+ const manager = new AsyncJobManager();
23
+ const tool = createAsyncBashTool(() => manager, () => process.cwd());
24
+
25
+ // Start a job that traps SIGTERM (ignores it), with a 2s timeout.
26
+ // The process installs a SIGTERM trap and sleeps for 60s.
27
+ // Before the fix, this would hang forever because SIGTERM is ignored
28
+ // and the close event never fires.
29
+ const result = await tool.execute(
30
+ "tc-timeout",
31
+ {
32
+ command: "trap '' TERM; sleep 60",
33
+ timeout: 2,
34
+ label: "sigterm-resistant",
35
+ },
36
+ noopSignal,
37
+ () => {},
38
+ undefined as never,
39
+ );
40
+
41
+ const text = getTextFromResult(result);
42
+ assert.match(text, /sigterm-resistant/);
43
+
44
+ const jobId = text.match(/\*\*(bg_[a-f0-9]+)\*\*/)?.[1];
45
+ assert.ok(jobId, "Should have returned a job ID");
46
+
47
+ // Now await the job — it should resolve within a reasonable time
48
+ // (timeout 2s + SIGKILL grace 5s + buffer = well under 15s)
49
+ const start = Date.now();
50
+ const job = manager.getJob(jobId)!;
51
+ assert.ok(job, "Job should exist");
52
+
53
+ await Promise.race([
54
+ job.promise,
55
+ new Promise<never>((_, reject) => {
56
+ const t = setTimeout(() => reject(new Error(
57
+ `Job promise hung for ${Date.now() - start}ms — ` +
58
+ `this is the bug from issue #2186: timeout hangs indefinitely`,
59
+ )), 15_000);
60
+ if (typeof t === "object" && "unref" in t) t.unref();
61
+ }),
62
+ ]);
63
+
64
+ const elapsed = Date.now() - start;
65
+ // Should have resolved well within 15s (timeout 2s + kill grace ~5s)
66
+ assert.ok(elapsed < 15_000, `Job took ${elapsed}ms — expected <15s`);
67
+
68
+ // Job should have completed (resolved, not rejected) with timeout message
69
+ assert.ok(
70
+ job.status === "completed" || job.status === "failed",
71
+ `Job status should be completed or failed, got: ${job.status}`,
72
+ );
73
+
74
+ if (job.status === "completed") {
75
+ assert.ok(
76
+ job.resultText?.includes("timed out") || job.resultText?.includes("Timed out"),
77
+ `Result should mention timeout, got: ${job.resultText}`,
78
+ );
79
+ }
80
+
81
+ manager.shutdown();
82
+ });
83
+
84
+ test("async_bash with timeout resolves normally when process exits on SIGTERM", async () => {
85
+ const manager = new AsyncJobManager();
86
+ const tool = createAsyncBashTool(() => manager, () => process.cwd());
87
+
88
+ // Start a normal sleep that will die on SIGTERM, with a 1s timeout
89
+ const result = await tool.execute(
90
+ "tc-normal-timeout",
91
+ {
92
+ command: "sleep 60",
93
+ timeout: 1,
94
+ label: "normal-timeout",
95
+ },
96
+ noopSignal,
97
+ () => {},
98
+ undefined as never,
99
+ );
100
+
101
+ const text = getTextFromResult(result);
102
+ const jobId = text.match(/\*\*(bg_[a-f0-9]+)\*\*/)?.[1];
103
+ assert.ok(jobId, "Should have returned a job ID");
104
+
105
+ const job = manager.getJob(jobId)!;
106
+ const start = Date.now();
107
+
108
+ await Promise.race([
109
+ job.promise,
110
+ new Promise<never>((_, reject) => {
111
+ const t = setTimeout(() => reject(new Error("Job hung")), 10_000);
112
+ if (typeof t === "object" && "unref" in t) t.unref();
113
+ }),
114
+ ]);
115
+
116
+ const elapsed = Date.now() - start;
117
+ assert.ok(elapsed < 5_000, `Expected quick resolution after SIGTERM, took ${elapsed}ms`);
118
+ assert.equal(job.status, "completed");
119
+ assert.ok(job.resultText?.includes("timed out"), `Should mention timeout: ${job.resultText}`);
120
+
121
+ manager.shutdown();
122
+ });
@@ -109,6 +109,10 @@ function executeBashInBackground(
109
109
  timeout?: number,
110
110
  ): Promise<string> {
111
111
  return new Promise<string>((resolve, reject) => {
112
+ let settled = false;
113
+ const safeResolve = (value: string) => { if (!settled) { settled = true; resolve(value); } };
114
+ const safeReject = (err: unknown) => { if (!settled) { settled = true; reject(err); } };
115
+
112
116
  const { shell, args } = getShellConfig();
113
117
  const resolvedCommand = sanitizeCommand(command);
114
118
 
@@ -121,11 +125,39 @@ function executeBashInBackground(
121
125
 
122
126
  let timedOut = false;
123
127
  let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
128
+ let sigkillHandle: ReturnType<typeof setTimeout> | undefined;
129
+ let hardDeadlineHandle: ReturnType<typeof setTimeout> | undefined;
130
+
131
+ /** Grace period (ms) between SIGTERM and SIGKILL. */
132
+ const SIGKILL_GRACE_MS = 5_000;
133
+ /** Hard deadline (ms) after SIGKILL to force-resolve the promise. */
134
+ const HARD_DEADLINE_MS = 3_000;
124
135
 
125
136
  if (timeout !== undefined && timeout > 0) {
126
137
  timeoutHandle = setTimeout(() => {
127
138
  timedOut = true;
128
139
  if (child.pid) killTree(child.pid);
140
+
141
+ // If the process ignores SIGTERM, escalate to SIGKILL
142
+ sigkillHandle = setTimeout(() => {
143
+ if (child.pid) {
144
+ try { process.kill(-child.pid, "SIGKILL"); } catch { /* ignore */ }
145
+ try { process.kill(child.pid, "SIGKILL"); } catch { /* ignore */ }
146
+ }
147
+
148
+ // Hard deadline: if even SIGKILL doesn't trigger 'close',
149
+ // force-resolve so the job doesn't hang forever (#2186).
150
+ hardDeadlineHandle = setTimeout(() => {
151
+ const output = Buffer.concat(chunks).toString("utf-8");
152
+ safeResolve(
153
+ output
154
+ ? `${output}\n\nCommand timed out after ${timeout} seconds (force-killed)`
155
+ : `Command timed out after ${timeout} seconds (force-killed)`,
156
+ );
157
+ }, HARD_DEADLINE_MS);
158
+ if (typeof hardDeadlineHandle === "object" && "unref" in hardDeadlineHandle) hardDeadlineHandle.unref();
159
+ }, SIGKILL_GRACE_MS);
160
+ if (typeof sigkillHandle === "object" && "unref" in sigkillHandle) sigkillHandle.unref();
129
161
  }, timeout * 1000);
130
162
  }
131
163
 
@@ -168,24 +200,28 @@ function executeBashInBackground(
168
200
 
169
201
  child.on("error", (err) => {
170
202
  if (timeoutHandle) clearTimeout(timeoutHandle);
203
+ if (sigkillHandle) clearTimeout(sigkillHandle);
204
+ if (hardDeadlineHandle) clearTimeout(hardDeadlineHandle);
171
205
  signal.removeEventListener("abort", onAbort);
172
- reject(err);
206
+ safeReject(err);
173
207
  });
174
208
 
175
209
  child.on("close", (code) => {
176
210
  if (timeoutHandle) clearTimeout(timeoutHandle);
211
+ if (sigkillHandle) clearTimeout(sigkillHandle);
212
+ if (hardDeadlineHandle) clearTimeout(hardDeadlineHandle);
177
213
  signal.removeEventListener("abort", onAbort);
178
214
  if (spillStream) spillStream.end();
179
215
 
180
216
  if (signal.aborted) {
181
217
  const output = Buffer.concat(chunks).toString("utf-8");
182
- resolve(output ? `${output}\n\nCommand aborted` : "Command aborted");
218
+ safeResolve(output ? `${output}\n\nCommand aborted` : "Command aborted");
183
219
  return;
184
220
  }
185
221
 
186
222
  if (timedOut) {
187
223
  const output = Buffer.concat(chunks).toString("utf-8");
188
- resolve(output ? `${output}\n\nCommand timed out after ${timeout} seconds` : `Command timed out after ${timeout} seconds`);
224
+ safeResolve(output ? `${output}\n\nCommand timed out after ${timeout} seconds` : `Command timed out after ${timeout} seconds`);
189
225
  return;
190
226
  }
191
227
 
@@ -208,7 +244,7 @@ function executeBashInBackground(
208
244
  text += `\n\nCommand exited with code ${code}`;
209
245
  }
210
246
 
211
- resolve(text);
247
+ safeResolve(text);
212
248
  });
213
249
  });
214
250
  }
@@ -118,3 +118,50 @@ test("await_job returns not-found message for invalid job IDs", async () => {
118
118
 
119
119
  manager.shutdown();
120
120
  });
121
+
122
+ test("await_job marks jobs as awaited to suppress follow-up delivery (#2248)", async () => {
123
+ const followUps: string[] = [];
124
+ const manager = new AsyncJobManager({
125
+ onJobComplete: (job) => {
126
+ if (!job.awaited) followUps.push(job.id);
127
+ },
128
+ });
129
+ const tool = createAwaitTool(() => manager);
130
+
131
+ // Register a job that completes in 50ms
132
+ const jobId = manager.register("bash", "awaited-job", async () => {
133
+ return new Promise<string>((resolve) => setTimeout(() => resolve("result"), 50));
134
+ });
135
+
136
+ // await_job consumes the result — should mark as awaited before promise resolves
137
+ await tool.execute("tc7", { jobs: [jobId] }, noopSignal, () => {}, undefined as never);
138
+
139
+ // Give the onJobComplete callback a tick to fire
140
+ await new Promise((r) => setTimeout(r, 50));
141
+
142
+ assert.equal(followUps.length, 0, "onJobComplete should not deliver follow-up for awaited jobs");
143
+
144
+ manager.shutdown();
145
+ });
146
+
147
+ test("unawaited jobs still get follow-up delivery (#2248)", async () => {
148
+ const followUps: string[] = [];
149
+ const manager = new AsyncJobManager({
150
+ onJobComplete: (job) => {
151
+ if (!job.awaited) followUps.push(job.id);
152
+ },
153
+ });
154
+
155
+ // Register a fire-and-forget job
156
+ const jobId = manager.register("bash", "fire-and-forget", async () => "done");
157
+ const job = manager.getJob(jobId)!;
158
+ await job.promise;
159
+
160
+ // Give the callback a tick
161
+ await new Promise((r) => setTimeout(r, 50));
162
+
163
+ assert.equal(followUps.length, 1, "onJobComplete should deliver follow-up for unawaited jobs");
164
+ assert.equal(followUps[0], jobId);
165
+
166
+ manager.shutdown();
167
+ });
@@ -66,6 +66,11 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
66
66
  }
67
67
  }
68
68
 
69
+ // Mark all watched jobs as awaited upfront so the onJobComplete
70
+ // callback (which fires synchronously in the promise .then()) knows
71
+ // to suppress the follow-up message.
72
+ for (const j of watched) j.awaited = true;
73
+
69
74
  // If all watched jobs are already done, return immediately
70
75
  const running = watched.filter((j) => j.status === "running");
71
76
  if (running.length === 0) {
@@ -42,6 +42,7 @@ export default function AsyncJobs(pi: ExtensionAPI) {
42
42
 
43
43
  manager = new AsyncJobManager({
44
44
  onJobComplete: (job) => {
45
+ if (job.awaited) return;
45
46
  const statusEmoji = job.status === "completed" ? "done" : "error";
46
47
  const elapsed = ((Date.now() - job.startTime) / 1000).toFixed(1);
47
48
  const output = job.status === "completed"
@@ -22,6 +22,8 @@ export interface Job {
22
22
  promise: Promise<void>;
23
23
  resultText?: string;
24
24
  errorText?: string;
25
+ /** Set by await_job when results are consumed. Suppresses follow-up delivery. */
26
+ awaited?: boolean;
25
27
  }
26
28
 
27
29
  export interface JobManagerOptions {
@@ -19,6 +19,7 @@ import type {
19
19
  import type { DispatchAction } from "../auto-dispatch.js";
20
20
  import type { WorktreeResolver } from "../worktree-resolver.js";
21
21
  import type { CmuxLogLevel } from "../../cmux/index.js";
22
+ import type { JournalEntry } from "../journal.js";
22
23
 
23
24
  /**
24
25
  * Dependencies injected by the caller (auto.ts startAuto) so autoLoop
@@ -102,13 +103,12 @@ export interface LoopDeps {
102
103
  basePath: string,
103
104
  milestoneId: string,
104
105
  roadmapContent: string,
105
- ) => { pushed: boolean };
106
+ ) => { pushed: boolean; codeFilesChanged: boolean };
106
107
  teardownAutoWorktree: (basePath: string, milestoneId: string) => void;
107
108
  createAutoWorktree: (basePath: string, milestoneId: string) => string;
108
109
  captureIntegrationBranch: (
109
110
  basePath: string,
110
111
  mid: string,
111
- opts?: { commitDocs?: boolean },
112
112
  ) => void;
113
113
  getIsolationMode: () => string;
114
114
  getCurrentBranch: (basePath: string) => string;
@@ -285,4 +285,7 @@ export interface LoopDeps {
285
285
 
286
286
  // Session manager
287
287
  getSessionFile: (ctx: ExtensionContext) => string;
288
+
289
+ // Journal
290
+ emitJournalEvent: (entry: JournalEntry) => void;
288
291
  }
@@ -9,6 +9,7 @@
9
9
 
10
10
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
11
11
 
12
+ import { randomUUID } from "node:crypto";
12
13
  import type { AutoSession, SidecarItem } from "./session.js";
13
14
  import type { LoopDeps } from "./loop-deps.js";
14
15
  import {
@@ -27,6 +28,7 @@ import {
27
28
  } from "./phases.js";
28
29
  import { debugLog } from "../debug-logger.js";
29
30
  import { isInfrastructureError } from "./infra-errors.js";
31
+ import { resolveEngine } from "../engine-resolver.js";
30
32
 
31
33
  /**
32
34
  * Main auto-mode execution loop. Iterates: derive → dispatch → guards →
@@ -51,6 +53,11 @@ export async function autoLoop(
51
53
  iteration++;
52
54
  debugLog("autoLoop", { phase: "loop-top", iteration });
53
55
 
56
+ // ── Journal: per-iteration flow grouping ──
57
+ const flowId = randomUUID();
58
+ let seqCounter = 0;
59
+ const nextSeq = () => ++seqCounter;
60
+
54
61
  if (iteration > MAX_LOOP_ITERATIONS) {
55
62
  debugLog("autoLoop", {
56
63
  phase: "exit",
@@ -84,6 +91,7 @@ export async function autoLoop(
84
91
  unitType: sidecarItem.unitType,
85
92
  unitId: sidecarItem.unitId,
86
93
  });
94
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
87
95
  }
88
96
 
89
97
  const sessionLockBase = deps.lockBase();
@@ -106,9 +114,100 @@ export async function autoLoop(
106
114
  }
107
115
  }
108
116
 
109
- const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration };
117
+ const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
118
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
110
119
  let iterData: IterationData;
111
120
 
121
+ // ── Custom engine path ──────────────────────────────────────────────
122
+ // When activeEngineId is a non-dev value, bypass runPreDispatch and
123
+ // runDispatch entirely — the custom engine drives its own state via
124
+ // GRAPH.yaml. Shares runGuards and runUnitPhase with the dev path.
125
+ // After unit execution, verifies then reconciles via the engine layer.
126
+ //
127
+ // GSD_ENGINE_BYPASS=1 skips the engine layer entirely — falls through
128
+ // to the dev path below.
129
+ if (s.activeEngineId != null && s.activeEngineId !== "dev" && !sidecarItem && process.env.GSD_ENGINE_BYPASS !== "1") {
130
+ debugLog("autoLoop", { phase: "custom-engine-derive", iteration, engineId: s.activeEngineId });
131
+
132
+ const { engine, policy } = resolveEngine({
133
+ activeEngineId: s.activeEngineId,
134
+ activeRunDir: s.activeRunDir,
135
+ });
136
+
137
+ const engineState = await engine.deriveState(s.basePath);
138
+ if (engineState.isComplete) {
139
+ await deps.stopAuto(ctx, pi, "Workflow complete");
140
+ break;
141
+ }
142
+
143
+ debugLog("autoLoop", { phase: "custom-engine-dispatch", iteration });
144
+ const dispatch = await engine.resolveDispatch(engineState, { basePath: s.basePath });
145
+
146
+ if (dispatch.action === "stop") {
147
+ await deps.stopAuto(ctx, pi, dispatch.reason ?? "Engine stopped");
148
+ break;
149
+ }
150
+ if (dispatch.action === "skip") {
151
+ continue;
152
+ }
153
+
154
+ // dispatch.action === "dispatch"
155
+ const step = dispatch.step!;
156
+ const gsdState = await deps.deriveState(s.basePath);
157
+
158
+ iterData = {
159
+ unitType: step.unitType,
160
+ unitId: step.unitId,
161
+ prompt: step.prompt,
162
+ finalPrompt: step.prompt,
163
+ pauseAfterUatDispatch: false,
164
+ observabilityIssues: [],
165
+ state: gsdState,
166
+ mid: s.currentMilestoneId ?? "workflow",
167
+ midTitle: "Workflow",
168
+ isRetry: false,
169
+ previousTier: undefined,
170
+ };
171
+
172
+ // ── Progress widget (mirrors dev path in runDispatch) ──
173
+ deps.updateProgressWidget(ctx, iterData.unitType, iterData.unitId, iterData.state);
174
+
175
+ // ── Guards (shared with dev path) ──
176
+ const guardsResult = await runGuards(ic, s.currentMilestoneId ?? "workflow");
177
+ if (guardsResult.action === "break") break;
178
+
179
+ // ── Unit execution (shared with dev path) ──
180
+ const unitPhaseResult = await runUnitPhase(ic, iterData, loopState);
181
+ if (unitPhaseResult.action === "break") break;
182
+
183
+ // ── Verify first, then reconcile (only mark complete on pass) ──
184
+ debugLog("autoLoop", { phase: "custom-engine-verify", iteration, unitId: iterData.unitId });
185
+ const verifyResult = await policy.verify(iterData.unitType, iterData.unitId, { basePath: s.basePath });
186
+ if (verifyResult === "pause") {
187
+ await deps.pauseAuto(ctx, pi);
188
+ break;
189
+ }
190
+ if (verifyResult === "retry") {
191
+ debugLog("autoLoop", { phase: "custom-engine-verify-retry", iteration, unitId: iterData.unitId });
192
+ continue;
193
+ }
194
+
195
+ // Verification passed — mark step complete
196
+ debugLog("autoLoop", { phase: "custom-engine-reconcile", iteration, unitId: iterData.unitId });
197
+ await engine.reconcile(engineState, {
198
+ unitType: iterData.unitType,
199
+ unitId: iterData.unitId,
200
+ startedAt: s.currentUnit?.startedAt ?? Date.now(),
201
+ finishedAt: Date.now(),
202
+ });
203
+
204
+ deps.clearUnitTimeout();
205
+ consecutiveErrors = 0;
206
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
207
+ debugLog("autoLoop", { phase: "iteration-complete", iteration });
208
+ continue;
209
+ }
210
+
112
211
  if (!sidecarItem) {
113
212
  // ── Phase 1: Pre-dispatch ─────────────────────────────────────────
114
213
  const preDispatchResult = await runPreDispatch(ic, loopState);
@@ -153,6 +252,7 @@ export async function autoLoop(
153
252
  if (finalizeResult.action === "continue") continue;
154
253
 
155
254
  consecutiveErrors = 0; // Iteration completed successfully
255
+ deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
156
256
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
157
257
  } catch (loopErr) {
158
258
  // ── Blanket catch: absorb unexpected exceptions, apply graduated recovery ──