gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.84a383f51

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 (383) hide show
  1. package/README.md +7 -7
  2. package/dist/claude-cli-check.js +64 -37
  3. package/dist/cli-policy.d.ts +13 -0
  4. package/dist/cli-policy.js +17 -0
  5. package/dist/cli.js +95 -55
  6. package/dist/headless-query.d.ts +22 -0
  7. package/dist/headless-query.js +24 -4
  8. package/dist/headless.d.ts +10 -0
  9. package/dist/headless.js +16 -1
  10. package/dist/loader.js +7 -10
  11. package/dist/onboarding.d.ts +10 -0
  12. package/dist/onboarding.js +2 -2
  13. package/dist/provider-migrations.d.ts +2 -2
  14. package/dist/provider-migrations.js +5 -2
  15. package/dist/resource-loader.d.ts +5 -2
  16. package/dist/resource-loader.js +28 -5
  17. package/dist/resources/.managed-resources-content-hash +1 -0
  18. package/dist/resources/extensions/claude-code-cli/readiness.js +77 -45
  19. package/dist/resources/extensions/gsd/auto/loop.js +23 -0
  20. package/dist/resources/extensions/gsd/auto/phases.js +2 -2
  21. package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
  22. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  23. package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
  24. package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
  25. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
  27. package/dist/resources/extensions/gsd/auto-worktree.js +30 -0
  28. package/dist/resources/extensions/gsd/auto.js +14 -5
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
  30. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
  33. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +94 -31
  34. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
  35. package/dist/resources/extensions/gsd/bootstrap/system-context.js +34 -8
  36. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -2
  37. package/dist/resources/extensions/gsd/commands/catalog.js +69 -5
  38. package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
  39. package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
  40. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
  41. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  42. package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -0
  43. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
  44. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  45. package/dist/resources/extensions/gsd/forensics.js +2 -2
  46. package/dist/resources/extensions/gsd/git-service.js +12 -5
  47. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  48. package/dist/resources/extensions/gsd/guided-flow.js +23 -23
  49. package/dist/resources/extensions/gsd/memory-store.js +66 -31
  50. package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
  51. package/dist/resources/extensions/gsd/model-router.js +114 -9
  52. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
  53. package/dist/resources/extensions/gsd/preferences-models.js +91 -15
  54. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  55. package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
  56. package/dist/resources/extensions/gsd/preferences.js +5 -3
  57. package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
  58. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
  59. package/dist/resources/extensions/gsd/state.js +42 -0
  60. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  61. package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
  62. package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
  63. package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
  64. package/dist/resources/extensions/gsd/worktree-command.js +26 -46
  65. package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
  66. package/dist/resources/extensions/mcp-client/index.js +6 -3
  67. package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
  68. package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
  69. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  70. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  71. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  72. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  73. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  74. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  75. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  76. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  77. package/dist/rtk-shared.d.ts +3 -0
  78. package/dist/rtk-shared.js +17 -0
  79. package/dist/rtk.d.ts +2 -5
  80. package/dist/rtk.js +3 -20
  81. package/dist/runtime-checks.d.ts +27 -0
  82. package/dist/runtime-checks.js +38 -0
  83. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  84. package/dist/web/standalone/.next/BUILD_ID +1 -1
  85. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  86. package/dist/web/standalone/.next/build-manifest.json +3 -3
  87. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  88. package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
  89. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
  110. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/index.html +1 -1
  112. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  113. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  114. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  115. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  120. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  122. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  124. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  125. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  126. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  127. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
  128. package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
  129. package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
  130. package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
  131. package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
  132. package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
  133. package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
  134. package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
  135. package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
  136. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
  137. package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
  138. package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
  139. package/dist/web/standalone/package.json +2 -1
  140. package/dist/worktree-status-banner.d.ts +1 -0
  141. package/dist/worktree-status-banner.js +132 -0
  142. package/package.json +1 -1
  143. package/packages/daemon/package.json +2 -2
  144. package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
  145. package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
  146. package/packages/mcp-server/dist/alias-telemetry.js +30 -0
  147. package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
  148. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/workflow-tools.js +74 -46
  150. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  151. package/packages/mcp-server/package.json +2 -2
  152. package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
  153. package/packages/mcp-server/src/alias-telemetry.ts +30 -0
  154. package/packages/mcp-server/src/workflow-tools.test.ts +26 -0
  155. package/packages/mcp-server/src/workflow-tools.ts +93 -58
  156. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  157. package/packages/native/package.json +1 -1
  158. package/packages/pi-agent-core/package.json +1 -1
  159. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  160. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
  161. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
  162. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
  163. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
  164. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  165. package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
  166. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  167. package/packages/pi-ai/dist/types.d.ts +13 -0
  168. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  169. package/packages/pi-ai/dist/types.js.map +1 -1
  170. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  171. package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
  172. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  173. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
  174. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  175. package/packages/pi-ai/package.json +1 -1
  176. package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
  177. package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
  178. package/packages/pi-ai/src/types.ts +13 -0
  179. package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
  180. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
  181. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  182. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
  184. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/messages.js +4 -0
  187. package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  189. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
  191. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
  193. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
  195. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
  197. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
  199. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
  201. package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
  202. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
  203. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
  204. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
  205. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
  206. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
  211. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
  212. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
  213. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
  214. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
  215. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
  216. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
  217. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
  218. package/packages/pi-coding-agent/package.json +1 -1
  219. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
  220. package/packages/pi-coding-agent/src/core/messages.ts +4 -0
  221. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
  222. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
  223. package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
  224. package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
  225. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
  226. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
  227. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
  228. package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
  229. package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
  230. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  231. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
  232. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  233. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
  234. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
  235. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
  236. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
  237. package/packages/pi-tui/package.json +1 -1
  238. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
  239. package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
  240. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  241. package/packages/rpc-client/package.json +1 -1
  242. package/pkg/package.json +1 -1
  243. package/src/resources/extensions/claude-code-cli/readiness.ts +78 -46
  244. package/src/resources/extensions/gsd/auto/loop.ts +24 -2
  245. package/src/resources/extensions/gsd/auto/phases.ts +3 -3
  246. package/src/resources/extensions/gsd/auto/run-unit.ts +3 -1
  247. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  248. package/src/resources/extensions/gsd/auto/types.ts +1 -0
  249. package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
  250. package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
  251. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  252. package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
  253. package/src/resources/extensions/gsd/auto-worktree.ts +38 -0
  254. package/src/resources/extensions/gsd/auto.ts +14 -4
  255. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
  256. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
  257. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  258. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
  259. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +102 -31
  260. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
  261. package/src/resources/extensions/gsd/bootstrap/system-context.ts +39 -8
  262. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +39 -11
  263. package/src/resources/extensions/gsd/commands/catalog.ts +75 -5
  264. package/src/resources/extensions/gsd/commands/handlers/core.ts +22 -1
  265. package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
  266. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
  267. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  268. package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -0
  269. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
  270. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  271. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  272. package/src/resources/extensions/gsd/forensics.ts +2 -2
  273. package/src/resources/extensions/gsd/git-service.ts +13 -5
  274. package/src/resources/extensions/gsd/gsd-db.ts +12 -2
  275. package/src/resources/extensions/gsd/guided-flow.ts +25 -25
  276. package/src/resources/extensions/gsd/memory-store.ts +81 -28
  277. package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
  278. package/src/resources/extensions/gsd/model-router.ts +172 -9
  279. package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
  280. package/src/resources/extensions/gsd/preferences-models.ts +101 -15
  281. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  282. package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
  283. package/src/resources/extensions/gsd/preferences.ts +16 -2
  284. package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
  285. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
  286. package/src/resources/extensions/gsd/state.ts +42 -0
  287. package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  288. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +178 -1
  289. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
  290. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +9 -5
  291. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
  292. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
  293. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
  294. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
  295. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
  296. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
  297. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
  298. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
  299. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
  300. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
  301. package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
  302. package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
  303. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
  304. package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
  305. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
  306. package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
  307. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
  308. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
  309. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
  310. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
  311. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
  312. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
  313. package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
  314. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
  315. package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
  316. package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
  317. package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
  318. package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
  319. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -0
  320. package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
  321. package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
  322. package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
  323. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
  324. package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
  325. package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
  326. package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
  327. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
  328. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
  329. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
  330. package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
  331. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
  332. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
  334. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
  335. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
  336. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
  337. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
  338. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
  339. package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
  340. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
  341. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
  342. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
  343. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
  344. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
  345. package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
  346. package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
  347. package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
  348. package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
  349. package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
  350. package/src/resources/extensions/gsd/worktree-command.ts +31 -44
  351. package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
  352. package/src/resources/extensions/mcp-client/index.ts +6 -3
  353. package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
  354. package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
  355. package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
  356. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  357. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  358. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  359. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  360. package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  361. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  362. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  363. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
  364. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
  365. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
  366. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  367. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
  368. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
  369. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
  370. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
  371. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
  372. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
  373. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
  374. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  375. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
  376. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
  377. package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
  378. package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
  379. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
  380. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
  381. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
  382. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_buildManifest.js +0 -0
  383. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_ssgManifest.js +0 -0
