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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (657) hide show
  1. package/README.md +11 -11
  2. package/dist/onboarding.js +11 -0
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/GSD-WORKFLOW.md +20 -25
  5. package/dist/resources/extensions/ask-user-questions.js +1 -1
  6. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +118 -53
  7. package/dist/resources/extensions/cursor-cli/index.js +16 -0
  8. package/dist/resources/extensions/cursor-cli/models.js +63 -0
  9. package/dist/resources/extensions/cursor-cli/package.json +11 -0
  10. package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
  11. package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
  12. package/dist/resources/extensions/gsd/activity-log.js +4 -1
  13. package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
  14. package/dist/resources/extensions/gsd/auto/closeout.js +137 -42
  15. package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
  16. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
  17. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
  18. package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
  19. package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
  20. package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
  21. package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
  22. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
  23. package/dist/resources/extensions/gsd/auto-dispatch.js +31 -7
  24. package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
  25. package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
  26. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  27. package/dist/resources/extensions/gsd/auto-start.js +8 -6
  28. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
  29. package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
  30. package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
  31. package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
  32. package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
  33. package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
  34. package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
  35. package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
  36. package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
  37. package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
  38. package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
  39. package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
  40. package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
  41. package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
  42. package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
  43. package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
  44. package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
  45. package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
  46. package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
  47. package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
  48. package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
  49. package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
  50. package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
  51. package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
  52. package/dist/resources/extensions/gsd/auto-worktree.js +20 -2046
  53. package/dist/resources/extensions/gsd/auto.js +31 -5
  54. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
  55. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
  56. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +134 -7
  57. package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
  58. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  59. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +194 -28
  60. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
  61. package/dist/resources/extensions/gsd/codebase-generator.js +164 -20
  62. package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
  63. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  64. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
  65. package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
  66. package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
  67. package/dist/resources/extensions/gsd/db/queries.js +48 -9
  68. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  69. package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
  70. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  71. package/dist/resources/extensions/gsd/doctor-engine-checks.js +292 -18
  72. package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
  73. package/dist/resources/extensions/gsd/doctor.js +93 -3
  74. package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
  75. package/dist/resources/extensions/gsd/flat-phase-migration.js +106 -26
  76. package/dist/resources/extensions/gsd/forensics.js +1 -1
  77. package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
  78. package/dist/resources/extensions/gsd/git-service.js +59 -18
  79. package/dist/resources/extensions/gsd/gsd-db.js +75 -11
  80. package/dist/resources/extensions/gsd/guidance.js +1 -0
  81. package/dist/resources/extensions/gsd/guided-flow.js +62 -19
  82. package/dist/resources/extensions/gsd/key-manager.js +1 -0
  83. package/dist/resources/extensions/gsd/markdown-renderer.js +40 -11
  84. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  85. package/dist/resources/extensions/gsd/migrate-external.js +24 -7
  86. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  87. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +14 -3
  88. package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
  89. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  90. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
  91. package/dist/resources/extensions/gsd/paths.js +17 -3
  92. package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
  93. package/dist/resources/extensions/gsd/preferences-models.js +6 -4
  94. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  95. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  96. package/dist/resources/extensions/gsd/preferences-validation.js +107 -2
  97. package/dist/resources/extensions/gsd/preferences.js +49 -2
  98. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  99. package/dist/resources/extensions/gsd/prompts/system.md +6 -6
  100. package/dist/resources/extensions/gsd/quick.js +12 -3
  101. package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
  102. package/dist/resources/extensions/gsd/repo-identity.js +16 -0
  103. package/dist/resources/extensions/gsd/repository-registry.js +6 -1
  104. package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
  105. package/dist/resources/extensions/gsd/safe-fs.js +23 -2
  106. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  107. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  108. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  109. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
  110. package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
  111. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  112. package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
  113. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  114. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  115. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
  116. package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
  117. package/dist/resources/extensions/gsd/unit-registry.js +37 -2
  118. package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
  119. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
  120. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
  121. package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
  122. package/dist/resources/extensions/gsd/workspace.js +26 -8
  123. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  124. package/dist/resources/extensions/gsd/worktree-lifecycle.js +81 -14
  125. package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
  126. package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
  127. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  128. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  129. package/dist/web/standalone/.next/BUILD_ID +1 -1
  130. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  131. package/dist/web/standalone/.next/build-manifest.json +3 -3
  132. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  133. package/dist/web/standalone/.next/required-server-files.json +3 -3
  134. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  135. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  137. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  145. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  147. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  148. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  149. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  151. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  161. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  173. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  195. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  203. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  205. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  210. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  211. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  213. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  217. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  218. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  219. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  221. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  223. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  225. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  226. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  227. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  228. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  229. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  230. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  231. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  232. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  233. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  234. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  235. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  236. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  237. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  238. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  239. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  240. package/dist/web/standalone/.next/server/app/index.html +1 -1
  241. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  242. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  243. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  244. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  245. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  246. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  247. package/dist/web/standalone/.next/server/app/page.js +2 -2
  248. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  249. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  250. package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
  251. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  252. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  253. package/dist/web/standalone/.next/server/middleware.js +3 -3
  254. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  255. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  256. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  257. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +1 -0
  258. package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
  259. package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
  260. package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
  261. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
  262. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  263. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  264. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  265. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  266. package/dist/web/standalone/server.js +1 -1
  267. package/dist/wizard.d.ts +8 -0
  268. package/dist/wizard.js +8 -1
  269. package/integrations/hermes/CONTEXT.md +47 -0
  270. package/integrations/hermes/README.md +21 -0
  271. package/integrations/hermes/__init__.py +31 -0
  272. package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
  273. package/integrations/hermes/docs/setup.md +4 -0
  274. package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
  275. package/integrations/hermes/open_gsd_hermes/config.py +2 -0
  276. package/integrations/hermes/open_gsd_hermes/gsd_client.py +591 -39
  277. package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
  278. package/integrations/hermes/plugin.yaml +4 -0
  279. package/integrations/hermes/tests/test_config.py +6 -0
  280. package/integrations/hermes/tests/test_directory_loader.py +37 -0
  281. package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
  282. package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
  283. package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
  284. package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
  285. package/integrations/hermes/tests/test_milestone_client.py +447 -0
  286. package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
  287. package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
  288. package/package.json +8 -8
  289. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
  290. package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
  291. package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
  292. package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
  293. package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
  294. package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
  295. package/packages/cloud-mcp-gateway/package.json +2 -2
  296. package/packages/contracts/package.json +1 -1
  297. package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
  298. package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
  299. package/packages/daemon/dist/cloud-runtime.js +25 -1
  300. package/packages/daemon/dist/cloud-runtime.js.map +1 -1
  301. package/packages/daemon/dist/cloud-runtime.test.js +51 -0
  302. package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
  303. package/packages/daemon/dist/message-batcher.d.ts +7 -0
  304. package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
  305. package/packages/daemon/dist/message-batcher.js +13 -0
  306. package/packages/daemon/dist/message-batcher.js.map +1 -1
  307. package/packages/daemon/dist/message-batcher.test.js +48 -0
  308. package/packages/daemon/dist/message-batcher.test.js.map +1 -1
  309. package/packages/daemon/dist/session-manager.js +1 -1
  310. package/packages/daemon/dist/session-manager.js.map +1 -1
  311. package/packages/daemon/package.json +5 -5
  312. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
  313. package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
  314. package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
  315. package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
  316. package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
  317. package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
  318. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
  319. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
  320. package/packages/gsd-agent-core/package.json +5 -5
  321. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  322. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
  323. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  324. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
  325. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
  326. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
  327. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
  328. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  329. package/packages/gsd-agent-modes/package.json +7 -7
  330. package/packages/mcp-server/dist/server.d.ts +18 -5
  331. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  332. package/packages/mcp-server/dist/server.js +46 -5
  333. package/packages/mcp-server/dist/server.js.map +1 -1
  334. package/packages/mcp-server/dist/session-manager.js +1 -1
  335. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  336. package/packages/mcp-server/package.json +4 -4
  337. package/packages/native/package.json +1 -1
  338. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  339. package/packages/pi-agent-core/dist/agent-loop.js +14 -9
  340. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  341. package/packages/pi-agent-core/package.json +1 -1
  342. package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
  343. package/packages/pi-ai/dist/env-api-keys.js +3 -0
  344. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  345. package/packages/pi-ai/dist/index.d.ts +1 -0
  346. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  347. package/packages/pi-ai/dist/index.js +1 -0
  348. package/packages/pi-ai/dist/index.js.map +1 -1
  349. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  350. package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
  351. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  352. package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
  353. package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
  354. package/packages/pi-ai/dist/tool-result-content.js +62 -0
  355. package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
  356. package/packages/pi-ai/dist/types.d.ts +10 -22
  357. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  358. package/packages/pi-ai/dist/types.js.map +1 -1
  359. package/packages/pi-ai/package.json +1 -1
  360. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  361. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  362. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  363. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  364. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  365. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  366. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  367. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  368. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  370. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
  372. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
  373. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
  374. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  375. package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
  376. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  377. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  378. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  379. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  380. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  381. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  382. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  383. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  384. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  385. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  386. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  387. package/packages/pi-coding-agent/dist/index.js +1 -0
  388. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  389. package/packages/pi-coding-agent/package.json +7 -7
  390. package/packages/pi-tui/package.json +2 -2
  391. package/packages/rpc-client/dist/rpc-client.js +1 -1
  392. package/packages/rpc-client/dist/rpc-client.js.map +1 -1
  393. package/packages/rpc-client/package.json +2 -2
  394. package/pkg/dist/core/export-html/template.js +53 -4
  395. package/pkg/package.json +1 -1
  396. package/scripts/link-workspace-packages.cjs +14 -4
  397. package/src/resources/GSD-WORKFLOW.md +20 -25
  398. package/src/resources/extensions/ask-user-questions.ts +1 -1
  399. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
  400. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
  401. package/src/resources/extensions/cursor-cli/index.ts +18 -0
  402. package/src/resources/extensions/cursor-cli/models.ts +65 -0
  403. package/src/resources/extensions/cursor-cli/package.json +11 -0
  404. package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
  405. package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
  406. package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
  407. package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
  408. package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
  409. package/src/resources/extensions/gsd/activity-log.ts +4 -1
  410. package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
  411. package/src/resources/extensions/gsd/auto/closeout.ts +163 -58
  412. package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
  413. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  414. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
  415. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
  416. package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
  417. package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
  418. package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
  419. package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
  420. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
  421. package/src/resources/extensions/gsd/auto-dispatch.ts +35 -7
  422. package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
  423. package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
  424. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  425. package/src/resources/extensions/gsd/auto-start.ts +9 -6
  426. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
  427. package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
  428. package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
  429. package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
  430. package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
  431. package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
  432. package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
  433. package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
  434. package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
  435. package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
  436. package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
  437. package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
  438. package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
  439. package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
  440. package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
  441. package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
  442. package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
  443. package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
  444. package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
  445. package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
  446. package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
  447. package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
  448. package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
  449. package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
  450. package/src/resources/extensions/gsd/auto-worktree.ts +69 -2390
  451. package/src/resources/extensions/gsd/auto.ts +32 -19
  452. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
  453. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
  454. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +148 -7
  455. package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
  456. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  457. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +229 -28
  458. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
  459. package/src/resources/extensions/gsd/codebase-generator.ts +186 -19
  460. package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
  461. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  462. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
  463. package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
  464. package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
  465. package/src/resources/extensions/gsd/db/queries.ts +49 -9
  466. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  467. package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
  468. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
  469. package/src/resources/extensions/gsd/doctor-engine-checks.ts +372 -22
  470. package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
  471. package/src/resources/extensions/gsd/doctor-types.ts +5 -1
  472. package/src/resources/extensions/gsd/doctor.ts +95 -3
  473. package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
  474. package/src/resources/extensions/gsd/flat-phase-migration.ts +132 -25
  475. package/src/resources/extensions/gsd/forensics.ts +1 -1
  476. package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
  477. package/src/resources/extensions/gsd/git-service.ts +67 -17
  478. package/src/resources/extensions/gsd/gsd-db.ts +105 -11
  479. package/src/resources/extensions/gsd/guidance.ts +2 -0
  480. package/src/resources/extensions/gsd/guided-flow.ts +71 -20
  481. package/src/resources/extensions/gsd/key-manager.ts +1 -0
  482. package/src/resources/extensions/gsd/markdown-renderer.ts +40 -10
  483. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  484. package/src/resources/extensions/gsd/migrate-external.ts +23 -6
  485. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  486. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +19 -4
  487. package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
  488. package/src/resources/extensions/gsd/model-router.ts +4 -5
  489. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  490. package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
  491. package/src/resources/extensions/gsd/paths.ts +21 -3
  492. package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
  493. package/src/resources/extensions/gsd/preferences-models.ts +6 -3
  494. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  495. package/src/resources/extensions/gsd/preferences-types.ts +30 -0
  496. package/src/resources/extensions/gsd/preferences-validation.ts +97 -2
  497. package/src/resources/extensions/gsd/preferences.ts +58 -4
  498. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  499. package/src/resources/extensions/gsd/prompts/system.md +6 -6
  500. package/src/resources/extensions/gsd/quick.ts +11 -3
  501. package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
  502. package/src/resources/extensions/gsd/repo-identity.ts +17 -0
  503. package/src/resources/extensions/gsd/repository-registry.ts +7 -1
  504. package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
  505. package/src/resources/extensions/gsd/safe-fs.ts +22 -2
  506. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  507. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
  508. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
  509. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
  510. package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
  511. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  512. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
  513. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
  514. package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
  515. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
  516. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
  517. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -0
  518. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
  519. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
  520. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
  521. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
  522. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
  523. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
  524. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +10 -1
  525. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
  526. package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
  527. package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
  528. package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
  529. package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
  530. package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
  531. package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
  532. package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
  533. package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
  534. package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
  535. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
  536. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
  537. package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
  538. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
  539. package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
  540. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
  541. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +387 -0
  542. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
  543. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
  544. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
  545. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
  546. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +571 -5
  547. package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
  548. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
  549. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +109 -2
  550. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
  551. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
  552. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
  553. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
  554. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
  555. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  556. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
  557. package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
  558. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
  559. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
  560. package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
  561. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
  562. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
  563. package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
  564. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
  565. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
  566. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  567. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
  568. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  569. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
  570. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
  571. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
  572. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
  573. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
  574. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
  575. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  576. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +592 -23
  577. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +25 -1
  578. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
  579. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
  580. package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
  581. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
  582. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
  583. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
  584. package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
  585. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
  586. package/src/resources/extensions/gsd/tests/preferences.test.ts +169 -0
  587. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +147 -0
  588. package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
  589. package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
  590. package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
  591. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
  592. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  593. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
  594. package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
  595. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
  596. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  597. package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
  598. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  599. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
  600. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
  601. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
  602. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
  603. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
  604. package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
  605. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
  606. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
  607. package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
  608. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
  609. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
  610. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
  611. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
  612. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +259 -0
  613. package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
  614. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
  615. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +81 -0
  616. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
  617. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
  618. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
  619. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
  620. package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
  621. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
  622. package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
  623. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
  624. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
  625. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
  626. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
  627. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
  628. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
  629. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
  630. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
  631. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +36 -3
  632. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
  633. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
  634. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
  635. package/src/resources/extensions/gsd/tool-contract.ts +29 -0
  636. package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
  637. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  638. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  639. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
  640. package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
  641. package/src/resources/extensions/gsd/unit-registry.ts +48 -2
  642. package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
  643. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
  644. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
  645. package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
  646. package/src/resources/extensions/gsd/workspace.ts +42 -8
  647. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  648. package/src/resources/extensions/gsd/worktree-lifecycle.ts +154 -26
  649. package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
  650. package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
  651. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  652. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  653. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
  654. package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
  655. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
  656. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
  657. /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
