gsd-pi 2.77.0 → 2.78.0-dev.aeeb2ca00

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 (878) hide show
  1. package/README.md +51 -33
  2. package/dist/claude-cli-check.js +46 -10
  3. package/dist/cli-web-branch.d.ts +1 -0
  4. package/dist/cli-web-branch.js +3 -0
  5. package/dist/cli.js +38 -2
  6. package/dist/extension-discovery.d.ts +6 -0
  7. package/dist/extension-discovery.js +37 -0
  8. package/dist/extension-registry.d.ts +3 -0
  9. package/dist/extension-sort.d.ts +18 -0
  10. package/dist/extension-sort.js +114 -0
  11. package/dist/extension-validator.d.ts +47 -0
  12. package/dist/extension-validator.js +127 -0
  13. package/dist/headless.js +49 -4
  14. package/dist/loader.js +35 -7
  15. package/dist/provider-migrations.d.ts +18 -0
  16. package/dist/provider-migrations.js +14 -0
  17. package/dist/resource-loader.d.ts +40 -0
  18. package/dist/resource-loader.js +32 -13
  19. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  20. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  21. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  22. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  23. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  24. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  25. package/dist/resources/extensions/claude-code-cli/readiness.js +72 -16
  26. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +552 -67
  27. package/dist/resources/extensions/cmux/index.js +20 -0
  28. package/dist/resources/extensions/github-sync/templates.js +103 -0
  29. package/dist/resources/extensions/google-search/extension-manifest.json +5 -4
  30. package/dist/resources/extensions/google-search/index.js +3 -375
  31. package/dist/resources/extensions/gsd/abandon-detect.js +44 -0
  32. package/dist/resources/extensions/gsd/auto/loop.js +124 -2
  33. package/dist/resources/extensions/gsd/auto/phases.js +57 -39
  34. package/dist/resources/extensions/gsd/auto/resolve.js +24 -0
  35. package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
  36. package/dist/resources/extensions/gsd/auto/session.js +6 -2
  37. package/dist/resources/extensions/gsd/auto/turn-epoch.js +95 -0
  38. package/dist/resources/extensions/gsd/auto-dispatch.js +201 -38
  39. package/dist/resources/extensions/gsd/auto-loop.js +1 -1
  40. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  41. package/dist/resources/extensions/gsd/auto-post-unit.js +215 -64
  42. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  43. package/dist/resources/extensions/gsd/auto-recovery.js +210 -24
  44. package/dist/resources/extensions/gsd/auto-start.js +122 -30
  45. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +11 -5
  46. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  47. package/dist/resources/extensions/gsd/auto-unit-closeout.js +11 -2
  48. package/dist/resources/extensions/gsd/auto-worktree.js +180 -34
  49. package/dist/resources/extensions/gsd/auto.js +107 -35
  50. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
  51. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  52. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -6
  53. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +11 -0
  54. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  55. package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -6
  56. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  57. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +31 -4
  58. package/dist/resources/extensions/gsd/commands-cmux.js +9 -6
  59. package/dist/resources/extensions/gsd/commands-extensions.js +634 -43
  60. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  61. package/dist/resources/extensions/gsd/component-types.js +69 -0
  62. package/dist/resources/extensions/gsd/context-store.js +23 -7
  63. package/dist/resources/extensions/gsd/detection.js +49 -1
  64. package/dist/resources/extensions/gsd/dispatch-guard.js +29 -3
  65. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  66. package/dist/resources/extensions/gsd/file-lock.js +49 -9
  67. package/dist/resources/extensions/gsd/forensics.js +106 -0
  68. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  69. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  70. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  71. package/dist/resources/extensions/gsd/git-service.js +127 -2
  72. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  73. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  74. package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -1
  75. package/dist/resources/extensions/gsd/guided-flow.js +39 -13
  76. package/dist/resources/extensions/gsd/journal.js +17 -2
  77. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  78. package/dist/resources/extensions/gsd/milestone-actions.js +15 -0
  79. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  80. package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
  81. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  82. package/dist/resources/extensions/gsd/model-router.js +6 -0
  83. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  84. package/dist/resources/extensions/gsd/notifications.js +30 -16
  85. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  86. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  87. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  88. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  89. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  90. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  91. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  92. package/dist/resources/extensions/gsd/reports.js +5 -4
  93. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  94. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  95. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  96. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  97. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  98. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  99. package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
  100. package/dist/resources/extensions/gsd/state.js +69 -58
  101. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  102. package/dist/resources/extensions/gsd/tools/complete-slice.js +21 -0
  103. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -0
  104. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  105. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  106. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  107. package/dist/resources/extensions/gsd/uok/audit.js +18 -2
  108. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
  109. package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
  110. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  111. package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
  112. package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
  113. package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
  114. package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
  115. package/dist/resources/extensions/gsd/uok/writer.js +82 -0
  116. package/dist/resources/extensions/gsd/workflow-logger.js +10 -2
  117. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  118. package/dist/resources/extensions/gsd/worktree-manager.js +86 -8
  119. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  120. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  121. package/dist/resources/extensions/mcp-client/auth.js +10 -1
  122. package/dist/resources/extensions/mcp-client/index.js +121 -10
  123. package/dist/resources/extensions/ollama/index.js +5 -1
  124. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  125. package/dist/resources/extensions/shared/cmux-events.js +12 -0
  126. package/dist/resources/extensions/shared/rtk-session-stats.js +1 -2
  127. package/dist/resources/skills/create-skill/SKILL.md +2 -2
  128. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
  129. package/dist/resources/skills/create-skill/workflows/audit-skill.md +4 -4
  130. package/dist/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
  131. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  132. package/dist/web/standalone/.next/BUILD_ID +1 -1
  133. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  134. package/dist/web/standalone/.next/build-manifest.json +4 -4
  135. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  136. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  137. package/dist/web/standalone/.next/required-server-files.json +3 -3
  138. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  139. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  141. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  148. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  149. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  151. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  152. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  153. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  155. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  156. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  157. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  158. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  168. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  177. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  184. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  187. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  190. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  193. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  196. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  199. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  202. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  203. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  205. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  208. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  210. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  211. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  213. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  214. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  217. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  218. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  219. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  221. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  222. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  223. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  224. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  225. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  226. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  227. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  228. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  229. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  230. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  231. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  232. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  233. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  234. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  235. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  236. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  237. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  238. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  239. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  240. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  241. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  242. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  243. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  244. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  245. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  246. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  247. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  248. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  249. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  250. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  251. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  252. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  253. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  254. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  255. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  256. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  257. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  258. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  259. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  260. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  261. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  262. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  263. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  264. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  265. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  266. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  267. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  268. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  269. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  270. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  271. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  272. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  273. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  274. package/dist/web/standalone/.next/server/app/index.html +1 -1
  275. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  276. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  277. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  278. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  279. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  280. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  281. package/dist/web/standalone/.next/server/app/page.js +2 -2
  282. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  283. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  284. package/dist/web/standalone/.next/server/chunks/1926.js +1 -0
  285. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  286. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  287. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  288. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  289. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  290. package/dist/web/standalone/.next/server/middleware.js +2 -2
  291. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  292. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  293. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  294. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  295. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  296. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +11 -0
  297. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  298. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  299. package/dist/web/standalone/.next/static/chunks/app/page-5b113fd32bc2a1c3.js +1 -0
  300. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  301. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  302. package/dist/web/standalone/.next/static/chunks/{webpack-5fc74f13a25fa1bb.js → webpack-2e68521d7c82f7c2.js} +1 -1
  303. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  304. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  305. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  306. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  307. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  308. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  309. package/dist/web/standalone/server.js +1 -1
  310. package/package.json +17 -16
  311. package/packages/daemon/package.json +2 -2
  312. package/packages/daemon/src/logger.ts +4 -3
  313. package/packages/mcp-server/README.md +3 -3
  314. package/packages/mcp-server/dist/env-writer.d.ts +1 -0
  315. package/packages/mcp-server/dist/env-writer.d.ts.map +1 -1
  316. package/packages/mcp-server/dist/env-writer.js +74 -6
  317. package/packages/mcp-server/dist/env-writer.js.map +1 -1
  318. package/packages/mcp-server/dist/server.d.ts +24 -0
  319. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  320. package/packages/mcp-server/dist/server.js +111 -87
  321. package/packages/mcp-server/dist/server.js.map +1 -1
  322. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  323. package/packages/mcp-server/dist/workflow-tools.js +15 -6
  324. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  325. package/packages/mcp-server/package.json +7 -2
  326. package/packages/mcp-server/src/env-writer.test.ts +79 -1
  327. package/packages/mcp-server/src/env-writer.ts +76 -6
  328. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  329. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  330. package/packages/mcp-server/src/readers/readers.test.ts +5 -1
  331. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  332. package/packages/mcp-server/src/server.ts +158 -105
  333. package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
  334. package/packages/mcp-server/src/workflow-tools.ts +19 -6
  335. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  336. package/packages/native/package.json +7 -2
  337. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  338. package/packages/native/src/__tests__/clipboard.test.mjs +69 -23
  339. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  340. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  341. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  342. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  343. package/packages/native/tsconfig.tsbuildinfo +1 -1
  344. package/packages/pi-agent-core/package.json +6 -1
  345. package/packages/pi-agent-core/src/agent-loop.test.ts +226 -31
  346. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  347. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  348. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  349. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  350. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  351. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  352. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  353. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  354. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  355. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  356. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  357. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  358. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  359. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  360. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  361. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  362. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  363. package/packages/pi-ai/dist/models.test.js +36 -11
  364. package/packages/pi-ai/dist/models.test.js.map +1 -1
  365. package/packages/pi-ai/package.json +6 -1
  366. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  367. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  368. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  369. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  370. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  371. package/packages/pi-ai/src/models.test.ts +48 -11
  372. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  373. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  374. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  375. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  376. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  377. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  378. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  379. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +25 -0
  380. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  381. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +105 -6
  382. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  383. package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js +230 -28
  384. package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js.map +1 -1
  385. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +30 -2
  386. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
  387. package/packages/pi-coding-agent/dist/core/compaction/utils.js +113 -12
  388. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  389. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +1 -0
  390. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  391. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +29 -18
  392. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  393. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts +2 -0
  394. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts.map +1 -0
  395. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js +130 -0
  396. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js.map +1 -0
  397. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +56 -1
  398. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -1
  399. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +8 -15
  400. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  401. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.d.ts +25 -0
  402. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.d.ts.map +1 -0
  403. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.js +109 -0
  404. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.js.map +1 -0
  405. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.d.ts +67 -0
  406. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.d.ts.map +1 -0
  407. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.js +167 -0
  408. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.js.map +1 -0
  409. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +8 -2
  410. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  411. package/packages/pi-coding-agent/dist/core/extensions/loader.js +85 -8
  412. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  413. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  414. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  415. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  416. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +41 -4
  417. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  418. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  419. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  420. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  421. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  422. package/packages/pi-coding-agent/dist/core/resource-loader.js +1 -1
  423. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  424. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  425. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  426. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  427. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  428. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  429. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  430. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  431. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  432. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  433. package/packages/pi-coding-agent/dist/core/sdk.d.ts +1 -0
  434. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  435. package/packages/pi-coding-agent/dist/core/sdk.js +4 -1
  436. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  437. package/packages/pi-coding-agent/dist/core/sdk.test.js +19 -1
  438. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
  439. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  440. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  441. package/packages/pi-coding-agent/dist/core/system-prompt.js +19 -5
  442. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  443. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js +2 -1
  444. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js.map +1 -1
  445. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  446. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  447. package/packages/pi-coding-agent/dist/index.js +1 -0
  448. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  449. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +15 -6
  450. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -1
  451. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  452. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  453. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  454. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  455. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  456. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -5
  457. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  458. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +7 -1
  459. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  460. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +31 -9
  461. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  462. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  463. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  464. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  465. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  466. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  467. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  468. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +139 -0
  469. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  470. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  471. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  472. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  473. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  474. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  475. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  476. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  477. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  478. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  479. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  480. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  481. package/packages/pi-coding-agent/package.json +6 -1
  482. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  483. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  484. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  485. package/packages/pi-coding-agent/src/core/compaction/compaction.test.ts +368 -28
  486. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +122 -6
  487. package/packages/pi-coding-agent/src/core/compaction/utils.ts +111 -13
  488. package/packages/pi-coding-agent/src/core/compaction-orchestrator.test.ts +154 -0
  489. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +32 -18
  490. package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +68 -1
  491. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +9 -18
  492. package/packages/pi-coding-agent/src/core/extensions/extension-discovery.ts +119 -0
  493. package/packages/pi-coding-agent/src/core/extensions/extension-registry.ts +222 -0
  494. package/packages/pi-coding-agent/src/core/extensions/loader.ts +82 -11
  495. package/packages/pi-coding-agent/src/core/extensions/types.ts +8 -0
  496. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +48 -4
  497. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +22 -2
  498. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  499. package/packages/pi-coding-agent/src/core/resource-loader.ts +1 -1
  500. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  501. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  502. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  503. package/packages/pi-coding-agent/src/core/sdk.test.ts +25 -1
  504. package/packages/pi-coding-agent/src/core/sdk.ts +10 -3
  505. package/packages/pi-coding-agent/src/core/system-prompt.ts +38 -4
  506. package/packages/pi-coding-agent/src/core/tools/path-utils.test.ts +2 -1
  507. package/packages/pi-coding-agent/src/index.ts +1 -0
  508. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +17 -7
  509. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  510. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  511. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +14 -5
  512. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +45 -11
  513. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  514. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +160 -1
  515. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  516. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  517. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  518. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  519. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  520. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +31 -14
  521. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  522. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  523. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  524. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +51 -6
  525. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  526. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  527. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  528. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  529. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  530. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  531. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  532. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  533. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  534. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  535. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  536. package/packages/pi-tui/dist/components/editor.js +19 -0
  537. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  538. package/packages/pi-tui/dist/components/image.test.js +6 -5
  539. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  540. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  541. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  542. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  543. package/packages/pi-tui/dist/stdin-buffer.d.ts +7 -0
  544. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  545. package/packages/pi-tui/dist/stdin-buffer.js +20 -0
  546. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  547. package/packages/pi-tui/package.json +6 -1
  548. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +46 -15
  549. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  550. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +62 -6
  551. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  552. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  553. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  554. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  555. package/packages/pi-tui/src/components/editor.ts +22 -0
  556. package/packages/pi-tui/src/components/image.test.ts +10 -5
  557. package/packages/pi-tui/src/editor-component.ts +3 -0
  558. package/packages/pi-tui/src/stdin-buffer.ts +26 -0
  559. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  560. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  561. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  562. package/packages/rpc-client/package.json +6 -1
  563. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  564. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  565. package/pkg/package.json +1 -1
  566. package/scripts/install.js +526 -0
  567. package/scripts/lib/workspace-manifest.cjs +86 -0
  568. package/scripts/link-workspace-packages.cjs +5 -17
  569. package/scripts/postinstall.js +9 -178
  570. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  571. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  572. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  573. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  574. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  575. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  576. package/src/resources/extensions/claude-code-cli/readiness.ts +75 -16
  577. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +602 -73
  578. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +1028 -91
  579. package/src/resources/extensions/cmux/index.ts +35 -10
  580. package/src/resources/extensions/github-sync/templates.ts +151 -0
  581. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  582. package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
  583. package/src/resources/extensions/google-search/extension-manifest.json +5 -4
  584. package/src/resources/extensions/google-search/index.ts +9 -470
  585. package/src/resources/extensions/gsd/abandon-detect.ts +62 -0
  586. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
  587. package/src/resources/extensions/gsd/auto/loop.ts +142 -2
  588. package/src/resources/extensions/gsd/auto/phases.ts +62 -38
  589. package/src/resources/extensions/gsd/auto/resolve.ts +29 -0
  590. package/src/resources/extensions/gsd/auto/run-unit.ts +16 -2
  591. package/src/resources/extensions/gsd/auto/session.ts +7 -2
  592. package/src/resources/extensions/gsd/auto/turn-epoch.ts +108 -0
  593. package/src/resources/extensions/gsd/auto/types.ts +1 -1
  594. package/src/resources/extensions/gsd/auto-dispatch.ts +214 -37
  595. package/src/resources/extensions/gsd/auto-loop.ts +1 -1
  596. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  597. package/src/resources/extensions/gsd/auto-post-unit.ts +226 -73
  598. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  599. package/src/resources/extensions/gsd/auto-recovery.ts +240 -25
  600. package/src/resources/extensions/gsd/auto-start.ts +146 -14
  601. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -5
  602. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  603. package/src/resources/extensions/gsd/auto-unit-closeout.ts +14 -3
  604. package/src/resources/extensions/gsd/auto-worktree.ts +190 -31
  605. package/src/resources/extensions/gsd/auto.ts +127 -41
  606. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
  607. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  608. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -6
  609. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +11 -0
  610. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  611. package/src/resources/extensions/gsd/bootstrap/system-context.ts +13 -9
  612. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  613. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +27 -8
  614. package/src/resources/extensions/gsd/commands-cmux.ts +10 -6
  615. package/src/resources/extensions/gsd/commands-extensions.ts +747 -41
  616. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  617. package/src/resources/extensions/gsd/component-types.ts +362 -0
  618. package/src/resources/extensions/gsd/context-store.ts +25 -8
  619. package/src/resources/extensions/gsd/detection.ts +58 -1
  620. package/src/resources/extensions/gsd/dispatch-guard.ts +26 -2
  621. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  622. package/src/resources/extensions/gsd/file-lock.ts +84 -11
  623. package/src/resources/extensions/gsd/forensics.ts +118 -1
  624. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  625. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  626. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  627. package/src/resources/extensions/gsd/git-service.ts +150 -2
  628. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  629. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  630. package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -1
  631. package/src/resources/extensions/gsd/guided-flow.ts +57 -14
  632. package/src/resources/extensions/gsd/journal.ts +38 -3
  633. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  634. package/src/resources/extensions/gsd/milestone-actions.ts +18 -0
  635. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  636. package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
  637. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  638. package/src/resources/extensions/gsd/model-router.ts +6 -0
  639. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  640. package/src/resources/extensions/gsd/notifications.ts +27 -15
  641. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  642. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  643. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  644. package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  645. package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  646. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  647. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  648. package/src/resources/extensions/gsd/reports.ts +5 -4
  649. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  650. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  651. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  652. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  653. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  654. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  655. package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
  656. package/src/resources/extensions/gsd/state.ts +76 -66
  657. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  658. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  659. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  660. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  661. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  662. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +135 -285
  663. package/src/resources/extensions/gsd/tests/auto-mode-guards.test.ts +79 -0
  664. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  665. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  666. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  667. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +166 -0
  668. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  669. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  670. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +64 -0
  671. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  672. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  673. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  674. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  675. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  676. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  677. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  678. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  679. package/src/resources/extensions/gsd/tests/cmux.test.ts +5 -9
  680. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  681. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  682. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  683. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  684. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  685. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  686. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  687. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  688. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  689. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  690. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
  691. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
  692. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  693. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  694. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  695. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
  696. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  697. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  698. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +14 -9
  699. package/src/resources/extensions/gsd/tests/dispatch-guard-summary-db-mismatch.test.ts +77 -0
  700. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
  701. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
  702. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  703. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  704. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  705. package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
  706. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  707. package/src/resources/extensions/gsd/tests/file-lock.test.ts +86 -12
  708. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  709. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  710. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  711. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +131 -0
  712. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
  713. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  714. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  715. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  716. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +30 -0
  717. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  718. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
  719. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  720. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  721. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  722. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  723. package/src/resources/extensions/gsd/tests/integration/worktree-e2e.test.ts +11 -0
  724. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  725. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  726. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
  727. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  728. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +47 -0
  729. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  730. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  731. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  732. package/src/resources/extensions/gsd/tests/milestone-status-authoritative.test.ts +3 -3
  733. package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
  734. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +4 -2
  735. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  736. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  737. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  738. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  739. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  740. package/src/resources/extensions/gsd/tests/parallel-commit-scope.test.ts +5 -0
  741. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  742. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +150 -0
  743. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  744. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  745. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  746. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  747. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  748. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +54 -41
  749. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +213 -0
  750. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  751. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  752. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +13 -7
  753. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  754. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  755. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  756. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  757. package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +170 -0
  758. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  759. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  760. package/src/resources/extensions/gsd/tests/rewrite-docs-abandon-detect.test.ts +195 -0
  761. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  762. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  763. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  764. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  765. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  766. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  767. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  768. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  769. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  770. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  771. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  772. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  773. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
  774. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
  775. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
  776. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  777. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  778. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +2 -2
  779. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  780. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  781. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  782. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  783. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  784. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  785. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  786. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  787. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  788. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +50 -2
  789. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +162 -0
  790. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  791. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  792. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
  793. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
  794. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  795. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  796. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
  797. package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
  798. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
  799. package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
  800. package/src/resources/extensions/gsd/tests/validate-extension-package.test.ts +168 -0
  801. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +139 -5
  802. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  803. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  804. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  805. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  806. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +25 -2
  807. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  808. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  809. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  810. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  811. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  812. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  813. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  814. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  815. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  816. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  817. package/src/resources/extensions/gsd/tools/complete-slice.ts +38 -0
  818. package/src/resources/extensions/gsd/tools/complete-task.ts +49 -0
  819. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  820. package/src/resources/extensions/gsd/types.ts +3 -3
  821. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  822. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  823. package/src/resources/extensions/gsd/uok/audit.ts +20 -2
  824. package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
  825. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
  826. package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
  827. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  828. package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
  829. package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
  830. package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
  831. package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
  832. package/src/resources/extensions/gsd/uok/writer.ts +113 -0
  833. package/src/resources/extensions/gsd/workflow-logger.ts +22 -3
  834. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  835. package/src/resources/extensions/gsd/worktree-manager.ts +109 -7
  836. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  837. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  838. package/src/resources/extensions/mcp-client/auth.ts +12 -1
  839. package/src/resources/extensions/mcp-client/index.ts +132 -11
  840. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  841. package/src/resources/extensions/ollama/index.ts +5 -1
  842. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  843. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  844. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  845. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  846. package/src/resources/extensions/shared/cmux-events.ts +59 -0
  847. package/src/resources/extensions/shared/rtk-session-stats.ts +1 -2
  848. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  849. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  850. package/src/resources/skills/create-skill/SKILL.md +2 -2
  851. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
  852. package/src/resources/skills/create-skill/workflows/audit-skill.md +4 -4
  853. package/src/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
  854. package/dist/web/standalone/.next/server/chunks/7461.js +0 -1
  855. package/dist/web/standalone/.next/static/chunks/2826.e59e8578e2e28639.js +0 -9
  856. package/dist/web/standalone/.next/static/chunks/app/page-151349214571e2b6.js +0 -1
  857. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  858. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  859. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  860. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  861. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  862. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  863. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  864. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  865. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -142
  866. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  867. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  868. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  869. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  870. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  871. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  872. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  873. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  874. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  875. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  876. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  877. /package/dist/web/standalone/.next/static/{pV-mPo7rYGb5JBC09C8GG → cAJH99yNS1UPbeSEiNRrV}/_buildManifest.js +0 -0
  878. /package/dist/web/standalone/.next/static/{pV-mPo7rYGb5JBC09C8GG → cAJH99yNS1UPbeSEiNRrV}/_ssgManifest.js +0 -0
