gsd-pi 2.76.0 → 2.77.0

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 (536) hide show
  1. package/README.md +45 -25
  2. package/dist/claude-cli-check.js +32 -3
  3. package/dist/mcp-server.d.ts +7 -0
  4. package/dist/mcp-server.js +35 -1
  5. package/dist/onboarding.js +45 -0
  6. package/dist/resource-loader.d.ts +1 -1
  7. package/dist/resource-loader.js +2 -8
  8. package/dist/resources/agents/researcher.md +1 -1
  9. package/dist/resources/extensions/claude-code-cli/readiness.js +31 -8
  10. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
  11. package/dist/resources/extensions/gsd/auto/loop.js +9 -0
  12. package/dist/resources/extensions/gsd/auto/phases.js +104 -11
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
  14. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
  16. package/dist/resources/extensions/gsd/auto-model-selection.js +53 -16
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
  18. package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
  19. package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
  20. package/dist/resources/extensions/gsd/auto-start.js +58 -57
  21. package/dist/resources/extensions/gsd/auto-verification.js +33 -0
  22. package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
  23. package/dist/resources/extensions/gsd/auto.js +70 -28
  24. package/dist/resources/extensions/gsd/blocked-models.js +68 -0
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +93 -1
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
  27. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
  28. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +3 -0
  29. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +12 -0
  30. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
  31. package/dist/resources/extensions/gsd/bootstrap/system-context.js +84 -23
  32. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
  33. package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
  34. package/dist/resources/extensions/gsd/commands-extract-learnings.js +54 -89
  35. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
  36. package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
  37. package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
  38. package/dist/resources/extensions/gsd/db-writer.js +88 -16
  39. package/dist/resources/extensions/gsd/doctor-git-checks.js +23 -29
  40. package/dist/resources/extensions/gsd/doctor-providers.js +51 -5
  41. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +1 -0
  42. package/dist/resources/extensions/gsd/error-classifier.js +31 -3
  43. package/dist/resources/extensions/gsd/exec-history.js +120 -0
  44. package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
  45. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  46. package/dist/resources/extensions/gsd/gsd-db.js +168 -23
  47. package/dist/resources/extensions/gsd/guided-flow.js +190 -1
  48. package/dist/resources/extensions/gsd/health-widget.js +4 -1
  49. package/dist/resources/extensions/gsd/hook-emitter.js +108 -0
  50. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  51. package/dist/resources/extensions/gsd/key-manager.js +28 -0
  52. package/dist/resources/extensions/gsd/memory-backfill.js +126 -0
  53. package/dist/resources/extensions/gsd/memory-store.js +19 -0
  54. package/dist/resources/extensions/gsd/model-router.js +36 -3
  55. package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
  56. package/dist/resources/extensions/gsd/preferences-types.js +9 -0
  57. package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
  58. package/dist/resources/extensions/gsd/preferences.js +17 -17
  59. package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
  60. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  61. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  62. package/dist/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
  63. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  64. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
  65. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
  66. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  67. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  68. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -0
  69. package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
  70. package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
  71. package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
  72. package/dist/resources/extensions/gsd/state.js +43 -4
  73. package/dist/resources/extensions/gsd/token-counter.js +22 -5
  74. package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
  75. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
  76. package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
  77. package/dist/resources/extensions/gsd/tools/memory-tools.js +26 -1
  78. package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
  79. package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
  80. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
  81. package/dist/resources/extensions/gsd/workflow-templates/spike.md +6 -0
  82. package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
  83. package/dist/resources/extensions/search-the-web/command-search-provider.js +5 -4
  84. package/dist/resources/extensions/search-the-web/native-search.js +45 -13
  85. package/dist/resources/skills/api-design/SKILL.md +190 -0
  86. package/dist/resources/skills/create-mcp-server/SKILL.md +121 -0
  87. package/dist/resources/skills/decompose-into-slices/SKILL.md +139 -0
  88. package/dist/resources/skills/dependency-upgrade/SKILL.md +158 -0
  89. package/dist/resources/skills/design-an-interface/SKILL.md +102 -0
  90. package/dist/resources/skills/forensics/SKILL.md +153 -0
  91. package/dist/resources/skills/grill-me/SKILL.md +93 -0
  92. package/dist/resources/skills/handoff/SKILL.md +121 -0
  93. package/dist/resources/skills/observability/SKILL.md +174 -0
  94. package/dist/resources/skills/security-review/SKILL.md +181 -0
  95. package/dist/resources/skills/spike-wrap-up/SKILL.md +138 -0
  96. package/dist/resources/skills/tdd/SKILL.md +112 -0
  97. package/dist/resources/skills/verify-before-complete/SKILL.md +98 -0
  98. package/dist/resources/skills/write-docs/SKILL.md +82 -0
  99. package/dist/resources/skills/write-milestone-brief/SKILL.md +135 -0
  100. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  101. package/dist/web/standalone/.next/BUILD_ID +1 -1
  102. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  103. package/dist/web/standalone/.next/build-manifest.json +2 -2
  104. package/dist/web/standalone/.next/required-server-files.json +1 -1
  105. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  106. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/index.html +1 -1
  122. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  129. package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
  130. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/middleware-manifest.json +1 -1
  132. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  133. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  134. package/dist/web/standalone/server.js +1 -1
  135. package/dist/welcome-screen.js +6 -1
  136. package/dist/wizard.js +2 -0
  137. package/package.json +1 -1
  138. package/packages/daemon/package.json +2 -2
  139. package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
  140. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
  141. package/packages/mcp-server/dist/remote-questions.js +732 -0
  142. package/packages/mcp-server/dist/remote-questions.js.map +1 -0
  143. package/packages/mcp-server/dist/server.d.ts +7 -0
  144. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  145. package/packages/mcp-server/dist/server.js +70 -8
  146. package/packages/mcp-server/dist/server.js.map +1 -1
  147. package/packages/mcp-server/dist/session-manager.d.ts +14 -0
  148. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/session-manager.js +49 -1
  150. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  151. package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
  152. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  153. package/packages/mcp-server/dist/workflow-tools.js +163 -25
  154. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  155. package/packages/mcp-server/package.json +4 -3
  156. package/packages/mcp-server/src/mcp-server.test.ts +67 -0
  157. package/packages/mcp-server/src/remote-questions.test.ts +294 -0
  158. package/packages/mcp-server/src/remote-questions.ts +916 -0
  159. package/packages/mcp-server/src/server.ts +89 -14
  160. package/packages/mcp-server/src/session-manager.ts +43 -1
  161. package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
  162. package/packages/mcp-server/src/workflow-tools.ts +215 -43
  163. package/packages/mcp-server/tsconfig.test.json +19 -0
  164. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  165. package/packages/native/package.json +1 -1
  166. package/packages/pi-agent-core/dist/agent-loop.js +12 -0
  167. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  168. package/packages/pi-agent-core/dist/types.d.ts +30 -0
  169. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  170. package/packages/pi-agent-core/dist/types.js.map +1 -1
  171. package/packages/pi-agent-core/package.json +1 -1
  172. package/packages/pi-agent-core/src/agent-loop.ts +14 -0
  173. package/packages/pi-agent-core/src/types.ts +34 -0
  174. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  175. package/packages/pi-ai/dist/models/custom.d.ts +38 -0
  176. package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
  177. package/packages/pi-ai/dist/models/custom.js +41 -0
  178. package/packages/pi-ai/dist/models/custom.js.map +1 -1
  179. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
  180. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  181. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts +2 -0
  182. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts.map +1 -0
  183. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +13 -0
  184. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -0
  185. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  186. package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
  187. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  188. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  189. package/packages/pi-ai/dist/providers/anthropic.js +13 -4
  190. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  191. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
  192. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
  193. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
  194. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
  195. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  196. package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
  197. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  198. package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
  199. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  200. package/packages/pi-ai/dist/providers/simple-options.js +16 -1
  201. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  202. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
  203. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
  204. package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
  205. package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
  206. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
  207. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
  208. package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
  209. package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
  210. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  211. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +12 -2
  212. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  213. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +164 -14
  214. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
  215. package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
  216. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +15 -3
  217. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
  218. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts +2 -0
  219. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts.map +1 -0
  220. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +67 -0
  221. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -0
  222. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
  223. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +16 -3
  224. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
  225. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts +2 -0
  226. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts.map +1 -0
  227. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +67 -0
  228. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -0
  229. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +2 -0
  230. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +1 -0
  231. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +289 -0
  232. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +1 -0
  233. package/packages/pi-ai/package.json +1 -1
  234. package/packages/pi-ai/src/models/custom.ts +42 -0
  235. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
  236. package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -0
  237. package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
  238. package/packages/pi-ai/src/providers/anthropic.ts +15 -4
  239. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
  240. package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
  241. package/packages/pi-ai/src/providers/simple-options.ts +17 -1
  242. package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
  243. package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
  244. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +200 -23
  245. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +12 -2
  246. package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +84 -0
  247. package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +15 -5
  248. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +84 -0
  249. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +16 -5
  250. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +363 -0
  251. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  252. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
  253. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -0
  255. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  256. package/packages/pi-coding-agent/dist/core/agent-session.js +32 -2
  257. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  258. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  259. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  260. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  261. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/core/extensions/loader.js +4 -0
  263. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  264. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +35 -2
  265. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  266. package/packages/pi-coding-agent/dist/core/extensions/runner.js +233 -0
  267. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  268. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +205 -2
  269. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  270. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts +53 -0
  272. package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts.map +1 -0
  273. package/packages/pi-coding-agent/dist/core/hooks-runner.js +337 -0
  274. package/packages/pi-coding-agent/dist/core/hooks-runner.js.map +1 -0
  275. package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts +2 -0
  276. package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts.map +1 -0
  277. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +234 -0
  278. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -0
  279. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  280. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  281. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  282. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
  284. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
  286. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
  288. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts +2 -0
  290. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts.map +1 -0
  291. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js +40 -0
  292. package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js.map +1 -0
  293. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
  294. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
  295. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
  296. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
  297. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
  298. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
  299. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
  300. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  301. package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
  302. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  303. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
  304. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
  305. package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
  306. package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
  307. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
  308. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
  309. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
  310. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
  311. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  312. package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
  313. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  314. package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
  315. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  316. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +55 -0
  317. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  318. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  319. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  320. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  321. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  322. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  323. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  324. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  325. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  326. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  327. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
  328. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  329. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  330. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  331. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  332. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  333. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  334. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
  335. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  336. package/packages/pi-coding-agent/package.json +1 -1
  337. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
  338. package/packages/pi-coding-agent/src/core/agent-session.ts +38 -2
  339. package/packages/pi-coding-agent/src/core/extensions/index.ts +16 -0
  340. package/packages/pi-coding-agent/src/core/extensions/loader.ts +5 -0
  341. package/packages/pi-coding-agent/src/core/extensions/runner.ts +351 -0
  342. package/packages/pi-coding-agent/src/core/extensions/types.ts +258 -0
  343. package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +269 -0
  344. package/packages/pi-coding-agent/src/core/hooks-runner.ts +460 -0
  345. package/packages/pi-coding-agent/src/core/index.ts +10 -0
  346. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
  347. package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
  348. package/packages/pi-coding-agent/src/core/model-registry-auth-header.test.ts +44 -0
  349. package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
  350. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
  351. package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
  352. package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
  353. package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
  354. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
  355. package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
  356. package/packages/pi-coding-agent/src/core/settings-manager.ts +57 -0
  357. package/packages/pi-coding-agent/src/index.ts +16 -0
  358. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  359. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
  360. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  361. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
  362. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  363. package/packages/pi-tui/package.json +1 -1
  364. package/packages/rpc-client/package.json +1 -1
  365. package/pkg/package.json +1 -1
  366. package/scripts/link-workspace-packages.cjs +1 -0
  367. package/src/resources/agents/researcher.md +1 -1
  368. package/src/resources/extensions/claude-code-cli/readiness.ts +32 -8
  369. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
  370. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
  371. package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
  372. package/src/resources/extensions/gsd/auto/loop.ts +9 -0
  373. package/src/resources/extensions/gsd/auto/phases.ts +131 -10
  374. package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
  375. package/src/resources/extensions/gsd/auto/session.ts +35 -2
  376. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
  377. package/src/resources/extensions/gsd/auto-model-selection.ts +71 -15
  378. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  379. package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
  380. package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
  381. package/src/resources/extensions/gsd/auto-start.ts +60 -68
  382. package/src/resources/extensions/gsd/auto-verification.ts +33 -0
  383. package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
  384. package/src/resources/extensions/gsd/auto.ts +73 -28
  385. package/src/resources/extensions/gsd/blocked-models.ts +98 -0
  386. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +120 -1
  387. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
  388. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
  389. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +5 -0
  390. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -0
  391. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
  392. package/src/resources/extensions/gsd/bootstrap/system-context.ts +89 -26
  393. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
  394. package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
  395. package/src/resources/extensions/gsd/commands-extract-learnings.ts +55 -90
  396. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
  397. package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
  398. package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
  399. package/src/resources/extensions/gsd/db-writer.ts +88 -17
  400. package/src/resources/extensions/gsd/doctor-git-checks.ts +23 -27
  401. package/src/resources/extensions/gsd/doctor-providers.ts +59 -6
  402. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +2 -0
  403. package/src/resources/extensions/gsd/error-classifier.ts +36 -3
  404. package/src/resources/extensions/gsd/exec-history.ts +153 -0
  405. package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
  406. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  407. package/src/resources/extensions/gsd/gsd-db.ts +186 -23
  408. package/src/resources/extensions/gsd/guided-flow.ts +222 -1
  409. package/src/resources/extensions/gsd/health-widget.ts +3 -1
  410. package/src/resources/extensions/gsd/hook-emitter.ts +188 -0
  411. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  412. package/src/resources/extensions/gsd/journal.ts +2 -1
  413. package/src/resources/extensions/gsd/key-manager.ts +28 -0
  414. package/src/resources/extensions/gsd/memory-backfill.ts +140 -0
  415. package/src/resources/extensions/gsd/memory-store.ts +26 -0
  416. package/src/resources/extensions/gsd/model-router.ts +42 -1
  417. package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
  418. package/src/resources/extensions/gsd/preferences-types.ts +46 -0
  419. package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
  420. package/src/resources/extensions/gsd/preferences.ts +17 -17
  421. package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
  422. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  423. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  424. package/src/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
  425. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  426. package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
  427. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
  428. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  429. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  430. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -0
  431. package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
  432. package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
  433. package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
  434. package/src/resources/extensions/gsd/state.ts +45 -4
  435. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
  436. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +95 -1
  437. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
  438. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
  439. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
  440. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
  441. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
  442. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
  443. package/src/resources/extensions/gsd/tests/blocked-models.test.ts +98 -0
  444. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +54 -0
  445. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
  446. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +68 -66
  447. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
  448. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
  449. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  450. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  451. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
  452. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  453. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +42 -0
  454. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +8 -4
  455. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +148 -3
  456. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
  457. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +306 -1
  458. package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
  459. package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
  460. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
  461. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
  462. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +40 -9
  463. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +62 -0
  464. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
  465. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  466. package/src/resources/extensions/gsd/tests/integration/doctor-git-symlink-cwd.test.ts +11 -0
  467. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +78 -0
  468. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
  469. package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
  470. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
  471. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +1 -1
  472. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
  473. package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
  474. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +37 -0
  475. package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
  476. package/src/resources/extensions/gsd/tests/load-memory-block.test.ts +36 -0
  477. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  478. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
  479. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  480. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
  481. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
  482. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
  483. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
  484. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
  485. package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
  486. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
  487. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
  488. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +103 -4
  489. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
  490. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
  491. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
  492. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
  493. package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
  494. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
  495. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
  496. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
  497. package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
  498. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
  499. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
  500. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -3
  501. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
  502. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
  503. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
  504. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
  505. package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
  506. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
  507. package/src/resources/extensions/gsd/token-counter.ts +22 -5
  508. package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
  509. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
  510. package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
  511. package/src/resources/extensions/gsd/tools/memory-tools.ts +31 -1
  512. package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
  513. package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
  514. package/src/resources/extensions/gsd/workflow-logger.ts +4 -1
  515. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
  516. package/src/resources/extensions/gsd/workflow-templates/spike.md +6 -0
  517. package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
  518. package/src/resources/extensions/search-the-web/command-search-provider.ts +5 -4
  519. package/src/resources/extensions/search-the-web/native-search.ts +48 -12
  520. package/src/resources/skills/api-design/SKILL.md +190 -0
  521. package/src/resources/skills/create-mcp-server/SKILL.md +121 -0
  522. package/src/resources/skills/decompose-into-slices/SKILL.md +139 -0
  523. package/src/resources/skills/dependency-upgrade/SKILL.md +158 -0
  524. package/src/resources/skills/design-an-interface/SKILL.md +102 -0
  525. package/src/resources/skills/forensics/SKILL.md +153 -0
  526. package/src/resources/skills/grill-me/SKILL.md +93 -0
  527. package/src/resources/skills/handoff/SKILL.md +121 -0
  528. package/src/resources/skills/observability/SKILL.md +174 -0
  529. package/src/resources/skills/security-review/SKILL.md +181 -0
  530. package/src/resources/skills/spike-wrap-up/SKILL.md +138 -0
  531. package/src/resources/skills/tdd/SKILL.md +112 -0
  532. package/src/resources/skills/verify-before-complete/SKILL.md +98 -0
  533. package/src/resources/skills/write-docs/SKILL.md +82 -0
  534. package/src/resources/skills/write-milestone-brief/SKILL.md +135 -0
  535. /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_buildManifest.js +0 -0
  536. /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_ssgManifest.js +0 -0