@@ -107,6 +107,50 @@ test("profile: resolveProfileDefaults exists and handles all 4 tiers", () => {
107
107
  );
108
108
  });
109
109
 
110
+ test("profile: PROFILE_TIER_MAP defines tier intentions for all profiles", async () => {
111
+ const { getProfileTierMap } = await import("../preferences-models.ts");
112
+ const profileMaps = {
113
+ budget: getProfileTierMap("budget"),
114
+ balanced: getProfileTierMap("balanced"),
115
+ quality: getProfileTierMap("quality"),
116
+ };
117
+
118
+ assert.deepEqual(
119
+ Object.keys(profileMaps).sort(),
120
+ ["balanced", "budget", "quality"],
121
+ "PROFILE_TIER_MAP should include the profile default tiers",
122
+ );
123
+
124
+ const expectedPhaseKeys = ["completion", "execution", "execution_simple", "planning", "research", "subagent"];
125
+ const validTiers = new Set(["light", "standard", "heavy"]);
126
+ for (const [profile, tierMap] of Object.entries(profileMaps)) {
127
+ assert.deepEqual(
128
+ Object.keys(tierMap).sort(),
129
+ expectedPhaseKeys,
130
+ `${profile} should define all model-bearing phases`,
131
+ );
132
+ for (const [phase, tier] of Object.entries(tierMap)) {
133
+ assert.ok(validTiers.has(tier), `${profile}.${phase} should be a tier name`);
134
+ assert.ok(!tier.includes("claude-"), `${profile}.${phase} should not be a model ID`);
135
+ }
136
+ }
137
+ });
138
+
139
+ test("profile: resolveProfileDefaults is provider-agnostic — picks OpenAI when only OpenAI is available", async () => {
140
+ // Behavioral check: with only OpenAI models in the available list, no slot
141
+ // should resolve to a claude-* model. Source-grep cannot prove this — only
142
+ // exercising the function with a controlled available-model list can.
143
+ const { resolveProfileDefaults } = await import("../preferences-models.ts");
144
+ const defaults = resolveProfileDefaults("balanced", ["gpt-4o", "gpt-4o-mini"]);
145
+ assert.ok(defaults.models, "balanced profile should populate models");
146
+ for (const [phase, modelId] of Object.entries(defaults.models!)) {
147
+ assert.ok(
148
+ typeof modelId === "string" && !String(modelId).startsWith("claude-"),
149
+ `${phase} resolved to ${modelId} but only OpenAI is available`,
150
+ );
151
+ }
152
+ });
153
+
110
154
  test("profile: budget profile sets phase skips to true", () => {
111
155
  // Extract the budget case block
112
156
  const budgetIdx = preferencesSrc.indexOf('case "budget":');
@@ -222,11 +266,14 @@ test("merge: mergePreferences handles phases with spread", () => {
222
266
  // Subagent Model Routing
223
267
  // ═══════════════════════════════════════════════════════════════════════════
224
268
 
225
- test("subagent: budget profile sets subagent model", () => {
226
- const budgetIdx = preferencesSrc.indexOf('case "budget":');
227
- const balancedIdx = preferencesSrc.indexOf('case "balanced":');
269
+ test("subagent: budget profile assigns light tier for subagent", () => {
270
+ // PROFILE_TIER_MAP.budget.subagent should be "light"
271
+ const tierMapIdx = preferencesSrc.indexOf("PROFILE_TIER_MAP");
272
+ const budgetIdx = preferencesSrc.indexOf("budget:", tierMapIdx);
273
+ const balancedIdx = preferencesSrc.indexOf("balanced:", tierMapIdx);
228
274
  const budgetBlock = preferencesSrc.slice(budgetIdx, balancedIdx);
229
- assert.ok(budgetBlock.includes("subagent:"), "budget profile should set subagent model");
275
+ assert.ok(budgetBlock.includes("subagent:"), "budget profile should define subagent tier");
276
+ assert.ok(budgetBlock.includes('"light"'), "budget subagent should use light tier");
230
277
  });
231
278
 
232
279
  test("subagent: resolveModelWithFallbacksForUnit handles subagent unit types", () => {
@@ -144,19 +144,10 @@ test("recoverTimedOutUnit: no top-level bumpTurnGeneration — steering branches
144
144
  );
145
145
  });
146
146
 
147
- test("recoverTimedOutUnit: bumpAndResolveSynthetic appears exactly four times (one per advance branch)", async () => {
148
- const fs = await import("node:fs");
149
- const path = await import("node:path");
150
- const url = await import("node:url");
151
- const here = path.dirname(url.fileURLToPath(import.meta.url));
152
- const src = fs.readFileSync(
153
- path.join(here, "..", "auto-timeout-recovery.ts"),
154
- "utf-8",
155
- );
156
- const matches = src.match(/bumpAndResolveSynthetic\s*\(/g) ?? [];
157
- assert.equal(
158
- matches.length,
159
- 4,
160
- `expected 4 advance-branch supersede sites (durableComplete, execute-task-exhausted, artifact-already-exists, non-execute-exhausted); found ${matches.length}`,
161
- );
162
- });
147
+ // Removed: source-grep count of `bumpAndResolveSynthetic\s*\(` occurrences.
148
+ // A literal 4 hardcodes the current branch shape, not behaviour. The
149
+ // behavioural invariant "advance branches supersede atomically; non-advance
150
+ // branches do not bump" — is enforced by the previous test (no direct
151
+ // bumpTurnGeneration calls) plus the per-branch behavioural tests above
152
+ // (`recoverTimedOutUnit: …`). Refactors that split a branch into two would
153
+ // trip a count test without affecting correctness. Refs #4851.
@@ -76,13 +76,11 @@ describe("#3615 — structural: fallback exists with correct guards", () => {
76
76
  );
77
77
  });
78
78
 
79
- test("only one return null at the end", () => {
80
- const returnNulls = fnBody.match(/return null;/g);
81
- assert.ok(
82
- returnNulls && returnNulls.length === 1,
83
- `expected exactly 1 'return null' (at end after fallback), got ${returnNulls?.length ?? 0}`,
84
- );
85
- });
79
+ // Removed: source-grep count of `return null;` occurrences. The behaviour
80
+ // we care about ("function returns null only when no auto-dispatch /
81
+ // guided-resume / fallback path matches") is exercised by the behavioural
82
+ // tests below counting literal `return null;` tokens encodes statement
83
+ // shape, not contract. Refs #4851.
86
84
  });
87
85
 
88
86
  // ── Behavioral tests: RESUME_INTENT_PATTERNS ────────────────────────
@@ -4,7 +4,7 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { execSync } from "node:child_process";
7
- import { runTurnGitAction } from "../git-service.ts";
7
+ import { handleTurnGitActionError, runTurnGitAction } from "../git-service.ts";
8
8
 
9
9
  function run(cmd: string, cwd: string): string {
10
10
  return execSync(cmd, { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
@@ -83,3 +83,17 @@ test("uok gitops turn action commit creates commit with unit trailer", () => {
83
83
  rmSync(repo, { recursive: true, force: true });
84
84
  }
85
85
  });
86
+
87
+ test("uok gitops turn action rethrows infrastructure failures", () => {
88
+ const err = Object.assign(new Error("ENFILE: file table overflow"), { code: "ENFILE" });
89
+
90
+ assert.throws(() => handleTurnGitActionError("commit", err), (thrown) => thrown === err);
91
+ });
92
+
93
+ test("uok gitops turn action keeps non-infrastructure git failures recoverable", () => {
94
+ const result = handleTurnGitActionError("commit", new Error("nothing to commit"));
95
+
96
+ assert.equal(result.action, "commit");
97
+ assert.equal(result.status, "failed");
98
+ assert.equal(result.error, "nothing to commit");
99
+ });
@@ -13,7 +13,7 @@
13
13
  import { test } from "node:test";
14
14
  import assert from "node:assert/strict";
15
15
 
16
- import { GSDVisualizerOverlay } from "../visualizer-overlay.ts";
16
+ import { GSDVisualizerOverlay, TAB_COUNT } from "../visualizer-overlay.ts";
17
17
 
18
18
  function makeTui() {
19
19
  const renders: number[] = [];
@@ -93,16 +93,16 @@ test("overlay switches tabs via 1–9,0 digit keys", (t) => {
93
93
 
94
94
  test("overlay Tab key cycles forward and wraps around at TAB_COUNT", (t) => {
95
95
  const { overlay } = makeOverlay(t);
96
- overlay.activeTab = 9;
96
+ overlay.activeTab = TAB_COUNT - 1;
97
97
  overlay.handleInput("\t");
98
- assert.equal(overlay.activeTab, 0, "Tab wraps from 9 back to 0");
98
+ assert.equal(overlay.activeTab, 0, `Tab wraps from ${TAB_COUNT - 1} back to 0`);
99
99
  });
100
100
 
101
101
  test("overlay Shift+Tab cycles backward and wraps", (t) => {
102
102
  const { overlay } = makeOverlay(t);
103
103
  overlay.activeTab = 0;
104
104
  overlay.handleInput("\u001b[Z");
105
- assert.equal(overlay.activeTab, 9, "Shift+Tab wraps from 0 to 9");
105
+ assert.equal(overlay.activeTab, TAB_COUNT - 1, `Shift+Tab wraps from 0 to ${TAB_COUNT - 1}`);
106
106
  });
107
107
 
108
108
  // ─── Filter mode ─────────────────────────────────────────────────────────
@@ -331,9 +331,9 @@ test("overlay footer hint mentions tab navigation, filter, scroll, and help", (t
331
331
 
332
332
  // ─── Scroll offsets array is sized to TAB_COUNT ──────────────────────────
333
333
 
334
- test("overlay scrollOffsets array has one slot per tab (10 tabs total)", (t) => {
334
+ test("overlay scrollOffsets array has one slot per tab", (t) => {
335
335
  const { overlay } = makeOverlay(t);
336
- assert.equal(overlay.scrollOffsets.length, 10, "scrollOffsets sized to TAB_COUNT=10");
336
+ assert.equal(overlay.scrollOffsets.length, TAB_COUNT, "scrollOffsets sized to TAB_COUNT");
337
337
  assert.ok(overlay.scrollOffsets.every((n: number) => n === 0), "initialized to zero");
338
338
  });
339
339
 
@@ -10,6 +10,7 @@ import assert from 'node:assert/strict';
10
10
  import { join, sep } from 'node:path';
11
11
 
12
12
  import { shouldBlockPlanningUnit } from '../bootstrap/write-gate.ts';
13
+ import { isDeterministicPolicyError } from '../auto-tool-tracking.ts';
13
14
  import type { ToolsPolicy } from '../unit-context-manifest.ts';
14
15
 
15
16
  const BASE = join('/tmp', 'fake-project');
@@ -36,6 +37,20 @@ test('planning-unit: blocks edit to user source (the b23 forensic)', () => {
36
37
  assert.match(r.reason!, /discuss-milestone/);
37
38
  });
38
39
 
40
+ test('planning-unit: deterministic block reason is suitable for retry short-circuiting', () => {
41
+ const r = shouldBlockPlanningUnit(
42
+ 'edit',
43
+ 'src/main.ts',
44
+ BASE,
45
+ 'discuss-milestone',
46
+ PLANNING,
47
+ );
48
+ assert.strictEqual(r.block, true);
49
+ assert.match(r.reason!, /HARD BLOCK/);
50
+ assert.match(r.reason!, /tools-policy/);
51
+ assert.strictEqual(isDeterministicPolicyError(r.reason!), true);
52
+ });
53
+
39
54
  test('planning-unit: blocks write to user source via relative path', () => {
40
55
  const r = shouldBlockPlanningUnit('write', 'src/main.ts', BASE, 'plan-milestone', PLANNING);
41
56
  assert.strictEqual(r.block, true);
@@ -96,8 +96,24 @@ export function executeMemoryCapture(params: MemoryCaptureParams): ToolExecution
96
96
  const tags = normalizeTags(params.tags);
97
97
 
98
98
  const structuredFields = normalizeStructuredFields(params.structuredFields);
99
- const id = createMemory({ category, content, confidence, scope, tags, structuredFields });
99
+ let id: string | null;
100
+ try {
101
+ id = createMemory({ category, content, confidence, scope, tags, structuredFields });
102
+ } catch (err) {
103
+ // Surface the underlying SQL message (e.g. "database disk image is
104
+ // malformed", "no such table: memories") so the operator gets the
105
+ // actionable signal instead of an opaque "create_failed". See #4967.
106
+ const message = err instanceof Error ? err.message : String(err);
107
+ return {
108
+ content: [{ type: "text", text: `Error: failed to create memory: ${message}` }],
109
+ details: { operation: "memory_capture", error: message },
110
+ isError: true,
111
+ };
112
+ }
100
113
  if (!id) {
114
+ // DB unavailable or adapter missing — distinct from the SQL-error path
115
+ // above. Keep the legacy create_failed token here so any consumers that
116
+ // explicitly key on the unavailable case continue to work.
101
117
  return {
102
118
  content: [{ type: "text", text: "Error: failed to create memory." }],
103
119
  details: { operation: "memory_capture", error: "create_failed" },
@@ -93,10 +93,10 @@ export type PreferencesPolicy = "none" | "active-only" | "full";
93
93
  /**
94
94
  * Tool-access policy per unit type (#4934).
95
95
  *
96
- * Declarative-only in this PR runtime enforcement (write-gate.ts predicate
97
- * + dispatch-time isolation) lands in follow-up PRs. The shape is the
98
- * agreement between manifest authors and enforcement; surfacing it now lets
99
- * reviewers ratify per-unit policy intent before any blocking logic ships.
96
+ * Runtime-enforced by the GSD write gate for active auto-mode units. The
97
+ * manifest declares the allowed tool surface; register-hooks.ts resolves the
98
+ * active unit's manifest before each tool call and write-gate.ts rejects
99
+ * violations before the tool executes.
100
100
  *
101
101
  * Modes:
102
102
  * - "all" — Read + Edit/Write/MultiEdit/NotebookEdit + Bash + Task.
@@ -214,10 +214,10 @@ export interface UnitContextManifest {
214
214
  /** Preferences block policy. */
215
215
  readonly preferences: PreferencesPolicy;
216
216
  /**
217
- * Tool-access policy (#4934). Declarative in this PR; runtime enforcement
218
- * (path-scoped write blocking + subagent denial + bash allowlist) lands
219
- * in follow-ups. Required on every manifest so missing entries fail loud
220
- * via the CI invariant test rather than defaulting to "all" silently.
217
+ * Tool-access policy (#4934). Runtime enforcement covers path-scoped write
218
+ * blocking, subagent denial, and bash allowlisting for active auto-mode
219
+ * units. Required on every manifest so missing entries fail loud via the CI
220
+ * invariant test rather than defaulting to "all" silently.
221
221
  */
222
222
  readonly tools: ToolsPolicy;
223
223
  /** Artifact handling: inline (full body), excerpt (compact), or on-demand (path only). */
@@ -20,7 +20,7 @@ import { writeExportFile } from "./export.js";
20
20
  import { gsdRoot } from "./paths.js";
21
21
  import { stripAnsi } from "../shared/mod.js";
22
22
 
23
- const TAB_COUNT = 10;
23
+ export const TAB_COUNT = 10;
24
24
  const TAB_LABELS = [
25
25
  "1 Progress",
26
26
  "2 Timeline",
@@ -98,13 +98,15 @@ export function readGitBranch(projectRoot: string): string {
98
98
  }
99
99
 
100
100
  /**
101
- * Read MCP server names from .mcp.json or .gsd/mcp.json.
101
+ * Read MCP server names from .mcp.json, .gsd/mcp.json, and the global
102
+ * ~/.gsd/mcp.json (or $GSD_HOME/mcp.json).
102
103
  * Returns array of server name strings.
103
104
  */
104
105
  export function readMcpServerNames(projectRoot: string): string[] {
105
106
  const configPaths = [
106
107
  join(projectRoot, ".mcp.json"),
107
108
  join(projectRoot, ".gsd", "mcp.json"),
109
+ join(process.env.GSD_HOME || join(homedir(), ".gsd"), "mcp.json"),
108
110
  ];
109
111
  const names: string[] = [];
110
112
  const seen = new Set<string>();
@@ -66,6 +66,7 @@ export type LogComponent =
66
66
  | "memory-embeddings" // Memory layer embedding generation
67
67
  | "memory-ingest" // Memory layer ingestion pipeline
68
68
  | "memory-backfill" // ADR-013: decisions->memories backfill
69
+ | "memory-store" // Memory CRUD layer — surfaces SQLite/store-level faults (#4967)
69
70
  | "context-mode" // Context-mode exec sandbox and compaction snapshot
70
71
  | "preflight" // Clean-root preflight gate at milestone completion
71
72
  | "postUnit"; // Post-unit processing (abandon detection, overrides)
@@ -33,29 +33,21 @@ import { inferCommitType } from "./git-service.js";
33
33
  import type { FileLineStat } from "./worktree-manager.js";
34
34
  import { existsSync, realpathSync, readdirSync, rmSync, unlinkSync } from "node:fs";
35
35
  import { nativeMergeAbort } from "./native-git-bridge.js";
36
- import { join, sep } from "node:path";
36
+ import { join } from "node:path";
37
+ import {
38
+ clearWorktreeOriginalCwd,
39
+ ensureWorktreeOriginalCwdFromPath,
40
+ getActiveWorktreeName,
41
+ getWorktreeOriginalCwd,
42
+ setWorktreeOriginalCwd,
43
+ } from "./worktree-session-state.js";
44
+
45
+ export { getActiveWorktreeName, getWorktreeOriginalCwd } from "./worktree-session-state.js";
37
46
 
38
47
  /**
39
48
  * Tracks the original project root so we can switch back.
40
49
  * Set when we first chdir into a worktree, cleared on return.
41
50
  */
42
- let originalCwd: string | null = null;
43
-
44
- /** Get the original project root if currently in a worktree, or null. */
45
- export function getWorktreeOriginalCwd(): string | null {
46
- return originalCwd;
47
- }
48
-
49
- /** Get the name of the active worktree, or null if not in one. */
50
- export function getActiveWorktreeName(): string | null {
51
- if (!originalCwd) return null;
52
- const cwd = process.cwd();
53
- const wtDir = join(originalCwd, ".gsd", "worktrees");
54
- if (!cwd.startsWith(wtDir)) return null;
55
- const rel = cwd.slice(wtDir.length + 1);
56
- const name = rel.split("/")[0] ?? rel.split("\\")[0];
57
- return name || null;
58
- }
59
51
 
60
52
  // ─── Shared completions and handler (used by both /worktree and /wt) ────────
61
53
 
@@ -145,7 +137,7 @@ async function worktreeHandler(
145
137
  return;
146
138
  }
147
139
  // create and switch both do the same thing: switch if exists, create if not
148
- const mainBase = originalCwd ?? basePath;
140
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
149
141
  const existing = listWorktrees(mainBase);
150
142
  if (existing.some(wt => wt.name === name)) {
151
143
  await handleSwitch(basePath, name, ctx);
@@ -157,7 +149,7 @@ async function worktreeHandler(
157
149
 
158
150
  if (trimmed === "merge" || trimmed.startsWith("merge ")) {
159
151
  const mergeArgs = trimmed.replace(/^merge\s*/, "").trim().split(/\s+/).filter(Boolean);
160
- const mainBase = originalCwd ?? basePath;
152
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
161
153
  const activeWt = getActiveWorktreeName();
162
154
 
163
155
  if (mergeArgs.length === 0) {
@@ -191,7 +183,7 @@ async function worktreeHandler(
191
183
 
192
184
  if (trimmed === "remove" || trimmed.startsWith("remove ")) {
193
185
  const name = trimmed.replace(/^remove\s*/, "").trim();
194
- const mainBase = originalCwd ?? basePath;
186
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
195
187
 
196
188
  if (name === "all") {
197
189
  await handleRemoveAll(mainBase, ctx);
@@ -213,7 +205,7 @@ async function worktreeHandler(
213
205
  return;
214
206
  }
215
207
 
216
- const mainBase = originalCwd ?? basePath;
208
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
217
209
  const nameOnly = trimmed.split(/\s+/)[0]!;
218
210
  if (trimmed !== nameOnly) {
219
211
  ctx.ui.notify(`Unknown command. Did you mean /${alias} switch ${nameOnly}?`, "warning");
@@ -241,14 +233,7 @@ export function registerWorktreeCommand(pi: ExtensionAPI): void {
241
233
  // Restore worktree state after /reload.
242
234
  // The module-level originalCwd resets to null when extensions are re-loaded,
243
235
  // but process.cwd() is still inside the worktree. Detect this and recover.
244
- if (!originalCwd) {
245
- const cwd = process.cwd();
246
- const marker = `${sep}.gsd${sep}worktrees${sep}`;
247
- const markerIdx = cwd.indexOf(marker);
248
- if (markerIdx !== -1) {
249
- originalCwd = cwd.slice(0, markerIdx);
250
- }
251
- }
236
+ ensureWorktreeOriginalCwdFromPath();
252
237
 
253
238
  pi.registerCommand("worktree", {
254
239
  description: "Git worktrees (also /wt): /worktree <name> | list | merge | remove",
@@ -320,7 +305,7 @@ async function handleCreate(
320
305
  const commitMsg = autoCommitCurrentBranch(basePath, "worktree-switch", name);
321
306
 
322
307
  // Create from the main tree, not from inside another worktree
323
- const mainBase = originalCwd ?? basePath;
308
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
324
309
  const info = createWorktree(mainBase, name);
325
310
 
326
311
  // Run user-configured post-create hook (#597) — e.g. copy .env, symlink assets
@@ -330,7 +315,7 @@ async function handleCreate(
330
315
  }
331
316
 
332
317
  // Track original cwd before switching
333
- if (!originalCwd) originalCwd = basePath;
318
+ if (!getWorktreeOriginalCwd()) setWorktreeOriginalCwd(basePath);
334
319
 
335
320
  const prevCwd = process.cwd();
336
321
  process.chdir(info.path);
@@ -390,7 +375,7 @@ async function handleSwitch(
390
375
  ctx: ExtensionCommandContext,
391
376
  ): Promise<void> {
392
377
  try {
393
- const mainBase = originalCwd ?? basePath;
378
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
394
379
  const wtPath = worktreePath(mainBase, name);
395
380
 
396
381
  if (!existsSync(wtPath)) {
@@ -405,7 +390,7 @@ async function handleSwitch(
405
390
  const commitMsg = autoCommitCurrentBranch(basePath, "worktree-switch", name);
406
391
 
407
392
  // Track original cwd before switching
408
- if (!originalCwd) originalCwd = basePath;
393
+ if (!getWorktreeOriginalCwd()) setWorktreeOriginalCwd(basePath);
409
394
 
410
395
  const prevCwd = process.cwd();
411
396
  process.chdir(wtPath);
@@ -433,6 +418,7 @@ async function handleSwitch(
433
418
  }
434
419
 
435
420
  async function handleReturn(ctx: ExtensionCommandContext): Promise<void> {
421
+ const originalCwd = getWorktreeOriginalCwd();
436
422
  if (!originalCwd) {
437
423
  ctx.ui.notify("Already in the main project tree.", "info");
438
424
  return;
@@ -442,7 +428,7 @@ async function handleReturn(ctx: ExtensionCommandContext): Promise<void> {
442
428
  const commitMsg = autoCommitCurrentBranch(process.cwd(), "worktree-return", "worktree");
443
429
 
444
430
  const returnTo = originalCwd;
445
- originalCwd = null;
431
+ clearWorktreeOriginalCwd();
446
432
 
447
433
  const prevCwd = process.cwd();
448
434
  process.chdir(returnTo);
@@ -504,7 +490,7 @@ async function handleList(
504
490
  ctx: ExtensionCommandContext,
505
491
  ): Promise<void> {
506
492
  try {
507
- const mainBase = originalCwd ?? basePath;
493
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
508
494
  const worktrees = listWorktrees(mainBase);
509
495
 
510
496
  if (worktrees.length === 0) {
@@ -552,6 +538,7 @@ async function handleList(
552
538
  lines.push("");
553
539
  }
554
540
 
541
+ const originalCwd = getWorktreeOriginalCwd();
555
542
  if (originalCwd) {
556
543
  lines.push(` ${CLR.label("main tree")} ${CLR.path(originalCwd)}`);
557
544
  }
@@ -651,11 +638,11 @@ async function handleMerge(
651
638
 
652
639
  // Switch to the main tree before merging.
653
640
  // Must be on the main branch to run git merge --squash.
654
- if (originalCwd) {
641
+ if (getWorktreeOriginalCwd()) {
655
642
  const prevCwd = process.cwd();
656
643
  process.chdir(basePath);
657
644
  nudgeGitBranchCache(prevCwd);
658
- originalCwd = null;
645
+ clearWorktreeOriginalCwd();
659
646
  }
660
647
 
661
648
  // --- Deterministic merge path (preferred) ---
@@ -754,7 +741,7 @@ async function handleRemove(
754
741
  ctx: ExtensionCommandContext,
755
742
  ): Promise<void> {
756
743
  try {
757
- const mainBase = originalCwd ?? basePath;
744
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
758
745
 
759
746
  // Validate the worktree exists before attempting removal
760
747
  const worktrees = listWorktrees(mainBase);
@@ -779,9 +766,9 @@ async function handleRemove(
779
766
  removeWorktree(mainBase, name, { deleteBranch: true });
780
767
 
781
768
  // If we were in that worktree, removeWorktree chdir'd us out — clear tracking
782
- if (originalCwd && process.cwd() !== prevCwd) {
769
+ if (getWorktreeOriginalCwd() && process.cwd() !== prevCwd) {
783
770
  nudgeGitBranchCache(prevCwd);
784
- originalCwd = null;
771
+ clearWorktreeOriginalCwd();
785
772
  }
786
773
 
787
774
  ctx.ui.notify(`${CLR.ok("✓")} Worktree ${CLR.name(name)} removed ${CLR.muted("(branch deleted)")}.`, "info");
@@ -796,7 +783,7 @@ async function handleRemoveAll(
796
783
  ctx: ExtensionCommandContext,
797
784
  ): Promise<void> {
798
785
  try {
799
- const mainBase = originalCwd ?? basePath;
786
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
800
787
  const worktrees = listWorktrees(mainBase);
801
788
 
802
789
  if (worktrees.length === 0) {
@@ -830,9 +817,9 @@ async function handleRemoveAll(
830
817
  }
831
818
 
832
819
  // If we were in a worktree that got removed, clear tracking
833
- if (originalCwd && process.cwd() !== prevCwd) {
820
+ if (getWorktreeOriginalCwd() && process.cwd() !== prevCwd) {
834
821
  nudgeGitBranchCache(prevCwd);
835
- originalCwd = null;
822
+ clearWorktreeOriginalCwd();
836
823
  }
837
824
 
838
825
  const lines: string[] = [];
@@ -0,0 +1,35 @@
1
+ // GSD worktree session state
2
+ let originalCwd: string | null = null;
3
+
4
+ export function getWorktreeOriginalCwd(): string | null {
5
+ return originalCwd;
6
+ }
7
+
8
+ export function setWorktreeOriginalCwd(cwd: string): void {
9
+ originalCwd = cwd;
10
+ }
11
+
12
+ export function clearWorktreeOriginalCwd(): void {
13
+ originalCwd = null;
14
+ }
15
+
16
+ export function ensureWorktreeOriginalCwdFromPath(cwd: string = process.cwd()): string | null {
17
+ if (originalCwd) return originalCwd;
18
+ const marker = `${/\\/.test(cwd) ? "\\" : "/"}.gsd${/\\/.test(cwd) ? "\\" : "/"}worktrees${/\\/.test(cwd) ? "\\" : "/"}`;
19
+ const markerIdx = cwd.indexOf(marker);
20
+ if (markerIdx !== -1) {
21
+ originalCwd = cwd.slice(0, markerIdx);
22
+ }
23
+ return originalCwd;
24
+ }
25
+
26
+ export function getActiveWorktreeName(): string | null {
27
+ if (!originalCwd) return null;
28
+ const cwd = process.cwd();
29
+ const wtDir = `${originalCwd.replace(/[\\/]+$/, "")}/.gsd/worktrees`.replaceAll("\\", "/");
30
+ const normalizedCwd = cwd.replaceAll("\\", "/");
31
+ if (!normalizedCwd.startsWith(`${wtDir}/`)) return null;
32
+ const rel = normalizedCwd.slice(wtDir.length + 1);
33
+ const name = rel.split("/")[0];
34
+ return name || null;
35
+ }
@@ -2,7 +2,8 @@
2
2
  * MCP Client Extension — Native MCP server integration for pi
3
3
  *
4
4
  * Provides on-demand access to MCP servers configured in project files
5
- * (.mcp.json, .gsd/mcp.json) using the @modelcontextprotocol/sdk Client
5
+ * (.mcp.json, .gsd/mcp.json) and the global ~/.gsd/mcp.json (or
6
+ * $GSD_HOME/mcp.json) using the @modelcontextprotocol/sdk Client
6
7
  * directly — no external CLI dependency required.
7
8
  *
8
9
  * Three tools:
@@ -24,6 +25,7 @@ import { Client } from "@modelcontextprotocol/sdk/client";
24
25
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
25
26
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
26
27
  import { readFileSync, existsSync } from "node:fs";
28
+ import { homedir } from "node:os";
27
29
  import { join } from "node:path";
28
30
  import { buildHttpTransportOpts } from "./auth.js";
29
31
  import type { McpHttpAuthConfig } from "./auth.js";
@@ -102,6 +104,7 @@ function readConfigs(): McpServerConfig[] {
102
104
  const configPaths = [
103
105
  join(process.cwd(), ".mcp.json"),
104
106
  join(process.cwd(), ".gsd", "mcp.json"),
107
+ join(process.env.GSD_HOME || join(homedir(), ".gsd"), "mcp.json"),
105
108
  ];
106
109
 
107
110
  for (const configPath of configPaths) {
@@ -321,7 +324,7 @@ async function closeAll(): Promise<void> {
321
324
  // ─── Formatters ───────────────────────────────────────────────────────────────
322
325
 
323
326
  function formatServerList(servers: McpServerConfig[]): string {
324
- if (servers.length === 0) return "No MCP servers configured. Add servers to .mcp.json or .gsd/mcp.json.";
327
+ if (servers.length === 0) return "No MCP servers configured. Add servers to .mcp.json, .gsd/mcp.json, or $GSD_HOME/mcp.json (default: ~/.gsd/mcp.json).";
325
328
 
326
329
  const lines: string[] = [`${servers.length} MCP servers configured:\n`];
327
330
 
@@ -384,7 +387,7 @@ export default function (pi: ExtensionAPI) {
384
387
  name: "mcp_servers",
385
388
  label: "MCP Servers",
386
389
  description:
387
- "List all available MCP servers configured in project files (.mcp.json, .gsd/mcp.json). " +
390
+ "List all available MCP servers configured in project files (.mcp.json, .gsd/mcp.json) or globally ($GSD_HOME/mcp.json, default: ~/.gsd/mcp.json). " +
388
391
  "Shows server names, transport type, and connection status. Use mcp_discover to get full tool schemas for a server.",
389
392
  promptSnippet:
390
393
  "List available MCP servers from project configuration",