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
@@ -7,14 +7,13 @@
7
7
  * Templates live at prompts/ relative to this module's directory.
8
8
  * They use {{variableName}} syntax for substitution.
9
9
  *
10
- * All templates are eagerly loaded into cache at module init via warmCache().
11
- * This prevents a running session from being invalidated when another `gsd`
12
- * launch overwrites ~/.gsd/agent/ with newer templates via initResources().
13
- * Without eager caching, the in-memory extension code (which knows variable
14
- * set A) can read a newer template from disk (which expects variable set B),
15
- * causing a "template declares {{X}} but no value was provided" crash
16
- * mid-session especially for late-loading templates like complete-milestone
17
- * that aren't read until the end of a long auto-mode run.
10
+ * Templates are snapshotted shortly after module init via warmCache().
11
+ * This keeps import/extension-registration fast while still preventing a
12
+ * running session from being invalidated when another `gsd` launch overwrites
13
+ * ~/.gsd/agent/ with newer templates via initResources(). Without caching, the
14
+ * in-memory extension code (which knows variable set A) can read a newer
15
+ * template from disk (which expects variable set B), causing a
16
+ * "template declares {{X}} but no value was provided" crash mid-session.
18
17
  */
19
18
  import { readFileSync, readdirSync, existsSync } from "node:fs";
20
19
  import { GSDError, GSD_PARSE_ERROR } from "./errors.js";
@@ -69,8 +68,8 @@ const templatesDir = join(__extensionDir, "templates");
69
68
  export function getTemplatesDir() {
70
69
  return templatesDir;
71
70
  }
72
- // Cache all templates eagerly at module load — a running session uses the
73
- // template versions that were on disk at startup, immune to later overwrites.
71
+ // Cache all templates from a startup snapshot — a running session uses the
72
+ // template versions that were on disk near startup, immune to later overwrites.
74
73
  const templateCache = new Map();
75
74
  /**
76
75
  * Eagerly read all .md files from prompts/ and templates/ into cache.
@@ -112,8 +111,20 @@ function warmCache() {
112
111
  }
113
112
  }
114
113
  }
115
- // Snapshot all templates at module load time
116
- warmCache();
114
+ let warmCacheScheduled = false;
115
+ function scheduleWarmCache() {
116
+ if (warmCacheScheduled)
117
+ return;
118
+ warmCacheScheduled = true;
119
+ const run = () => {
120
+ warmCache();
121
+ };
122
+ const timer = setTimeout(run, 1000);
123
+ timer.unref?.();
124
+ }
125
+ // Snapshot the full prompt/template tree after import so extension startup only
126
+ // pays for prompts that are actually needed immediately.
127
+ scheduleWarmCache();
117
128
  /**
118
129
  * Load a prompt template and substitute variables.
119
130
  *
@@ -32,6 +32,7 @@ let sliceState = null;
32
32
  // on next session start. (Issue #4980 HIGH-8)
33
33
  const SLICE_ORCHESTRATOR_STATE_FILE = "slice-orchestrator.json";
34
34
  const TMP_SUFFIX = ".tmp";
35
+ export const SLICE_WORKER_AUTO_ARGS = ["headless", "--json", "auto"];
35
36
  function sliceStateFilePath(basePath) {
36
37
  return join(gsdRoot(basePath), SLICE_ORCHESTRATOR_STATE_FILE);
37
38
  }
@@ -272,7 +273,7 @@ export function getSliceOrchestratorState() {
272
273
  /**
273
274
  * Start parallel execution for eligible slices within a milestone.
274
275
  *
275
- * For each eligible slice: create a worktree, spawn `gsd --mode json --print "/gsd auto"`
276
+ * For each eligible slice: create a worktree, spawn `gsd headless --json auto`
276
277
  * with env GSD_SLICE_LOCK=<SID> + GSD_MILESTONE_LOCK=<MID> + GSD_PARALLEL_WORKER=1.
277
278
  */