@@ -8,10 +8,15 @@ import {
8
8
  getResultErrorMessage,
9
9
  makeAbortedMessage,
10
10
  mergePendingToolCalls,
11
+ buildFinalAssistantContent,
11
12
  resolveClaudePermissionMode,
12
13
  buildPromptFromContext,
13
14
  buildSdkQueryPrompt,
14
15
  buildSdkOptions,
16
+ createClaudeCodeCanUseToolHandler,
17
+ buildBashPermissionPattern,
18
+ buildBashPermissionPatternOptions,
19
+ bashCommandMatchesSavedRules,
15
20
  createClaudeCodeElicitationHandler,
16
21
  extractImageBlocksFromContext,
17
22
  extractToolResultsFromSdkUserMessage,
@@ -19,11 +24,58 @@ import {
19
24
  parseAskUserQuestionsElicitation,
20
25
  parseTextInputElicitation,
21
26
  parseClaudeLookupOutput,
27
+ resolveBundledClaudeCliPath,
28
+ normalizeClaudePathForSdk,
22
29
  roundResultToElicitationContent,
23
30
  } from "../stream-adapter.ts";
24
31
  import type { AssistantMessage, Context, Message } from "@gsd/pi-ai";
25
32
  import type { SDKUserMessage } from "../sdk-types.ts";
26
33
 
34
+ // ---------------------------------------------------------------------------
35
+ // Env helpers — `GSD_WORKFLOW_MCP_*` save/restore
36
+ //
37
+ // The naive pattern `process.env.X = prev.X` breaks when `prev.X` is
38
+ // undefined: Node coerces the assignment to the literal string
39
+ // "undefined", which then pollutes subsequent tests that read the var
40
+ // and assume it's absent. Issue #4808 documents the resulting bleed.
41
+ //
42
+ // `setWorkflowMcpEnv` returns a `restore()` closure that either
43
+ // re-assigns the previous string value OR `delete`s the key when the
44
+ // original was absent. Call in a try/finally; restore in the finally.
45
+ // ---------------------------------------------------------------------------
46
+
47
+ const WORKFLOW_MCP_ENV_KEYS = [
48
+ "GSD_WORKFLOW_MCP_COMMAND",
49
+ "GSD_WORKFLOW_MCP_NAME",
50
+ "GSD_WORKFLOW_MCP_ARGS",
51
+ "GSD_WORKFLOW_MCP_ENV",
52
+ "GSD_WORKFLOW_MCP_CWD",
53
+ ] as const;
54
+
55
+ type WorkflowMcpEnvKey = (typeof WORKFLOW_MCP_ENV_KEYS)[number];
56
+
57
+ function setWorkflowMcpEnv(
58
+ values: Partial<Record<WorkflowMcpEnvKey, string>>,
59
+ ): () => void {
60
+ const prev: Partial<Record<WorkflowMcpEnvKey, string | undefined>> = {};
61
+ for (const key of WORKFLOW_MCP_ENV_KEYS) {
62
+ prev[key] = process.env[key];
63
+ }
64
+ for (const [key, value] of Object.entries(values)) {
65
+ process.env[key] = value;
66
+ }
67
+ return function restore() {
68
+ for (const key of WORKFLOW_MCP_ENV_KEYS) {
69
+ const previous = prev[key];
70
+ if (previous === undefined) {
71
+ delete process.env[key];
72
+ } else {
73
+ process.env[key] = previous;
74
+ }
75
+ }
76
+ };
77
+ }
78
+
27
79
  // ---------------------------------------------------------------------------
28
80
  // Existing tests — exhausted stream fallback (#2575)
29
81
  // ---------------------------------------------------------------------------
@@ -539,6 +591,71 @@ describe("stream-adapter — Claude Code external tool results", () => {
539
591
  },
540
592
  ]);