@@ -14,34 +14,173 @@
14
14
  * to stop tooling for the rest of that turn and answer in text.
15
15
  *
16
16
  * The per-tool-name check (#783 Brief C) tracks call counts within a
17
- * turn regardless of args, decaying after successful state-mutating tools.
17
+ * turn regardless of args, decaying after successful state-progressing tools.
18
18
  * This catches improvisation
19
19
  * loops where the model attempts the same missing workflow tool through
20
20
  * varied surfaces (bash → `node -e` → CLI), each with a different
21
21
  * signature, so the identical-args streak never trips, while allowing
22
- * tool-heavy turns that are making file-mutation progress. Whichever guard
23
- * trips first blocks.
22
+ * tool-heavy turns that are making progress. Progress means a successful file
23
+ * mutation (edit/write) or a successful shell/exec call (bg_shell,
24
+ * gsd_uat_exec, …) — so a local model running a legitimate iterative
25
+ * debugging loop (restart server, npm install, curl an endpoint) is not
26
+ * blocked as a false loop (#1206). Whichever guard trips first blocks.
27
+ *
28
+ * Thresholds, exempt tools, and enable flags are user-tunable (#1198) via the
29
+ * `tool_call_loop_guard` key in `.gsd/PREFERENCES.md` and `GSD_TOOL_LOOP_*`
30
+ * environment variables, applied through {@link configureToolCallLoopGuard}.
31
+ * Defaults preserve the original hardcoded behavior.
24
32
  */
25
33
  import { createHash } from "node:crypto";
26
34
  import { INHERENTLY_REPEATABLE_TOOL_SET } from "./core-session-tools.js";
27
- const MAX_CONSECUTIVE_IDENTICAL_CALLS = 4;
35
+ import { hasBrowserContractPrefix } from "../../shared/browser-contract.js";
36
+ import { canonicalToolName } from "../engine-hook-contract.js";
37
+ /** Built-in defaults. Preserved when preferences/env do not override them. */
38
+ const DEFAULT_MAX_CONSECUTIVE_IDENTICAL_CALLS = 4;
39
+ const DEFAULT_PER_TOOL_DEFAULT_CAP = 6;
40
+ const DEFAULT_PER_TOOL_REPEATABLE_CAP = 15;
41
+ const DEFAULT_PER_TOOL_CAP_EXEMPT_TOOLS = ["find", "glob", "grep", "ls", "read", "search_and_read"];
28
42
  /** Interactive/user-facing tools where even 1 duplicate is confusing. */
29
43
  const STRICT_LOOP_TOOLS = new Set(["ask_user_questions"]);
30
44
  const MAX_CONSECUTIVE_STRICT = 1;
45
+ const STATE_MUTATING_TOOL_SET = new Set(["edit", "write", "multi_edit", "notebook_edit"]);
31
46
  /**
32
- * Per-turn cap on calls to the SAME tool name, regardless of args (#783).
47
+ * Successful shell/exec calls are state progression too (#1206), not just file
48
+ * mutations. A local model debugging a UAT scenario legitimately restarts
49
+ * servers, runs `npm install`, and curls endpoints via bg_shell / gsd_uat_exec
50
+ * with varied args — productive work that never touches a file, so Guard 2's
51
+ * per-tool cap would otherwise misfire as a false loop. Decaying on these
52
+ * successful calls keeps the guard from aborting a progressing turn.
33
53
  *
34
- * General-purpose execution tools are routinely called many times per turn
35
- * (touching multiple files, running several commands), so they get a higher
36
- * ceiling. Everything else workflow one-shot tools (e.g. gsd_complete_milestone)
37
- * and any non-allowlisted tool gets the default cap. The default is generous
38
- * enough to absorb legitimate retries but catches the reported improvisation
39
- * loop (~51 calls) well before a cost spike.
54
+ * Only successful calls reach {@link recordToolCallLoopMutation}: the
55
+ * tool_result hook skips `isError` results, and the bash tool throws on
56
+ * non-zero exit, so a genuinely stuck improvisation loop of failing commands
57
+ * (#783) never decays and still trips the cap. `async_bash` is excluded
58
+ * because it only registers a background job; command success is reported
59
+ * later via `await_job`. `bg_shell` may return `isError: false` for failed
60
+ * `run`/`start` outcomes, so the hook passes `details` for validation.
40
61
  */
41
- const PER_TOOL_DEFAULT_CAP = 6;
42
- const PER_TOOL_REPEATABLE_CAP = 15;
43
- const PER_TOOL_CAP_EXEMPT_TOOLS = new Set(["find", "glob", "grep", "ls", "read", "search_and_read"]);
44
- const STATE_MUTATING_TOOL_SET = new Set(["edit", "write", "multi_edit", "notebook_edit"]);
62
+ const STATE_PROGRESSING_EXEC_TOOL_SET = new Set([
63
+ "bash",
64
+ "bg_shell",
65
+ "shell",
66
+ "powershell",
67
+ "gsd_exec",
68
+ "gsd_uat_exec",
69
+ ]);
70
+ function isSuccessfulBgShellLoopProgress(details) {
71
+ if (!details || typeof details !== "object")
72
+ return false;
73
+ const record = details;
74
+ switch (record.action) {
75
+ case "run":
76
+ return record.exitCode === 0 && record.timedOut !== true;
77
+ case "start":
78
+ case "restart": {
79
+ const process = record.process;
80
+ if (!process || typeof process !== "object")
81
+ return false;
82
+ return process.alive === true;
83
+ }
84
+ case "wait_for_ready":
85
+ return record.ready === true;
86
+ case "send_and_wait":
87
+ return record.matched === true;
88
+ default:
89
+ return true;
90
+ }
91
+ }
92
+ function defaultGuardConfig() {
93
+ return {
94
+ guardEnabled: true,
95
+ identicalEnabled: true,
96
+ maxConsecutiveIdentical: DEFAULT_MAX_CONSECUTIVE_IDENTICAL_CALLS,
97
+ repeatedEnabled: true,
98
+ perToolDefaultCap: DEFAULT_PER_TOOL_DEFAULT_CAP,
99
+ perToolRepeatableCap: DEFAULT_PER_TOOL_REPEATABLE_CAP,
100
+ perToolExempt: new Set(DEFAULT_PER_TOOL_CAP_EXEMPT_TOOLS),
101
+ };
102
+ }
103
+ let config = defaultGuardConfig();
104
+ /** Parse a positive-integer env var, returning undefined when unset/invalid. */
105
+ function envPositiveInt(name) {
106
+ const raw = process.env[name];
107
+ if (raw === undefined || raw.trim() === "")
108
+ return undefined;
109
+ const n = Number(raw);
110
+ return Number.isFinite(n) && n >= 1 ? Math.floor(n) : undefined;
111
+ }
112
+ /** Parse a boolean env var (`true`/`1` vs `false`/`0`), undefined when unset. */
113
+ function envBool(name) {
114
+ const raw = process.env[name];
115
+ if (raw === undefined || raw.trim() === "")
116
+ return undefined;
117
+ const v = raw.trim().toLowerCase();
118
+ if (v === "true" || v === "1" || v === "yes" || v === "on")
119
+ return true;
120
+ if (v === "false" || v === "0" || v === "no" || v === "off")
121
+ return false;
122
+ return undefined;
123
+ }
124
+ /** Parse a comma-separated tool-name list env var. */
125
+ function envToolList(name) {
126
+ const raw = process.env[name];
127
+ if (!raw)
128
+ return [];
129
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
130
+ }
131
+ /**
132
+ * Resolve preferences + environment overrides into the active configuration.
133
+ * Environment variables win over preferences; preferences win over built-in
134
+ * defaults. User-supplied exempt tools are additive to the built-in exempt
135
+ * set so defaults are always preserved.
136
+ *
137
+ * Applied to both interactive sessions and `/gsd auto` (called from the
138
+ * session_start / session_switch hooks).
139
+ */
140
+ export function configureToolCallLoopGuard(prefs) {
141
+ const next = defaultGuardConfig();
142
+ if (prefs) {
143
+ if (typeof prefs.enabled === "boolean")
144
+ next.guardEnabled = prefs.enabled;
145
+ if (prefs.identical_args) {
146
+ if (typeof prefs.identical_args.enabled === "boolean")
147
+ next.identicalEnabled = prefs.identical_args.enabled;
148
+ if (typeof prefs.identical_args.max_consecutive_calls === "number" && prefs.identical_args.max_consecutive_calls >= 1) {
149
+ next.maxConsecutiveIdentical = Math.floor(prefs.identical_args.max_consecutive_calls);
150
+ }
151
+ }
152
+ if (prefs.repeated_tool) {
153
+ if (typeof prefs.repeated_tool.enabled === "boolean")
154
+ next.repeatedEnabled = prefs.repeated_tool.enabled;
155
+ if (typeof prefs.repeated_tool.default_cap === "number" && prefs.repeated_tool.default_cap >= 1) {
156
+ next.perToolDefaultCap = Math.floor(prefs.repeated_tool.default_cap);
157
+ }
158
+ if (typeof prefs.repeated_tool.repeatable_cap === "number" && prefs.repeated_tool.repeatable_cap >= 1) {
159
+ next.perToolRepeatableCap = Math.floor(prefs.repeated_tool.repeatable_cap);
160
+ }
161
+ for (const tool of prefs.repeated_tool.exempt_tools ?? []) {
162
+ if (typeof tool === "string" && tool.trim())
163
+ next.perToolExempt.add(tool.trim());
164
+ }
165
+ }
166
+ }
167
+ // Environment overrides (win over preferences).
168
+ const envEnabled = envBool("GSD_TOOL_LOOP_GUARD_ENABLED");
169
+ if (envEnabled !== undefined)
170
+ next.guardEnabled = envEnabled;
171
+ const envIdenticalMax = envPositiveInt("GSD_TOOL_LOOP_IDENTICAL_MAX");
172
+ if (envIdenticalMax !== undefined)
173
+ next.maxConsecutiveIdentical = envIdenticalMax;
174
+ const envDefaultCap = envPositiveInt("GSD_TOOL_LOOP_REPEATED_DEFAULT_CAP");
175
+ if (envDefaultCap !== undefined)
176
+ next.perToolDefaultCap = envDefaultCap;
177
+ const envRepeatableCap = envPositiveInt("GSD_TOOL_LOOP_REPEATED_REPEATABLE_CAP");
178
+ if (envRepeatableCap !== undefined)
179
+ next.perToolRepeatableCap = envRepeatableCap;
180
+ for (const tool of envToolList("GSD_TOOL_LOOP_EXEMPT_TOOLS"))
181
+ next.perToolExempt.add(tool);
182
+ config = next;
183
+ }
45
184
  let consecutiveCount = 0;
46
185
  let lastSignature = "";
47
186
  let lastToolName = "";
@@ -70,14 +209,17 @@ function hashToolCall(toolName, args) {
70
209
  * Returns `{ block: true, reason }` when the loop threshold is exceeded.
71
210
  *
72
211
  * Two independent guards run; whichever trips first blocks:
73
- * 1. Identical-signature streak (MAX_CONSECUTIVE_IDENTICAL_CALLS, strict for
212
+ * 1. Identical-signature streak (config.maxConsecutiveIdentical, strict for
74
213
  * ask_user_questions).
75
- * 2. Per-tool-name cap (PER_TOOL_DEFAULT_CAP / PER_TOOL_REPEATABLE_CAP),
214
+ * 2. Per-tool-name cap (config.perToolDefaultCap / config.perToolRepeatableCap),
76
215
  * independent of args, reset after file-mutation progress — catches
77
216
  * improvisation loops (#783).
217
+ *
218
+ * Both guards, their thresholds, and the per-tool exempt set are user-tunable
219
+ * via {@link configureToolCallLoopGuard} (#1198).
78
220
  */
79
221
  export function checkToolCallLoop(toolName, args) {
80
- if (!enabled)
222
+ if (!enabled || !config.guardEnabled)
81
223
  return { block: false, count: 0 };
82
224
  const sig = hashToolCall(toolName, args);
83
225
  if (sig === lastSignature) {
@@ -91,12 +233,14 @@ export function checkToolCallLoop(toolName, args) {
91
233
  // ── Guard 1: identical-signature streak ──
92
234
  const threshold = STRICT_LOOP_TOOLS.has(toolName)
93
235
  ? MAX_CONSECUTIVE_STRICT
94
- : MAX_CONSECUTIVE_IDENTICAL_CALLS;
95
- if (consecutiveCount > threshold) {
236
+ : config.maxConsecutiveIdentical;
237
+ if (config.identicalEnabled && consecutiveCount > threshold) {
96
238
  return {
97
239
  block: true,
98
240
  reason: `Tool loop detected (identical args): ${toolName} called ${consecutiveCount} times ` +
99
- `with identical arguments. Blocking to prevent infinite loop. ` +
241
+ `with identical arguments (max ${threshold}). Blocking to prevent infinite loop. ` +
242
+ `Raise tool_call_loop_guard.identical_args.max_consecutive_calls in .gsd/PREFERENCES.md ` +
243
+ `(or GSD_TOOL_LOOP_IDENTICAL_MAX) if this is expected. ` +
100
244
  `Do not retry this tool or call other tools this turn — stop and respond to the user in text.`,
101
245
  count: consecutiveCount,
102
246
  };
@@ -111,30 +255,52 @@ export function checkToolCallLoop(toolName, args) {
111
255
  perToolCounts.set(toolName, perToolCount);
112
256
  perToolLastMutationEpoch.set(toolName, mutationEpoch);
113
257
  // Read-only navigation tools are normal context gathering; Guard 1 still
114
- // catches true reread loops with identical arguments.
115
- if (PER_TOOL_CAP_EXEMPT_TOOLS.has(toolName)) {
258
+ // catches true reread loops with identical arguments. Browser Automation
259
+ // Contract tools (browser_*) are the same shape: a browser-backed UAT makes
260
+ // many distinct-arg calls (read a message, count edits, inspect a card, …),
261
+ // so the arg-independent per-tool cap misfires on legitimate verification;
262
+ // Guard 1's identical-signature streak still catches a genuinely stuck
263
+ // browser loop.
264
+ if (!config.repeatedEnabled ||
265
+ config.perToolExempt.has(toolName) ||
266
+ hasBrowserContractPrefix(canonicalToolName(toolName))) {
116
267
  return { block: false, count: consecutiveCount };
117
268
  }
118
269
  const perToolCap = INHERENTLY_REPEATABLE_TOOL_SET.has(toolName)
119
- ? PER_TOOL_REPEATABLE_CAP
120
- : PER_TOOL_DEFAULT_CAP;
270
+ ? config.perToolRepeatableCap
271
+ : config.perToolDefaultCap;
121
272
  if (perToolCount > perToolCap) {
122
273
  return {
123
274
  block: true,
124
275
  reason: `Tool loop detected (repeated tool): ${toolName} called ${perToolCount} times ` +
125
276
  `this turn (cap ${perToolCap}). Blocking to prevent infinite loop. ` +
126
277
  `The tool may be unavailable or failing repeatedly. ` +
278
+ `Raise tool_call_loop_guard.repeated_tool.default_cap/repeatable_cap or add "${toolName}" ` +
279
+ `to tool_call_loop_guard.repeated_tool.exempt_tools in .gsd/PREFERENCES.md ` +
280
+ `(or GSD_TOOL_LOOP_REPEATED_* / GSD_TOOL_LOOP_EXEMPT_TOOLS) if this is expected. ` +
127
281
  `Do not retry this tool or pivot to other tools this turn — stop and respond to the user in text.`,
128
282
  count: perToolCount,
129
283
  };
130
284
  }
131
285
  return { block: false, count: consecutiveCount };
132
286
  }
133
- /** Record successful mutation progress so Guard 2 can decay on the next count. */
134
- export function recordToolCallLoopMutation(toolName) {
287
+ /**
288
+ * Record successful state progress so Guard 2 can decay on the next count.
289
+ * File mutations (edit/write/…) and successful shell/exec calls (bash,
290
+ * bg_shell, gsd_uat_exec, …) both count as progress (#1092, #1206). The caller
291
+ * only invokes this for non-error results, so failing improvisation loops
292
+ * (#783) never decay and still trip the per-tool cap.
293
+ */
294
+ export function recordToolCallLoopMutation(toolName, details) {
135
295
  if (!enabled)
136
296
  return;
137
- if (!STATE_MUTATING_TOOL_SET.has(toolName))
297
+ if (STATE_MUTATING_TOOL_SET.has(toolName)) {
298
+ mutationEpoch++;
299
+ return;
300
+ }
301
+ if (!STATE_PROGRESSING_EXEC_TOOL_SET.has(toolName))
302
+ return;
303
+ if (toolName === "bg_shell" && details !== undefined && !isSuccessfulBgShellLoopProgress(details))
138
304
  return;
139
305
  mutationEpoch++;
140
306
  }
@@ -947,7 +947,7 @@ function allowedPlanningDispatchAgentsList() {
947
947
  return [...ALLOWED_PLANNING_DISPATCH_AGENTS].join(", ");
948
948
  }
949
949
  function allowsControlledSubagentDispatch(policy) {
950
- return ((policy.mode === "planning-dispatch" || policy.mode === "verification") &&
950
+ return ((policy.mode === "planning-dispatch" || policy.mode === "verification" || policy.mode === "workflow-only") &&
951
951
  Array.isArray(policy.allowedSubagents));
952
952
  }
953
953
  function warnMissingControlledDispatchAgentClasses(unitType, mode, toolName) {
@@ -975,6 +975,7 @@ function warnMissingControlledDispatchAgentClasses(unitType, mode, toolName) {
975
975
  const PLANNING_SAFE_TOOLS = new Set([
976
976
  "read", "grep", "find", "ls", "glob",
977
977
  "ask_user_questions",
978
+ "memory_query",
978
979
  "search-the-web", "resolve_library", "get_library_docs", "fetch_page",
979
980
  "search_and_read",
980
981
  ]);
@@ -1031,6 +1032,9 @@ function planningBlock(unitType, mode, what) {
1031
1032
  * → allows Bash for project verification commands, keeps
1032
1033
  * writes restricted to .gsd/, and permits subagent dispatch
1033
1034
  * only when the manifest declares allowedSubagents.
1035
+ * - "workflow-only"
1036
+ * → allows reads, GSD workflow tools, and declared read-only
1037
+ * subagents; blocks generic Bash and direct file writes.
1034
1038
  *
1035
1039
  * `pathOrCommand` is the file path for write/edit-shaped tools and the
1036
1040
  * shell command for bash. Other tools ignore this argument.
@@ -1101,6 +1105,15 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
1101
1105
  }
1102
1106
  return planningBlock(unitType, policy.mode, "subagent dispatch is not permitted in planning units");
1103
1107
  }
1108
+ if (policy.mode === "workflow-only") {
1109
+ if (tool === "bash") {
1110
+ return planningBlock(unitType, policy.mode, `bash is not permitted; use the unit's dedicated GSD workflow tools for durable validation output`);
1111
+ }
1112
+ if (PLANNING_WRITE_TOOLS.has(tool)) {
1113
+ return planningBlock(unitType, policy.mode, `cannot ${tool} "${pathOrCommand}" — direct artifact writes are disabled; call the required GSD workflow tool instead`);
1114
+ }
1115
+ return planningBlock(unitType, policy.mode, `tool "${tool}" is not on the workflow-only allowlist`);
1116
+ }
1104
1117
  if (tool === "bash") {
1105
1118
  if (policy.mode === "verification") {
1106
1119
  if (BASH_VERIFICATION_RE.test(pathOrCommand) || BASH_READ_ONLY_RE.test(pathOrCommand))
@@ -9,9 +9,11 @@
9
9
  */
10
10
  import { createHash } from "node:crypto";
11
11
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
12
- import { join, dirname, extname } from "node:path";
12
+ import { join, dirname, extname, relative, sep } from "node:path";
13
13
  import { execSync } from "node:child_process";
14
14
  import { gsdRoot } from "./paths.js";
15
+ import { createRepositoryRegistryFromPreferences, } from "./repository-registry.js";
16
+ import { loadEffectiveGSDPreferences } from "./preferences.js";
15
17
  // ─── Defaults ────────────────────────────────────────────────────────────────
16
18
  const DEFAULT_EXCLUDES = [
17
19
  // ── AI / tooling meta ──
@@ -144,6 +146,84 @@ function enumerateFiles(basePath, excludes, maxFiles) {
144
146
  const truncated = filtered.length > maxFiles;
145
147
  return { files: truncated ? filtered.slice(0, maxFiles) : filtered, truncated };
146
148
  }
149
+ /**
150
+ * Build a workspace-aware registry for parent mode, or null when the project is
151
+ * single-repo (no declared child repositories). Returns null so the caller falls
152
+ * back to the legacy single-root enumeration and produces byte-identical output.
153
+ */
154
+ function loadWorkspaceRegistry(basePath) {
155
+ const preferences = loadEffectiveGSDPreferences(basePath)?.preferences;
156
+ const workspace = preferences?.workspace;
157
+ const mode = workspace?.mode ?? "project";
158
+ // Parent mode is only meaningful with at least one declared child repo; the
159
+ // implicit "project" repo is always present, so require a declared non-project id.
160
+ const hasDeclaredChildRepo = workspace?.repositories
161
+ ? Object.keys(workspace.repositories).some((id) => id !== "project")
162
+ : false;
163
+ if (mode !== "parent" || !hasDeclaredChildRepo) {
164
+ return null;
165
+ }
166
+ return createRepositoryRegistryFromPreferences(basePath, preferences);
167
+ }
168
+ /** Stable key fragment for workspace-aware enumeration inputs (mode + declared repos). */
169
+ function workspaceEnumerationSignature(basePath) {
170
+ const registry = loadWorkspaceRegistry(basePath);
171
+ if (!registry)
172
+ return "single-root";
173
+ return JSON.stringify({
174
+ mode: registry.mode,
175
+ repositories: registry.repositories
176
+ .map((repo) => ({ id: repo.id, root: repo.root }))
177
+ .sort((a, b) => a.id.localeCompare(b.id)),
178
+ });
179
+ }
180
+ /**
181
+ * Enumerate tracked files across every declared repository in a parent workspace.
182
+ * Child-repo paths are rewritten to be workspace-relative (e.g. `frontend/src/x`)
183
+ * and tagged with their declaring repo id so the map can render repo-labelled
184
+ * sections. The maxFiles cap is applied across the union of all repositories.
185
+ */
186
+ function enumerateWorkspaceFiles(registry, excludes, maxFiles) {
187
+ const perRepo = [];
188
+ const seen = new Set();
189
+ let totalEligible = 0;
190
+ for (const repo of registry.repositories) {
191
+ const rawFiles = lsFiles(repo.root);
192
+ const repoPrefix = relative(registry.projectRoot, repo.root).split(sep).join("/");
193
+ const repoEntries = [];
194
+ for (const file of rawFiles) {
195
+ // Rewrite before exclusion so workspace-relative patterns (e.g. frontend/…) apply.
196
+ const workspacePath = repoPrefix ? `${repoPrefix}/${file}` : file;
197
+ if (shouldExclude(workspacePath, excludes))
198
+ continue;
199
+ if (seen.has(workspacePath))
200
+ continue;
201
+ seen.add(workspacePath);
202
+ totalEligible++;
203
+ repoEntries.push({ workspacePath, repoId: repo.id });
204
+ }
205
+ perRepo.push(repoEntries);
206
+ }
207
+ const files = [];
208
+ const repos = [];
209
+ const indices = new Array(perRepo.length).fill(0);
210
+ while (files.length < maxFiles) {
211
+ let progressed = false;
212
+ for (let i = 0; i < perRepo.length; i++) {
213
+ if (indices[i] < perRepo[i].length) {
214
+ const entry = perRepo[i][indices[i]++];
215
+ files.push(entry.workspacePath);
216
+ repos.push(entry.repoId);
217
+ progressed = true;
218
+ if (files.length >= maxFiles)
219
+ break;
220
+ }
221
+ }
222
+ if (!progressed)
223
+ break;
224
+ }
225
+ return { files, repos, truncated: totalEligible > files.length };
226
+ }
147
227
  function resolveGeneratorOptions(options) {
148
228
  const excludes = [...DEFAULT_EXCLUDES, ...(options?.excludePatterns ?? [])];
149
229
  const maxFiles = options?.maxFiles ?? DEFAULT_MAX_FILES;
@@ -159,38 +239,69 @@ function resolveGeneratorOptions(options) {
159
239
  }),
160
240
  };
161
241
  }
162
- function computeCodebaseFingerprint(files, resolved, truncated) {
242
+ function computeCodebaseFingerprint(files, resolved, truncated, repos) {
163
243
  return createHash("sha1")
164
244
  .update(JSON.stringify({
165
245
  files,
246
+ ...(repos ? { repos } : {}),
166
247
  truncated,
167
248
  optionSignature: resolved.optionSignature,
168
249
  }))
169
250
  .digest("hex");
170
251
  }
171
252
  // ─── Grouping ────────────────────────────────────────────────────────────────
172
- function groupByDirectory(files, descriptions, collapseThreshold) {
253
+ function groupByDirectory(files, descriptions, collapseThreshold, repos) {
173
254
  const dirMap = new Map();
174
- for (const file of files) {
255
+ for (let i = 0; i < files.length; i++) {
256
+ const file = files[i];
175
257
  const dir = dirname(file);
176
- const dirKey = dir === "." ? "" : dir;
177
- if (!dirMap.has(dirKey)) {
178
- dirMap.set(dirKey, []);
258
+ const dirPath = dir === "." ? "" : dir;
259
+ const repoId = repos?.[i];
260
+ const groupKey = repoId !== undefined ? `${repoId}\0${dirPath}` : dirPath;
261
+ if (!dirMap.has(groupKey)) {
262
+ dirMap.set(groupKey, []);
179
263
  }
180
- dirMap.get(dirKey).push({
264
+ dirMap.get(groupKey).push({
181
265
  path: file,
182
266
  description: descriptions.get(file) ?? "",
267
+ repo: repoId,
183
268
  });
184
269
  }
185
270
  const groups = [];
186
- const sortedDirs = [...dirMap.keys()].sort();
187
- for (const dir of sortedDirs) {
188
- const dirFiles = dirMap.get(dir);
271
+ const repoOrder = new Map();
272
+ if (repos) {
273
+ for (let i = 0; i < repos.length; i++) {
274
+ if (!repoOrder.has(repos[i])) {
275
+ repoOrder.set(repos[i], repoOrder.size);
276
+ }
277
+ }
278
+ }
279
+ const sortedDirs = [...dirMap.keys()].sort((a, b) => {
280
+ // Single-repo (project) mode must stay byte-identical to the pre-workspace
281
+ // output, which ordered directories with a bare `.sort()` (UTF-16 code-unit
282
+ // order, so uppercase sorts before lowercase). localeCompare would diverge
283
+ // for mixed-case names (e.g. "Zoo" before "apple" flips), so preserve
284
+ // code-unit ordering here; only the repo-partitioning key is added on top.
285
+ if (!repos)
286
+ return a < b ? -1 : a > b ? 1 : 0;
287
+ const repoA = dirMap.get(a)[0]?.repo ?? "";
288
+ const repoB = dirMap.get(b)[0]?.repo ?? "";
289
+ const orderA = repoOrder.get(repoA) ?? 0;
290
+ const orderB = repoOrder.get(repoB) ?? 0;
291
+ if (orderA !== orderB)
292
+ return orderA - orderB;
293
+ return a.localeCompare(b);
294
+ });
295
+ for (const groupKey of sortedDirs) {
296
+ const dirFiles = dirMap.get(groupKey);
189
297
  dirFiles.sort((a, b) => a.path.localeCompare(b.path));
298
+ const dirPath = repos ? groupKey.slice(groupKey.indexOf("\0") + 1) : groupKey;
190
299
  groups.push({
191
- path: dir,
300
+ path: dirPath,
192
301
  files: dirFiles,
193
302
  collapsed: dirFiles.length > collapseThreshold,
303
+ // Each group is scoped to one repo in workspace-aware mode.
304
+ repo: dirFiles[0]?.repo,
194
305
  });
195
306
  }
196
307
  return groups;
@@ -207,7 +318,18 @@ function renderCodebaseMap(groups, totalFiles, truncated, metadata) {
207
318
  lines.push(`Note: Truncated to first ${totalFiles} files. Run with higher --max-files to include all.`);
208
319
  }
209
320
  lines.push("");
321
+ let emittedRepo;
210
322
  for (const group of groups) {
323
+ // In a workspace-aware map, partition directories under their declaring
324
+ // repository. `## [repo-id]` headings are used (not `###`) so they never
325
+ // collide with the per-directory `### <dir>/` headings or the
326
+ // `parseCodebaseMap` list-item regexes. Groups are sorted by declaring
327
+ // repo (enumeration order) then directory path so each repo is contiguous.
328
+ if (group.repo && group.repo !== emittedRepo) {
329
+ emittedRepo = group.repo;
330
+ lines.push(`## [${group.repo}]`);
331
+ lines.push("");
332
+ }
211
333
  const heading = group.path || "(root)";
212
334
  lines.push(`### ${heading}/`);
213
335
  if (group.collapsed) {
@@ -250,13 +372,15 @@ function renderCodebaseMap(groups, totalFiles, truncated, metadata) {
250
372
  function buildCodebaseMap(basePath, resolved, existingDescriptions, enumerated) {
251
373
  const listed = enumerated ?? enumerateFiles(basePath, resolved.excludes, resolved.maxFiles);
252
374
  const descriptions = existingDescriptions ?? new Map();
253
- const groups = groupByDirectory(listed.files, descriptions, resolved.collapseThreshold);
375
+ const groups = groupByDirectory(listed.files, descriptions, resolved.collapseThreshold, listed.repos);
254
376
  const generatedAt = new Date().toISOString().split(".")[0] + "Z";
377
+ const repoIds = listed.repos ? Array.from(new Set(listed.repos)) : undefined;
255
378
  const metadata = {
256
379
  generatedAt,
257
- fingerprint: computeCodebaseFingerprint(listed.files, resolved, listed.truncated),
380
+ fingerprint: computeCodebaseFingerprint(listed.files, resolved, listed.truncated, listed.repos),
258
381
  fileCount: listed.files.length,
259
382
  truncated: listed.truncated,
383
+ ...(repoIds ? { repositories: repoIds } : {}),
260
384
  };
261
385
  const content = renderCodebaseMap(groups, listed.files.length, listed.truncated, metadata);
262
386
  return {
@@ -273,9 +397,21 @@ function buildCodebaseMap(basePath, resolved, existingDescriptions, enumerated)
273
397
  * Generate a fresh CODEBASE.md from scratch.
274
398
  * Preserves existing descriptions if `existingDescriptions` is provided.
275
399
  */
400
+ /**
401
+ * Resolve workspace-aware enumeration for parent mode, or undefined to fall
402
+ * back to the legacy single-root enumeration. Centralized so all three public
403
+ * entry points (generate/update/ensure) share one discovery path.
404
+ */
405
+ function resolveWorkspaceEnumeration(basePath, resolved) {
406
+ const registry = loadWorkspaceRegistry(basePath);
407
+ if (!registry)
408
+ return undefined;
409
+ return enumerateWorkspaceFiles(registry, resolved.excludes, resolved.maxFiles);
410
+ }
276
411
  export function generateCodebaseMap(basePath, options, existingDescriptions) {
277
412
  const resolved = resolveGeneratorOptions(options);
278
- return buildCodebaseMap(basePath, resolved, existingDescriptions);
413
+ const enumerated = resolveWorkspaceEnumeration(basePath, resolved);
414
+ return buildCodebaseMap(basePath, resolved, existingDescriptions, enumerated);
279
415
  }
280
416
  /**
281
417
  * Incremental update: re-scan files, preserve existing descriptions,
@@ -293,7 +429,8 @@ export function updateCodebaseMap(basePath, options) {
293
429
  const existingFiles = new Set(existingDescriptions.keys());
294
430
  // Generate new map preserving descriptions — reuse the returned file list
295
431
  // to avoid a second enumeration (prevents race between content and stats).
296
- const result = buildCodebaseMap(basePath, resolved, existingDescriptions);
432
+ const enumerated = resolveWorkspaceEnumeration(basePath, resolved);
433
+ const result = buildCodebaseMap(basePath, resolved, existingDescriptions, enumerated);
297
434
  const currentSet = new Set(result.files);
298
435
  // Count changes
299
436
  let added = 0;
@@ -326,7 +463,7 @@ function clearFreshnessCache(basePath) {
326
463
  }
327
464
  export function ensureCodebaseMapFresh(basePath, options, ensureOptions) {
328
465
  const resolved = resolveGeneratorOptions(options);
329
- const cacheKey = `${basePath}::${resolved.optionSignature}`;
466
+ const cacheKey = `${basePath}::${resolved.optionSignature}::${workspaceEnumerationSignature(basePath)}`;
330
467
  const ttlMs = ensureOptions?.ttlMs ?? DEFAULT_REFRESH_TTL_MS;
331
468
  const force = ensureOptions?.force === true;
332
469
  const now = Date.now();
@@ -338,8 +475,9 @@ export function ensureCodebaseMapFresh(basePath, options, ensureOptions) {
338
475
  }
339
476
  }
340
477
  const existing = readCodebaseMap(basePath);
341
- const listed = enumerateFiles(basePath, resolved.excludes, resolved.maxFiles);
342
- const fingerprint = computeCodebaseFingerprint(listed.files, resolved, listed.truncated);
478
+ const listed = resolveWorkspaceEnumeration(basePath, resolved)
479
+ ?? enumerateFiles(basePath, resolved.excludes, resolved.maxFiles);
480
+ const fingerprint = computeCodebaseFingerprint(listed.files, resolved, listed.truncated, listed.repos);
343
481
  const cacheAndReturn = (result) => {
344
482
  freshnessCache.set(cacheKey, { checkedAt: now, result });
345
483
  return result;
@@ -368,9 +506,15 @@ export function ensureCodebaseMapFresh(basePath, options, ensureOptions) {
368
506
  }
369
507
  const metadata = parseCodebaseMapMetadata(existing);
370
508
  const existingDescriptions = parseCodebaseMap(existing);
509
+ // Compare against the truncated count: when the repository has more tracked
510
+ // files than maxFiles, the map only lists the first maxFiles entries, so a
511
+ // stored fileCount above the cap (e.g. from an older map that recorded the
512
+ // full count) still matches the current truncated listing. Comparing raw
513
+ // values would flag "file-count-changed" on every turn, rewriting the
514
+ // timestamp and invalidating the provider KV cache prefix.
371
515
  const staleReason = !metadata ? "missing-metadata"
372
516
  : metadata.fingerprint !== fingerprint ? "files-changed"
373
- : metadata.fileCount !== listed.files.length ? "file-count-changed"
517
+ : Math.min(metadata.fileCount, resolved.maxFiles) !== listed.files.length ? "file-count-changed"
374
518
  : metadata.truncated !== listed.truncated ? "truncation-changed"
375
519
  : undefined;
376
520
  if (!staleReason) {