278
279
  export async function startSliceParallel(basePath, milestoneId, eligibleSlices, opts = {}) {
@@ -482,8 +483,13 @@ function resolveGsdBin() {
482
483
  }
483
484
  /**
484
485
  * Spawn a worker process for a slice.
485
- * The worker runs `gsd --mode json --print "/gsd auto"` in the slice's worktree
486
+ * The worker runs `gsd headless --json auto` in the slice's worktree
486
487
  * with GSD_SLICE_LOCK, GSD_MILESTONE_LOCK, and GSD_PARALLEL_WORKER set.
488
+ *
489
+ * Print-mode slash commands return after the command handler schedules
490
+ * auto-mode, so the worker process can exit before doing any LLM work. The
491
+ * headless auto entrypoint keeps the process alive until auto-mode reaches a
492
+ * terminal notification, matching milestone-level parallel workers.
487
493
  */
488
494
  function spawnSliceWorker(basePath, milestoneId, sliceId) {
489
495
  if (!sliceState)
@@ -498,7 +504,7 @@ function spawnSliceWorker(basePath, milestoneId, sliceId) {
498
504
  return false;
499
505
  let child;
500
506
  try {
501
- child = spawn(process.execPath, [binPath, "--mode", "json", "--print", "/gsd auto"], {
507
+ child = spawn(process.execPath, [binPath, ...SLICE_WORKER_AUTO_ARGS], {
502
508
  cwd: worker.worktreePath,
503
509
  env: {
504
510
  ...process.env,
@@ -60,6 +60,48 @@ export function isGhostMilestone(basePath, mid) {
60
60
  const summary = resolveMilestoneFile(basePath, mid, "SUMMARY");
61
61
  return !context && !draft && !roadmap && !summary;
62
62
  }
63
+ /**
64
+ * A "reusable ghost" milestone is an orphaned filesystem stub that is safe
65
+ * to reclaim as the next milestone ID.
66
+ *
67
+ * Stricter than `isGhostMilestone`: returns true ONLY when ALL of the
68
+ * following hold:
69
+ * 1. No DB row exists for `mid` (any status, including "queued") — a DB row
70
+ * means the milestone was intentionally registered by
71
+ * `gsd_milestone_generate_id` and may have an in-flight discuss flow.
72
+ * Reusing it would collide with that flow. (#4996 race window)
73
+ * 2. No worktree directory exists at `gsdRoot/worktrees/{mid}` — a worktree
74
+ * means the milestone is legitimately in-flight.
75
+ * 3. No content files exist (CONTEXT, CONTEXT-DRAFT, ROADMAP, SUMMARY) —
76
+ * any content means the discuss flow already ran.
77
+ *
78
+ * The looser `isGhostMilestone` also classifies queued-row-without-content as
79
+ * a ghost to help state queries filter phantoms. `isReusableGhostMilestone`
80
+ * intentionally does NOT reclaim those — a queued row is sufficient proof of
81
+ * a live in-flight ID reservation.
82
+ *
83
+ * Used by `nextMilestoneIdReserved` and both MCP ID-generator tools to fill
84
+ * gaps left by phantom directories before resorting to max+1.
85
+ */
86
+ export function isReusableGhostMilestone(basePath, mid) {
87
+ // Condition 1: no DB row (any status).
88
+ if (!isDbAvailable())
89
+ return false;
90
+ const dbRow = getMilestone(mid);
91
+ if (dbRow != null)
92
+ return false;
93
+ // Condition 2: no worktree.
94
+ const root = gsdRoot(basePath);
95
+ const wtPath = join(root, 'worktrees', mid);
96
+ if (existsSync(wtPath))
97
+ return false;
98
+ // Condition 3: no content files.
99
+ const context = resolveMilestoneFile(basePath, mid, "CONTEXT");
100
+ const draft = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
101
+ const roadmap = resolveMilestoneFile(basePath, mid, "ROADMAP");
102
+ const summary = resolveMilestoneFile(basePath, mid, "SUMMARY");
103
+ return !context && !draft && !roadmap && !summary;
104
+ }
63
105
  // ─── Query Functions ───────────────────────────────────────────────────────
64
106
  /**
65
107
  * Check if all tasks in a slice plan are done.
@@ -39,6 +39,7 @@ dynamic_routing:
39
39
  budget_pressure:
40
40
  cross_provider:
41
41
  hooks:
42
+ disabled_model_providers: []
42
43
  uok:
43
44
  enabled: true
44
45
  legacy_fallback:
@@ -59,8 +59,25 @@ export function executeMemoryCapture(params) {
59
59
  const scope = normalizeScope(params.scope);
60
60
  const tags = normalizeTags(params.tags);
61
61
  const structuredFields = normalizeStructuredFields(params.structuredFields);
62
- const id = createMemory({ category, content, confidence, scope, tags, structuredFields });
62
+ let id;
63
+ try {
64
+ id = createMemory({ category, content, confidence, scope, tags, structuredFields });
65
+ }
66
+ catch (err) {
67
+ // Surface the underlying SQL message (e.g. "database disk image is
68
+ // malformed", "no such table: memories") so the operator gets the
69
+ // actionable signal instead of an opaque "create_failed". See #4967.
70
+ const message = err instanceof Error ? err.message : String(err);
71
+ return {
72
+ content: [{ type: "text", text: `Error: failed to create memory: ${message}` }],
73
+ details: { operation: "memory_capture", error: message },
74
+ isError: true,
75
+ };
76
+ }
63
77
  if (!id) {
78
+ // DB unavailable or adapter missing — distinct from the SQL-error path
79
+ // above. Keep the legacy create_failed token here so any consumers that
80
+ // explicitly key on the unavailable case continue to work.
64
81
  return {
65
82
  content: [{ type: "text", text: "Error: failed to create memory." }],
66
83
  details: { operation: "memory_capture", error: "create_failed" },
@@ -6,7 +6,7 @@ import { join } from "node:path";
6
6
  import { writeExportFile } from "./export.js";
7
7
  import { gsdRoot } from "./paths.js";
8
8
  import { stripAnsi } from "../shared/mod.js";
9
- const TAB_COUNT = 10;
9
+ export const TAB_COUNT = 10;
10
10
  const TAB_LABELS = [
11
11
  "1 Progress",
12
12
  "2 Timeline",
@@ -96,13 +96,15 @@ export function readGitBranch(projectRoot) {
96
96
  }
97
97
  }
98
98
  /**
99
- * Read MCP server names from .mcp.json or .gsd/mcp.json.
99
+ * Read MCP server names from .mcp.json, .gsd/mcp.json, and the global
100
+ * ~/.gsd/mcp.json (or $GSD_HOME/mcp.json).
100
101
  * Returns array of server name strings.
101
102
  */
102
103
  export function readMcpServerNames(projectRoot) {
103
104
  const configPaths = [
104
105
  join(projectRoot, ".mcp.json"),
105
106
  join(projectRoot, ".gsd", "mcp.json"),
107
+ join(process.env.GSD_HOME || join(homedir(), ".gsd"), "mcp.json"),
106
108
  ];
107
109
  const names = [];
108
110
  const seen = new Set();
@@ -18,28 +18,13 @@ import { createWorktree, listWorktrees, removeWorktree, mergeWorktreeToMain, dif
18
18
  import { inferCommitType } from "./git-service.js";
19
19
  import { existsSync, realpathSync, readdirSync, rmSync, unlinkSync } from "node:fs";
20
20
  import { nativeMergeAbort } from "./native-git-bridge.js";
21
- import { join, sep } from "node:path";
21
+ import { join } from "node:path";
22
+ import { clearWorktreeOriginalCwd, ensureWorktreeOriginalCwdFromPath, getActiveWorktreeName, getWorktreeOriginalCwd, setWorktreeOriginalCwd, } from "./worktree-session-state.js";
23
+ export { getActiveWorktreeName, getWorktreeOriginalCwd } from "./worktree-session-state.js";
22
24
  /**
23
25
  * Tracks the original project root so we can switch back.
24
26
  * Set when we first chdir into a worktree, cleared on return.
25
27
  */
26
- let originalCwd = null;
27
- /** Get the original project root if currently in a worktree, or null. */
28
- export function getWorktreeOriginalCwd() {
29
- return originalCwd;
30
- }
31
- /** Get the name of the active worktree, or null if not in one. */
32
- export function getActiveWorktreeName() {
33
- if (!originalCwd)
34
- return null;
35
- const cwd = process.cwd();
36
- const wtDir = join(originalCwd, ".gsd", "worktrees");
37
- if (!cwd.startsWith(wtDir))
38
- return null;
39
- const rel = cwd.slice(wtDir.length + 1);
40
- const name = rel.split("/")[0] ?? rel.split("\\")[0];
41
- return name || null;
42
- }
43
28
  // ─── Shared completions and handler (used by both /worktree and /wt) ────────
44
29
  function worktreeCompletions(prefix) {
45
30
  const parts = prefix.trim().split(/\s+/);
@@ -111,7 +96,7 @@ async function worktreeHandler(args, ctx, pi, alias) {
111
96
  return;
112
97
  }
113
98
  // create and switch both do the same thing: switch if exists, create if not
114
- const mainBase = originalCwd ?? basePath;
99
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
115
100
  const existing = listWorktrees(mainBase);
116
101
  if (existing.some(wt => wt.name === name)) {
117
102
  await handleSwitch(basePath, name, ctx);
@@ -123,7 +108,7 @@ async function worktreeHandler(args, ctx, pi, alias) {
123
108
  }
124
109
  if (trimmed === "merge" || trimmed.startsWith("merge ")) {
125
110
  const mergeArgs = trimmed.replace(/^merge\s*/, "").trim().split(/\s+/).filter(Boolean);
126
- const mainBase = originalCwd ?? basePath;
111
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
127
112
  const activeWt = getActiveWorktreeName();
128
113
  if (mergeArgs.length === 0) {
129
114
  // Bare "/worktree merge" — only valid when inside a worktree
@@ -154,7 +139,7 @@ async function worktreeHandler(args, ctx, pi, alias) {
154
139
  }
155
140
  if (trimmed === "remove" || trimmed.startsWith("remove ")) {
156
141
  const name = trimmed.replace(/^remove\s*/, "").trim();
157
- const mainBase = originalCwd ?? basePath;
142
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
158
143
  if (name === "all") {
159
144
  await handleRemoveAll(mainBase, ctx);
160
145
  return;
@@ -171,7 +156,7 @@ async function worktreeHandler(args, ctx, pi, alias) {
171
156
  ctx.ui.notify(`Usage: /${alias} ${trimmed}${trimmed === "list" || trimmed === "return" ? "" : " <name>"}`, "warning");
172
157
  return;
173
158
  }
174
- const mainBase = originalCwd ?? basePath;
159
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
175
160
  const nameOnly = trimmed.split(/\s+/)[0];
176
161
  if (trimmed !== nameOnly) {
177
162
  ctx.ui.notify(`Unknown command. Did you mean /${alias} switch ${nameOnly}?`, "warning");
@@ -192,14 +177,7 @@ export function registerWorktreeCommand(pi) {
192
177
  // Restore worktree state after /reload.
193
178
  // The module-level originalCwd resets to null when extensions are re-loaded,
194
179
  // but process.cwd() is still inside the worktree. Detect this and recover.
195
- if (!originalCwd) {
196
- const cwd = process.cwd();
197
- const marker = `${sep}.gsd${sep}worktrees${sep}`;
198
- const markerIdx = cwd.indexOf(marker);
199
- if (markerIdx !== -1) {
200
- originalCwd = cwd.slice(0, markerIdx);
201
- }
202
- }
180
+ ensureWorktreeOriginalCwdFromPath();
203
181
  pi.registerCommand("worktree", {
204
182
  description: "Git worktrees (also /wt): /worktree <name> | list | merge | remove",
205
183
  getArgumentCompletions: worktreeCompletions,
@@ -260,7 +238,7 @@ async function handleCreate(basePath, name, ctx) {
260
238
  // before createWorktree so the new worktree forks from committed HEAD)
261
239
  const commitMsg = autoCommitCurrentBranch(basePath, "worktree-switch", name);
262
240
  // Create from the main tree, not from inside another worktree
263
- const mainBase = originalCwd ?? basePath;
241
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
264
242
  const info = createWorktree(mainBase, name);
265
243
  // Run user-configured post-create hook (#597) — e.g. copy .env, symlink assets
266
244
  const hookError = runWorktreePostCreateHook(mainBase, info.path);
@@ -268,8 +246,8 @@ async function handleCreate(basePath, name, ctx) {
268
246
  ctx.ui.notify(hookError, "warning");
269
247
  }
270
248
  // Track original cwd before switching
271
- if (!originalCwd)
272
- originalCwd = basePath;
249
+ if (!getWorktreeOriginalCwd())
250
+ setWorktreeOriginalCwd(basePath);
273
251
  const prevCwd = process.cwd();
274
252
  process.chdir(info.path);
275
253
  nudgeGitBranchCache(prevCwd);
@@ -319,7 +297,7 @@ async function handleCreate(basePath, name, ctx) {
319
297
  }
320
298
  async function handleSwitch(basePath, name, ctx) {
321
299
  try {
322
- const mainBase = originalCwd ?? basePath;
300
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
323
301
  const wtPath = worktreePath(mainBase, name);
324
302
  if (!existsSync(wtPath)) {
325
303
  ctx.ui.notify(`Worktree "${name}" not found. Run /worktree list to see available worktrees.`, "warning");
@@ -328,8 +306,8 @@ async function handleSwitch(basePath, name, ctx) {
328
306
  // Auto-commit dirty files before leaving current workspace
329
307
  const commitMsg = autoCommitCurrentBranch(basePath, "worktree-switch", name);
330
308
  // Track original cwd before switching
331
- if (!originalCwd)
332
- originalCwd = basePath;
309
+ if (!getWorktreeOriginalCwd())
310
+ setWorktreeOriginalCwd(basePath);
333
311
  const prevCwd = process.cwd();
334
312
  process.chdir(wtPath);
335
313
  nudgeGitBranchCache(prevCwd);
@@ -352,6 +330,7 @@ async function handleSwitch(basePath, name, ctx) {
352
330
  }
353
331
  }
354
332
  async function handleReturn(ctx) {
333
+ const originalCwd = getWorktreeOriginalCwd();
355
334
  if (!originalCwd) {
356
335
  ctx.ui.notify("Already in the main project tree.", "info");
357
336
  return;
@@ -359,7 +338,7 @@ async function handleReturn(ctx) {
359
338
  // Auto-commit dirty files before leaving worktree
360
339
  const commitMsg = autoCommitCurrentBranch(process.cwd(), "worktree-return", "worktree");
361
340
  const returnTo = originalCwd;
362
- originalCwd = null;
341
+ clearWorktreeOriginalCwd();
363
342
  const prevCwd = process.cwd();
364
343
  process.chdir(returnTo);
365
344
  nudgeGitBranchCache(prevCwd);
@@ -409,7 +388,7 @@ const CLR = {
409
388
  };
410
389
  async function handleList(basePath, ctx) {
411
390
  try {
412
- const mainBase = originalCwd ?? basePath;
391
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
413
392
  const worktrees = listWorktrees(mainBase);
414
393
  if (worktrees.length === 0) {
415
394
  ctx.ui.notify("No GSD worktrees found. Create one with /worktree <name>.", "info");
@@ -452,6 +431,7 @@ async function handleList(basePath, ctx) {
452
431
  }
453
432
  lines.push("");
454
433
  }
434
+ const originalCwd = getWorktreeOriginalCwd();
455
435
  if (originalCwd) {
456
436
  lines.push(` ${CLR.label("main tree")} ${CLR.path(originalCwd)}`);
457
437
  }
@@ -539,11 +519,11 @@ async function handleMerge(basePath, name, ctx, pi, targetBranch) {
539
519
  }
540
520
  // Switch to the main tree before merging.
541
521
  // Must be on the main branch to run git merge --squash.
542
- if (originalCwd) {
522
+ if (getWorktreeOriginalCwd()) {
543
523
  const prevCwd = process.cwd();
544
524
  process.chdir(basePath);
545
525
  nudgeGitBranchCache(prevCwd);
546
- originalCwd = null;
526
+ clearWorktreeOriginalCwd();
547
527
  }
548
528
  // --- Deterministic merge path (preferred) ---
549
529
  // Try a direct squash-merge first. Only fall back to LLM on conflict.
@@ -620,7 +600,7 @@ async function handleMerge(basePath, name, ctx, pi, targetBranch) {
620
600
  }
621
601
  async function handleRemove(basePath, name, ctx) {
622
602
  try {
623
- const mainBase = originalCwd ?? basePath;
603
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
624
604
  // Validate the worktree exists before attempting removal
625
605
  const worktrees = listWorktrees(mainBase);
626
606
  const wt = worktrees.find(w => w.name === name);
@@ -641,9 +621,9 @@ async function handleRemove(basePath, name, ctx) {
641
621
  const prevCwd = process.cwd();
642
622
  removeWorktree(mainBase, name, { deleteBranch: true });
643
623
  // If we were in that worktree, removeWorktree chdir'd us out — clear tracking
644
- if (originalCwd && process.cwd() !== prevCwd) {
624
+ if (getWorktreeOriginalCwd() && process.cwd() !== prevCwd) {
645
625
  nudgeGitBranchCache(prevCwd);
646
- originalCwd = null;
626
+ clearWorktreeOriginalCwd();
647
627
  }
648
628
  ctx.ui.notify(`${CLR.ok("✓")} Worktree ${CLR.name(name)} removed ${CLR.muted("(branch deleted)")}.`, "info");
649
629
  }
@@ -654,7 +634,7 @@ async function handleRemove(basePath, name, ctx) {
654
634
  }
655
635
  async function handleRemoveAll(basePath, ctx) {
656
636
  try {
657
- const mainBase = originalCwd ?? basePath;
637
+ const mainBase = getWorktreeOriginalCwd() ?? basePath;
658
638
  const worktrees = listWorktrees(mainBase);
659
639
  if (worktrees.length === 0) {
660
640
  ctx.ui.notify("No worktrees to remove.", "info");
@@ -684,9 +664,9 @@ async function handleRemoveAll(basePath, ctx) {
684
664
  }
685
665
  }
686
666
  // If we were in a worktree that got removed, clear tracking
687
- if (originalCwd && process.cwd() !== prevCwd) {
667
+ if (getWorktreeOriginalCwd() && process.cwd() !== prevCwd) {
688
668
  nudgeGitBranchCache(prevCwd);
689
- originalCwd = null;
669
+ clearWorktreeOriginalCwd();
690
670
  }
691
671
  const lines = [];
692
672
  if (removed.length > 0)
@@ -0,0 +1,33 @@
1
+ // GSD worktree session state
2
+ let originalCwd = null;
3
+ export function getWorktreeOriginalCwd() {
4
+ return originalCwd;
5
+ }
6
+ export function setWorktreeOriginalCwd(cwd) {
7
+ originalCwd = cwd;
8
+ }
9
+ export function clearWorktreeOriginalCwd() {
10
+ originalCwd = null;
11
+ }
12
+ export function ensureWorktreeOriginalCwdFromPath(cwd = process.cwd()) {
13
+ if (originalCwd)
14
+ return originalCwd;
15
+ const marker = `${/\\/.test(cwd) ? "\\" : "/"}.gsd${/\\/.test(cwd) ? "\\" : "/"}worktrees${/\\/.test(cwd) ? "\\" : "/"}`;
16
+ const markerIdx = cwd.indexOf(marker);
17
+ if (markerIdx !== -1) {
18
+ originalCwd = cwd.slice(0, markerIdx);
19
+ }
20
+ return originalCwd;
21
+ }
22
+ export function getActiveWorktreeName() {
23
+ if (!originalCwd)
24
+ return null;
25
+ const cwd = process.cwd();
26
+ const wtDir = `${originalCwd.replace(/[\\/]+$/, "")}/.gsd/worktrees`.replaceAll("\\", "/");
27
+ const normalizedCwd = cwd.replaceAll("\\", "/");
28
+ if (!normalizedCwd.startsWith(`${wtDir}/`))
29
+ return null;
30
+ const rel = normalizedCwd.slice(wtDir.length + 1);
31
+ const name = rel.split("/")[0];
32
+ return name || null;
33
+ }
@@ -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:
@@ -17,6 +18,7 @@ import { Client } from "@modelcontextprotocol/sdk/client";
17
18
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
18
19
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
19
20
  import { readFileSync, existsSync } from "node:fs";
21
+ import { homedir } from "node:os";
20
22
  import { join } from "node:path";
21
23
  import { buildHttpTransportOpts } from "./auth.js";
22
24
  // ─── Connection Manager ───────────────────────────────────────────────────────
@@ -61,6 +63,7 @@ function readConfigs() {
61
63
  const configPaths = [
62
64
  join(process.cwd(), ".mcp.json"),
63
65
  join(process.cwd(), ".gsd", "mcp.json"),
66
+ join(process.env.GSD_HOME || join(homedir(), ".gsd"), "mcp.json"),
64
67
  ];
65
68
  for (const configPath of configPaths) {
66
69
  try {
@@ -258,7 +261,7 @@ async function closeAll() {
258
261
  // ─── Formatters ───────────────────────────────────────────────────────────────
259
262
  function formatServerList(servers) {
260
263
  if (servers.length === 0)
261
- return "No MCP servers configured. Add servers to .mcp.json or .gsd/mcp.json.";
264
+ return "No MCP servers configured. Add servers to .mcp.json, .gsd/mcp.json, or $GSD_HOME/mcp.json (default: ~/.gsd/mcp.json).";
262
265
  const lines = [`${servers.length} MCP servers configured:\n`];
263
266
  for (const s of servers) {
264
267
  const connected = connections.has(s.name) ? "✓" : "○";
@@ -306,7 +309,7 @@ export default function (pi) {
306
309
  pi.registerTool({
307
310
  name: "mcp_servers",
308
311
  label: "MCP Servers",
309
- description: "List all available MCP servers configured in project files (.mcp.json, .gsd/mcp.json). " +
312
+ description: "List all available MCP servers configured in project files (.mcp.json, .gsd/mcp.json) or globally ($GSD_HOME/mcp.json, default: ~/.gsd/mcp.json). " +
310
313
  "Shows server names, transport type, and connection status. Use mcp_discover to get full tool schemas for a server.",
311
314
  promptSnippet: "List available MCP servers from project configuration",
312
315
  promptGuidelines: [
@@ -193,38 +193,35 @@ function sendPrompt(description, result, pi) {
193
193
  ? (Array.isArray(persistenceAnswer.selected) ? persistenceAnswer.selected[0] : persistenceAnswer.selected)
194
194
  : "";
195
195
  const docHints = [
196
- "- `~/.gsd/agent/docs/extending-pi/01-what-are-extensions.md` — capabilities overview",
197
- "- `~/.gsd/agent/docs/extending-pi/03-getting-started.md` — minimal extension, hot reload",
198
- "- `~/.gsd/agent/docs/extending-pi/08-extensioncontext-what-you-can-access.md` — ExtensionContext API",
199
- "- `~/.gsd/agent/docs/extending-pi/09-extensionapi-what-you-can-do.md` — ExtensionAPI: registration, messaging",
200
- "- `~/.gsd/agent/docs/extending-pi/22-key-rules-gotchas.md` — must-read rules before shipping",
196
+ "- `docs/extension-sdk/README.md` — overview, quick start, directory layout",
197
+ "- `docs/extension-sdk/api-reference.md` — ExtensionAPI and ExtensionContext surfaces",
198
+ "- `docs/extension-sdk/building-extensions.md` — tools, commands, events, UI, state",
199
+ "- `docs/extension-sdk/rules.md` — non-negotiable rules and gotchas",
201
200
  ];
202
201
  if (uiSelected.includes("custom component")) {
203
- docHints.push("- `~/.gsd/agent/docs/extending-pi/12-custom-ui-visual-components.md` — dialogs, widgets, overlays");
204
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/06-ctx-ui-custom-full-custom-components.md` — ctx.ui.custom() API");
205
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/07-built-in-components-the-building-blocks.md` — Text, Box, SelectList");
206
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/09-keyboard-input-how-to-handle-keys.md` — Key, matchesKey");
207
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/10-line-width-the-cardinal-rule.md` — truncation, width rules");
208
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/19-building-a-complete-component-step-by-step.md` — step-by-step guide");
209
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/21-common-mistakes-and-how-to-avoid-them.md` — pitfalls");
202
+ docHints.push("- `docs/extension-sdk/building-extensions.md#custom-components` — ctx.ui.custom() API");
203
+ docHints.push("- `docs/dev/pi-ui-tui/06-ctx-ui-custom-full-custom-components.md` — step-by-step component guide");
204
+ docHints.push("- `docs/dev/pi-ui-tui/07-built-in-components-the-building-blocks.md` — Text, Box, SelectList");
205
+ docHints.push("- `docs/dev/pi-ui-tui/09-keyboard-input-how-to-handle-keys.md` — Key, matchesKey");
206
+ docHints.push("- `docs/dev/pi-ui-tui/10-line-width-the-cardinal-rule.md` — truncation, width rules");
210
207
  }
211
208
  else if (uiSelected.includes("Dialogs")) {
212
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/04-built-in-dialog-methods.md` — select, confirm, input, editor");
209
+ docHints.push("- `docs/extension-sdk/building-extensions.md#built-in-dialogs` — select, confirm, input");
213
210
  }
214
211
  else if (uiSelected.includes("Status")) {
215
- docHints.push("- `~/.gsd/agent/docs/pi-ui-tui/05-persistent-ui-elements.md` — status, widgets, footer, header");
212
+ docHints.push("- `docs/extension-sdk/building-extensions.md#persistent-ui-elements` — status, widgets");
216
213
  }
217
214
  if (uiSelected.includes("tool") || result.answers["purpose"]) {
218
- docHints.push("- `~/.gsd/agent/docs/extending-pi/14-custom-rendering-controlling-what-the-user-sees.md` — renderCall / renderResult");
215
+ docHints.push("- `docs/dev/extending-pi/14-custom-rendering-controlling-what-the-user-sees.md` — renderCall / renderResult");
219
216
  }
220
217
  if (eventsSelected && !eventsSelected.includes("standalone")) {
221
- docHints.push("- `~/.gsd/agent/docs/extending-pi/07-events-the-nervous-system.md` — all events reference");
218
+ docHints.push("- `docs/dev/extending-pi/07-events-the-nervous-system.md` — all events reference");
222
219
  }
223
220
  if (eventsSelected.includes("context / prompt")) {
224
- docHints.push("- `~/.gsd/agent/docs/extending-pi/15-system-prompt-modification.md` — system prompt hooks");
221
+ docHints.push("- `docs/dev/extending-pi/15-system-prompt-modification.md` — system prompt hooks");
225
222
  }
226
223
  if (persistenceSelected.includes("session")) {
227
- docHints.push("- `~/.gsd/agent/docs/extending-pi/13-state-management-persistence.md` — pi.appendEntry, session state");
224
+ docHints.push("- `docs/extension-sdk/building-extensions.md#state-management` — state reconstruction, appendEntry");
228
225
  }
229
226
  const prompt = `Create a new pi extension based on this description:
230
227
 
@@ -238,13 +235,30 @@ ${docHints.join("\n")}
238
235
 
239
236
  ## Output
240
237
 
241
- Write the complete implementation as a single self-contained extension file:
238
+ Write the complete implementation as a directory-based extension:
242
239
 
243
- \`~/.gsd/agent/extensions/<kebab-case-name>.ts\`
240
+ \`~/.gsd/agent/extensions/<kebab-case-name>/index.ts\`
241
+ \`~/.gsd/agent/extensions/<kebab-case-name>/extension-manifest.json\`
244
242
 
245
- Then register it in the main extensions index:
243
+ The manifest must follow this format:
244
+ \`\`\`json
245
+ {
246
+ "id": "<kebab-case-name>",
247
+ "name": "<Human Name>",
248
+ "version": "1.0.0",
249
+ "description": "<one-line description>",
250
+ "tier": "community",
251
+ "requires": { "platform": ">=2.29.0" },
252
+ "provides": {
253
+ "tools": ["<tool_names_registered>"],
254
+ "commands": ["<command_names_registered>"],
255
+ "hooks": ["<event_names_subscribed>"],
256
+ "shortcuts": ["<shortcut_keys_registered>"]
257
+ }
258
+ }
259
+ \`\`\`
246
260
 
247
- \`~/.gsd/agent/extensions/index.ts\` import and call the new extension's default export alongside existing ones
261
+ Only include non-empty arrays in \`provides\`. See \`docs/extension-sdk/manifest-spec.md\` for the full spec.
248
262
 
249
263
  ## Rules you must follow exactly
250
264
 
@@ -19,8 +19,8 @@ Note: `~/.gsd/agent/extensions/` is reserved for bundled extensions synced from
19
19
  3. **Commands** — Give users slash commands (`pi.registerCommand()`). Users type `/mycommand`.
20
20
 
21
21
  **Non-negotiable rules:**
22
- - Use `StringEnum` from `@mariozechner/pi-ai` for string enum params (NOT `Type.Union`/`Type.Literal` — breaks Google's API)
23
- - Truncate tool output to 50KB / 2000 lines max (use `truncateHead`/`truncateTail` from `@mariozechner/pi-coding-agent`)
22
+ - Use `StringEnum` from `@gsd/pi-ai` for string enum params (NOT `Type.Union`/`Type.Literal` — breaks Google's API)
23
+ - Truncate tool output to 50KB / 2000 lines max (use `truncateHead`/`truncateTail` from `@gsd/pi-coding-agent`)
24
24
  - Store stateful tool state in `details` for branching support
25
25
  - Check `signal?.aborted` in long-running tool executions
26
26
  - Use `pi.exec()` not `child_process` for shell commands
@@ -34,10 +34,10 @@ Note: `~/.gsd/agent/extensions/` is reserved for bundled extensions synced from
34
34
 
35
35
  | Package | Purpose |
36
36
  |---------|---------|
37
- | `@mariozechner/pi-coding-agent` | `ExtensionAPI`, `ExtensionContext`, `Theme`, event types, tool utilities, `DynamicBorder`, `BorderedLoader`, `CustomEditor`, `highlightCode` |
37
+ | `@gsd/pi-coding-agent` | `ExtensionAPI`, `ExtensionContext`, `Theme`, event types, tool utilities, `DynamicBorder`, `BorderedLoader`, `CustomEditor`, `highlightCode` |
38
38
  | `@sinclair/typebox` | `Type.Object`, `Type.String`, `Type.Number`, `Type.Optional`, `Type.Boolean`, `Type.Array` |
39
- | `@mariozechner/pi-ai` | `StringEnum` (required for string enums), `Type` re-export |
40
- | `@mariozechner/pi-tui` | `Text`, `Box`, `Container`, `Spacer`, `Markdown`, `SelectList`, `Input`, `matchesKey`, `Key`, `truncateToWidth`, `visibleWidth` |
39
+ | `@gsd/pi-ai` | `StringEnum` (required for string enums), `Type` re-export |
40
+ | `@gsd/pi-tui` | `Text`, `Box`, `Container`, `Spacer`, `Markdown`, `SelectList`, `Input`, `matchesKey`, `Key`, `truncateToWidth`, `visibleWidth` |
41
41
  | Node.js built-ins | `node:fs`, `node:path`, `node:child_process`, etc. |
42
42
 
43
43
  </essential_principles>
@@ -65,6 +65,9 @@ All domain knowledge in `references/`:
65
65
  **Capabilities:** custom-tools.md, custom-commands.md, custom-ui.md, custom-rendering.md
66
66
  **Patterns:** state-management.md, system-prompt-modification.md, compaction-session-control.md
67
67
  **Infrastructure:** model-provider-management.md, remote-execution-overrides.md, packaging-distribution.md, mode-behavior.md
68
+ **Spec:** `docs/extension-sdk/manifest-spec.md` — manifest format, tiers, validation
69
+ **Testing:** `docs/extension-sdk/testing.md` — mock patterns, test conventions
70
+ **SDK:** `docs/extension-sdk/` — the authoritative GSD-2 extension guide
68
71
  **Gotchas:** key-rules-gotchas.md
69
72
  </reference_index>
70
73
 
@@ -78,6 +81,7 @@ All domain knowledge in `references/`:
78
81
 
79
82
  <success_criteria>
80
83
  Extension is complete when:
84
+ - `extension-manifest.json` exists with accurate `provides` listing all registered tools/commands/hooks/shortcuts
81
85
  - TypeScript compiles without errors (jiti handles this at runtime)
82
86
  - Extension loads on GSD startup or `/reload` without errors
83
87
  - Tools appear in the LLM's system prompt and are callable
@@ -19,7 +19,7 @@ pi.registerCommand("deploy", {
19
19
  Add tab-completion for command arguments:
20
20
 
21
21
  ```typescript
22
- import type { AutocompleteItem } from "@mariozechner/pi-tui";
22
+ import type { AutocompleteItem } from "@gsd/pi-tui";
23
23
 
24
24
  pi.registerCommand("deploy", {
25
25
  description: "Deploy to an environment",