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
@@ -69,7 +69,7 @@ function runPlanV2Gate(ctx, basePath, state) {
69
69
  const compiled = ensurePlanV2Graph(basePath, state);
70
70
  if (!compiled.ok) {
71
71
  const reason = compiled.reason ?? "plan-v2 compilation failed";
72
- ctx.ui.notify(`Plan gate failed-closed: ${reason}. Complete plan/discuss artifacts before execution.`, "error");
72
+ ctx.ui.notify(`Plan gate failed-closed: ${reason}. Complete plan/discuss artifacts before execution.\n\nIf this keeps happening, try: /gsd doctor heal`, "error");
73
73
  return false;
74
74
  }
75
75
  return true;
@@ -79,6 +79,13 @@ function runPlanV2Gate(ctx, basePath, state) {
79
79
  function buildDocsCommitInstruction(_message) {
80
80
  return "Do not commit planning artifacts — .gsd/ is managed externally.";
81
81
  }
82
+ // #4573: cap for how many times we nudge the LLM after a premature ready
83
+ // phrase before giving up and asking the user to re-run /gsd.
84
+ const MAX_READY_REJECTS = 2;
85
+ // #4573: matches the canonical ready phrase the discuss prompt asks the LLM
86
+ // to emit. Accepts any M-prefixed milestone ID (three digits + optional
87
+ // suffix) with optional trailing punctuation.
88
+ const READY_PHRASE_RE = /\bMilestone\s+M\d{3}[A-Z0-9-]*\s+ready\.?/i;
82
89
  const pendingAutoStartMap = new Map();
83
90
  /**
84
91
  * Backward-compat bridge: returns a mutable reference to the entry matching
@@ -225,6 +232,188 @@ export function checkAutoStartAfterDiscuss() {
225
232
  startAutoDetached(ctx, pi, basePath, false, { step });
226
233
  return true;
227
234
  }
235
+ /**
236
+ * Extract the concatenated text content from an assistant message, whether it
237
+ * stores content as a string or as an array of text blocks.
238
+ */
239
+ function extractAssistantText(msg) {
240
+ if (!msg)
241
+ return "";
242
+ const content = msg.content;
243
+ if (typeof content === "string")
244
+ return content;
245
+ if (!Array.isArray(content))
246
+ return "";
247
+ const parts = [];
248
+ for (const block of content) {
249
+ if (!block || typeof block !== "object")
250
+ continue;
251
+ if (block.type === "text" && typeof block.text === "string")
252
+ parts.push(block.text);
253
+ }
254
+ return parts.join("\n");
255
+ }
256
+ /**
257
+ * Return true if the assistant message contains any tool-use block.
258
+ */
259
+ function hasToolUse(msg) {
260
+ if (!msg)
261
+ return false;
262
+ const content = msg.content;
263
+ if (!Array.isArray(content))
264
+ return false;
265
+ return content.some((b) => b && typeof b === "object" && (b.type === "tool_use" || b.type === "tool-use"));
266
+ }
267
+ /**
268
+ * #4573 — Detect and recover from the "ready phrase without files" failure mode.
269
+ *
270
+ * When the LLM emits "Milestone {{id}} ready." but has not written CONTEXT.md
271
+ * or ROADMAP.md, `checkAutoStartAfterDiscuss()` silently returns false and the
272
+ * next /gsd invocation loops into the "All milestones complete" warning.
273
+ *
274
+ * This function, called from `handleAgentEnd` after `checkAutoStartAfterDiscuss`
275
+ * returns false, pattern-matches the ready phrase on the last assistant message.
276
+ * If it fired AND neither CONTEXT.md nor ROADMAP.md exists, it:
277
+ * 1. Notifies the user that the signal was rejected.
278
+ * 2. Injects a system message via `pi.sendMessage(..., {triggerTurn:true})`
279
+ * telling the LLM the signal was premature and to emit the writes now.
280
+ * 3. Caps at `MAX_READY_REJECTS` per-entry; beyond that, gives up and asks
281
+ * the user to re-run /gsd.
282
+ *
283
+ * Returns true when a nudge (or give-up) was emitted, signaling the caller to
284
+ * skip `resolveAgentEnd`.
285
+ */
286
+ export function maybeHandleReadyPhraseWithoutFiles(event) {
287
+ const entry = _getPendingAutoStart();
288
+ if (!entry)
289
+ return false;
290
+ const { ctx, pi, basePath, milestoneId } = entry;
291
+ // Gate: last assistant message must contain the ready phrase
292
+ const lastMsg = event.messages[event.messages.length - 1];
293
+ const text = extractAssistantText(lastMsg);
294
+ if (!READY_PHRASE_RE.test(text))
295
+ return false;
296
+ // Gate: artifacts must still be missing — if they exist, the happy path
297
+ // already fired and we have nothing to do.
298
+ const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
299
+ const roadmapFile = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
300
+ if (contextFile || roadmapFile)
301
+ return false;
302
+ entry.readyRejectCount = (entry.readyRejectCount ?? 0) + 1;
303
+ if (entry.readyRejectCount > MAX_READY_REJECTS) {
304
+ // Give up: clear state and tell the user to re-run /gsd. Avoids an
305
+ // infinite nudge loop when the LLM never produces the writes.
306
+ pendingAutoStartMap.delete(basePath);
307
+ ctx.ui.notify(`Milestone ${milestoneId}: LLM signaled "ready" ${entry.readyRejectCount} times without writing files. ` +
308
+ `Stopping auto-nudge. Run /gsd to try again.`, "error");
309
+ return true;
310
+ }
311
+ ctx.ui.notify(`Milestone ${milestoneId}: "ready" signal rejected — CONTEXT.md and ROADMAP.md are missing. Asking the LLM to complete the writes.`, "warning");
312
+ const nudge = `You emitted "Milestone ${milestoneId} ready." but neither ` +
313
+ `.gsd/milestones/${milestoneId}/${milestoneId}-CONTEXT.md nor ` +
314
+ `.gsd/milestones/${milestoneId}/${milestoneId}-ROADMAP.md exists on disk. ` +
315
+ `The ready phrase is a POST-WRITE signal and has been rejected. ` +
316
+ `In this turn: (1) write PROJECT.md, REQUIREMENTS.md, and the milestone ` +
317
+ `CONTEXT.md, (2) call gsd_plan_milestone, then (3) emit the ready phrase. ` +
318
+ `Do not describe these steps — execute them as tool calls. ` +
319
+ `This is retry ${entry.readyRejectCount}/${MAX_READY_REJECTS}; further ` +
320
+ `premature signals will clear the session.`;
321
+ try {
322
+ pi.sendMessage({ customType: "gsd-ready-no-files", content: nudge, display: false }, { triggerTurn: true });
323
+ }
324
+ catch (e) {
325
+ logWarning("guided", `ready-phrase nudge sendMessage failed: ${e.message}`);
326
+ return false;
327
+ }
328
+ return true;
329
+ }
330
+ /**
331
+ * #4573 — Detect and recover from the "announces tool, never calls it" stall.
332
+ *
333
+ * The LLM emits text like "I'll now write the CONTEXT.md file" but the turn
334
+ * ends with zero tool-use blocks. The harness has no post-turn tool-call
335
+ * validation, so the unit promise resolves and the user sees a stalled state.
336
+ *
337
+ * This function, called from `handleAgentEnd`, inspects the last assistant
338
+ * message. If ALL of the following are true, it injects a recovery message:
339
+ * - Text-only (no tool-use blocks)
340
+ * - Contains a commit-intent phrase ("I'll write", "I'll call", etc.)
341
+ * - Auto-mode is active OR a discussion autostart is pending
342
+ * - `emptyTurnRetryCount` is under the cap
343
+ *
344
+ * Per-handler state is held on the `PendingAutoStartEntry` when present, and
345
+ * on a module-level map otherwise. The counter resets on any successful
346
+ * tool-use turn via `resetEmptyTurnCounter`.
347
+ */
348
+ const emptyTurnCounterByBase = new Map();
349
+ const MAX_EMPTY_TURN_RETRIES = 2;
350
+ // Phrases that indicate the LLM is about to do something but has not yet.
351
+ // Kept tight to avoid flagging legitimate narration like "I'll wait for your answer."
352
+ const COMMIT_INTENT_RE = /\b(?:I['’]ll|I will|Next,? I['’]ll|Now I['’]ll|Let me|I['’]m going to|I am going to)\s+(?:now\s+)?(?:write|create|call|invoke|update|add|make|run|execute|generate|produce|emit|compose|implement|save|apply|commit)\b/i;
353
+ /**
354
+ * Reset the empty-turn counter for a basePath after a successful tool-use turn.
355
+ * Called from handleAgentEnd when the last message contains tool_use blocks.
356
+ */
357
+ export function resetEmptyTurnCounter(basePath) {
358
+ if (basePath)
359
+ emptyTurnCounterByBase.delete(basePath);
360
+ else
361
+ emptyTurnCounterByBase.clear();
362
+ }
363
+ export function maybeHandleEmptyIntentTurn(event, isAuto) {
364
+ // Gate: only fire when there is system-driven work in flight. Interactive
365
+ // /gsd discuss (user-driven) produces legitimate text-only turns.
366
+ if (!isAuto && pendingAutoStartMap.size === 0)
367
+ return false;
368
+ const lastMsg = event.messages[event.messages.length - 1];
369
+ if (!lastMsg)
370
+ return false;
371
+ if (hasToolUse(lastMsg))
372
+ return false;
373
+ const text = extractAssistantText(lastMsg).trim();
374
+ if (!text)
375
+ return false;
376
+ // Skip if the LLM is emitting the ready phrase — that is the ready-no-files
377
+ // path, handled by maybeHandleReadyPhraseWithoutFiles.
378
+ if (READY_PHRASE_RE.test(text))
379
+ return false;
380
+ // Skip if the LLM is clearly handing back to the user. Keep the heuristic
381
+ // tight: a trailing question mark on the last non-empty line is the common
382
+ // signal for "I asked the user a question and stopped."
383
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
384
+ const lastLine = lines[lines.length - 1] ?? "";
385
+ if (lastLine.endsWith("?"))
386
+ return false;
387
+ // Must contain a commit-intent phrase — this is the stall we care about.
388
+ if (!COMMIT_INTENT_RE.test(text))
389
+ return false;
390
+ // Resolve the target basePath + pi for injection. Prefer the pending
391
+ // autostart entry (discuss flow); otherwise we cannot inject.
392
+ const entry = _getPendingAutoStart();
393
+ if (!entry)
394
+ return false;
395
+ const { ctx, pi, basePath } = entry;
396
+ const count = (emptyTurnCounterByBase.get(basePath) ?? 0) + 1;
397
+ emptyTurnCounterByBase.set(basePath, count);
398
+ if (count > MAX_EMPTY_TURN_RETRIES) {
399
+ ctx.ui.notify(`Empty-turn recovery: LLM announced intent ${count} times without calling any tool. ` +
400
+ `Stopping auto-nudge.`, "error");
401
+ return false; // let the normal flow resolve/pause the unit
402
+ }
403
+ ctx.ui.notify(`Empty-turn detected: LLM announced intent but called no tool. Prompting it to execute.`, "info");
404
+ const nudge = `Your last turn announced an action (e.g. "I'll write…" or "Let me call…") ` +
405
+ `but contained no tool call. The system records zero tool-use blocks for ` +
406
+ `that turn. Execute the announced action NOW as a tool call in this turn. ` +
407
+ `Do not describe it again. Retry ${count}/${MAX_EMPTY_TURN_RETRIES}.`;
408
+ try {
409
+ pi.sendMessage({ customType: "gsd-empty-turn-recovery", content: nudge, display: false }, { triggerTurn: true });
410
+ }
411
+ catch (e) {
412
+ logWarning("guided", `empty-turn nudge sendMessage failed: ${e.message}`);
413
+ return false;
414
+ }
415
+ return true;
416
+ }
228
417
  /**
229
418
  * Extract milestone IDs from PROJECT.md milestone sequence table.
230
419
  * Looks for rows like "| M001 | Name | Status |" and extracts the ID column.
@@ -91,6 +91,7 @@ export function initHealthWidget(ctx) {
91
91
  let data = initialData;
92
92
  let cachedLines;
93
93
  let refreshInFlight = false;
94
+ let isDisposed = false;
94
95
  const refresh = async () => {
95
96
  if (refreshInFlight)
96
97
  return;
@@ -98,7 +99,8 @@ export function initHealthWidget(ctx) {
98
99
  try {
99
100
  data = loadHealthWidgetData(basePath);
100
101
  cachedLines = undefined;
101
- _tui.requestRender();
102
+ if (!isDisposed)
103
+ _tui.requestRender();
102
104
  }
103
105
  catch { /* non-fatal */ }
104
106
  finally {
@@ -122,6 +124,7 @@ export function initHealthWidget(ctx) {
122
124
  },
123
125
  invalidate() { cachedLines = undefined; cachedWidth = undefined; },
124
126
  dispose() {
127
+ isDisposed = true;
125
128
  clearInterval(refreshTimer);
126
129
  },
127
130
  };
@@ -0,0 +1,108 @@
1
+ // GSD Extension — Layer 2 Event Emitter Bridge
2
+ //
3
+ // Holds a module-scoped reference to the ExtensionAPI so deeply-nested code
4
+ // (auto-loop, git-service callers, verification, budget) can emit Layer 2
5
+ // events without having to thread `pi` through every function signature.
6
+ //
7
+ // Set once from `registerGsdExtension`. All emitters are best-effort — a
8
+ // missing `pi` (e.g. in standalone unit tests) silently becomes a no-op.
9
+ let _pi;
10
+ export function setHookEmitter(pi) {
11
+ _pi = pi;
12
+ }
13
+ export function clearHookEmitter() {
14
+ _pi = undefined;
15
+ }
16
+ // ─── Notification ──────────────────────────────────────────────────────────
17
+ export async function emitNotification(kind, message, details) {
18
+ if (!_pi)
19
+ return;
20
+ await _pi.emitExtensionEvent({ type: "notification", kind, message, details });
21
+ }
22
+ // ─── Git Lifecycle ─────────────────────────────────────────────────────────
23
+ export async function emitBeforeCommit(args) {
24
+ if (!_pi)
25
+ return undefined;
26
+ return (await _pi.emitExtensionEvent({
27
+ type: "before_commit",
28
+ ...args,
29
+ }));
30
+ }
31
+ export async function emitCommit(args) {
32
+ if (!_pi)
33
+ return;
34
+ await _pi.emitExtensionEvent({ type: "commit", ...args });
35
+ }
36
+ export async function emitBeforePush(args) {
37
+ if (!_pi)
38
+ return undefined;
39
+ return (await _pi.emitExtensionEvent({
40
+ type: "before_push",
41
+ ...args,
42
+ }));
43
+ }
44
+ export async function emitPush(args) {
45
+ if (!_pi)
46
+ return;
47
+ await _pi.emitExtensionEvent({ type: "push", ...args });
48
+ }
49
+ export async function emitBeforePr(args) {
50
+ if (!_pi)
51
+ return undefined;
52
+ return (await _pi.emitExtensionEvent({
53
+ type: "before_pr",
54
+ ...args,
55
+ }));
56
+ }
57
+ export async function emitPrOpened(args) {
58
+ if (!_pi)
59
+ return;
60
+ await _pi.emitExtensionEvent({ type: "pr_opened", ...args });
61
+ }
62
+ // ─── Verification ──────────────────────────────────────────────────────────
63
+ export async function emitBeforeVerify(args) {
64
+ if (!_pi)
65
+ return undefined;
66
+ return (await _pi.emitExtensionEvent({
67
+ type: "before_verify",
68
+ ...args,
69
+ }));
70
+ }
71
+ export async function emitVerifyResult(args) {
72
+ if (!_pi)
73
+ return;
74
+ await _pi.emitExtensionEvent({ type: "verify_result", ...args });
75
+ }
76
+ // ─── Budget ────────────────────────────────────────────────────────────────
77
+ export async function emitBudgetThreshold(args) {
78
+ if (!_pi)
79
+ return undefined;
80
+ return (await _pi.emitExtensionEvent({
81
+ type: "budget_threshold",
82
+ fraction: args.fraction,
83
+ spent: args.spent,
84
+ limit: args.limit,
85
+ currency: "USD",
86
+ }));
87
+ }
88
+ // ─── Orchestrator Boundaries ───────────────────────────────────────────────
89
+ export async function emitMilestoneStart(args) {
90
+ if (!_pi)
91
+ return;
92
+ await _pi.emitExtensionEvent({ type: "milestone_start", ...args });
93
+ }
94
+ export async function emitMilestoneEnd(args) {
95
+ if (!_pi)
96
+ return;
97
+ await _pi.emitExtensionEvent({ type: "milestone_end", ...args });
98
+ }
99
+ export async function emitUnitStart(args) {
100
+ if (!_pi)
101
+ return;
102
+ await _pi.emitExtensionEvent({ type: "unit_start", ...args });
103
+ }
104
+ export async function emitUnitEnd(args) {
105
+ if (!_pi)
106
+ return;
107
+ await _pi.emitExtensionEvent({ type: "unit_end", ...args });
108
+ }
@@ -8,7 +8,7 @@
8
8
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  import { showNextAction } from "../shared/tui.js";
11
- import { nativeInit } from "./native-git-bridge.js";
11
+ import { nativeInit, nativeAddAll, nativeCommit } from "./native-git-bridge.js";
12
12
  import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
13
13
  import { gsdRoot } from "./paths.js";
14
14
  import { assertSafeDirectory } from "./validate-directory.js";
@@ -40,6 +40,7 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
40
40
  ctx.ui.notify(`Project detected:\n${detectionSummary.join("\n")}`, "info");
41
41
  }