541
593
  });
594
+
595
+ test("buildFinalAssistantContent preserves intermediate tool calls with attached external results", () => {
596
+ const finalContent = buildFinalAssistantContent({
597
+ intermediateToolBlocks: [
598
+ {
599
+ type: "toolCall",
600
+ id: "tool-bash-1",
601
+ name: "bash",
602
+ arguments: { command: "echo hi" },
603
+ } as any,
604
+ ],
605
+ pendingContent: [{ type: "text", text: "All done." }],
606
+ toolResultsById: new Map([
607
+ [
608
+ "tool-bash-1",
609
+ {
610
+ content: [{ type: "text", text: "hi\n" }],
611
+ details: { source: "claude-code" },
612
+ isError: false,
613
+ },
614
+ ],
615
+ ]),
616
+ });
617
+
618
+ assert.equal(finalContent[0]?.type, "toolCall");
619
+ assert.deepEqual((finalContent[0] as any).externalResult, {
620
+ content: [{ type: "text", text: "hi\n" }],
621
+ details: { source: "claude-code" },
622
+ isError: false,
623
+ });
624
+ assert.deepEqual(finalContent[1], { type: "text", text: "All done." });
625
+ });
626
+
627
+ test("buildFinalAssistantContent keeps final-turn tool calls when result arrives without a synthetic user boundary", () => {
628
+ const finalContent = buildFinalAssistantContent({
629
+ intermediateToolBlocks: [],
630
+ pendingContent: [
631
+ {
632
+ type: "toolCall",
633
+ id: "tool-read-1",
634
+ name: "read",
635
+ arguments: { path: "README.md" },
636
+ } as any,
637
+ { type: "text", text: "Read complete." },
638
+ ],
639
+ toolResultsById: new Map([
640
+ [
641
+ "tool-read-1",
642
+ {
643
+ content: [{ type: "text", text: "file contents" }],
644
+ details: { path: "README.md" },
645
+ isError: false,
646
+ },
647
+ ],
648
+ ]),
649
+ });
650
+
651
+ assert.equal(finalContent[0]?.type, "toolCall");
652
+ assert.deepEqual((finalContent[0] as any).externalResult, {
653
+ content: [{ type: "text", text: "file contents" }],
654
+ details: { path: "README.md" },
655
+ isError: false,
656
+ });
657
+ assert.deepEqual(finalContent[1], { type: "text", text: "Read complete." });
658
+ });
542
659
  });
543
660
 
