gsd-pi 2.76.0-dev.4100bd590 → 2.76.0-dev.479ad0e78

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 (362) hide show
  1. package/dist/claude-cli-check.js +32 -3
  2. package/dist/mcp-server.d.ts +7 -0
  3. package/dist/mcp-server.js +35 -1
  4. package/dist/onboarding.js +45 -0
  5. package/dist/resource-loader.d.ts +1 -1
  6. package/dist/resource-loader.js +2 -8
  7. package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
  9. package/dist/resources/extensions/gsd/auto/loop.js +9 -0
  10. package/dist/resources/extensions/gsd/auto/phases.js +58 -5
  11. package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
  12. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  13. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +14 -3
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
  16. package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
  17. package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
  18. package/dist/resources/extensions/gsd/auto-start.js +58 -57
  19. package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
  20. package/dist/resources/extensions/gsd/auto.js +70 -28
  21. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
  23. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
  27. package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
  28. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
  29. package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
  30. package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
  31. package/dist/resources/extensions/gsd/error-classifier.js +10 -3
  32. package/dist/resources/extensions/gsd/exec-history.js +120 -0
  33. package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
  34. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  35. package/dist/resources/extensions/gsd/gsd-db.js +149 -31
  36. package/dist/resources/extensions/gsd/guided-flow.js +190 -1
  37. package/dist/resources/extensions/gsd/health-widget.js +4 -1
  38. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  39. package/dist/resources/extensions/gsd/key-manager.js +28 -0
  40. package/dist/resources/extensions/gsd/model-router.js +36 -3
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
  42. package/dist/resources/extensions/gsd/preferences-types.js +9 -0
  43. package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
  44. package/dist/resources/extensions/gsd/preferences.js +17 -17
  45. package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
  46. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  47. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
  48. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  49. package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
  50. package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
  51. package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
  52. package/dist/resources/extensions/gsd/token-counter.js +22 -5
  53. package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
  54. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
  55. package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
  56. package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
  57. package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
  58. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
  59. package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
  60. package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
  61. package/dist/resources/skills/write-docs/SKILL.md +2 -1
  62. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  65. package/dist/web/standalone/.next/build-manifest.json +2 -2
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/required-server-files.json +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.html +1 -1
  85. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  92. package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
  93. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  95. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  96. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  97. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  98. package/dist/web/standalone/server.js +1 -1
  99. package/dist/welcome-screen.js +6 -1
  100. package/dist/wizard.js +2 -0
  101. package/package.json +1 -1
  102. package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
  103. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
  104. package/packages/mcp-server/dist/remote-questions.js +732 -0
  105. package/packages/mcp-server/dist/remote-questions.js.map +1 -0
  106. package/packages/mcp-server/dist/server.d.ts +7 -0
  107. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  108. package/packages/mcp-server/dist/server.js +70 -8
  109. package/packages/mcp-server/dist/server.js.map +1 -1
  110. package/packages/mcp-server/dist/session-manager.d.ts +14 -0
  111. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
  112. package/packages/mcp-server/dist/session-manager.js +49 -1
  113. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  114. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  115. package/packages/mcp-server/dist/workflow-tools.js +64 -25
  116. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  117. package/packages/mcp-server/package.json +2 -1
  118. package/packages/mcp-server/src/mcp-server.test.ts +67 -0
  119. package/packages/mcp-server/src/remote-questions.test.ts +294 -0
  120. package/packages/mcp-server/src/remote-questions.ts +916 -0
  121. package/packages/mcp-server/src/server.ts +89 -14
  122. package/packages/mcp-server/src/session-manager.ts +43 -1
  123. package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
  124. package/packages/mcp-server/src/workflow-tools.ts +84 -43
  125. package/packages/mcp-server/tsconfig.test.json +19 -0
  126. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  127. package/packages/pi-ai/dist/models/custom.d.ts +38 -0
  128. package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
  129. package/packages/pi-ai/dist/models/custom.js +41 -0
  130. package/packages/pi-ai/dist/models/custom.js.map +1 -1
  131. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
  132. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  133. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  134. package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
  135. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  136. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/providers/anthropic.js +8 -3
  138. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
  140. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
  141. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
  142. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
  143. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  144. package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
  145. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  146. package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
  147. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/providers/simple-options.js +16 -1
  149. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  150. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
  151. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
  152. package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
  153. package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
  154. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
  155. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
  156. package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
  157. package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
  158. package/packages/pi-ai/src/models/custom.ts +42 -0
  159. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
  160. package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
  161. package/packages/pi-ai/src/providers/anthropic.ts +9 -3
  162. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
  163. package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
  164. package/packages/pi-ai/src/providers/simple-options.ts +17 -1
  165. package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
  166. package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
  167. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  168. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
  169. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -0
  171. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/agent-session.js +7 -0
  173. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  175. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  178. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
  181. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
  183. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
  185. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
  187. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
  188. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
  189. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
  190. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
  191. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
  193. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
  195. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
  197. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
  198. package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
  199. package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
  201. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
  202. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
  203. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
  204. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
  206. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
  208. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  223. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
  224. package/packages/pi-coding-agent/src/core/agent-session.ts +11 -0
  225. package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -0
  226. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  227. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
  228. package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
  229. package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
  230. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
  231. package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
  232. package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
  233. package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
  234. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
  235. package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
  236. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  237. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
  238. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  239. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
  240. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  241. package/scripts/link-workspace-packages.cjs +1 -0
  242. package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
  243. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
  244. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
  245. package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
  246. package/src/resources/extensions/gsd/auto/loop.ts +9 -0
  247. package/src/resources/extensions/gsd/auto/phases.ts +82 -4
  248. package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
  249. package/src/resources/extensions/gsd/auto/session.ts +35 -2
  250. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
  251. package/src/resources/extensions/gsd/auto-model-selection.ts +17 -2
  252. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  253. package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
  254. package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
  255. package/src/resources/extensions/gsd/auto-start.ts +60 -68
  256. package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
  257. package/src/resources/extensions/gsd/auto.ts +73 -28
  258. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
  259. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
  260. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
  261. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  262. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
  263. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
  264. package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
  265. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
  266. package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
  267. package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
  268. package/src/resources/extensions/gsd/error-classifier.ts +10 -3
  269. package/src/resources/extensions/gsd/exec-history.ts +153 -0
  270. package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
  271. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  272. package/src/resources/extensions/gsd/gsd-db.ts +157 -33
  273. package/src/resources/extensions/gsd/guided-flow.ts +222 -1
  274. package/src/resources/extensions/gsd/health-widget.ts +3 -1
  275. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  276. package/src/resources/extensions/gsd/journal.ts +2 -1
  277. package/src/resources/extensions/gsd/key-manager.ts +28 -0
  278. package/src/resources/extensions/gsd/model-router.ts +42 -1
  279. package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
  280. package/src/resources/extensions/gsd/preferences-types.ts +46 -0
  281. package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
  282. package/src/resources/extensions/gsd/preferences.ts +17 -17
  283. package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
  284. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  285. package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
  286. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  287. package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
  288. package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
  289. package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
  290. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
  291. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
  292. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
  293. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
  294. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
  295. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
  296. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
  297. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
  298. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
  299. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
  300. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
  301. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  302. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  303. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
  304. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  305. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
  306. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
  307. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
  308. package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
  309. package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
  310. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
  311. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
  312. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
  313. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  314. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
  315. package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
  316. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
  317. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
  318. package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
  319. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +2 -0
  320. package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
  321. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  322. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
  323. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  324. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
  325. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
  326. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
  327. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
  328. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
  329. package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
  330. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
  331. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
  332. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
  333. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
  334. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
  335. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
  336. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
  337. package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
  338. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
  339. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
  340. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
  341. package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
  342. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
  343. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
  344. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
  345. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
  346. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
  347. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
  348. package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
  349. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
  350. package/src/resources/extensions/gsd/token-counter.ts +22 -5
  351. package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
  352. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
  353. package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
  354. package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
  355. package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
  356. package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
  357. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
  358. package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
  359. package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
  360. package/src/resources/skills/write-docs/SKILL.md +2 -1
  361. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_buildManifest.js +0 -0
  362. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_ssgManifest.js +0 -0