@@ -22,7 +22,11 @@ async function getEncoder(): Promise<TokenEncoder | null> {
22
22
  try {
23
23
  // @ts-ignore — tiktoken may not have type declarations in extensions tsconfig
24
24
  const tiktoken = await import("tiktoken");
25
- encoder = tiktoken.encoding_for_model("gpt-4o") as TokenEncoder;
25
+ // Use cl100k_base the most conservative and broadly compatible BPE encoding.
26
+ // It is shared by GPT-3.5/GPT-4 and gives a safer (larger) estimate than
27
+ // gpt-4o's o200k_base encoding, which produces fewer tokens for the same text
28
+ // and would cause context windows for non-OpenAI providers to be under-counted.
29
+ encoder = tiktoken.get_encoding("cl100k_base") as TokenEncoder;
26
30
  return encoder;
27
31
  } catch {
28
32
  encoderFailed = true;
@@ -30,20 +34,33 @@ async function getEncoder(): Promise<TokenEncoder | null> {
30
34
  }
31
35
  }
32
36
 
33
- export async function countTokens(text: string): Promise<number> {
37
+ /**
38
+ * Count tokens in `text` using tiktoken (cl100k_base) when available.
39
+ *
40
+ * When tiktoken is not loaded, falls back to a provider-aware character-ratio
41
+ * estimate via `estimateTokensForProvider`. Passing `provider` is recommended
42
+ * so the heuristic fallback is as accurate as possible.
43
+ */
44
+ export async function countTokens(text: string, provider?: TokenProvider): Promise<number> {
34
45
  const enc = await getEncoder();
35
46
  if (enc) {
36
47
  const tokens = enc.encode(text);
37
48
  return tokens.length;
38
49
  }
39
- return Math.ceil(text.length / 4);
50
+ return estimateTokensForProvider(text, provider ?? "unknown");
40
51
  }
41
52
 
42
- export function countTokensSync(text: string): number {
53
+ /**
54
+ * Synchronous token count — only accurate after `initTokenCounter()` resolves.
55
+ *
56
+ * Before init, or when tiktoken is unavailable, falls back to a provider-aware
57
+ * character-ratio estimate. Passing `provider` is recommended.
58
+ */
59
+ export function countTokensSync(text: string, provider?: TokenProvider): number {
43
60
  if (encoder) {
44
61
  return encoder.encode(text).length;
45
62
  }
46
- return Math.ceil(text.length / 4);
63
+ return estimateTokensForProvider(text, provider ?? "unknown");
47
64
  }
48
65
 
49
66
  export async function initTokenCounter(): Promise<boolean> {
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { join } from "node:path";
10
- import { mkdirSync } from "node:fs";
10
+ import { mkdirSync, existsSync } from "node:fs";
11
11
 
12
12
  import {
13
13
  transaction,
@@ -202,14 +202,20 @@ export async function handleCompleteMilestone(
202
202
  summaryPath = join(manualDir, `${params.milestoneId}-SUMMARY.md`);
203
203
  }
204
204
 
205
- try {
206
- await saveFile(summaryPath, summaryMd);
207
- } catch (renderErr) {
208
- // Disk render failed roll back DB status so state stays consistent
209
- logWarning("tool", `complete_milestone — disk render failed, rolling back DB status: ${(renderErr as Error).message}`);
210
- updateMilestoneStatus(params.milestoneId, 'active', null);
211
- invalidateStateCache();
212
- return { error: `disk render failed: ${(renderErr as Error).message}` };
205
+ // Guard (#4598): if SUMMARY.md already exists on disk, do not overwrite it.
206
+ // This handles re-dispatch scenarios (DB/disk state divergence) where a prior
207
+ // completion already wrote the file. Overwriting would silently destroy the
208
+ // richer content the agent produced during the original completion run.
209
+ if (!existsSync(summaryPath)) {
210
+ try {
211
+ await saveFile(summaryPath, summaryMd);
212
+ } catch (renderErr) {
213
+ // Disk render failed — roll back DB status so state stays consistent
214
+ logWarning("tool", `complete_milestone — disk render failed, rolling back DB status: ${(renderErr as Error).message}`);
215
+ updateMilestoneStatus(params.milestoneId, 'active', null);
216
+ invalidateStateCache();
217
+ return { error: `disk render failed: ${(renderErr as Error).message}` };
218
+ }
213
219
  }
214
220
 
215
221
  // Invalidate all caches
@@ -0,0 +1,81 @@
1
+ // GSD Exec Search Tool — lists and filters prior gsd_exec runs.
2
+ //
3
+ // Scans .gsd/exec/*.meta.json and returns a ranked summary so agents can
4
+ // re-discover past runs without re-executing. Read-only; no DB writes.
5
+
6
+ import { searchExecHistory, type ExecSearchOptions } from "../exec-history.js";
7
+
8
+ export interface ExecSearchToolParams {
9
+ query?: string;
10
+ runtime?: "bash" | "node" | "python";
11
+ failing_only?: boolean;
12
+ limit?: number;
13
+ }
14
+
15
+ export interface ToolExecutionResult {
16
+ content: Array<{ type: "text"; text: string }>;
17
+ details: Record<string, unknown>;
18
+ isError?: boolean;
19
+ }
20
+
21
+ export function executeExecSearch(
22
+ params: ExecSearchToolParams,
23
+ opts: { baseDir: string },
24
+ ): ToolExecutionResult {
25
+ const searchOpts: ExecSearchOptions = {
26
+ query: typeof params.query === "string" ? params.query : undefined,
27
+ runtime: params.runtime,
28
+ failing_only: params.failing_only === true,
29
+ limit: typeof params.limit === "number" ? params.limit : undefined,
30
+ };
31
+ const hits = searchExecHistory(opts.baseDir, searchOpts);
32
+
33
+ if (hits.length === 0) {
34
+ return {
35
+ content: [{ type: "text", text: "No prior gsd_exec runs match those filters." }],
36
+ details: { operation: "gsd_exec_search", matches: 0 },
37
+ };
38
+ }
39
+
40
+ const lines: string[] = [`Found ${hits.length} exec run(s), most recent first:`];
41
+ for (const hit of hits) {
42
+ const e = hit.entry;
43
+ const status = formatStatus(e);
44
+ const purpose = e.purpose ? ` — ${e.purpose}` : "";
45
+ const truncated = e.stdout_truncated ? " (stdout truncated)" : "";
46
+ lines.push(
47
+ `- [${e.id}] ${e.runtime} ${status} ${e.duration_ms}ms${truncated}${purpose}`,
48
+ ` stdout: ${e.stdout_path}`,
49
+ );
50
+ if (hit.digest_preview) {
51
+ const preview = hit.digest_preview.replace(/\n/g, "\n ");
52
+ lines.push(` preview:\n ${preview}`);
53
+ }
54
+ }
55
+
56
+ return {
57
+ content: [{ type: "text", text: lines.join("\n") }],
58
+ details: {
59
+ operation: "gsd_exec_search",
60
+ matches: hits.length,
61
+ results: hits.map((hit) => ({
62
+ id: hit.entry.id,
63
+ runtime: hit.entry.runtime,
64
+ exit_code: hit.entry.exit_code,
65
+ timed_out: hit.entry.timed_out,
66
+ duration_ms: hit.entry.duration_ms,
67
+ purpose: hit.entry.purpose,
68
+ stdout_path: hit.entry.stdout_path,
69
+ stderr_path: hit.entry.stderr_path,
70
+ meta_path: hit.entry.meta_path,
71
+ })),
72
+ },
73
+ };
74
+ }
75
+
76
+ function formatStatus(entry: { exit_code: number | null; timed_out: boolean; signal: string | null }): string {
77
+ if (entry.timed_out) return "timeout";
78
+ if (entry.signal) return `signal:${entry.signal}`;
79
+ if (entry.exit_code === null) return "exit:null";
80
+ return `exit:${entry.exit_code}`;
81
+ }
@@ -0,0 +1,183 @@
1
+ // GSD Exec Tool — executor for the gsd_exec MCP tool.
2
+ //
3
+ // Thin wrapper around exec-sandbox.ts that reads effective options from
4
+ // the project preferences (context_mode block) and formats the result
5
+ // for MCP return.
6
+
7
+ import {
8
+ EXEC_DEFAULTS,
9
+ runExecSandbox,
10
+ type ExecSandboxOptions,
11
+ type ExecSandboxRequest,
12
+ type ExecSandboxResult,
13
+ } from "../exec-sandbox.js";
14
+ import { isContextModeEnabled, type ContextModeConfig } from "../preferences-types.js";
15
+
16
+ export interface ExecToolParams {
17
+ runtime: ExecSandboxRequest["runtime"];
18
+ script: string;
19
+ purpose?: string;
20
+ timeout_ms?: number;
21
+ }
22
+
23
+ export interface ToolExecutionResult {
24
+ content: Array<{ type: "text"; text: string }>;
25
+ details: Record<string, unknown>;
26
+ isError?: boolean;
27
+ }
28
+
29
+ export interface ExecToolDeps {
30
+ baseDir: string;
31
+ preferences: { context_mode?: ContextModeConfig } | null;
32
+ /** Optional override for testing. */
33
+ run?: (req: ExecSandboxRequest, opts: ExecSandboxOptions) => Promise<ExecSandboxResult>;
34
+ now?: () => Date;
35
+ generateId?: () => string;
36
+ }
37
+
38
+ export function buildExecOptions(
39
+ baseDir: string,
40
+ cfg: ContextModeConfig | undefined,
41
+ extras?: Pick<ExecSandboxOptions, "env" | "now" | "generateId">,
42
+ ): ExecSandboxOptions {
43
+ const allowlist = Array.isArray(cfg?.exec_env_allowlist) ? cfg!.exec_env_allowlist! : EXEC_DEFAULTS.envAllowlist;
44
+ const stdoutCap = clampNumber(
45
+ cfg?.exec_stdout_cap_bytes,
46
+ EXEC_DEFAULTS.stdoutCapBytes,
47
+ 4_096,
48
+ 16_777_216,
49
+ );
50
+ const defaultTimeout = clampNumber(
51
+ cfg?.exec_timeout_ms,
52
+ EXEC_DEFAULTS.defaultTimeoutMs,
53
+ 1_000,
54
+ EXEC_DEFAULTS.clampTimeoutMs,
55
+ );
56
+ const digestChars = clampNumber(cfg?.exec_digest_chars, EXEC_DEFAULTS.digestChars, 0, 4_000);
57
+ return {
58
+ baseDir,
59
+ clamp_timeout_ms: EXEC_DEFAULTS.clampTimeoutMs,
60
+ default_timeout_ms: defaultTimeout,
61
+ stdout_cap_bytes: stdoutCap,
62
+ stderr_cap_bytes: EXEC_DEFAULTS.stderrCapBytes,
63
+ digest_chars: digestChars,
64
+ env_allowlist: allowlist,
65
+ ...extras,
66
+ };
67
+ }
68
+
69
+ function clampNumber(value: unknown, fallback: number, min: number, max: number): number {
70
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
71
+ if (value < min) return min;
72
+ if (value > max) return max;
73
+ return Math.floor(value);
74
+ }
75
+
76
+ function isEnabled(prefs: ExecToolDeps["preferences"]): boolean {
77
+ return isContextModeEnabled(prefs);
78
+ }
79
+
80
+ function disabledResult(): ToolExecutionResult {
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text:
86
+ "gsd_exec is disabled by `context_mode.enabled: false` in preferences. Remove that " +
87
+ "override (or set it to true) to re-enable sandboxed tool-output execution.",
88
+ },
89
+ ],
90
+ details: { operation: "gsd_exec", error: "context_mode_disabled" },
91
+ isError: true,
92
+ };
93
+ }
94
+
95
+ function paramError(message: string): ToolExecutionResult {
96
+ return {
97
+ content: [{ type: "text", text: `Error: ${message}` }],
98
+ details: { operation: "gsd_exec", error: "invalid_params", detail: message },
99
+ isError: true,
100
+ };
101
+ }
102
+
103
+ export async function executeGsdExec(
104
+ params: ExecToolParams,
105
+ deps: ExecToolDeps,
106
+ ): Promise<ToolExecutionResult> {
107
+ if (!isEnabled(deps.preferences)) return disabledResult();
108
+
109
+ const runtime = params.runtime;
110
+ if (runtime !== "bash" && runtime !== "node" && runtime !== "python") {
111
+ return paramError(`invalid runtime "${String(runtime)}" — must be bash | node | python`);
112
+ }
113
+ const script = typeof params.script === "string" ? params.script : "";
114
+ if (script.trim().length === 0) {
115
+ return paramError("script is required and must be a non-empty string");
116
+ }
117
+ if (Buffer.byteLength(script, "utf8") > 200_000) {
118
+ return paramError("script exceeds the 200 KB length limit");
119
+ }
120
+
121
+ const opts = buildExecOptions(
122
+ deps.baseDir,
123
+ deps.preferences?.context_mode,
124
+ { now: deps.now, generateId: deps.generateId },
125
+ );
126
+ const run = deps.run ?? runExecSandbox;
127
+
128
+ try {
129
+ const result = await run(
130
+ {
131
+ runtime,
132
+ script,
133
+ ...(typeof params.purpose === "string" ? { purpose: params.purpose } : {}),
134
+ ...(typeof params.timeout_ms === "number" ? { timeout_ms: params.timeout_ms } : {}),
135
+ },
136
+ opts,
137
+ );
138
+ return formatResult(result);
139
+ } catch (err) {
140
+ const message = err instanceof Error ? err.message : String(err);
141
+ return {
142
+ content: [{ type: "text", text: `Error: gsd_exec failed — ${message}` }],
143
+ details: { operation: "gsd_exec", error: message },
144
+ isError: true,
145
+ };
146
+ }
147
+ }
148
+
149
+ function formatResult(result: ExecSandboxResult): ToolExecutionResult {
150
+ const headerLines = [
151
+ `gsd_exec[${result.id}] runtime=${result.runtime} exit=${formatExit(result)} duration=${result.duration_ms}ms`,
152
+ ` stdout: ${result.stdout_bytes}B${result.stdout_truncated ? " (truncated)" : ""} → ${result.stdout_path}`,
153
+ ` stderr: ${result.stderr_bytes}B${result.stderr_truncated ? " (truncated)" : ""} → ${result.stderr_path}`,
154
+ ];
155
+ const summary = `${headerLines.join("\n")}\n--- digest ---\n${result.digest}`.trimEnd();
156
+ return {
157
+ content: [{ type: "text", text: summary }],
158
+ details: {
159
+ operation: "gsd_exec",
160
+ id: result.id,
161
+ runtime: result.runtime,
162
+ exit_code: result.exit_code,
163
+ signal: result.signal,
164
+ timed_out: result.timed_out,
165
+ duration_ms: result.duration_ms,
166
+ stdout_bytes: result.stdout_bytes,
167
+ stderr_bytes: result.stderr_bytes,
168
+ stdout_truncated: result.stdout_truncated,
169
+ stderr_truncated: result.stderr_truncated,
170
+ stdout_path: result.stdout_path,
171
+ stderr_path: result.stderr_path,
172
+ meta_path: result.meta_path,
173
+ },
174
+ isError: result.timed_out || result.signal !== null || result.exit_code !== 0,
175
+ };
176
+ }
177
+
178
+ function formatExit(result: ExecSandboxResult): string {
179
+ if (result.timed_out) return "timeout";
180
+ if (result.signal) return `signal:${result.signal}`;
181
+ if (result.exit_code === null) return "null";
182
+ return String(result.exit_code);
183
+ }
@@ -48,6 +48,14 @@ export interface MemoryCaptureParams {
48
48
  confidence?: number;
49
49
  tags?: string[];
50
50
  scope?: string;
51
+ /**
52
+ * ADR-013 Step 2: optional structured payload preserved verbatim on the
53
+ * memories row. Used when capturing decisions that need to retain
54
+ * gsd_save_decision-style fields (scope, decision, choice, rationale,
55
+ * made_by, revisable) so the eventual cutover (Step 6) is lossless.
56
+ * Plain pattern/gotcha/convention captures may omit this entirely.
57
+ */
58
+ structuredFields?: Record<string, unknown> | null;
51
59
  }
52
60
 
53
61
  const VALID_CATEGORIES = new Set([
@@ -87,7 +95,8 @@ export function executeMemoryCapture(params: MemoryCaptureParams): ToolExecution
87
95
  const scope = normalizeScope(params.scope);
88
96
  const tags = normalizeTags(params.tags);
89
97
 
90
- const id = createMemory({ category, content, confidence, scope, tags });
98
+ const structuredFields = normalizeStructuredFields(params.structuredFields);
99
+ const id = createMemory({ category, content, confidence, scope, tags, structuredFields });
91
100
  if (!id) {
92
101
  return {
93
102
  content: [{ type: "text", text: "Error: failed to create memory." }],
@@ -113,6 +122,17 @@ function normalizeTags(value: unknown): string[] {
113
122
  return value.filter((t): t is string => typeof t === "string" && t.trim().length > 0).slice(0, 10);
114
123
  }
115
124
 
125
+ function normalizeStructuredFields(value: unknown): Record<string, unknown> | null {
126
+ if (value == null) return null;
127
+ if (typeof value !== "object" || Array.isArray(value)) return null;
128
+ // Only accept plain objects (Object.prototype or null prototype). Class
129
+ // instances and exotic objects won't round-trip cleanly through JSON, so
130
+ // reject them here instead of producing a partially-serialized payload.
131
+ const proto = Object.getPrototypeOf(value);
132
+ if (proto !== null && proto !== Object.prototype) return null;
133
+ return value as Record<string, unknown>;
134
+ }
135
+
116
136
  function clampConfidence(value: unknown): number {
117
137
  if (typeof value !== "number" || !Number.isFinite(value)) return 0.8;
118
138
  if (value < 0.1) return 0.1;
@@ -248,6 +268,15 @@ function includeSupersededMemories(rankedActive: Memory[]): Memory[] {
248
268
  /* leave empty */
249
269
  }
250
270
  }
271
+ let structuredFields: Record<string, unknown> | null = null;
272
+ if (typeof row["structured_fields"] === "string" && (row["structured_fields"] as string).length > 0) {
273
+ try {
274
+ const parsed = JSON.parse(row["structured_fields"] as string);
275
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
276
+ structuredFields = parsed as Record<string, unknown>;
277
+ }
278
+ } catch { /* leave null */ }
279
+ }
251
280
  return {
252
281
  seq: row["seq"] as number,
253
282
  id: row["id"] as string,
@@ -262,6 +291,7 @@ function includeSupersededMemories(rankedActive: Memory[]): Memory[] {
262
291
  hit_count: row["hit_count"] as number,
263
292
  scope: (row["scope"] as string) ?? "project",
264
293
  tags,
294
+ structured_fields: structuredFields,
265
295
  };
266
296
  });
267
297
  } catch {
@@ -0,0 +1,40 @@
1
+ // GSD Resume Tool — returns the contents of .gsd/last-snapshot.md so
2
+ // agents can re-orient after compaction or session resume without
3
+ // re-deriving project memory state.
4
+
5
+ import { readCompactionSnapshot } from "../compaction-snapshot.js";
6
+
7
+ export interface ResumeToolParams {
8
+ /** Ignored — reserved for future variant (e.g. dated snapshots). */
9
+ _variant?: string;
10
+ }
11
+
12
+ export interface ToolExecutionResult {
13
+ content: Array<{ type: "text"; text: string }>;
14
+ details: Record<string, unknown>;
15
+ isError?: boolean;
16
+ }
17
+
18
+ export function executeResume(
19
+ _params: ResumeToolParams,
20
+ opts: { baseDir: string },
21
+ ): ToolExecutionResult {
22
+ const snapshot = readCompactionSnapshot(opts.baseDir);
23
+ if (snapshot == null) {
24
+ return {
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text:
29
+ "No snapshot found at .gsd/last-snapshot.md. The snapshot is written automatically " +
30
+ "on session_before_compact (enabled by default; set context_mode.enabled=false to opt out).",
31
+ },
32
+ ],
33
+ details: { operation: "gsd_resume", found: false },
34
+ };
35
+ }
36
+ return {
37
+ content: [{ type: "text", text: snapshot }],
38
+ details: { operation: "gsd_resume", found: true, bytes: Buffer.byteLength(snapshot, "utf-8") },
39
+ };
40
+ }
@@ -38,6 +38,29 @@ function hasFileContent(path: string | null): boolean {
38
38
  }
39
39
  }
40
40
 
41
+ function getArtifactLookupBases(basePath: string): string[] {
42
+ const bases = [basePath];
43
+ const projectRoot = process.env.GSD_PROJECT_ROOT;
44
+ if (projectRoot && projectRoot.trim().length > 0 && projectRoot !== basePath) {
45
+ bases.push(projectRoot);
46
+ }
47
+ return bases;
48
+ }
49
+
50
+ function hasMilestoneFileContent(
51
+ basePath: string,
52
+ milestoneId: string,
53
+ suffix: string,
54
+ ): boolean {
55
+ const bases = getArtifactLookupBases(basePath);
56
+ for (const candidateBase of bases) {
57
+ if (hasFileContent(resolveMilestoneFile(candidateBase, milestoneId, suffix))) {
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+
41
64
  function countSliceResearchArtifacts(basePath: string, milestoneId: string, slices: SliceRow[]): number {
42
65
  let count = 0;
43
66
  for (const slice of slices) {
@@ -60,9 +83,9 @@ export function compileUnitGraphFromState(basePath: string, state: GSDState): Pl
60
83
  const slices = getMilestoneSlices(mid).sort((a, b) => Number(a.sequence ?? 0) - Number(b.sequence ?? 0));
61
84
  const nodes: UokGraphNode[] = [];
62
85
  const clarifyRoundLimit = PLAN_V2_CLARIFY_ROUND_LIMIT;
63
- const draftContextIncluded = hasFileContent(resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT"));
64
- const finalizedContextIncluded = hasFileContent(resolveMilestoneFile(basePath, mid, "CONTEXT"));
65
- const researchSynthesized = hasFileContent(resolveMilestoneFile(basePath, mid, "RESEARCH"))
86
+ const draftContextIncluded = hasMilestoneFileContent(basePath, mid, "CONTEXT-DRAFT");
87
+ const finalizedContextIncluded = hasMilestoneFileContent(basePath, mid, "CONTEXT");
88
+ const researchSynthesized = hasMilestoneFileContent(basePath, mid, "RESEARCH")
66
89
  || countSliceResearchArtifacts(basePath, mid, slices) > 0;
67
90
 
68
91
  if (isExecutionEntryPhase(state.phase) && !finalizedContextIncluded) {
@@ -56,7 +56,10 @@ export type LogComponent =
56
56
  | "safety" // LLM safety harness
57
57
  | "ecosystem" // GSD ecosystem extension loader and dispatch
58
58
  | "memory-embeddings" // Memory layer embedding generation
59
- | "memory-ingest"; // Memory layer ingestion pipeline
59
+ | "memory-ingest" // Memory layer ingestion pipeline
60
+ | "memory-backfill" // ADR-013: decisions->memories backfill
61
+ | "context-mode" // Context-mode exec sandbox and compaction snapshot
62
+ | "preflight"; // Clean-root preflight gate at milestone completion
60
63
 
61
64
  export interface LogEntry {
62
65
  ts: string;
@@ -23,6 +23,9 @@ export interface WorkflowCapabilityOptions {
23
23
  const MCP_WORKFLOW_TOOL_SURFACE = new Set([
24
24
  "ask_user_questions",
25
25
  "gsd_decision_save",
26
+ "gsd_exec",
27
+ "gsd_exec_search",
28
+ "gsd_resume",
26
29
  "gsd_complete_milestone",
27
30
  "gsd_complete_task",
28
31
  "gsd_complete_slice",
@@ -66,5 +66,11 @@ Use for: technology evaluation, architecture decisions, "should we X?" questions
66
66
  - Recommendation with rationale
67
67
  - Next steps if the recommendation is accepted
68
68
  4. **Present** the recommendation to the user for discussion
69
+ 5. **Offer wrap-up:** If the findings are reusable on future work, offer to run
70
+ the `spike-wrap-up` skill to package them as a project-local skill at
71
+ `.claude/skills/<name>/SKILL.md`. That skill will auto-load on future
72
+ similar tasks via `skill-discovery.ts`. If the recommendation is
73
+ decision-only (no reusable guidance), suggest appending a one-liner to
74
+ `.gsd/DECISIONS.md` instead.
69
75
 
70
76
  </process>
@@ -79,6 +79,35 @@ export interface NotifyCtx {
79
79
  ) => void;
80
80
  }
81
81
 
82
+ // ─── Path Helpers ──────────────────────────────────────────────────────────
83
+
84
+ /**
85
+ * Worktree marker segment — present in any path produced by worktreePath().
86
+ * Used to strip the worktree suffix and recover the project root (#3729).
87
+ */
88
+ const WORKTREE_MARKER = "/.gsd/worktrees/";
89
+
90
+ /**
91
+ * Resolve the project root from session path state.
92
+ *
93
+ * Prefers `originalBasePath` (always the project root when set), but falls
94
+ * back to `basePath` when `originalBasePath` is falsy (e.g. fresh AutoSession
95
+ * with default empty string). If `basePath` itself is inside a worktree
96
+ * directory (contains `/.gsd/worktrees/`), strip that suffix to recover the
97
+ * actual project root — preventing double-nested worktree paths (#3729).
98
+ */
99
+ export function resolveProjectRoot(
100
+ originalBasePath: string,
101
+ basePath: string,
102
+ ): string {
103
+ let resolved = originalBasePath || basePath;
104
+ const markerIdx = resolved.indexOf(WORKTREE_MARKER);
105
+ if (markerIdx !== -1) {
106
+ resolved = resolved.slice(0, markerIdx);
107
+ }
108
+ return resolved;
109
+ }
110
+
82
111
  // ─── WorktreeResolver ──────────────────────────────────────────────────────
83
112
 
84
113
  export class WorktreeResolver {
@@ -99,12 +128,12 @@ export class WorktreeResolver {
99
128
 
100
129
  /** Original project root — always the non-worktree path. */
101
130
  get projectRoot(): string {
102
- return this.s.originalBasePath || this.s.basePath;
131
+ return resolveProjectRoot(this.s.originalBasePath, this.s.basePath);
103
132
  }
104
133
 
105
134
  /** Path for auto.lock file — same as the old lockBase(). */
106
135
  get lockPath(): string {
107
- return this.s.originalBasePath || this.s.basePath;
136
+ return resolveProjectRoot(this.s.originalBasePath, this.s.basePath);
108
137
  }
109
138
 
110
139
  // ── Private Helpers ────────────────────────────────────────────────────
@@ -182,7 +211,10 @@ export class WorktreeResolver {
182
211
  return;
183
212
  }
184
213
 
185
- const basePath = this.s.originalBasePath || this.s.basePath;
214
+ // Resolve the project root for worktree operations via shared helper.
215
+ // Handles the case where originalBasePath is falsy and basePath is itself
216
+ // a worktree path — prevents double-nested worktree paths (#3729).
217
+ const basePath = resolveProjectRoot(this.s.originalBasePath, this.s.basePath);
186
218
  debugLog("WorktreeResolver", {
187
219
  action: "enterMilestone",
188
220
  milestoneId,
@@ -569,11 +601,13 @@ export class WorktreeResolver {
569
601
  }
570
602
  }
571
603
 
572
- // Re-throw MergeConflictError so the auto loop can detect real code
573
- // conflicts and stop instead of retrying forever (#2330).
574
- if (err instanceof MergeConflictError) {
575
- throw err;
576
- }
604
+ // Restore state before re-throwing so callers always get a consistent
605
+ // session (#4380).
606
+ this.restoreToProjectRoot();
607
+ // Re-throw: MergeConflictError stops the auto loop (#2330); non-conflict
608
+ // errors (permission denied, filesystem failures) must also propagate so
609
+ // broken states are diagnosable (#4380).
610
+ throw err;
577
611
  }
578
612
 
579
613
  // Always restore basePath and rebuild — whether merge succeeded or failed
@@ -659,6 +693,8 @@ export class WorktreeResolver {
659
693
  error: msg,
660
694
  });
661
695
  ctx.notify(`Milestone merge failed (branch mode): ${msg}`, "warning");
696
+ // Re-throw all errors so callers can apply their own recovery logic (#4380).
697
+ throw err;
662
698
  }
663
699
  }
664
700
 
@@ -681,7 +717,16 @@ export class WorktreeResolver {
681
717
  currentMilestoneId,
682
718
  nextMilestoneId,
683
719
  });
684
- this.mergeAndExit(currentMilestoneId, ctx);
720
+ try {
721
+ this.mergeAndExit(currentMilestoneId, ctx);
722
+ } catch (err) {
723
+ // mergeAndExit emits a warning and restores state when it fails during
724
+ // merge/cleanup. But if it throws before recovery runs (e.g., in
725
+ // validateMilestoneId or emitJournalEvent), basePath won't be restored
726
+ // to projectRoot — re-throw so we don't enter the next milestone with
727
+ // the current one unmerged.
728
+ if (this.s.basePath !== this.projectRoot) throw err;
729
+ }
685
730
  this.enterMilestone(nextMilestoneId, ctx);
686
731
  }
687
732
  }
@@ -8,7 +8,7 @@
8
8
  * All provider logic lives in provider.ts (S01) — this is pure UI wiring.
9
9
  */
10
10
 
11
- import { isAnthropicApi } from '@gsd/pi-ai'
11
+ import { supportsNativeWebSearch } from './native-search.js'
12
12
  import type { ExtensionAPI } from '@gsd/pi-coding-agent'
13
13
  import type { AutocompleteItem } from '@gsd/pi-tui'
14
14
  import {
@@ -91,9 +91,10 @@ export function registerSearchProviderCommand(pi: ExtensionAPI): void {
91
91
 
92
92
  setSearchProviderPreference(chosen)
93
93
  const effective = resolveSearchProvider()
94
- // Gate on api (#4478 / ADR-012): covers claude-code, anthropic-vertex, and
95
- // other Anthropic-fronting transports not just the plain `anthropic` provider.
96
- const isAnthropic = isAnthropicApi(ctx.model)
94
+ // Gate on api shape + provider allowlist: the info note must match the
95
+ // actual runtime behavior in native-search.ts. Claude served via copilot
96
+ // / minimax / kimi is anthropic-shaped but does NOT run native search.
97
+ const isAnthropic = supportsNativeWebSearch(ctx.model)
97
98
  const nativeNote = isAnthropic ? '\nNote: Native Anthropic web search is also active (automatic, no API key needed).' : ''
98
99
  ctx.ui.notify(
99
100
  `Search provider set to ${chosen}. Effective provider: ${effective ?? 'none (no API keys)'}${nativeNote}`,