@opengsd/gsd-pi 1.4.0 → 1.5.0-dev.553dc518

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