544
661
  describe("stream-adapter — session persistence (#2859)", () => {
@@ -671,19 +788,14 @@ describe("stream-adapter — session persistence (#2859)", () => {
671
788
  });
672
789
 
673
790
  test("buildSdkOptions includes workflow MCP server config when env is set", () => {
674
- const prev = {
675
- GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
676
- GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
677
- GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
678
- GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
679
- GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
680
- };
791
+ const restore = setWorkflowMcpEnv({
792
+ GSD_WORKFLOW_MCP_COMMAND: "node",
793
+ GSD_WORKFLOW_MCP_NAME: "gsd-workflow",
794
+ GSD_WORKFLOW_MCP_ARGS: JSON.stringify(["packages/mcp-server/dist/cli.js"]),
795
+ GSD_WORKFLOW_MCP_ENV: JSON.stringify({ GSD_CLI_PATH: "/tmp/gsd" }),
796
+ GSD_WORKFLOW_MCP_CWD: "/tmp/project",
797
+ });
681
798
  try {
682
- process.env.GSD_WORKFLOW_MCP_COMMAND = "node";
683
- process.env.GSD_WORKFLOW_MCP_NAME = "gsd-workflow";
684
- process.env.GSD_WORKFLOW_MCP_ARGS = JSON.stringify(["packages/mcp-server/dist/cli.js"]);
685
- process.env.GSD_WORKFLOW_MCP_ENV = JSON.stringify({ GSD_CLI_PATH: "/tmp/gsd" });
686
- process.env.GSD_WORKFLOW_MCP_CWD = "/tmp/project";
687
799
 
688
800
  const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
689
801
  const mcpServers = options.mcpServers as Record<string, any>;
@@ -695,72 +807,63 @@ describe("stream-adapter — session persistence (#2859)", () => {
695
807
  assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
696
808
  assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
697
809
  assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
698
- assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
810
+ assert.deepEqual(options.disallowedTools, []);
699
811
  assert.deepEqual(options.allowedTools, [
700
812
  "Read",
701
813
  "Write",
702
814
  "Edit",
703
815
  "Glob",
704
816
  "Grep",
705
- "Bash(ls:*)",
706
- "Bash(pwd)",
817
+ "Bash",
818
+ "Agent",
819
+ "WebFetch",
820
+ "WebSearch",
821
+ "AskUserQuestion",
707
822
  "mcp__gsd-workflow__*",
708
823
  ]);
709
824
  } finally {
710
- process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
711
- process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
712
- process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
713
- process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
714
- process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
825
+ restore();
715
826
  }
716
827
  });
717
828
 
718
- test("buildSdkOptions disables AskUserQuestion for custom workflow MCP server names", () => {
719
- const prev = {
720
- GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
721
- GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
722
- GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
723
- GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
724
- GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
725
- };
829
+ test("buildSdkOptions auto-approves every tool for custom workflow MCP server names", () => {
830
+ const restore = setWorkflowMcpEnv({
831
+ GSD_WORKFLOW_MCP_COMMAND: "node",
832
+ GSD_WORKFLOW_MCP_NAME: "custom-workflow",
833
+ GSD_WORKFLOW_MCP_ARGS: JSON.stringify(["packages/mcp-server/dist/cli.js"]),
834
+ GSD_WORKFLOW_MCP_ENV: JSON.stringify({ GSD_CLI_PATH: "/tmp/gsd" }),
835
+ GSD_WORKFLOW_MCP_CWD: "/tmp/project",
836
+ });
726
837
  try {
727
- process.env.GSD_WORKFLOW_MCP_COMMAND = "node";
728
- process.env.GSD_WORKFLOW_MCP_NAME = "custom-workflow";
729
- process.env.GSD_WORKFLOW_MCP_ARGS = JSON.stringify(["packages/mcp-server/dist/cli.js"]);
730
- process.env.GSD_WORKFLOW_MCP_ENV = JSON.stringify({ GSD_CLI_PATH: "/tmp/gsd" });
731
- process.env.GSD_WORKFLOW_MCP_CWD = "/tmp/project";
732
838
 
733
839
  const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
734
840
  const mcpServers = options.mcpServers as Record<string, any>;
735
841
  assert.ok(mcpServers?.["custom-workflow"], "expected custom workflow server config");
736
- assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
842
+ assert.deepEqual(options.disallowedTools, []);
737
843
  assert.deepEqual(options.allowedTools, [
738
844
  "Read",
739
845
  "Write",
740
846
  "Edit",
741
847
  "Glob",
742
848
  "Grep",
743
- "Bash(ls:*)",
744
- "Bash(pwd)",
849
+ "Bash",
850
+ "Agent",
851
+ "WebFetch",
852
+ "WebSearch",
853
+ "AskUserQuestion",
745
854
  "mcp__custom-workflow__*",
746
855
  ]);
747
856
  } finally {
748
- process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
749
- process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
750
- process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
751
- process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
752
- process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
857
+ restore();
753
858
  }
754
859
  });
755
860
 
756
861
  test("buildSdkOptions auto-discovers bundled MCP server even without env hints", () => {
757
- const prev = {
758
- GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
759
- GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
760
- GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
761
- GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
762
- GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
763
- };
862
+ // Use setWorkflowMcpEnv with no values to save current state;
863
+ // restore() in finally will put it back correctly (including
864
+ // deleting any keys that started as undefined — the #4808 bug
865
+ // the naive `process.env.X = prev.X` pattern introduced).
866
+ const restore = setWorkflowMcpEnv({});
764
867
  try {
765
868
  delete process.env.GSD_WORKFLOW_MCP_COMMAND;
766
869
  delete process.env.GSD_WORKFLOW_MCP_NAME;
@@ -779,29 +882,21 @@ describe("stream-adapter — session persistence (#2859)", () => {
779
882
  const mcpServers = (options as any).mcpServers;
780
883
  if (mcpServers) {
781
884
  assert.ok(mcpServers["gsd-workflow"], "if present, must be gsd-workflow");
782
- assert.deepEqual((options as any).disallowedTools, ["AskUserQuestion"]);
885
+ assert.deepEqual((options as any).disallowedTools, []);
783
886
  } else {
784
- assert.deepEqual((options as any).disallowedTools, ["AskUserQuestion"]);
887
+ assert.deepEqual((options as any).disallowedTools, []);
785
888
  }
786
889
  rmSync(emptyDir, { recursive: true, force: true });
787
890
  } finally {
788
- process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
789
- process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
790
- process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
791
- process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
792
- process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
891
+ restore();
793
892
  }
794
893
  });
795
894
 
796
895
  test("buildSdkOptions auto-detects local workflow MCP dist CLI when present", () => {
797
- const prev = {
798
- GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
799
- GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
800
- GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
801
- GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
802
- GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
803
- GSD_CLI_PATH: process.env.GSD_CLI_PATH,
804
- };
896
+ // GSD_CLI_PATH isn't in WORKFLOW_MCP_ENV_KEYS, so save+restore it
897
+ // manually around setWorkflowMcpEnv which handles the MCP keys.
898
+ const prevCliPath = process.env.GSD_CLI_PATH;
899
+ const restore = setWorkflowMcpEnv({});
805
900
  const originalCwd = process.cwd();
806
901
  const repoDir = mkdtempSync(join(tmpdir(), "claude-mcp-detect-"));
807
902
  try {
@@ -828,27 +923,22 @@ describe("stream-adapter — session persistence (#2859)", () => {
828
923
  assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
829
924
  assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
830
925
  assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, resolvedRepoDir);
831
- assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
926
+ assert.deepEqual(options.disallowedTools, []);
832
927
  } finally {
833
928
  process.chdir(originalCwd);
834
929
  rmSync(repoDir, { recursive: true, force: true });
835
- process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
836
- process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
837
- process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
838
- process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
839
- process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
840
- process.env.GSD_CLI_PATH = prev.GSD_CLI_PATH;
930
+ restore();
931
+ // GSD_CLI_PATH isn't in setWorkflowMcpEnv's scope — restore it here.
932
+ if (prevCliPath === undefined) {
933
+ delete process.env.GSD_CLI_PATH;
934
+ } else {
935
+ process.env.GSD_CLI_PATH = prevCliPath;
936
+ }
841
937
  }
842
938
  });
843
939
 
844
940
  test("buildSdkOptions preserves runtime callbacks such as onElicitation", () => {
845
- const prev = {
846
- GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
847
- GSD_WORKFLOW_MCP_NAME: process.env.GSD_WORKFLOW_MCP_NAME,
848
- GSD_WORKFLOW_MCP_ARGS: process.env.GSD_WORKFLOW_MCP_ARGS,
849
- GSD_WORKFLOW_MCP_ENV: process.env.GSD_WORKFLOW_MCP_ENV,
850
- GSD_WORKFLOW_MCP_CWD: process.env.GSD_WORKFLOW_MCP_CWD,
851
- };
941
+ const restore = setWorkflowMcpEnv({});
852
942
  const onElicitation = async () => ({ action: "decline" as const });
853
943
  try {
854
944
  delete process.env.GSD_WORKFLOW_MCP_COMMAND;
@@ -859,11 +949,7 @@ describe("stream-adapter — session persistence (#2859)", () => {
859
949
  const options = buildSdkOptions("claude-sonnet-4-20250514", "test", undefined, { onElicitation });
860
950
  assert.equal(options.onElicitation, onElicitation);
861
951
  } finally {
862
- process.env.GSD_WORKFLOW_MCP_COMMAND = prev.GSD_WORKFLOW_MCP_COMMAND;
863
- process.env.GSD_WORKFLOW_MCP_NAME = prev.GSD_WORKFLOW_MCP_NAME;
864
- process.env.GSD_WORKFLOW_MCP_ARGS = prev.GSD_WORKFLOW_MCP_ARGS;
865
- process.env.GSD_WORKFLOW_MCP_ENV = prev.GSD_WORKFLOW_MCP_ENV;
866
- process.env.GSD_WORKFLOW_MCP_CWD = prev.GSD_WORKFLOW_MCP_CWD;
952
+ restore();
867
953
  }
868
954
  });
869
955
  });
@@ -1216,14 +1302,14 @@ describe("stream-adapter — permission mode (F10)", () => {
1216
1302
  }
1217
1303
  }
1218
1304
 
1219
- test("buildSdkOptions defaults to acceptEdits (#4383)", () => {
1305
+ test("buildSdkOptions defaults to bypassPermissions (globally unblocks all tools)", () => {
1220
1306
  clearWorkflowMcpEnv();
1221
1307
  const opts = buildSdkOptions("claude-sonnet-4-6", "test");
1222
- assert.equal(opts.permissionMode, "acceptEdits");
1308
+ assert.equal(opts.permissionMode, "bypassPermissions");
1223
1309
  assert.equal(
1224
1310
  opts.allowDangerouslySkipPermissions,
1225
- false,
1226
- "allowDangerouslySkipPermissions must be false when permissionMode is acceptEdits",
1311
+ true,
1312
+ "allowDangerouslySkipPermissions must be true when permissionMode is bypassPermissions",
1227
1313
  );
1228
1314
  });
1229
1315
 
@@ -1238,9 +1324,9 @@ describe("stream-adapter — permission mode (F10)", () => {
1238
1324
  );
1239
1325
  });
1240
1326
 
1241
- test("resolveClaudePermissionMode defaults to acceptEdits when no env var is set (#4383)", async () => {
1327
+ test("resolveClaudePermissionMode defaults to bypassPermissions when no env var is set (globally unblocks all tools)", async () => {
1242
1328
  const mode = await resolveClaudePermissionMode({});
1243
- assert.equal(mode, "acceptEdits");
1329
+ assert.equal(mode, "bypassPermissions");
1244
1330
  });
1245
1331
 
1246
1332
  test("resolveClaudePermissionMode honours the GSD_CLAUDE_CODE_PERMISSION_MODE env override", async () => {
@@ -1258,6 +1344,27 @@ describe("stream-adapter — permission mode (F10)", () => {
1258
1344
  `expected bypass or acceptEdits, got ${mode}`,
1259
1345
  );
1260
1346
  });
1347
+
1348
+ test("resolveClaudePermissionMode flips to bypassPermissions when GSD_HEADLESS=1 (#4657)", async () => {
1349
+ const originalWarn = console.warn;
1350
+ console.warn = () => {};
1351
+ try {
1352
+ const env = { GSD_HEADLESS: "1" } as NodeJS.ProcessEnv;
1353
+ const mode = await resolveClaudePermissionMode(env);
1354
+ assert.equal(mode, "bypassPermissions");
1355
+ } finally {
1356
+ console.warn = originalWarn;
1357
+ }
1358
+ });
1359
+
1360
+ test("resolveClaudePermissionMode: explicit override wins over GSD_HEADLESS=1", async () => {
1361
+ const env = {
1362
+ GSD_HEADLESS: "1",
1363
+ GSD_CLAUDE_CODE_PERMISSION_MODE: "acceptEdits",
1364
+ } as NodeJS.ProcessEnv;
1365
+ const mode = await resolveClaudePermissionMode(env);
1366
+ assert.equal(mode, "acceptEdits");
1367
+ });
1261
1368
  });
1262
1369
 
1263
1370
  describe("stream-adapter — Windows Claude path lookup (#3770)", () => {
@@ -1270,8 +1377,838 @@ describe("stream-adapter — Windows Claude path lookup (#3770)", () => {
1270
1377
  assert.equal(getClaudeLookupCommand("linux"), "which claude");
1271
1378
  });
1272
1379
 
1273
- test("parseClaudeLookupOutput keeps the first native path from multi-line lookup output", () => {
1274
- const output = "C:\\Users\\Binoy\\.local\\bin\\claude.exe\r\nC:\\Program Files\\Claude\\claude.exe\r\n";
1275
- assert.equal(parseClaudeLookupOutput(output), "C:\\Users\\Binoy\\.local\\bin\\claude.exe");
1380
+ test("parseClaudeLookupOutput prefers .exe on win32 when where output includes shims", () => {
1381
+ const output = [
1382
+ "C:\\Users\\djeff\\AppData\\Roaming\\npm\\claude",
1383
+ "C:\\Users\\djeff\\AppData\\Roaming\\npm\\claude.cmd",
1384
+ "C:\\Program Files\\Claude\\claude.exe",
1385
+ ].join("\r\n");
1386
+ assert.equal(parseClaudeLookupOutput(output, "win32"), "C:\\Program Files\\Claude\\claude.exe");
1387
+ });
1388
+
1389
+ test("parseClaudeLookupOutput keeps first line on non-win32 platforms", () => {
1390
+ const output = "/usr/local/bin/claude\n/opt/homebrew/bin/claude\n";
1391
+ assert.equal(parseClaudeLookupOutput(output, "darwin"), "/usr/local/bin/claude");
1392
+ });
1393
+
1394
+ test("normalizeClaudePathForSdk swaps Windows shim paths to bundled cli.js", () => {
1395
+ const shimPath = "C:\\Users\\djeff\\AppData\\Roaming\\npm\\claude";
1396
+ const bundled = "C:\\repo\\node_modules\\@anthropic-ai\\claude-agent-sdk\\cli.js";
1397
+ assert.equal(normalizeClaudePathForSdk(shimPath, "win32", bundled), bundled);
1398
+ assert.equal(normalizeClaudePathForSdk("C:\\Program Files\\Claude\\claude.exe", "win32", bundled), "C:\\Program Files\\Claude\\claude.exe");
1399
+ });
1400
+
1401
+ test("resolveBundledClaudeCliPath returns a .js path when SDK package is present", () => {
1402
+ const resolved = resolveBundledClaudeCliPath();
1403
+ assert.ok(resolved, "expected sdk cli.js to be resolvable in test workspace");
1404
+ assert.match(resolved!, /[\\/]@anthropic-ai[\\/]claude-agent-sdk[\\/]cli\.js$/);
1405
+ });
1406
+ });
1407
+
1408
+ // ---------------------------------------------------------------------------
1409
+ // canUseTool handler (#4383)
1410
+ // ---------------------------------------------------------------------------
1411
+
1412
+ describe("stream-adapter — canUseTool handler", () => {
1413
+ function makeOptions(overrides: Partial<{ signal: AbortSignal; suggestions: Array<Record<string, unknown>>; title: string; description: string; toolUseID: string }> = {}) {
1414
+ return {
1415
+ signal: overrides.signal ?? new AbortController().signal,
1416
+ toolUseID: overrides.toolUseID ?? "toolu_test123",
1417
+ ...(overrides.title !== undefined ? { title: overrides.title } : {}),
1418
+ ...(overrides.description !== undefined ? { description: overrides.description } : {}),
1419
+ ...(overrides.suggestions !== undefined ? { suggestions: overrides.suggestions } : {}),
1420
+ };
1421
+ }
1422
+
1423
+ // Point process.cwd() at an empty temp dir so the real repo's
1424
+ // .claude/settings.local.json (which may already contain rules like
1425
+ // "Bash(gh pr list:*)") does not short-circuit the permission flow.
1426
+ // Returns a cleanup function that restores cwd and removes the temp dir.
1427
+ // biome-ignore lint/suspicious/noExplicitAny: test-only monkey-patch
1428
+ function withIsolatedCwd(): () => void {
1429
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-canusetool-")));
1430
+ const orig = process.cwd;
1431
+ process.cwd = () => dir;
1432
+ return () => {
1433
+ process.cwd = orig;
1434
+ rmSync(dir, { recursive: true, force: true });
1435
+ };
1436
+ }
1437
+
1438
+ test("returns undefined when no UI context is provided", () => {
1439
+ const handler = createClaudeCodeCanUseToolHandler(undefined);
1440
+ assert.equal(handler, undefined);
1441
+ });
1442
+
1443
+ test("shows select dialog with Allow/Always Allow/Deny and returns allow", async () => {
1444
+ let selectPrompt = "";
1445
+ let selectOptions: string[] = [];
1446
+ const ui = {
1447
+ select: async (prompt: string, options: string[]) => {
1448
+ selectPrompt = prompt;
1449
+ selectOptions = options;
1450
+ return "Allow";
1451
+ },
1452
+ };
1453
+
1454
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1455
+ assert.ok(handler);
1456
+
1457
+ const input = { command: "ls -la" };
1458
+ const result = await handler!("Bash", input, makeOptions({
1459
+ title: "Claude wants to run: ls -la",
1460
+ description: "List directory contents",
1461
+ }));
1462
+
1463
+ assert.equal(result.behavior, "allow");
1464
+ assert.deepEqual((result as any).updatedInput, input);
1465
+ assert.equal((result as any).toolUseID, "toolu_test123");
1466
+ // Allow (one-time) should NOT include updatedPermissions
1467
+ assert.equal((result as any).updatedPermissions, undefined);
1468
+ assert.deepEqual(selectOptions, ["Allow", "Always Allow", "Deny"]);
1469
+ // Prompt includes title and input summary
1470
+ assert.ok(selectPrompt.includes("Claude wants to run: ls -la"));
1471
+ assert.ok(selectPrompt.includes("ls -la"));
1472
+ });
1473
+
1474
+ test("returns deny when user selects Deny", async () => {
1475
+ const ui = {
1476
+ select: async () => "Deny",
1477
+ };
1478
+
1479
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1480
+ const result = await handler!("Bash", { command: "rm -rf /" }, makeOptions());
1481
+
1482
+ assert.equal(result.behavior, "deny");
1483
+ assert.equal((result as any).message, "User denied");
1484
+ assert.equal((result as any).toolUseID, "toolu_test123");
1485
+ });
1486
+
1487
+ test("returns deny when user dismisses dialog (undefined)", async () => {
1488
+ const ui = {
1489
+ select: async () => undefined,
1490
+ };
1491
+
1492
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1493
+ const result = await handler!("Bash", { command: "echo hi" }, makeOptions());
1494
+
1495
+ assert.equal(result.behavior, "deny");
1496
+ assert.equal((result as any).message, "User denied");
1497
+ });
1498
+
1499
+ test("Always Allow for Bash patches SDK suggestions with smart ruleContent", async () => {
1500
+ const notified: string[] = [];
1501
+ const ui = { select: async (_p: string, opts: string[]) => opts.find((o) => o.startsWith("Always Allow"))!, notify: (msg: string) => notified.push(msg) };
1502
+ const suggestions = [{
1503
+ type: "addRules",
1504
+ rules: [{ toolName: "Bash", ruleContent: "ls -la /tmp" }],
1505
+ behavior: "allow",
1506
+ destination: "localSettings",
1507
+ }];
1508
+
1509
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1510
+ const result = await handler!("Bash", { command: "ls -la /tmp" }, makeOptions({ suggestions }));
1511
+
1512
+ assert.equal(result.behavior, "allow");
1513
+ // Should patch ruleContent with our smart pattern, preserving SDK structure
1514
+ assert.deepEqual((result as any).updatedPermissions, [{
1515
+ type: "addRules",
1516
+ rules: [{ toolName: "Bash", ruleContent: "ls:*" }],
1517
+ behavior: "allow",
1518
+ destination: "localSettings",
1519
+ }]);
1520
+ assert.equal(notified.length, 1);
1521
+ assert.ok(notified[0].includes("Saved:") && notified[0].includes("Bash(ls:*)"));
1522
+ });
1523
+
1524
+ test("Always Allow for Bash with subcommand-sensitive CLI captures verb", async () => {
1525
+ const cleanup = withIsolatedCwd();
1526
+ try {
1527
+ const notified: string[] = [];
1528
+ // First select call: pick "Always Allow ..."; second call (level
1529
+ // picker): pick the "git push" granularity explicitly.
1530
+ let selectCall = 0;
1531
+ const ui = {
1532
+ select: async (_p: string, opts: string[]) => {
1533
+ selectCall++;
1534
+ if (selectCall === 1) return opts.find((o) => o.startsWith("Always Allow"))!;
1535
+ return "Bash(git push:*)";
1536
+ },
1537
+ notify: (msg: string) => notified.push(msg),
1538
+ };
1539
+ const suggestions = [{
1540
+ type: "addRules",
1541
+ rules: [{ toolName: "Bash", ruleContent: "git push origin main" }],
1542
+ behavior: "allow",
1543
+ destination: "localSettings",
1544
+ }];
1545
+
1546
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1547
+ const result = await handler!("Bash", { command: "git push origin main" }, makeOptions({ suggestions }));
1548
+
1549
+ assert.equal(result.behavior, "allow");
1550
+ assert.deepEqual((result as any).updatedPermissions, [{
1551
+ type: "addRules",
1552
+ rules: [{ toolName: "Bash", ruleContent: "git push:*" }],
1553
+ behavior: "allow",
1554
+ destination: "localSettings",
1555
+ }]);
1556
+ assert.ok(notified[0].includes("Saved:") && notified[0].includes("Bash(git push:*)"));
1557
+ } finally {
1558
+ cleanup();
1559
+ }
1560
+ });
1561
+
1562
+ test("Always Allow for Bash without suggestions builds proper PermissionUpdate", async () => {
1563
+ const cleanup = withIsolatedCwd();
1564
+ try {
1565
+ const notified: string[] = [];
1566
+ let selectCall = 0;
1567
+ const ui = {
1568
+ select: async (_p: string, opts: string[]) => {
1569
+ selectCall++;
1570
+ if (selectCall === 1) return opts.find((o) => o.startsWith("Always Allow"))!;
1571
+ return "Bash(gh pr list:*)";
1572
+ },
1573
+ notify: (msg: string) => notified.push(msg),
1574
+ };
1575
+
1576
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1577
+ const result = await handler!("Bash", { command: "gh pr list" }, makeOptions());
1578
+
1579
+ assert.equal(result.behavior, "allow");
1580
+ // No SDK suggestions → builds PermissionUpdate from scratch
1581
+ assert.deepEqual((result as any).updatedPermissions, [{
1582
+ type: "addRules",
1583
+ rules: [{ toolName: "Bash", ruleContent: "gh pr list:*" }],
1584
+ behavior: "allow",
1585
+ destination: "localSettings",
1586
+ }]);
1587
+ assert.ok(notified[0].includes("Saved:") && notified[0].includes("Bash(gh pr list:*)"));
1588
+ } finally {
1589
+ cleanup();
1590
+ }
1591
+ });
1592
+
1593
+ test("Always Allow for non-Bash tools passes SDK suggestions through", async () => {
1594
+ const notified: string[] = [];
1595
+ const ui = { select: async (_p: string, opts: string[]) => opts.find((o) => o.startsWith("Always Allow"))!, notify: (msg: string) => notified.push(msg) };
1596
+ const suggestions = [{
1597
+ type: "addRules",
1598
+ rules: [{ toolName: "Write" }],
1599
+ behavior: "allow",
1600
+ destination: "localSettings",
1601
+ }];
1602
+
1603
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1604
+ const result = await handler!("Write", { file_path: "/tmp/test.txt" }, makeOptions({ suggestions }));
1605
+
1606
+ assert.equal(result.behavior, "allow");
1607
+ assert.deepEqual((result as any).updatedPermissions, suggestions);
1608
+ // Non-Bash tools don't emit a post-selection notification (only Bash runs the level picker)
1609
+ assert.equal(notified.length, 0);
1610
+ });
1611
+
1612
+ test("Always Allow for non-Bash without suggestions omits updatedPermissions", async () => {
1613
+ const notified: string[] = [];
1614
+ const ui = { select: async (_p: string, opts: string[]) => opts.find((o) => o.startsWith("Always Allow"))!, notify: (msg: string) => notified.push(msg) };
1615
+
1616
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1617
+ const result = await handler!("Write", { file_path: "/tmp/test.txt" }, makeOptions());
1618
+
1619
+ assert.equal(result.behavior, "allow");
1620
+ assert.equal((result as any).updatedPermissions, undefined);
1621
+ // No suggestions → no notification
1622
+ assert.equal(notified.length, 0);
1623
+ });
1624
+
1625
+ test("prompt includes command text for Bash tools", async () => {
1626
+ let selectPrompt = "";
1627
+ const ui = {
1628
+ select: async (prompt: string) => {
1629
+ selectPrompt = prompt;
1630
+ return "Allow";
1631
+ },
1632
+ };
1633
+
1634
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1635
+ await handler!("Bash", { command: "git status" }, makeOptions());
1636
+ assert.ok(selectPrompt.includes("git status"), `prompt should include command: ${selectPrompt}`);
1637
+ });
1638
+
1639
+ test("prompt includes file_path for file tools", async () => {
1640
+ let selectPrompt = "";
1641
+ const ui = {
1642
+ select: async (prompt: string) => {
1643
+ selectPrompt = prompt;
1644
+ return "Allow";
1645
+ },
1646
+ };
1647
+
1648
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1649
+ await handler!("Write", { file_path: "/tmp/test.txt", content: "hello" }, makeOptions());
1650
+ assert.ok(selectPrompt.includes("/tmp/test.txt"), `prompt should include file path: ${selectPrompt}`);
1651
+ });
1652
+
1653
+ test("uses title from options when available", async () => {
1654
+ let selectPrompt = "";
1655
+ const ui = {
1656
+ select: async (prompt: string) => {
1657
+ selectPrompt = prompt;
1658
+ return "Allow";
1659
+ },
1660
+ };
1661
+
1662
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1663
+ await handler!("WebFetch", {}, makeOptions({ title: "Claude wants to fetch: https://example.com" }));
1664
+ assert.ok(selectPrompt.includes("Claude wants to fetch: https://example.com"));
1665
+ });
1666
+
1667
+ test("falls back to default title when options.title is missing", async () => {
1668
+ let selectPrompt = "";
1669
+ const ui = {
1670
+ select: async (prompt: string) => {
1671
+ selectPrompt = prompt;
1672
+ return "Allow";
1673
+ },
1674
+ };
1675
+
1676
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1677
+ await handler!("WebFetch", { url: "https://example.com" }, makeOptions());
1678
+ assert.ok(selectPrompt.includes("Allow Claude Code to use: WebFetch?"));
1679
+ });
1680
+
1681
+ test("returns deny when signal is already aborted", async () => {
1682
+ const ui = {
1683
+ select: async () => { throw new Error("should not be called"); },
1684
+ };
1685
+
1686
+ const controller = new AbortController();
1687
+ controller.abort();
1688
+
1689
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1690
+ const result = await handler!("Bash", {}, makeOptions({ signal: controller.signal }));
1691
+
1692
+ assert.equal(result.behavior, "deny");
1693
+ assert.equal((result as any).message, "Aborted");
1694
+ });
1695
+
1696
+ test("returns deny when ui.select throws", async () => {
1697
+ const ui = {
1698
+ select: async () => { throw new Error("dialog crashed"); },
1699
+ };
1700
+
1701
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1702
+ const result = await handler!("Bash", {}, makeOptions());
1703
+
1704
+ assert.equal(result.behavior, "deny");
1705
+ assert.equal((result as any).message, "Aborted");
1706
+ });
1707
+
1708
+ test("buildSdkOptions passes canUseTool through extraOptions", () => {
1709
+ const canUseTool = async () => ({ behavior: "allow" as const, updatedInput: {}, toolUseID: "test" });
1710
+ const opts = buildSdkOptions("claude-sonnet-4-6", "test", undefined, { canUseTool });
1711
+ assert.equal(opts.canUseTool, canUseTool);
1712
+ });
1713
+
1714
+ test("Always Allow shows level picker and user broadens to base command", async () => {
1715
+ const cleanup = withIsolatedCwd();
1716
+ try {
1717
+ const prompts: string[] = [];
1718
+ const levelOpts: string[][] = [];
1719
+ let selectCall = 0;
1720
+ const ui = {
1721
+ select: async (prompt: string, opts: string[]) => {
1722
+ prompts.push(prompt);
1723
+ selectCall++;
1724
+ if (selectCall === 1) return opts.find((o) => o.startsWith("Always Allow"))!;
1725
+ levelOpts.push(opts);
1726
+ return "Bash(gh:*)";
1727
+ },
1728
+ notify: () => {},
1729
+ };
1730
+
1731
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1732
+ const result = await handler!("Bash", { command: "gh pr list" }, makeOptions());
1733
+
1734
+ assert.equal(result.behavior, "allow");
1735
+ assert.deepEqual((result as any).updatedPermissions, [{
1736
+ type: "addRules",
1737
+ rules: [{ toolName: "Bash", ruleContent: "gh:*" }],
1738
+ behavior: "allow",
1739
+ destination: "localSettings",
1740
+ }]);
1741
+ // Second dialog offered every granularity level
1742
+ assert.deepEqual(levelOpts[0], [
1743
+ "Bash(gh:*)",
1744
+ "Bash(gh pr:*)",
1745
+ "Bash(gh pr list:*)",
1746
+ ]);
1747
+ assert.ok(prompts[1].includes("Save permission at which level?"));
1748
+ } finally {
1749
+ cleanup();
1750
+ }
1751
+ });
1752
+
1753
+ test("Always Allow narrows to mid-level pattern when user picks Bash(gh pr:*)", async () => {
1754
+ const cleanup = withIsolatedCwd();
1755
+ try {
1756
+ let selectCall = 0;
1757
+ const ui = {
1758
+ select: async (_p: string, opts: string[]) => {
1759
+ selectCall++;
1760
+ if (selectCall === 1) return opts.find((o) => o.startsWith("Always Allow"))!;
1761
+ return "Bash(gh pr:*)";
1762
+ },
1763
+ notify: () => {},
1764
+ };
1765
+
1766
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1767
+ const result = await handler!("Bash", { command: "gh pr list --limit 5" }, makeOptions());
1768
+
1769
+ assert.equal(result.behavior, "allow");
1770
+ assert.deepEqual((result as any).updatedPermissions, [{
1771
+ type: "addRules",
1772
+ rules: [{ toolName: "Bash", ruleContent: "gh pr:*" }],
1773
+ behavior: "allow",
1774
+ destination: "localSettings",
1775
+ }]);
1776
+ } finally {
1777
+ cleanup();
1778
+ }
1779
+ });
1780
+
1781
+ test("Always Allow skips level picker when only one pattern is available", async () => {
1782
+ const cleanup = withIsolatedCwd();
1783
+ try {
1784
+ const prompts: string[] = [];
1785
+ const ui = {
1786
+ select: async (prompt: string, opts: string[]) => {
1787
+ prompts.push(prompt);
1788
+ return opts.find((o) => o.startsWith("Always Allow"))!;
1789
+ },
1790
+ notify: () => {},
1791
+ };
1792
+
1793
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1794
+ const result = await handler!("Bash", { command: "ls -la /tmp" }, makeOptions());
1795
+
1796
+ assert.equal(result.behavior, "allow");
1797
+ // "ls" has no subcommand tokens before the flag → single-option path
1798
+ assert.equal(prompts.length, 1, "should not show a second dialog");
1799
+ assert.deepEqual((result as any).updatedPermissions, [{
1800
+ type: "addRules",
1801
+ rules: [{ toolName: "Bash", ruleContent: "ls:*" }],
1802
+ behavior: "allow",
1803
+ destination: "localSettings",
1804
+ }]);
1805
+ } finally {
1806
+ cleanup();
1807
+ }
1808
+ });
1809
+
1810
+ test("Always Allow denies the tool when level picker is dismissed", async () => {
1811
+ const cleanup = withIsolatedCwd();
1812
+ try {
1813
+ const notified: string[] = [];
1814
+ let selectCall = 0;
1815
+ const ui = {
1816
+ select: async (_p: string, opts: string[]) => {
1817
+ selectCall++;
1818
+ if (selectCall === 1) return opts.find((o) => o.startsWith("Always Allow"))!;
1819
+ return undefined; // user dismissed level picker
1820
+ },
1821
+ notify: (msg: string) => notified.push(msg),
1822
+ };
1823
+
1824
+ const handler = createClaudeCodeCanUseToolHandler(ui as any);
1825
+ const result = await handler!("Bash", { command: "gh pr list" }, makeOptions());
1826
+
1827
+ // Dismissing the level picker cancels the tool use — a one-time allow
1828
+ // would leave the spawned agent running even though the user bailed.
1829
+ assert.equal(result.behavior, "deny");
1830
+ assert.equal((result as any).updatedPermissions, undefined);
1831
+ assert.equal(notified.length, 0, "no 'Saved:' notification when nothing was saved");
1832
+ } finally {
1833
+ cleanup();
1834
+ }
1835
+ });
1836
+ });
1837
+
1838
+ // ---------------------------------------------------------------------------
1839
+ // buildBashPermissionPattern — smart permission granularity
1840
+ // ---------------------------------------------------------------------------
1841
+
1842
+ describe("buildBashPermissionPattern", () => {
1843
+ test("simple command wildcards all args", () => {
1844
+ assert.equal(buildBashPermissionPattern("ping -n 4 localhost"), "Bash(ping:*)");
1845
+ assert.equal(buildBashPermissionPattern("echo hello world"), "Bash(echo:*)");
1846
+ assert.equal(buildBashPermissionPattern("ls -la /tmp"), "Bash(ls:*)");
1847
+ assert.equal(buildBashPermissionPattern("node server.js"), "Bash(node:*)");
1848
+ });
1849
+
1850
+ test("git captures one subcommand", () => {
1851
+ assert.equal(buildBashPermissionPattern("git push origin main"), "Bash(git push:*)");
1852
+ assert.equal(buildBashPermissionPattern("git log --oneline"), "Bash(git log:*)");
1853
+ assert.equal(buildBashPermissionPattern("git status"), "Bash(git status:*)");
1854
+ });
1855
+
1856
+ test("gh captures two subcommands", () => {
1857
+ assert.equal(buildBashPermissionPattern("gh pr list"), "Bash(gh pr list:*)");
1858
+ assert.equal(buildBashPermissionPattern("gh pr create --title foo"), "Bash(gh pr create:*)");
1859
+ assert.equal(buildBashPermissionPattern("gh issue view 123"), "Bash(gh issue view:*)");
1860
+ });
1861
+
1862
+ test("npm captures one subcommand", () => {
1863
+ assert.equal(buildBashPermissionPattern("npm install lodash"), "Bash(npm install:*)");
1864
+ assert.equal(buildBashPermissionPattern("npm publish"), "Bash(npm publish:*)");
1865
+ assert.equal(buildBashPermissionPattern("npm run test"), "Bash(npm run:*)");
1866
+ });
1867
+
1868
+ test("npx captures package name", () => {
1869
+ assert.equal(buildBashPermissionPattern("npx vitest run"), "Bash(npx vitest:*)");
1870
+ assert.equal(buildBashPermissionPattern("npx --version"), "Bash(npx --version:*)");
1871
+ });
1872
+
1873
+ test("docker captures one subcommand", () => {
1874
+ assert.equal(buildBashPermissionPattern("docker ps -a"), "Bash(docker ps:*)");
1875
+ assert.equal(buildBashPermissionPattern("docker rm container1"), "Bash(docker rm:*)");
1876
+ });
1877
+
1878
+ test("aws captures two subcommands", () => {
1879
+ assert.equal(buildBashPermissionPattern("aws s3 cp file.txt s3://bucket/"), "Bash(aws s3 cp:*)");
1880
+ assert.equal(buildBashPermissionPattern("aws ec2 describe-instances"), "Bash(aws ec2 describe-instances:*)");
1881
+ });
1882
+
1883
+ test("skips sudo wrapper", () => {
1884
+ assert.equal(buildBashPermissionPattern("sudo ping localhost"), "Bash(ping:*)");
1885
+ assert.equal(buildBashPermissionPattern("sudo git push"), "Bash(git push:*)");
1886
+ });
1887
+
1888
+ test("skips env wrapper and VAR=val assignments", () => {
1889
+ assert.equal(buildBashPermissionPattern("env NODE_ENV=prod node server.js"), "Bash(node:*)");
1890
+ assert.equal(buildBashPermissionPattern("NODE_ENV=prod node server.js"), "Bash(node:*)");
1891
+ assert.equal(buildBashPermissionPattern("FOO=bar BAZ=qux git push"), "Bash(git push:*)");
1892
+ });
1893
+
1894
+ test("strips path from executable", () => {
1895
+ assert.equal(buildBashPermissionPattern("/usr/bin/git push"), "Bash(git push:*)");
1896
+ assert.equal(buildBashPermissionPattern("C:\\Windows\\ping.exe localhost"), "Bash(ping:*)");
1897
+ });
1898
+
1899
+ test("empty or whitespace-only command", () => {
1900
+ assert.equal(buildBashPermissionPattern(""), "Bash(*)");
1901
+ assert.equal(buildBashPermissionPattern(" "), "Bash(*)");
1902
+ });
1903
+
1904
+ test("chained commands — extracts pattern from the meaningful segment", () => {
1905
+ assert.equal(buildBashPermissionPattern("cd /foo && gh pr list --limit 5"), "Bash(gh pr list:*)");
1906
+ assert.equal(buildBashPermissionPattern("cd C:/Users/djeff/repos/gsd-2 && gh pr list --limit 5"), "Bash(gh pr list:*)");
1907
+ assert.equal(buildBashPermissionPattern("cd /tmp && git push origin main"), "Bash(git push:*)");
1908
+ assert.equal(buildBashPermissionPattern("export FOO=1 && npm install lodash"), "Bash(npm install:*)");
1909
+ assert.equal(buildBashPermissionPattern("mkdir -p out; docker ps -a"), "Bash(docker ps:*)");
1910
+ assert.equal(buildBashPermissionPattern("echo start || ping localhost"), "Bash(ping:*)");
1911
+ });
1912
+
1913
+ test("skips trailing || true / || : error suppressors", () => {
1914
+ assert.equal(
1915
+ buildBashPermissionPattern("cd C:/Users/djeff/repos/gsd-2 && gh pr create --dry-run --title \"test\" --body \"test\" 2>&1 || true"),
1916
+ "Bash(gh pr create:*)",
1917
+ );
1918
+ assert.equal(buildBashPermissionPattern("gh pr list || true"), "Bash(gh pr list:*)");
1919
+ assert.equal(buildBashPermissionPattern("git push || :"), "Bash(git push:*)");
1920
+ assert.equal(buildBashPermissionPattern("cd /tmp && npm install || echo failed"), "Bash(npm install:*)");
1921
+ });
1922
+
1923
+ test("single command is unaffected by chain extraction", () => {
1924
+ assert.equal(buildBashPermissionPattern("gh pr list"), "Bash(gh pr list:*)");
1925
+ assert.equal(buildBashPermissionPattern("git push origin main"), "Bash(git push:*)");
1926
+ });
1927
+ });
1928
+
1929
+ // ---------------------------------------------------------------------------
1930
+ // buildBashPermissionPatternOptions — granularity level menu
1931
+ // ---------------------------------------------------------------------------
1932
+
1933
+ describe("buildBashPermissionPatternOptions", () => {
1934
+ test("offers every prefix from base to full subcommand chain", () => {
1935
+ assert.deepEqual(buildBashPermissionPatternOptions("gh pr list"), [
1936
+ "Bash(gh:*)",
1937
+ "Bash(gh pr:*)",
1938
+ "Bash(gh pr list:*)",
1939
+ ]);
1940
+ assert.deepEqual(buildBashPermissionPatternOptions("git push origin main"), [
1941
+ "Bash(git:*)",
1942
+ "Bash(git push:*)",
1943
+ "Bash(git push origin:*)",
1944
+ "Bash(git push origin main:*)",
1945
+ ]);
1946
+ });
1947
+
1948
+ test("stops at first flag — flags are args, not verbs", () => {
1949
+ assert.deepEqual(buildBashPermissionPatternOptions("gh pr create --title foo"), [
1950
+ "Bash(gh:*)",
1951
+ "Bash(gh pr:*)",
1952
+ "Bash(gh pr create:*)",
1953
+ ]);
1954
+ assert.deepEqual(buildBashPermissionPatternOptions("git log --oneline"), [
1955
+ "Bash(git:*)",
1956
+ "Bash(git log:*)",
1957
+ ]);
1958
+ });
1959
+
1960
+ test("single-option when there is no subcommand to choose from", () => {
1961
+ assert.deepEqual(buildBashPermissionPatternOptions("ls -la /tmp"), ["Bash(ls:*)"]);
1962
+ assert.deepEqual(buildBashPermissionPatternOptions("ping -n 4 localhost"), ["Bash(ping:*)"]);
1963
+ assert.deepEqual(buildBashPermissionPatternOptions("node"), ["Bash(node:*)"]);
1964
+ });
1965
+
1966
+ test("extracts meaningful segment from compound commands", () => {
1967
+ assert.deepEqual(buildBashPermissionPatternOptions("cd /foo && gh pr list"), [
1968
+ "Bash(gh:*)",
1969
+ "Bash(gh pr:*)",
1970
+ "Bash(gh pr list:*)",
1971
+ ]);
1972
+ assert.deepEqual(buildBashPermissionPatternOptions("gh pr create --dry-run || true"), [
1973
+ "Bash(gh:*)",
1974
+ "Bash(gh pr:*)",
1975
+ "Bash(gh pr create:*)",
1976
+ ]);
1977
+ });
1978
+
1979
+ test("caps at three subcommand tokens to keep the menu short", () => {
1980
+ const result = buildBashPermissionPatternOptions("foo bar baz qux quux corge");
1981
+ // base + 3 sub tokens = 4 patterns max
1982
+ assert.equal(result.length, 4);
1983
+ assert.deepEqual(result, [
1984
+ "Bash(foo:*)",
1985
+ "Bash(foo bar:*)",
1986
+ "Bash(foo bar baz:*)",
1987
+ "Bash(foo bar baz qux:*)",
1988
+ ]);
1989
+ });
1990
+
1991
+ test("skips sudo/env wrappers like the single-pattern variant", () => {
1992
+ assert.deepEqual(buildBashPermissionPatternOptions("sudo git push origin"), [
1993
+ "Bash(git:*)",
1994
+ "Bash(git push:*)",
1995
+ "Bash(git push origin:*)",
1996
+ ]);
1997
+ assert.deepEqual(buildBashPermissionPatternOptions("NODE_ENV=prod node server.js"), [
1998
+ "Bash(node:*)",
1999
+ "Bash(node server.js:*)",
2000
+ ]);
2001
+ });
2002
+
2003
+ test("empty command returns the catch-all pattern", () => {
2004
+ assert.deepEqual(buildBashPermissionPatternOptions(""), ["Bash(*)"]);
2005
+ assert.deepEqual(buildBashPermissionPatternOptions(" "), ["Bash(*)"]);
2006
+ });
2007
+ });
2008
+
2009
+ // ---------------------------------------------------------------------------
2010
+ // bashCommandMatchesSavedRules — compound command bypass for saved rules
2011
+ // ---------------------------------------------------------------------------
2012
+
2013
+ describe("bashCommandMatchesSavedRules — compound command bypass", () => {
2014
+ let tempDir: string;
2015
+ let originalCwd: string;
2016
+
2017
+ // Create a temp project directory with .claude/settings.local.json
2018
+ function setupSettings(allow: string[]): void {
2019
+ const claudeDir = join(tempDir, ".claude");
2020
+ mkdirSync(claudeDir, { recursive: true });
2021
+ writeFileSync(
2022
+ join(claudeDir, "settings.local.json"),
2023
+ JSON.stringify({ permissions: { allow } }),
2024
+ );
2025
+ }
2026
+
2027
+ // biome-ignore lint/suspicious/noExplicitAny: test-only monkey-patch
2028
+ let origCwd: any;
2029
+
2030
+ // Monkey-patch process.cwd() to point at our temp dir
2031
+ function setCwd(dir: string): void {
2032
+ origCwd = process.cwd;
2033
+ process.cwd = () => dir;
2034
+ }
2035
+ function restoreCwd(): void {
2036
+ if (origCwd) process.cwd = origCwd;
2037
+ }
2038
+
2039
+ test("matches cd-prefixed compound command against saved prefix rule", () => {
2040
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2041
+ try {
2042
+ setupSettings(["Bash(gh pr list:*)"]);
2043
+ setCwd(tempDir);
2044
+ assert.equal(
2045
+ bashCommandMatchesSavedRules("cd /some/path && gh pr list --limit 5"),
2046
+ true,
2047
+ );
2048
+ } finally {
2049
+ restoreCwd();
2050
+ rmSync(tempDir, { recursive: true, force: true });
2051
+ }
2052
+ });
2053
+
2054
+ test("matches cd-prefixed compound command with exact subcommand", () => {
2055
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2056
+ try {
2057
+ setupSettings(["Bash(gh pr list:*)"]);
2058
+ setCwd(tempDir);
2059
+ assert.equal(
2060
+ bashCommandMatchesSavedRules("cd C:/Users/foo/repos/bar && gh pr list"),
2061
+ true,
2062
+ );
2063
+ } finally {
2064
+ restoreCwd();
2065
+ rmSync(tempDir, { recursive: true, force: true });
2066
+ }
2067
+ });
2068
+
2069
+ test("rejects when leading segment is not cd", () => {
2070
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2071
+ try {
2072
+ setupSettings(["Bash(gh pr list:*)"]);
2073
+ setCwd(tempDir);
2074
+ // "rm -rf /tmp" is not a cd command — should not auto-approve
2075
+ assert.equal(
2076
+ bashCommandMatchesSavedRules("rm -rf /tmp && gh pr list"),
2077
+ false,
2078
+ );
2079
+ } finally {
2080
+ restoreCwd();
2081
+ rmSync(tempDir, { recursive: true, force: true });
2082
+ }
2083
+ });
2084
+
2085
+ test("rejects when meaningful segment does not match any rule", () => {
2086
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2087
+ try {
2088
+ setupSettings(["Bash(gh pr list:*)"]);
2089
+ setCwd(tempDir);
2090
+ assert.equal(
2091
+ bashCommandMatchesSavedRules("cd /path && gh issue create --title foo"),
2092
+ false,
2093
+ );
2094
+ } finally {
2095
+ restoreCwd();
2096
+ rmSync(tempDir, { recursive: true, force: true });
2097
+ }
2098
+ });
2099
+
2100
+ test("matches simple (non-compound) commands against on-disk rules", () => {
2101
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2102
+ try {
2103
+ setupSettings(["Bash(gh pr list:*)"]);
2104
+ setCwd(tempDir);
2105
+ // Simple commands must also be checked — the SDK's in-memory cache
2106
+ // may be stale if the rule was added mid-session via "Always Allow"
2107
+ assert.equal(bashCommandMatchesSavedRules("gh pr list --limit 5"), true);
2108
+ assert.equal(bashCommandMatchesSavedRules("gh pr list"), true);
2109
+ } finally {
2110
+ restoreCwd();
2111
+ rmSync(tempDir, { recursive: true, force: true });
2112
+ }
2113
+ });
2114
+
2115
+ test("returns false for simple commands with no matching rule", () => {
2116
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2117
+ try {
2118
+ setupSettings(["Bash(gh pr list:*)"]);
2119
+ setCwd(tempDir);
2120
+ assert.equal(bashCommandMatchesSavedRules("gh issue list --limit 5"), false);
2121
+ } finally {
2122
+ restoreCwd();
2123
+ rmSync(tempDir, { recursive: true, force: true });
2124
+ }
2125
+ });
2126
+
2127
+ test("returns false when no settings file exists", () => {
2128
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2129
+ try {
2130
+ // No .claude/settings.local.json created
2131
+ setCwd(tempDir);
2132
+ assert.equal(
2133
+ bashCommandMatchesSavedRules("cd /path && gh pr list"),
2134
+ false,
2135
+ );
2136
+ } finally {
2137
+ restoreCwd();
2138
+ rmSync(tempDir, { recursive: true, force: true });
2139
+ }
2140
+ });
2141
+
2142
+ test("matches exact rule (non-prefix)", () => {
2143
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2144
+ try {
2145
+ setupSettings(["Bash(ping -n 4 localhost)"]);
2146
+ setCwd(tempDir);
2147
+ assert.equal(
2148
+ bashCommandMatchesSavedRules("cd /path && ping -n 4 localhost"),
2149
+ true,
2150
+ );
2151
+ } finally {
2152
+ restoreCwd();
2153
+ rmSync(tempDir, { recursive: true, force: true });
2154
+ }
2155
+ });
2156
+
2157
+ test("handles multiple cd segments before the meaningful command", () => {
2158
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2159
+ try {
2160
+ setupSettings(["Bash(npm install:*)"]);
2161
+ setCwd(tempDir);
2162
+ assert.equal(
2163
+ bashCommandMatchesSavedRules("cd /home && cd project && npm install lodash"),
2164
+ true,
2165
+ );
2166
+ } finally {
2167
+ restoreCwd();
2168
+ rmSync(tempDir, { recursive: true, force: true });
2169
+ }
2170
+ });
2171
+
2172
+ test("matches compound command with trailing || true suppressor", () => {
2173
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2174
+ try {
2175
+ setupSettings(["Bash(gh pr create:*)"]);
2176
+ setCwd(tempDir);
2177
+ assert.equal(
2178
+ bashCommandMatchesSavedRules('cd C:/Users/djeff/repos/gsd-2 && gh pr create --dry-run --title "test" --body "test" 2>&1 || true'),
2179
+ true,
2180
+ );
2181
+ assert.equal(
2182
+ bashCommandMatchesSavedRules("gh pr create --dry-run || true"),
2183
+ true,
2184
+ );
2185
+ assert.equal(
2186
+ bashCommandMatchesSavedRules("cd /tmp && git push || :"),
2187
+ false, // rule is for gh pr create, not git push
2188
+ );
2189
+ } finally {
2190
+ restoreCwd();
2191
+ rmSync(tempDir, { recursive: true, force: true });
2192
+ }
2193
+ });
2194
+
2195
+ test("reads rules from settings.json as well as settings.local.json", () => {
2196
+ tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-rules-")));
2197
+ try {
2198
+ const claudeDir = join(tempDir, ".claude");
2199
+ mkdirSync(claudeDir, { recursive: true });
2200
+ writeFileSync(
2201
+ join(claudeDir, "settings.json"),
2202
+ JSON.stringify({ permissions: { allow: ["Bash(git push:*)"] } }),
2203
+ );
2204
+ setCwd(tempDir);
2205
+ assert.equal(
2206
+ bashCommandMatchesSavedRules("cd /repo && git push origin main"),
2207
+ true,
2208
+ );
2209
+ } finally {
2210
+ restoreCwd();
2211
+ rmSync(tempDir, { recursive: true, force: true });
2212
+ }
1276
2213
  });
1277
2214
  });