@@ -35,6 +35,12 @@ export interface ProviderInfo {
35
35
  export const PROVIDER_REGISTRY: ProviderInfo[] = [
36
36
  // LLM Providers
37
37
  { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], hasOAuth: true, dashboardUrl: "console.anthropic.com" },
38
+ // Claude Code CLI: routes through the local `claude` binary — no API key,
39
+ // authentication is handled by the CLI's own OAuth flow.
40
+ // Referenced by doctor-providers.ts, auto-model-selection.ts, and others;
41
+ // must be in the canonical registry so all consumers see the same catalog.
42
+ // See: https://github.com/gsd-build/gsd-2/issues/4541
43
+ { id: "claude-code", label: "Claude Code CLI", category: "llm", hasOAuth: true },
38
44
  { id: "openai", label: "OpenAI", category: "llm", envVar: "OPENAI_API_KEY", prefixes: ["sk-"], dashboardUrl: "platform.openai.com/api-keys" },
39
45
  { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", hasOAuth: true },
40
46
  { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)",category: "llm", hasOAuth: true },
@@ -45,6 +51,8 @@ export const PROVIDER_REGISTRY: ProviderInfo[] = [
45
51
  { id: "xai", label: "xAI (Grok)", category: "llm", envVar: "XAI_API_KEY", dashboardUrl: "console.x.ai" },
46
52
  { id: "openrouter", label: "OpenRouter", category: "llm", envVar: "OPENROUTER_API_KEY", dashboardUrl: "openrouter.ai/keys" },
47
53
  { id: "mistral", label: "Mistral", category: "llm", envVar: "MISTRAL_API_KEY", dashboardUrl: "console.mistral.ai" },
54
+ { id: "minimax", label: "MiniMax", category: "llm", envVar: "MINIMAX_API_KEY", dashboardUrl: "platform.minimax.io" },
55
+ { id: "minimax-cn", label: "MiniMax CN", category: "llm", envVar: "MINIMAX_CN_API_KEY", dashboardUrl: "platform.minimax.io" },
48
56
  { id: "ollama-cloud", label: "Ollama Cloud", category: "llm", envVar: "OLLAMA_API_KEY" },
49
57
  { id: "custom-openai", label: "Custom (OpenAI-compat)", category: "llm", envVar: "CUSTOM_OPENAI_API_KEY" },
50
58
  { id: "cerebras", label: "Cerebras", category: "llm", envVar: "CEREBRAS_API_KEY" },
@@ -480,6 +488,26 @@ const TEST_ENDPOINTS: Record<string, { url: string; method?: string; headers?: (
480
488
  url: "https://api.mistral.ai/v1/models",
481
489
  headers: (key) => ({ Authorization: `Bearer ${key}` }),
482
490
  },
491
+ minimax: {
492
+ url: "https://api.minimax.io/anthropic/v1/messages",
493
+ method: "POST",
494
+ headers: (key) => ({
495
+ "x-api-key": key,
496
+ "anthropic-version": "2023-06-01",
497
+ "content-type": "application/json",
498
+ }),
499
+ body: JSON.stringify({ model: "MiniMax-M2.7", max_tokens: 1, messages: [{ role: "user", content: "hi" }] }),
500
+ },
501
+ "minimax-cn": {
502
+ url: "https://api.minimaxi.com/anthropic/v1/messages",
503
+ method: "POST",
504
+ headers: (key) => ({
505
+ "x-api-key": key,
506
+ "anthropic-version": "2023-06-01",
507
+ "content-type": "application/json",
508
+ }),
509
+ body: JSON.stringify({ model: "MiniMax-M2.7", max_tokens: 1, messages: [{ role: "user", content: "hi" }] }),
510
+ },
483
511
  openrouter: {
484
512
  url: "https://openrouter.ai/api/v1/models",
485
513
  headers: (key) => ({ Authorization: `Bearer ${key}` }),
@@ -561,6 +561,23 @@ function bareModelId(modelId: string): string {
561
561
  return modelId.includes("/") ? modelId.split("/").pop()! : modelId;
562
562
  }
563
563
 
564
+ // ─── Provider-specific Tool Limits ─────────────────────────────────────────
565
+
566
+ /**
567
+ * Groq enforces a hard limit of 128 tools per request.
568
+ * Requests exceeding this limit receive a 400 error:
569
+ * "maximum number of items is 128"
570
+ * @see https://console.groq.com/docs/tool-use
571
+ */
572
+ export const GROQ_MAX_TOOLS = 128;
573
+
574
+ /**
575
+ * Provider IDs that map to the Groq API backend.
576
+ * Used to detect Groq at the GSD routing layer where only the provider string
577
+ * is available (the pi-ai openai-completions adapter is shared across providers).
578
+ */
579
+ const GROQ_PROVIDER_IDS = new Set(["groq"]);
580
+
564
581
  // ─── Tool Compatibility Filter (ADR-005 Phase 3) ───────────────────────────
565
582
 
566
583
  /**
@@ -588,10 +605,17 @@ export function isToolCompatibleWithProvider(
588
605
  /**
589
606
  * Filter a list of tool names to only those compatible with a provider.
590
607
  * Used by the routing pipeline to adjust tool sets when switching providers.
608
+ *
609
+ * @param toolNames - The full list of active tool names to filter.
610
+ * @param providerApi - The pi-ai API string (e.g. "openai-completions").
611
+ * @param provider - Optional provider ID (e.g. "groq"). Used to apply
612
+ * provider-specific limits that can't be expressed as API-level capabilities
613
+ * (e.g. Groq's 128-tool hard limit on the shared openai-completions adapter).
591
614
  */
592
615
  export function filterToolsForProvider(
593
616
  toolNames: string[],
594
617
  providerApi: string,
618
+ provider?: string,
595
619
  ): { compatible: string[]; filtered: string[] } {
596
620
  const providerCaps = getProviderCapabilities(providerApi);
597
621
 
@@ -611,6 +635,17 @@ export function filterToolsForProvider(
611
635
  }
612
636
  }
613
637
 
638
+ // Groq enforces a hard limit of 128 tools per request (#4376).
639
+ // Trim the compatible list to GROQ_MAX_TOOLS and move the excess to filtered.
640
+ if (provider && GROQ_PROVIDER_IDS.has(provider) && compatible.length > GROQ_MAX_TOOLS) {
641
+ const trimmed = compatible.splice(GROQ_MAX_TOOLS);
642
+ filtered.push(...trimmed);
643
+ console.warn(
644
+ `[gsd] Groq tool limit: ${compatible.length + trimmed.length} tools active but Groq allows at most ${GROQ_MAX_TOOLS}. ` +
645
+ `Trimming to the first ${GROQ_MAX_TOOLS} tools. Removed: ${trimmed.join(", ")}`,
646
+ );
647
+ }
648
+
614
649
  return { compatible, filtered };
615
650
  }
616
651
 
@@ -620,11 +655,17 @@ export function filterToolsForProvider(
620
655
  *
621
656
  * This is a hard filter only — it removes tools that would fail at the
622
657
  * provider level. It does NOT remove tools based on soft heuristics.
658
+ *
659
+ * @param activeToolNames - The full list of currently active tool names.
660
+ * @param selectedModelApi - The pi-ai API string for the selected model.
661
+ * @param provider - Optional provider ID (e.g. "groq") for provider-specific
662
+ * limits beyond what the API-level capability profile expresses.
623
663
  */
624
664
  export function adjustToolSet(
625
665
  activeToolNames: string[],
626
666
  selectedModelApi: string,
667
+ provider?: string,
627
668
  ): { toolNames: string[]; removedTools: string[] } {
628
- const { compatible, filtered } = filterToolsForProvider(activeToolNames, selectedModelApi);
669
+ const { compatible, filtered } = filterToolsForProvider(activeToolNames, selectedModelApi, provider);
629
670
  return { toolNames: compatible, removedTools: filtered };
630
671
  }
@@ -91,8 +91,13 @@ export function extractPackageReferences(description: string): string[] {
91
91
  }
92
92
  }
93
93
 
94
- // require('pkg') or import from 'pkg' in code blocks
95
- const importPattern = /(?:require\s*\(\s*['"]|from\s+['"])([a-zA-Z0-9@/_-]+)['"\)]/g;
94
+ // require('pkg') or `import ... from 'pkg'` in code blocks.
95
+ // The `from\s+['"]` branch MUST be preceded by an `import` keyword so that
96
+ // natural-language prose like `from "What's Next"` or `from 'master'` does
97
+ // not produce false package-existence failures. Requiring the leading import
98
+ // keyword anchors the match to JavaScript/TypeScript syntax.
99
+ // See: https://github.com/gsd-build/gsd-2/issues/4388
100
+ const importPattern = /(?:require\s*\(\s*['"]|import\b[\s\S]*?\bfrom\s+['"])([a-zA-Z0-9@/_-]+)['"\)]/g;
96
101
  let importMatch: RegExpExecArray | null;
97
102
  while ((importMatch = importPattern.exec(description)) !== null) {
98
103
  // Skip relative imports and node builtins
@@ -323,9 +328,24 @@ function extractPathFromAnnotation(raw: string): string {
323
328
  return backtickMatch[2].trim();
324
329
  }
325
330
 
331
+ // Strip leading/trailing double or single quotes wrapping the whole value.
332
+ // Plan documents sometimes emit `"src/foo.ts"` or `'src/bar.ts'` as input
333
+ // annotations. Stripping the wrapper allows the inner path to be checked
334
+ // correctly instead of producing a false-positive "file not found" error
335
+ // for a literal string with quote characters in it (#3747).
336
+ const quoteMatch = trimmed.match(/^(["'])([^"']+)\1$/);
337
+ if (quoteMatch) {
338
+ return quoteMatch[2].trim();
339
+ }
340
+
326
341
  const annotatedMatch = trimmed.match(/^(.+?)\s+[—–-]\s+.+$/);
327
342
  if (annotatedMatch) {
328
- return annotatedMatch[1].trim();
343
+ const prefix = annotatedMatch[1].trim();
344
+ const prefixBacktickMatch = prefix.match(/`([^`]+)`/);
345
+ if (prefixBacktickMatch && looksLikePathOrUrl(prefixBacktickMatch[1].trim())) {
346
+ return prefixBacktickMatch[1].trim();
347
+ }
348
+ return prefix.replace(/`/g, "").trim();
329
349
  }
330
350
 
331
351
  // Fallback: scan all backticked tokens and return the first one that looks
@@ -388,13 +408,19 @@ function containsGlobPattern(candidate: string): boolean {
388
408
 
389
409
  /**
390
410
  * Build a set of files that will be created by tasks up to (but not including) taskIndex.
411
+ * Also includes outputs of completed tasks at any position — a completed task has already
412
+ * run and its outputs are available regardless of sequence position or disk state (#4071).
391
413
  * All paths are normalized for consistent comparison.
392
414
  */
393
415
  function getExpectedOutputsUpTo(tasks: TaskRow[], taskIndex: number): Set<string> {
394
416
  const outputs = new Set<string>();
395
- for (let i = 0; i < taskIndex; i++) {
396
- for (const file of tasks[i].expected_output) {
397
- outputs.add(normalizeFilePath(file));
417
+ for (let i = 0; i < tasks.length; i++) {
418
+ const task = tasks[i];
419
+ // Include prior tasks (i < taskIndex) OR completed tasks at any position
420
+ if (i < taskIndex || task.status === "completed") {
421
+ for (const file of task.expected_output) {
422
+ outputs.add(normalizeFilePath(file));
423
+ }
398
424
  }
399
425
  }
400
426
  return outputs;
@@ -481,13 +507,19 @@ export function checkTaskOrdering(
481
507
  const results: PreExecutionCheckJSON[] = [];
482
508
 
483
509
  // Build map: normalized file → task index that creates it
484
- const fileCreators = new Map<string, { taskId: string; index: number; originalPath: string }>();
510
+ const fileCreators = new Map<string, { taskId: string; index: number; originalPath: string; completed: boolean }>();
485
511
  for (let i = 0; i < tasks.length; i++) {
486
512
  const task = tasks[i];
487
513
  for (const file of task.expected_output) {
488
514
  const normalizedFile = normalizeFilePath(file);
489
- if (!fileCreators.has(normalizedFile)) {
490
- fileCreators.set(normalizedFile, { taskId: task.id, index: i, originalPath: file });
515
+ const existing = fileCreators.get(normalizedFile);
516
+ if (!existing || (!existing.completed && task.status === "completed")) {
517
+ fileCreators.set(normalizedFile, {
518
+ taskId: task.id,
519
+ index: i,
520
+ originalPath: file,
521
+ completed: task.status === "completed",
522
+ });
491
523
  }
492
524
  }
493
525
  }
@@ -511,7 +543,11 @@ export function checkTaskOrdering(
511
543
  const creator = fileCreators.get(normalizedFile);
512
544
  const absolutePath = resolve(basePath, normalizedFile);
513
545
  const existsOnDisk = existsSync(absolutePath);
514
- if (creator && creator.index > i && !existsOnDisk) {
546
+ // Skip if the creating task has already completed its output is available
547
+ // regardless of disk state (e.g. file was a temp artifact cleaned up after
548
+ // the task ran, or a replan introduced a new earlier-sequence task that
549
+ // reads this pre-execution output). (#4071)
550
+ if (creator && creator.index > i && !existsOnDisk && !creator.completed) {
515
551
  // Task reads file that is created later — impossible ordering
516
552
  results.push({
517
553
  category: "file",
@@ -28,6 +28,37 @@ export interface ContextManagementConfig {
28
28
  compaction_threshold_percent?: number; // default: 0.70, range: 0.5-0.95
29
29
  tool_result_max_chars?: number; // default: 800, range: 200-10000
30
30
  }
31
+
32
+ /**
33
+ * Opt-in tool-output sandboxing for sub-sessions. When enabled, the gsd_exec
34
+ * MCP tool runs scripts in an isolated subprocess and returns only a short
35
+ * digest to the calling agent's context window; full stdout/stderr persist
36
+ * in the project memory store and can be retrieved by id later.
37
+ *
38
+ * Inspired by mksglu/context-mode (Elastic License 2.0). This is an
39
+ * independent implementation — no upstream code is incorporated.
40
+ */
41
+ export interface ContextModeConfig {
42
+ /** Master switch. Default: true (opt-out via `enabled: false`). */
43
+ enabled?: boolean;
44
+ /** Per-invocation timeout in milliseconds. Default: 30_000. Range: 1_000–600_000. */
45
+ exec_timeout_ms?: number;
46
+ /** Cap on persisted stdout bytes per invocation. Default: 1_048_576 (1 MiB). Range: 4_096–16_777_216. */
47
+ exec_stdout_cap_bytes?: number;
48
+ /** Number of trailing stdout characters returned in the digest. Default: 300. Range: 0–4_000. */
49
+ exec_digest_chars?: number;
50
+ /** Environment variables forwarded to sandboxed processes (case-sensitive names). PATH and HOME are always forwarded. */
51
+ exec_env_allowlist?: string[];
52
+ }
53
+
54
+ /**
55
+ * Resolve whether context-mode features (gsd_exec sandbox + compaction
56
+ * snapshot) should be active. Default is ON: missing config or missing
57
+ * `enabled` is treated as true. Only `enabled: false` disables.
58
+ */
59
+ export function isContextModeEnabled(prefs: { context_mode?: ContextModeConfig } | null | undefined): boolean {
60
+ return prefs?.context_mode?.enabled !== false;
61
+ }
31
62
  import type { GitHubSyncConfig } from "../github-sync/types.js";
32
63
 
33
64
  // ─── Workflow Modes ──────────────────────────────────────────────────────────
@@ -117,6 +148,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
117
148
  "flat_rate_providers",
118
149
  "language",
119
150
  "context_window_override",
151
+ "context_mode",
120
152
  ]);
121
153
 
122
154
  /** Canonical list of all dispatch unit types. */
@@ -300,6 +332,12 @@ export interface GSDPreferences {
300
332
  */
301
333
  context_window_override?: number;
302
334
  context_management?: ContextManagementConfig;
335
+ /**
336
+ * Tool-output sandboxing via gsd_exec. Keeps sub-session context windows
337
+ * clean by running scripts in a subprocess and only surfacing a short
338
+ * digest. See `ContextModeConfig`. Default: disabled.
339
+ */
340
+ context_mode?: ContextModeConfig;
303
341
  token_profile?: TokenProfile;
304
342
  phases?: PhaseSkipPreferences;
305
343
  auto_visualize?: boolean;
@@ -354,6 +392,14 @@ export interface GSDPreferences {
354
392
  checkpoints?: boolean;
355
393
  auto_rollback?: boolean;
356
394
  timeout_scale_cap?: number;
395
+ /**
396
+ * Glob patterns for files that are always expected side-effects of any task.
397
+ * Files matching any pattern here are excluded from unexpected-change warnings.
398
+ * Supports standard glob syntax (e.g. `tracking/history/**`, `*.log`).
399
+ * Fixes #4385/#4436 — audit-trail snapshots, build artifacts, and other
400
+ * project-level secondary writes shouldn't require per-task declaration.
401
+ */
402
+ file_change_allowlist?: string[];
357
403
  };
358
404
 
359
405
 
@@ -644,6 +644,50 @@ export function validatePreferences(preferences: GSDPreferences): {
644
644
  }
645
645
  }
646
646
 
647
+ // ─── Context Mode (gsd_exec sandbox) ────────────────────────────────────
648
+ if (preferences.context_mode !== undefined) {
649
+ if (typeof preferences.context_mode === "object" && preferences.context_mode !== null) {
650
+ const cmode = preferences.context_mode as unknown as Record<string, unknown>;
651
+ const validCmode: Record<string, unknown> = {};
652
+
653
+ if (cmode.enabled !== undefined) {
654
+ if (typeof cmode.enabled === "boolean") validCmode.enabled = cmode.enabled;
655
+ else errors.push("context_mode.enabled must be a boolean");
656
+ }
657
+ if (cmode.exec_timeout_ms !== undefined) {
658
+ const t = cmode.exec_timeout_ms;
659
+ if (typeof t === "number" && t >= 1000 && t <= 600_000) validCmode.exec_timeout_ms = Math.floor(t);
660
+ else errors.push("context_mode.exec_timeout_ms must be a number between 1000 and 600000");
661
+ }
662
+ if (cmode.exec_stdout_cap_bytes !== undefined) {
663
+ const b = cmode.exec_stdout_cap_bytes;
664
+ if (typeof b === "number" && b >= 4096 && b <= 16_777_216) validCmode.exec_stdout_cap_bytes = Math.floor(b);
665
+ else errors.push("context_mode.exec_stdout_cap_bytes must be a number between 4096 and 16777216");
666
+ }
667
+ if (cmode.exec_digest_chars !== undefined) {
668
+ const c = cmode.exec_digest_chars;
669
+ if (typeof c === "number" && c >= 0 && c <= 4000) validCmode.exec_digest_chars = Math.floor(c);
670
+ else errors.push("context_mode.exec_digest_chars must be a number between 0 and 4000");
671
+ }
672
+ if (cmode.exec_env_allowlist !== undefined) {
673
+ if (
674
+ Array.isArray(cmode.exec_env_allowlist) &&
675
+ cmode.exec_env_allowlist.every((v) => typeof v === "string" && /^[A-Z_][A-Z0-9_]*$/i.test(v))
676
+ ) {
677
+ validCmode.exec_env_allowlist = cmode.exec_env_allowlist;
678
+ } else {
679
+ errors.push("context_mode.exec_env_allowlist must be an array of valid env var names");
680
+ }
681
+ }
682
+
683
+ if (Object.keys(validCmode).length > 0) {
684
+ validated.context_mode = validCmode as any;
685
+ }
686
+ } else {
687
+ errors.push("context_mode must be an object");
688
+ }
689
+ }
690
+
647
691
  // ─── Parallel Config ────────────────────────────────────────────────────
648
692
  if (preferences.parallel && typeof preferences.parallel === "object") {
649
693
  const p = preferences.parallel as unknown as Record<string, unknown>;
@@ -697,6 +741,41 @@ export function validatePreferences(preferences: GSDPreferences): {
697
741
  }
698
742
  }
699
743
 
744
+ // ─── Slice Parallel Config ───────────────────────────────────────────────
745
+ if (preferences.slice_parallel !== undefined) {
746
+ if (typeof preferences.slice_parallel === "object" && preferences.slice_parallel !== null) {
747
+ const sp = preferences.slice_parallel as Record<string, unknown>;
748
+ const validSp: NonNullable<GSDPreferences["slice_parallel"]> = {};
749
+
750
+ if (sp.enabled !== undefined) {
751
+ if (typeof sp.enabled === "boolean") validSp.enabled = sp.enabled;
752
+ else errors.push("slice_parallel.enabled must be a boolean");
753
+ }
754
+
755
+ if (sp.max_workers !== undefined) {
756
+ const maxWorkers = typeof sp.max_workers === "number" ? sp.max_workers : Number(sp.max_workers);
757
+ if (Number.isFinite(maxWorkers) && maxWorkers >= 1 && maxWorkers <= 8) {
758
+ validSp.max_workers = Math.floor(maxWorkers);
759
+ } else {
760
+ errors.push("slice_parallel.max_workers must be a number between 1 and 8");
761
+ }
762
+ }
763
+
764
+ const knownSliceParallelKeys = new Set(["enabled", "max_workers"]);
765
+ for (const key of Object.keys(sp)) {
766
+ if (!knownSliceParallelKeys.has(key)) {
767
+ warnings.push(`unknown slice_parallel key "${key}" — ignored`);
768
+ }
769
+ }
770
+
771
+ if (Object.keys(validSp).length > 0) {
772
+ validated.slice_parallel = validSp;
773
+ }
774
+ } else {
775
+ errors.push("slice_parallel must be an object");
776
+ }
777
+ }
778
+
700
779
  // ─── Reactive Execution ─────────────────────────────────────────────────
701
780
  if (preferences.reactive_execution !== undefined) {
702
781
  if (typeof preferences.reactive_execution === "object" && preferences.reactive_execution !== null) {
@@ -68,13 +68,13 @@ export { resolveAllSkillReferences } from "./preferences-skills.js";
68
68
  // These lived in preferences-skills.ts but imported loadEffectiveGSDPreferences
69
69
  // back from this file, creating a circular dependency. Moved here since they
70
70
  // are trivial wrappers over loadEffectiveGSDPreferences.
71
- export function resolveSkillDiscoveryMode(): SkillDiscoveryMode {
72
- const prefs = loadEffectiveGSDPreferences();
71
+ export function resolveSkillDiscoveryMode(basePath?: string): SkillDiscoveryMode {
72
+ const prefs = loadEffectiveGSDPreferences(basePath);
73
73
  return prefs?.preferences.skill_discovery ?? "suggest";
74
74
  }
75
75
 
76
- export function resolveSkillStalenessDays(): number {
77
- const prefs = loadEffectiveGSDPreferences();
76
+ export function resolveSkillStalenessDays(basePath?: string): number {
77
+ const prefs = loadEffectiveGSDPreferences(basePath);
78
78
  return prefs?.preferences.skill_staleness_days ?? 60;
79
79
  }
80
80
 
@@ -109,16 +109,16 @@ function legacyGlobalPreferencesPath(): string {
109
109
  return join(homedir(), ".pi", "agent", "gsd-preferences.md");
110
110
  }
111
111
 
112
- function projectPreferencesPath(): string {
113
- return join(gsdRoot(process.cwd()), "PREFERENCES.md");
112
+ function projectPreferencesPath(basePath: string = process.cwd()): string {
113
+ return join(gsdRoot(basePath), "PREFERENCES.md");
114
114
  }
115
115
  // Legacy lowercase files can still exist in older projects. Keep them as a
116
116
  // compatibility-only fallback, but route new reads/writes through PREFERENCES.md.
117
117
  function legacyGlobalPreferencesPathLowercase(): string {
118
118
  return join(gsdHome(), "preferences.md");
119
119
  }
120
- function legacyProjectPreferencesPathLowercase(): string {
121
- return join(gsdRoot(process.cwd()), "preferences.md");
120
+ function legacyProjectPreferencesPathLowercase(basePath: string = process.cwd()): string {
121
+ return join(gsdRoot(basePath), "preferences.md");
122
122
  }
123
123
 
124
124
  export function getGlobalGSDPreferencesPath(): string {
@@ -129,8 +129,8 @@ export function getLegacyGlobalGSDPreferencesPath(): string {
129
129
  return legacyGlobalPreferencesPath();
130
130
  }
131
131
 
132
- export function getProjectGSDPreferencesPath(): string {
133
- return projectPreferencesPath();
132
+ export function getProjectGSDPreferencesPath(basePath?: string): string {
133
+ return projectPreferencesPath(basePath);
134
134
  }
135
135
 
136
136
  // ─── Loading ────────────────────────────────────────────────────────────────
@@ -141,14 +141,14 @@ export function loadGlobalGSDPreferences(): LoadedGSDPreferences | null {
141
141
  ?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global");
142
142
  }
143
143
 
144
- export function loadProjectGSDPreferences(): LoadedGSDPreferences | null {
145
- return loadPreferencesFile(projectPreferencesPath(), "project")
146
- ?? loadPreferencesFile(legacyProjectPreferencesPathLowercase(), "project");
144
+ export function loadProjectGSDPreferences(basePath?: string): LoadedGSDPreferences | null {
145
+ return loadPreferencesFile(projectPreferencesPath(basePath), "project")
146
+ ?? loadPreferencesFile(legacyProjectPreferencesPathLowercase(basePath), "project");
147
147
  }
148
148
 
149
- export function loadEffectiveGSDPreferences(): LoadedGSDPreferences | null {
149
+ export function loadEffectiveGSDPreferences(basePath?: string): LoadedGSDPreferences | null {
150
150
  const globalPreferences = loadGlobalGSDPreferences();
151
- const projectPreferences = loadProjectGSDPreferences();
151
+ const projectPreferences = loadProjectGSDPreferences(basePath);
152
152
 
153
153
  if (!globalPreferences && !projectPreferences) return null;
154
154
 
@@ -603,8 +603,8 @@ export function resolvePreDispatchHooks(): PreDispatchHookConfig[] {
603
603
  * Worktree isolation requires explicit opt-in because it depends on git
604
604
  * branch infrastructure that must be set up before use.
605
605
  */
606
- export function getIsolationMode(): "none" | "worktree" | "branch" {
607
- const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
606
+ export function getIsolationMode(basePath?: string): "none" | "worktree" | "branch" {
607
+ const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
608
608
  if (prefs?.isolation === "worktree") return "worktree";
609
609
  if (prefs?.isolation === "branch") return "branch";
610
610
  return "none"; // default — no isolation, work on current branch
@@ -24,6 +24,35 @@ import { fileURLToPath } from "node:url";
24
24
  import { homedir } from "node:os";
25
25
  import { logWarning } from "./workflow-logger.js";
26
26
 
27
+ type ExistsFn = (path: string) => boolean;
28
+
29
+ function hasRequiredExtensionAssets(rootDir: string, exists: ExistsFn = existsSync): boolean {
30
+ return (
31
+ exists(join(rootDir, "prompts")) &&
32
+ exists(join(rootDir, "templates", "task-summary.md"))
33
+ );
34
+ }
35
+
36
+ export function resolveExtensionDirFromCandidates(
37
+ moduleDir: string,
38
+ agentGsdDir: string,
39
+ exists: ExistsFn = existsSync,
40
+ ): string {
41
+ const moduleUsable = hasRequiredExtensionAssets(moduleDir, exists);
42
+ const agentUsable = hasRequiredExtensionAssets(agentGsdDir, exists);
43
+
44
+ // Prefer the user-local extension tree when both are valid. This avoids
45
+ // leaking npm/global-install paths into prompts on Windows.
46
+ if (agentUsable) return agentGsdDir;
47
+ if (moduleUsable) return moduleDir;
48
+
49
+ // Degraded fallback: if required template is missing in both locations,
50
+ // keep previous behavior and prefer whichever still has prompts/.
51
+ if (exists(join(moduleDir, "prompts"))) return moduleDir;
52
+ if (exists(join(agentGsdDir, "prompts"))) return agentGsdDir;
53
+ return moduleDir;
54
+ }
55
+
27
56
  /**
28
57
  * Resolve the GSD extension directory.
29
58
  *
@@ -36,15 +65,9 @@ import { logWarning } from "./workflow-logger.js";
36
65
  */
37
66
  function resolveExtensionDir(): string {
38
67
  const moduleDir = dirname(fileURLToPath(import.meta.url));
39
- if (existsSync(join(moduleDir, "prompts"))) return moduleDir;
40
-
41
- // Fallback: user-local agent directory
42
68
  const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
43
69
  const agentGsdDir = join(gsdHome, "agent", "extensions", "gsd");
44
- if (existsSync(join(agentGsdDir, "prompts"))) return agentGsdDir;
45
-
46
- // Last resort: return the module dir (warmCache will silently handle the miss)
47
- return moduleDir;
70
+ return resolveExtensionDirFromCandidates(moduleDir, agentGsdDir);
48
71
  }
49
72
 
50
73
  const __extensionDir = resolveExtensionDir();
@@ -162,6 +162,10 @@ Preserve the specification's exact terminology, emphasis, and specific framing.
162
162
  6. For each architectural or pattern decision, call `gsd_decision_save` — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
163
163
  7. {{commitInstruction}}
164
164
 
165
+ ### Ready-phrase pre-condition (NON-BYPASSABLE)
166
+
167
+ Before emitting the ready phrase, verify in the CURRENT turn that you have written `.gsd/PROJECT.md`, `.gsd/REQUIREMENTS.md`, `{{contextPath}}`, and called `gsd_plan_milestone`. If any is missing, **STOP** — emit the missing tool calls in this same turn. The system rejects premature ready signals and retries are capped.
168
+
165
169
  After writing the files, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
166
170
 
167
171
  ### Multi-Milestone
@@ -234,6 +238,10 @@ For single-milestone projects, do NOT write this file.
234
238
 
235
239
  7. {{multiMilestoneCommitInstruction}}
236
240
 
241
+ ### Ready-phrase pre-condition (NON-BYPASSABLE)
242
+
243
+ Before emitting the ready phrase, verify in the CURRENT turn that you have written `.gsd/PROJECT.md`, `.gsd/REQUIREMENTS.md`, the primary `CONTEXT.md`, called `gsd_plan_milestone` for the primary milestone, and written `.gsd/DISCUSSION-MANIFEST.json` with `gates_completed === total`. If any is missing, **STOP** — emit the missing tool calls in this same turn. The system rejects premature ready signals and retries are capped.
244
+
237
245
  After writing the files, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
238
246
 
239
247
  ## Critical Rules
@@ -339,7 +339,20 @@ These sections are in addition to whatever other context the discussion surfaced
339
339
  6. For each architectural or pattern decision made during discussion, call `gsd_decision_save` — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
340
340
  7. {{commitInstruction}}
341
341
 
342
- After writing the files, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
342
+ ### Ready-phrase pre-condition (NON-BYPASSABLE)
343
+
344
+ Before emitting the ready phrase, verify in the CURRENT turn that you have:
345
+
346
+ - [ ] Written `.gsd/PROJECT.md` (step 2)
347
+ - [ ] Written `.gsd/REQUIREMENTS.md` (step 3)
348
+ - [ ] Written `{{contextPath}}` (step 4)
349
+ - [ ] Called `gsd_plan_milestone` (step 5)
350
+
351
+ If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missing tool calls in this same turn. The system detects missing artifacts and will reject premature ready signals — you will be asked again and retries are capped.
352
+
353
+ Do not announce the ready phrase as something you are "about to" do. Do not narrate "now writing the files" as a substitute for actually writing them. The ready phrase is a post-write signal, not an intent signal.
354
+
355
+ After completing steps 1–7 above, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
343
356
 
344
357
  ### Multi-Milestone
345
358
 
@@ -418,6 +431,20 @@ For single-milestone projects, do NOT write this file — it is only for multi-m
418
431
 
419
432
  7. {{multiMilestoneCommitInstruction}}
420
433
 
421
- After writing the files, say exactly: "Milestone M001 ready." — nothing else. Auto-mode will start automatically.
434
+ ### Ready-phrase pre-condition (NON-BYPASSABLE)
435
+
436
+ Before emitting the ready phrase, verify in the CURRENT turn that you have:
437
+
438
+ - [ ] Written `.gsd/PROJECT.md` (Phase 1)
439
+ - [ ] Written `.gsd/REQUIREMENTS.md` (Phase 1)
440
+ - [ ] Written primary-milestone `CONTEXT.md` (Phase 2)
441
+ - [ ] Called `gsd_plan_milestone` for the primary milestone (Phase 2)
442
+ - [ ] Written `.gsd/DISCUSSION-MANIFEST.json` with `gates_completed === total` (Phase 3)
443
+
444
+ If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missing tool calls in this same turn. The system detects missing artifacts and will reject premature ready signals — you will be asked again and retries are capped.
445
+
446
+ Do not announce the ready phrase as something you are "about to" do. Do not narrate "now writing the files" as a substitute for actually writing them. The ready phrase is a post-write signal, not an intent signal.
447
+
448
+ After completing all phases above, say exactly: "Milestone M001 ready." — nothing else. Auto-mode will start automatically.
422
449
 
423
450
  {{inlinedTemplates}}
@@ -15,8 +15,11 @@ Dispatch ALL slices simultaneously using the `subagent` tool in **parallel mode*
15
15
  1. Call `subagent` with `tasks: [...]` containing one entry per slice below
16
16
  2. Wait for ALL subagents to complete
17
17
  3. Verify each slice's RESEARCH file was written (check the `.gsd/{{mid}}/` directory)
18
- 4. If any subagent failed to write its RESEARCH file, re-run it individually
19
- 5. Report which slices completed research and which (if any) failed
18
+ 4. If a subagent failed to write its RESEARCH file, retry it **once** individually
19
+ 5. If it fails a second time, write a partial RESEARCH file for that slice with a `## BLOCKER` section explaining the failure — do NOT retry again
20
+ 6. Report which slices completed research and which (if any) needed a blocker note
21
+
22
+ **Important**: Each failed slice gets exactly one retry. After that, write the blocker and move on. Never retry the same slice more than once.
20
23
 
21
24
  ## Subagent Prompts
22
25