42
42
  // ── Step 2: Git setup ──────────────────────────────────────────────────────
43
+ let didInitGit = false;
43
44
  if (!signals.isGitRepo) {
44
45
  const gitChoice = await showNextAction(ctx, {
45
46
  title: "GSD — Project Setup",
@@ -54,6 +55,7 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
54
55
  return { completed: false, bootstrapped: false };
55
56
  if (gitChoice === "init_git") {
56
57
  nativeInit(basePath, prefs.mainBranch);
58
+ didInitGit = true;
57
59
  }
58
60
  }
59
61
  else {
@@ -244,6 +246,18 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
244
246
  // Ensure .gitignore
245
247
  ensureGitignore(basePath);
246
248
  untrackRuntimeFiles(basePath);
249
+ // Create initial commit so git log and git worktree work immediately (#4530).
250
+ // Without this, the branch is "unborn" (zero commits) and downstream operations
251
+ // like `git log` and `git worktree add` fail.
252
+ if (didInitGit) {
253
+ try {
254
+ nativeAddAll(basePath);
255
+ nativeCommit(basePath, "chore: init project");
256
+ }
257
+ catch {
258
+ // Non-fatal — user can commit manually; don't block project init
259
+ }
260
+ }
247
261
  // Auto-generate codebase map for instant agent orientation
248
262
  try {
249
263
  const result = generateCodebaseMap(basePath);
@@ -12,6 +12,12 @@ import { getErrorMessage } from "./error-utils.js";
12
12
  export const PROVIDER_REGISTRY = [
13
13
  // LLM Providers
14
14
  { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], hasOAuth: true, dashboardUrl: "console.anthropic.com" },
15
+ // Claude Code CLI: routes through the local `claude` binary — no API key,
16
+ // authentication is handled by the CLI's own OAuth flow.
17
+ // Referenced by doctor-providers.ts, auto-model-selection.ts, and others;
18
+ // must be in the canonical registry so all consumers see the same catalog.
19
+ // See: https://github.com/gsd-build/gsd-2/issues/4541
20
+ { id: "claude-code", label: "Claude Code CLI", category: "llm", hasOAuth: true },
15
21
  { id: "openai", label: "OpenAI", category: "llm", envVar: "OPENAI_API_KEY", prefixes: ["sk-"], dashboardUrl: "platform.openai.com/api-keys" },
16
22
  { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", hasOAuth: true },
17
23
  { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)", category: "llm", hasOAuth: true },
@@ -22,6 +28,8 @@ export const PROVIDER_REGISTRY = [
22
28
  { id: "xai", label: "xAI (Grok)", category: "llm", envVar: "XAI_API_KEY", dashboardUrl: "console.x.ai" },
23
29
  { id: "openrouter", label: "OpenRouter", category: "llm", envVar: "OPENROUTER_API_KEY", dashboardUrl: "openrouter.ai/keys" },
24
30
  { id: "mistral", label: "Mistral", category: "llm", envVar: "MISTRAL_API_KEY", dashboardUrl: "console.mistral.ai" },
31
+ { id: "minimax", label: "MiniMax", category: "llm", envVar: "MINIMAX_API_KEY", dashboardUrl: "platform.minimax.io" },
32
+ { id: "minimax-cn", label: "MiniMax CN", category: "llm", envVar: "MINIMAX_CN_API_KEY", dashboardUrl: "platform.minimax.io" },
25
33
  { id: "ollama-cloud", label: "Ollama Cloud", category: "llm", envVar: "OLLAMA_API_KEY" },
26
34
  { id: "custom-openai", label: "Custom (OpenAI-compat)", category: "llm", envVar: "CUSTOM_OPENAI_API_KEY" },
27
35
  { id: "cerebras", label: "Cerebras", category: "llm", envVar: "CEREBRAS_API_KEY" },
@@ -387,6 +395,26 @@ const TEST_ENDPOINTS = {
387
395
  url: "https://api.mistral.ai/v1/models",
388
396
  headers: (key) => ({ Authorization: `Bearer ${key}` }),
389
397
  },
398
+ minimax: {
399
+ url: "https://api.minimax.io/anthropic/v1/messages",
400
+ method: "POST",
401
+ headers: (key) => ({
402
+ "x-api-key": key,
403
+ "anthropic-version": "2023-06-01",
404
+ "content-type": "application/json",
405
+ }),
406
+ body: JSON.stringify({ model: "MiniMax-M2.7", max_tokens: 1, messages: [{ role: "user", content: "hi" }] }),
407
+ },
408
+ "minimax-cn": {
409
+ url: "https://api.minimaxi.com/anthropic/v1/messages",
410
+ method: "POST",
411
+ headers: (key) => ({
412
+ "x-api-key": key,
413
+ "anthropic-version": "2023-06-01",
414
+ "content-type": "application/json",
415
+ }),
416
+ body: JSON.stringify({ model: "MiniMax-M2.7", max_tokens: 1, messages: [{ role: "user", content: "hi" }] }),
417
+ },
390
418
  openrouter: {
391
419
  url: "https://openrouter.ai/api/v1/models",
392
420
  headers: (key) => ({ Authorization: `Bearer ${key}` }),
@@ -0,0 +1,126 @@
1
+ // GSD2 — Decisions -> memories backfill (ADR-013 step 5)
2
+ //
3
+ // Idempotent one-shot migration that copies every active decisions row into
4
+ // the memories table with category="architecture" and a structured_fields
5
+ // payload preserving the original gsd_save_decision schema (when_context,
6
+ // scope, decision, choice, rationale, made_by, revisable, sourceDecisionId).
7
+ //
8
+ // The backfill exists so the cutover in ADR-013 step 6 can drop the
9
+ // decisions table without losing schema fidelity. Idempotency is enforced
10
+ // by tagging each backfilled memory with structured_fields.sourceDecisionId
11
+ // and skipping any decision whose ID already appears in the memories table.
12
+ //
13
+ // Triggered opportunistically by buildBeforeAgentStartResult so the cost
14
+ // only ever fires once per project. Costs O(N) inserts on first run where
15
+ // N is the active-decisions count; subsequent runs are an O(N) lookup that
16
+ // finds existing markers and exits.
17
+ import { isDbAvailable, _getAdapter } from "./gsd-db.js";
18
+ import { createMemory } from "./memory-store.js";
19
+ import { logWarning } from "./workflow-logger.js";
20
+ /**
21
+ * Backfill active decisions rows into the memories table.
22
+ *
23
+ * - Idempotent (per-row): every row written carries
24
+ * `structured_fields.sourceDecisionId = "<decisionId>"`. Each candidate
25
+ * decision is checked individually; only decisions whose id is already
26
+ * present in the memory store are skipped. A user-authored memory with
27
+ * their own `sourceDecisionId` does NOT abort the backfill.
28
+ * - Best-effort: never throws. Logs and returns 0 on failure so a broken
29
+ * backfill cannot block agent startup.
30
+ * - Active-only: skips rows where `superseded_by IS NOT NULL`. Superseded
31
+ * decisions are historical record; the memory store is for active
32
+ * knowledge.
33
+ *
34
+ * Returns the number of memories written (0 when already backfilled or
35
+ * when the DB has no decisions). Callers can log the result or surface it
36
+ * to the user.
37
+ */
38
+ export function backfillDecisionsToMemories() {
39
+ if (!isDbAvailable())
40
+ return 0;
41
+ const adapter = _getAdapter();
42
+ if (!adapter)
43
+ return 0;
44
+ try {
45
+ const decisions = adapter
46
+ .prepare("SELECT id, when_context, scope, decision, choice, rationale, made_by, revisable, superseded_by FROM decisions WHERE superseded_by IS NULL")
47
+ .all();
48
+ if (decisions.length === 0)
49
+ return 0;
50
+ // Per-row idempotency: each memory backfilled from a decision carries
51
+ // structured_fields.sourceDecisionId="<decisionId>". Skipping is decided
52
+ // per row by matching that exact id, NOT by a global sentinel — a global
53
+ // sentinel would silently abort the backfill if a user manually called
54
+ // capture_thought with their own structuredFields.sourceDecisionId.
55
+ const checkExisting = adapter.prepare("SELECT 1 FROM memories WHERE structured_fields LIKE :pattern LIMIT 1");
56
+ let written = 0;
57
+ for (const raw of decisions) {
58
+ const row = {
59
+ id: String(raw["id"] ?? ""),
60
+ when_context: String(raw["when_context"] ?? ""),
61
+ scope: String(raw["scope"] ?? ""),
62
+ decision: String(raw["decision"] ?? ""),
63
+ choice: String(raw["choice"] ?? ""),
64
+ rationale: String(raw["rationale"] ?? ""),
65
+ made_by: String(raw["made_by"] ?? "agent"),
66
+ revisable: String(raw["revisable"] ?? ""),
67
+ superseded_by: raw["superseded_by"] == null ? null : String(raw["superseded_by"]),
68
+ };
69
+ if (!row.id)
70
+ continue;
71
+ // Pattern is anchored to the JSON-stringified shape and the exact
72
+ // decision id to avoid prefix collisions (e.g. "D1" vs "D10").
73
+ if (checkExisting.get({ ":pattern": `%"sourceDecisionId":"${row.id}"%` }))
74
+ continue;
75
+ const content = synthesizeContent(row);
76
+ const id = createMemory({
77
+ category: "architecture",
78
+ content,
79
+ scope: row.scope || "project",
80
+ confidence: 0.85,
81
+ structuredFields: {
82
+ sourceDecisionId: row.id,
83
+ when_context: row.when_context,
84
+ scope: row.scope,
85
+ decision: row.decision,
86
+ choice: row.choice,
87
+ rationale: row.rationale,
88
+ made_by: row.made_by,
89
+ revisable: row.revisable,
90
+ },
91
+ });
92
+ if (id)
93
+ written += 1;
94
+ }
95
+ return written;
96
+ }
97
+ catch (e) {
98
+ logWarning("memory-backfill", `decisions->memories backfill failed: ${e.message}`);
99
+ return 0;
100
+ }
101
+ }
102
+ /**
103
+ * Combine the decision's structured fields into a 1-3 sentence content
104
+ * string suitable for keyword retrieval and human review.
105
+ *
106
+ * Format: "<decision> Chose: <choice>. Rationale: <rationale>."
107
+ * Truncates each field to keep the synthesized line under ~600 chars so
108
+ * memory_query rendering stays readable.
109
+ */
110
+ function synthesizeContent(row) {
111
+ const trim = (value, max) => {
112
+ const cleaned = value.replace(/\s+/g, " ").trim();
113
+ return cleaned.length > max ? cleaned.slice(0, max - 1) + "\u2026" : cleaned;
114
+ };
115
+ const parts = [];
116
+ const decision = trim(row.decision, 240);
117
+ const choice = trim(row.choice, 200);
118
+ const rationale = trim(row.rationale, 200);
119
+ if (decision)
120
+ parts.push(decision);
121
+ if (choice)
122
+ parts.push(`Chose: ${choice}.`);
123
+ if (rationale)
124
+ parts.push(`Rationale: ${rationale}.`);
125
+ return parts.join(" ");
126
+ }
@@ -29,8 +29,22 @@ function rowToMemory(row) {
29
29
  hit_count: row['hit_count'],
30
30
  scope: row['scope'] ?? 'project',
31
31
  tags: parseTags(row['tags']),
32
+ structured_fields: parseStructuredFields(row['structured_fields']),
32
33
  };
33
34
  }
35
+ function parseStructuredFields(raw) {
36
+ if (typeof raw !== 'string' || raw.length === 0)
37
+ return null;
38
+ try {
39
+ const parsed = JSON.parse(raw);
40
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
41
+ ? parsed
42
+ : null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
34
48
  function parseTags(raw) {
35
49
  if (typeof raw !== 'string' || raw.length === 0)
36
50
  return [];
@@ -376,6 +390,7 @@ export function createMemory(fields) {
376
390
  updatedAt: now,
377
391
  scope: fields.scope ?? 'project',
378
392
  tags: fields.tags ?? [],
393
+ structuredFields: fields.structuredFields ?? null,
379
394
  });
380
395
  // Derive the real ID from the assigned seq (SELECT is still fine via adapter)
381
396
  const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
@@ -553,6 +568,10 @@ export function applyMemoryActions(actions, unitType, unitId) {
553
568
  source_unit_id: unitId,
554
569
  scope: action.scope,
555
570
  tags: action.tags,
571
+ // ADR-013: forward structured payload through the action layer so
572
+ // bulk applyMemoryActions callers (extraction, ingestion) don't
573
+ // silently drop it.
574
+ structuredFields: action.structuredFields ?? null,
556
575
  });
557
576
  break;
558
577
  case 'UPDATE':
@@ -440,6 +440,20 @@ function getModelCost(modelId) {
440
440
  function bareModelId(modelId) {
441
441
  return modelId.includes("/") ? modelId.split("/").pop() : modelId;
442
442
  }
443
+ // ─── Provider-specific Tool Limits ─────────────────────────────────────────
444
+ /**
445
+ * Groq enforces a hard limit of 128 tools per request.
446
+ * Requests exceeding this limit receive a 400 error:
447
+ * "maximum number of items is 128"
448
+ * @see https://console.groq.com/docs/tool-use
449
+ */
450
+ export const GROQ_MAX_TOOLS = 128;
451
+ /**
452
+ * Provider IDs that map to the Groq API backend.
453
+ * Used to detect Groq at the GSD routing layer where only the provider string
454
+ * is available (the pi-ai openai-completions adapter is shared across providers).
455
+ */
456
+ const GROQ_PROVIDER_IDS = new Set(["groq"]);
443
457
  // ─── Tool Compatibility Filter (ADR-005 Phase 3) ───────────────────────────
444
458
  /**
445
459
  * Check if a tool is compatible with a provider's capabilities.
@@ -461,8 +475,14 @@ export function isToolCompatibleWithProvider(toolName, providerCaps) {
461
475
  /**
462
476
  * Filter a list of tool names to only those compatible with a provider.
463
477
  * Used by the routing pipeline to adjust tool sets when switching providers.
478
+ *
479
+ * @param toolNames - The full list of active tool names to filter.
480
+ * @param providerApi - The pi-ai API string (e.g. "openai-completions").
481
+ * @param provider - Optional provider ID (e.g. "groq"). Used to apply
482
+ * provider-specific limits that can't be expressed as API-level capabilities
483
+ * (e.g. Groq's 128-tool hard limit on the shared openai-completions adapter).
464
484
  */
465
- export function filterToolsForProvider(toolNames, providerApi) {
485
+ export function filterToolsForProvider(toolNames, providerApi, provider) {
466
486
  const providerCaps = getProviderCapabilities(providerApi);
467
487
  // Provider doesn't support tool calling at all
468
488
  if (!providerCaps.toolCalling) {
@@ -478,6 +498,14 @@ export function filterToolsForProvider(toolNames, providerApi) {
478
498
  filtered.push(name);
479
499
  }
480
500
  }
501
+ // Groq enforces a hard limit of 128 tools per request (#4376).
502
+ // Trim the compatible list to GROQ_MAX_TOOLS and move the excess to filtered.
503
+ if (provider && GROQ_PROVIDER_IDS.has(provider) && compatible.length > GROQ_MAX_TOOLS) {
504
+ const trimmed = compatible.splice(GROQ_MAX_TOOLS);
505
+ filtered.push(...trimmed);
506
+ console.warn(`[gsd] Groq tool limit: ${compatible.length + trimmed.length} tools active but Groq allows at most ${GROQ_MAX_TOOLS}. ` +
507
+ `Trimming to the first ${GROQ_MAX_TOOLS} tools. Removed: ${trimmed.join(", ")}`);
508
+ }
481
509
  return { compatible, filtered };
482
510
  }
483
511
  /**
@@ -486,8 +514,13 @@ export function filterToolsForProvider(toolNames, providerApi) {
486
514
  *
487
515
  * This is a hard filter only — it removes tools that would fail at the
488
516
  * provider level. It does NOT remove tools based on soft heuristics.
517
+ *
518
+ * @param activeToolNames - The full list of currently active tool names.
519
+ * @param selectedModelApi - The pi-ai API string for the selected model.
520
+ * @param provider - Optional provider ID (e.g. "groq") for provider-specific
521
+ * limits beyond what the API-level capability profile expresses.
489
522
  */
490
- export function adjustToolSet(activeToolNames, selectedModelApi) {
491
- const { compatible, filtered } = filterToolsForProvider(activeToolNames, selectedModelApi);
523
+ export function adjustToolSet(activeToolNames, selectedModelApi, provider) {
524
+ const { compatible, filtered } = filterToolsForProvider(activeToolNames, selectedModelApi, provider);
492
525
  return { toolNames: compatible, removedTools: filtered };
493
526
  }