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
@@ -4,13 +4,18 @@
4
4
  *
5
5
  * Uses tasks.expected_output (DB column, populated from per-task ## Expected Output)
6
6
  * and tasks.files (from slice PLAN.md - Files: subline) as the expected set.
7
- * Compares against git diff HEAD~1 --name-only after auto-commit.
7
+ * Compares against `git diff-tree --root --no-commit-id -r --name-only HEAD` after auto-commit.
8
+ * Using diff-tree --root handles initial commits, shallow clones, and merge commits correctly
9
+ * (Bug #4385 — git diff HEAD~1 failed on initial commits).
8
10
  *
9
11
  * Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
10
12
  */
13
+ import { createRequire } from "node:module";
11
14
  import { execFileSync } from "node:child_process";
12
15
  import { normalizePlannedFileReference } from "../files.js";
13
16
  import { logWarning } from "../workflow-logger.js";
17
+ const _require = createRequire(import.meta.url);
18
+ const picomatch = _require("picomatch");
14
19
  // ─── Public API ─────────────────────────────────────────────────────────────
15
20
  /**
16
21
  * Validate file changes after auto-commit for an execute-task unit.
@@ -20,7 +25,7 @@ import { logWarning } from "../workflow-logger.js";
20
25
  * @param expectedOutput - JSON array from tasks.expected_output DB column
21
26
  * @param plannedFiles - JSON array from tasks.files DB column
22
27
  */
23
- export function validateFileChanges(basePath, expectedOutput, plannedFiles) {
28
+ export function validateFileChanges(basePath, expectedOutput, plannedFiles, fileChangeAllowlist = []) {
24
29
  const allExpected = new Set([...expectedOutput, ...plannedFiles]);
25
30
  // If no expected files were planned, skip validation
26
31
  if (allExpected.size === 0)
@@ -33,8 +38,11 @@ export function validateFileChanges(basePath, expectedOutput, plannedFiles) {
33
38
  const projectFiles = actualFiles.filter(f => !f.startsWith(".gsd/") && !f.startsWith(".gsd\\"));
34
39
  // Normalize expected paths (strip leading ./ or /)
35
40
  const normalizedExpected = new Set([...allExpected].map((f) => normalizePlannedFileReference(f).replace(/^\.\//, "").replace(/^\//, "")));
36
- // Compute symmetric difference
37
- const unexpectedFiles = projectFiles.filter(f => !normalizedExpected.has(f));
41
+ // Build allowlist matchers once (dot: true so patterns like `**/.hidden` work).
42
+ const allowlistMatchers = fileChangeAllowlist.map(p => picomatch(p, { dot: true }));
43
+ const isAllowlisted = (f) => allowlistMatchers.some(m => m(f));
44
+ // Compute symmetric difference, excluding allowlisted files
45
+ const unexpectedFiles = projectFiles.filter(f => !normalizedExpected.has(f) && !isAllowlisted(f));
38
46
  const missingFiles = [...normalizedExpected].filter(f => !projectFiles.includes(f));
39
47
  const violations = [];
40
48
  for (const f of unexpectedFiles) {
@@ -62,7 +70,7 @@ export function validateFileChanges(basePath, expectedOutput, plannedFiles) {
62
70
  // ─── Internals ──────────────────────────────────────────────────────────────
63
71
  function getChangedFilesFromLastCommit(basePath) {
64
72
  try {
65
- const result = execFileSync("git", ["diff", "--name-only", "HEAD~1", "HEAD"], { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
73
+ const result = execFileSync("git", ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"], { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
66
74
  return result ? result.split("\n").filter(Boolean) : [];
67
75
  }
68
76
  catch (e) {
@@ -23,6 +23,7 @@ const DEFAULTS = {
23
23
  checkpoints: true,
24
24
  auto_rollback: false,
25
25
  timeout_scale_cap: 6,
26
+ file_change_allowlist: [],
26
27
  };
27
28
  // ─── Public API ─────────────────────────────────────────────────────────────
28
29
  /**
@@ -42,6 +43,9 @@ export function resolveSafetyHarnessConfig(raw) {
42
43
  checkpoints: typeof raw.checkpoints === "boolean" ? raw.checkpoints : DEFAULTS.checkpoints,
43
44
  auto_rollback: typeof raw.auto_rollback === "boolean" ? raw.auto_rollback : DEFAULTS.auto_rollback,
44
45
  timeout_scale_cap: typeof raw.timeout_scale_cap === "number" ? raw.timeout_scale_cap : DEFAULTS.timeout_scale_cap,
46
+ file_change_allowlist: Array.isArray(raw.file_change_allowlist)
47
+ ? raw.file_change_allowlist.filter((p) => typeof p === "string")
48
+ : DEFAULTS.file_change_allowlist,
45
49
  };
46
50
  }
47
51
  /**
@@ -56,7 +60,7 @@ export function isHarnessEnabled(raw) {
56
60
  return DEFAULTS.enabled;
57
61
  }
58
62
  // ─── Re-exports ─────────────────────────────────────────────────────────────
59
- export { resetEvidence, getEvidence, getBashEvidence, getFilePaths, recordToolCall, recordToolResult, } from "./evidence-collector.js";
63
+ export { resetEvidence, getEvidence, getBashEvidence, getFilePaths, recordToolCall, recordToolResult, saveEvidenceToDisk, loadEvidenceFromDisk, clearEvidenceFromDisk, } from "./evidence-collector.js";
60
64
  export { classifyCommand } from "./destructive-guard.js";
61
65
  export { validateFileChanges } from "./file-change-validator.js";
62
66
  export { crossReferenceEvidence } from "./evidence-cross-ref.js";
@@ -501,7 +501,7 @@ async function handleAllSlicesDone(basePath, activeMilestone, registry, requirem
501
501
  const validationContent = validationFile ? await loadFile(validationFile) : null;
502
502
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
503
503
  const verdict = validationContent ? extractVerdict(validationContent) : undefined;
504
- if (!validationTerminal || verdict === 'needs-remediation') {
504
+ if (!validationTerminal) {
505
505
  return {
506
506
  activeMilestone, activeSlice: null, activeTask: null,
507
507
  phase: 'validating-milestone',
@@ -511,6 +511,23 @@ async function handleAllSlicesDone(basePath, activeMilestone, registry, requirem
511
511
  progress: { milestones: milestoneProgress, slices: sliceProgress },
512
512
  };
513
513
  }
514
+ // All roadmap slices are done (enforced by caller) and verdict is
515
+ // needs-remediation — remediation cannot progress without new slices.
516
+ // Return blocked instead of re-dispatching validate-milestone (#4506).
517
+ if (verdict === 'needs-remediation') {
518
+ return {
519
+ activeMilestone, activeSlice: null, activeTask: null,
520
+ phase: 'blocked',
521
+ recentDecisions: [],
522
+ blockers: [
523
+ `Milestone ${activeMilestone.id} validation verdict is needs-remediation but all slices are complete. ` +
524
+ `Add remediation slices via gsd_reassess_roadmap or override the verdict manually.`,
525
+ ],
526
+ nextAction: `Resolve ${activeMilestone.id} remediation before proceeding.`,
527
+ registry, requirements,
528
+ progress: { milestones: milestoneProgress, slices: sliceProgress },
529
+ };
530
+ }
514
531
  return {
515
532
  activeMilestone, activeSlice: null, activeTask: null,
516
533
  phase: 'completing-milestone',
@@ -1278,9 +1295,11 @@ export async function _deriveStateImpl(basePath) {
1278
1295
  done: activeRoadmap.slices.length,
1279
1296
  total: activeRoadmap.slices.length,
1280
1297
  };
1281
- // Force re-validation when verdict is needs-remediation remediation slices
1282
- // may have completed since the stale validation was written (#3596).
1283
- if (!validationTerminal || verdict === 'needs-remediation') {
1298
+ // Force re-validation when VALIDATION.md is absent or non-terminal
1299
+ // remediation slices may have completed since the stale validation was
1300
+ // written (#3596). But needs-remediation with all slices done is a dead
1301
+ // end — return blocked to avoid an infinite dispatch loop (#4506).
1302
+ if (!validationTerminal) {
1284
1303
  return {
1285
1304
  activeMilestone,
1286
1305
  activeSlice: null,
@@ -1297,6 +1316,26 @@ export async function _deriveStateImpl(basePath) {
1297
1316
  },
1298
1317
  };
1299
1318
  }
1319
+ if (verdict === 'needs-remediation') {
1320
+ return {
1321
+ activeMilestone,
1322
+ activeSlice: null,
1323
+ activeTask: null,
1324
+ phase: 'blocked',
1325
+ recentDecisions: [],
1326
+ blockers: [
1327
+ `Milestone ${activeMilestone.id} validation verdict is needs-remediation but all slices are complete. ` +
1328
+ `Add remediation slices via gsd_reassess_roadmap or override the verdict manually.`,
1329
+ ],
1330
+ nextAction: `Resolve ${activeMilestone.id} remediation before proceeding.`,
1331
+ registry,
1332
+ requirements,
1333
+ progress: {
1334
+ milestones: milestoneProgress,
1335
+ slices: sliceProgress,
1336
+ },
1337
+ };
1338
+ }
1300
1339
  return {
1301
1340
  activeMilestone,
1302
1341
  activeSlice: null,
@@ -16,7 +16,11 @@ async function getEncoder() {
16
16
  try {
17
17
  // @ts-ignore — tiktoken may not have type declarations in extensions tsconfig
18
18
  const tiktoken = await import("tiktoken");
19
- encoder = tiktoken.encoding_for_model("gpt-4o");
19
+ // Use cl100k_base — the most conservative and broadly compatible BPE encoding.
20
+ // It is shared by GPT-3.5/GPT-4 and gives a safer (larger) estimate than
21
+ // gpt-4o's o200k_base encoding, which produces fewer tokens for the same text
22
+ // and would cause context windows for non-OpenAI providers to be under-counted.
23
+ encoder = tiktoken.get_encoding("cl100k_base");
20
24
  return encoder;
21
25
  }
22
26
  catch {
@@ -24,19 +28,32 @@ async function getEncoder() {
24
28
  return null;
25
29
  }
26
30
  }
27
- export async function countTokens(text) {
31
+ /**
32
+ * Count tokens in `text` using tiktoken (cl100k_base) when available.
33
+ *
34
+ * When tiktoken is not loaded, falls back to a provider-aware character-ratio
35
+ * estimate via `estimateTokensForProvider`. Passing `provider` is recommended
36
+ * so the heuristic fallback is as accurate as possible.
37
+ */
38
+ export async function countTokens(text, provider) {
28
39
  const enc = await getEncoder();
29
40
  if (enc) {
30
41
  const tokens = enc.encode(text);
31
42
  return tokens.length;
32
43
  }
33
- return Math.ceil(text.length / 4);
44
+ return estimateTokensForProvider(text, provider ?? "unknown");
34
45
  }
35
- export function countTokensSync(text) {
46
+ /**
47
+ * Synchronous token count — only accurate after `initTokenCounter()` resolves.
48
+ *
49
+ * Before init, or when tiktoken is unavailable, falls back to a provider-aware
50
+ * character-ratio estimate. Passing `provider` is recommended.
51
+ */
52
+ export function countTokensSync(text, provider) {
36
53
  if (encoder) {
37
54
  return encoder.encode(text).length;
38
55
  }
39
- return Math.ceil(text.length / 4);
56
+ return estimateTokensForProvider(text, provider ?? "unknown");
40
57
  }
41
58
  export async function initTokenCounter() {
42
59
  const enc = await getEncoder();
@@ -6,7 +6,7 @@
6
6
  * for recovery, and invalidates caches.
7
7
  */
8
8
  import { join } from "node:path";
9
- import { mkdirSync } from "node:fs";
9
+ import { mkdirSync, existsSync } from "node:fs";
10
10
  import { transaction, getMilestone, getMilestoneSlices, getSliceTasks, updateMilestoneStatus, } from "../gsd-db.js";
11
11
  import { resolveMilestonePath, clearPathCache } from "../paths.js";
12
12
  import { isClosedStatus } from "../status-guards.js";
@@ -141,15 +141,21 @@ export async function handleCompleteMilestone(params, basePath) {
141
141
  mkdirSync(manualDir, { recursive: true });
142
142
  summaryPath = join(manualDir, `${params.milestoneId}-SUMMARY.md`);
143
143
  }
144
- try {
145
- await saveFile(summaryPath, summaryMd);
146
- }
147
- catch (renderErr) {
148
- // Disk render failed — roll back DB status so state stays consistent
149
- logWarning("tool", `complete_milestone — disk render failed, rolling back DB status: ${renderErr.message}`);
150
- updateMilestoneStatus(params.milestoneId, 'active', null);
151
- invalidateStateCache();
152
- return { error: `disk render failed: ${renderErr.message}` };
144
+ // Guard (#4598): if SUMMARY.md already exists on disk, do not overwrite it.
145
+ // This handles re-dispatch scenarios (DB/disk state divergence) where a prior
146
+ // completion already wrote the file. Overwriting would silently destroy the
147
+ // richer content the agent produced during the original completion run.
148
+ if (!existsSync(summaryPath)) {
149
+ try {
150
+ await saveFile(summaryPath, summaryMd);
151
+ }
152
+ catch (renderErr) {
153
+ // Disk render failed — roll back DB status so state stays consistent
154
+ logWarning("tool", `complete_milestone — disk render failed, rolling back DB status: ${renderErr.message}`);
155
+ updateMilestoneStatus(params.milestoneId, 'active', null);
156
+ invalidateStateCache();
157
+ return { error: `disk render failed: ${renderErr.message}` };
158
+ }
153
159
  }
154
160
  // Invalidate all caches
155
161
  invalidateStateCache();
@@ -0,0 +1,59 @@
1
+ // GSD Exec Search Tool — lists and filters prior gsd_exec runs.
2
+ //
3
+ // Scans .gsd/exec/*.meta.json and returns a ranked summary so agents can
4
+ // re-discover past runs without re-executing. Read-only; no DB writes.
5
+ import { searchExecHistory } from "../exec-history.js";
6
+ export function executeExecSearch(params, opts) {
7
+ const searchOpts = {
8
+ query: typeof params.query === "string" ? params.query : undefined,
9
+ runtime: params.runtime,
10
+ failing_only: params.failing_only === true,
11
+ limit: typeof params.limit === "number" ? params.limit : undefined,
12
+ };
13
+ const hits = searchExecHistory(opts.baseDir, searchOpts);
14
+ if (hits.length === 0) {
15
+ return {
16
+ content: [{ type: "text", text: "No prior gsd_exec runs match those filters." }],
17
+ details: { operation: "gsd_exec_search", matches: 0 },
18
+ };
19
+ }
20
+ const lines = [`Found ${hits.length} exec run(s), most recent first:`];
21
+ for (const hit of hits) {
22
+ const e = hit.entry;
23
+ const status = formatStatus(e);
24
+ const purpose = e.purpose ? ` — ${e.purpose}` : "";
25
+ const truncated = e.stdout_truncated ? " (stdout truncated)" : "";
26
+ lines.push(`- [${e.id}] ${e.runtime} ${status} ${e.duration_ms}ms${truncated}${purpose}`, ` stdout: ${e.stdout_path}`);
27
+ if (hit.digest_preview) {
28
+ const preview = hit.digest_preview.replace(/\n/g, "\n ");
29
+ lines.push(` preview:\n ${preview}`);
30
+ }
31
+ }
32
+ return {
33
+ content: [{ type: "text", text: lines.join("\n") }],
34
+ details: {
35
+ operation: "gsd_exec_search",
36
+ matches: hits.length,
37
+ results: hits.map((hit) => ({
38
+ id: hit.entry.id,
39
+ runtime: hit.entry.runtime,
40
+ exit_code: hit.entry.exit_code,
41
+ timed_out: hit.entry.timed_out,
42
+ duration_ms: hit.entry.duration_ms,
43
+ purpose: hit.entry.purpose,
44
+ stdout_path: hit.entry.stdout_path,
45
+ stderr_path: hit.entry.stderr_path,
46
+ meta_path: hit.entry.meta_path,
47
+ })),
48
+ },
49
+ };
50
+ }
51
+ function formatStatus(entry) {
52
+ if (entry.timed_out)
53
+ return "timeout";
54
+ if (entry.signal)
55
+ return `signal:${entry.signal}`;
56
+ if (entry.exit_code === null)
57
+ return "exit:null";
58
+ return `exit:${entry.exit_code}`;
59
+ }
@@ -0,0 +1,126 @@
1
+ // GSD Exec Tool — executor for the gsd_exec MCP tool.
2
+ //
3
+ // Thin wrapper around exec-sandbox.ts that reads effective options from
4
+ // the project preferences (context_mode block) and formats the result
5
+ // for MCP return.
6
+ import { EXEC_DEFAULTS, runExecSandbox, } from "../exec-sandbox.js";
7
+ import { isContextModeEnabled } from "../preferences-types.js";
8
+ export function buildExecOptions(baseDir, cfg, extras) {
9
+ const allowlist = Array.isArray(cfg?.exec_env_allowlist) ? cfg.exec_env_allowlist : EXEC_DEFAULTS.envAllowlist;
10
+ const stdoutCap = clampNumber(cfg?.exec_stdout_cap_bytes, EXEC_DEFAULTS.stdoutCapBytes, 4_096, 16_777_216);
11
+ const defaultTimeout = clampNumber(cfg?.exec_timeout_ms, EXEC_DEFAULTS.defaultTimeoutMs, 1_000, EXEC_DEFAULTS.clampTimeoutMs);
12
+ const digestChars = clampNumber(cfg?.exec_digest_chars, EXEC_DEFAULTS.digestChars, 0, 4_000);
13
+ return {
14
+ baseDir,
15
+ clamp_timeout_ms: EXEC_DEFAULTS.clampTimeoutMs,
16
+ default_timeout_ms: defaultTimeout,
17
+ stdout_cap_bytes: stdoutCap,
18
+ stderr_cap_bytes: EXEC_DEFAULTS.stderrCapBytes,
19
+ digest_chars: digestChars,
20
+ env_allowlist: allowlist,
21
+ ...extras,
22
+ };
23
+ }
24
+ function clampNumber(value, fallback, min, max) {
25
+ if (typeof value !== "number" || !Number.isFinite(value))
26
+ return fallback;
27
+ if (value < min)
28
+ return min;
29
+ if (value > max)
30
+ return max;
31
+ return Math.floor(value);
32
+ }
33
+ function isEnabled(prefs) {
34
+ return isContextModeEnabled(prefs);
35
+ }
36
+ function disabledResult() {
37
+ return {
38
+ content: [
39
+ {
40
+ type: "text",
41
+ text: "gsd_exec is disabled by `context_mode.enabled: false` in preferences. Remove that " +
42
+ "override (or set it to true) to re-enable sandboxed tool-output execution.",
43
+ },
44
+ ],
45
+ details: { operation: "gsd_exec", error: "context_mode_disabled" },
46
+ isError: true,
47
+ };
48
+ }
49
+ function paramError(message) {
50
+ return {
51
+ content: [{ type: "text", text: `Error: ${message}` }],
52
+ details: { operation: "gsd_exec", error: "invalid_params", detail: message },
53
+ isError: true,
54
+ };
55
+ }
56
+ export async function executeGsdExec(params, deps) {
57
+ if (!isEnabled(deps.preferences))
58
+ return disabledResult();
59
+ const runtime = params.runtime;
60
+ if (runtime !== "bash" && runtime !== "node" && runtime !== "python") {
61
+ return paramError(`invalid runtime "${String(runtime)}" — must be bash | node | python`);
62
+ }
63
+ const script = typeof params.script === "string" ? params.script : "";
64
+ if (script.trim().length === 0) {
65
+ return paramError("script is required and must be a non-empty string");
66
+ }
67
+ if (Buffer.byteLength(script, "utf8") > 200_000) {
68
+ return paramError("script exceeds the 200 KB length limit");
69
+ }
70
+ const opts = buildExecOptions(deps.baseDir, deps.preferences?.context_mode, { now: deps.now, generateId: deps.generateId });
71
+ const run = deps.run ?? runExecSandbox;
72
+ try {
73
+ const result = await run({
74
+ runtime,
75
+ script,
76
+ ...(typeof params.purpose === "string" ? { purpose: params.purpose } : {}),
77
+ ...(typeof params.timeout_ms === "number" ? { timeout_ms: params.timeout_ms } : {}),
78
+ }, opts);
79
+ return formatResult(result);
80
+ }
81
+ catch (err) {
82
+ const message = err instanceof Error ? err.message : String(err);
83
+ return {
84
+ content: [{ type: "text", text: `Error: gsd_exec failed — ${message}` }],
85
+ details: { operation: "gsd_exec", error: message },
86
+ isError: true,
87
+ };
88
+ }
89
+ }
90
+ function formatResult(result) {
91
+ const headerLines = [
92
+ `gsd_exec[${result.id}] runtime=${result.runtime} exit=${formatExit(result)} duration=${result.duration_ms}ms`,
93
+ ` stdout: ${result.stdout_bytes}B${result.stdout_truncated ? " (truncated)" : ""} → ${result.stdout_path}`,
94
+ ` stderr: ${result.stderr_bytes}B${result.stderr_truncated ? " (truncated)" : ""} → ${result.stderr_path}`,
95
+ ];
96
+ const summary = `${headerLines.join("\n")}\n--- digest ---\n${result.digest}`.trimEnd();
97
+ return {
98
+ content: [{ type: "text", text: summary }],
99
+ details: {
100
+ operation: "gsd_exec",
101
+ id: result.id,
102
+ runtime: result.runtime,
103
+ exit_code: result.exit_code,
104
+ signal: result.signal,
105
+ timed_out: result.timed_out,
106
+ duration_ms: result.duration_ms,
107
+ stdout_bytes: result.stdout_bytes,
108
+ stderr_bytes: result.stderr_bytes,
109
+ stdout_truncated: result.stdout_truncated,
110
+ stderr_truncated: result.stderr_truncated,
111
+ stdout_path: result.stdout_path,
112
+ stderr_path: result.stderr_path,
113
+ meta_path: result.meta_path,
114
+ },
115
+ isError: result.timed_out || result.signal !== null || result.exit_code !== 0,
116
+ };
117
+ }
118
+ function formatExit(result) {
119
+ if (result.timed_out)
120
+ return "timeout";
121
+ if (result.signal)
122
+ return `signal:${result.signal}`;
123
+ if (result.exit_code === null)
124
+ return "null";
125
+ return String(result.exit_code);
126
+ }
@@ -58,7 +58,8 @@ export function executeMemoryCapture(params) {
58
58
  const confidence = clampConfidence(params.confidence);
59
59
  const scope = normalizeScope(params.scope);
60
60
  const tags = normalizeTags(params.tags);
61
- const id = createMemory({ category, content, confidence, scope, tags });
61
+ const structuredFields = normalizeStructuredFields(params.structuredFields);
62
+ const id = createMemory({ category, content, confidence, scope, tags, structuredFields });
62
63
  if (!id) {
63
64
  return {
64
65
  content: [{ type: "text", text: "Error: failed to create memory." }],
@@ -82,6 +83,19 @@ function normalizeTags(value) {
82
83
  return [];
83
84
  return value.filter((t) => typeof t === "string" && t.trim().length > 0).slice(0, 10);
84
85
  }
86
+ function normalizeStructuredFields(value) {
87
+ if (value == null)
88
+ return null;
89
+ if (typeof value !== "object" || Array.isArray(value))
90
+ return null;
91
+ // Only accept plain objects (Object.prototype or null prototype). Class
92
+ // instances and exotic objects won't round-trip cleanly through JSON, so
93
+ // reject them here instead of producing a partially-serialized payload.
94
+ const proto = Object.getPrototypeOf(value);
95
+ if (proto !== null && proto !== Object.prototype)
96
+ return null;
97
+ return value;
98
+ }
85
99
  function clampConfidence(value) {
86
100
  if (typeof value !== "number" || !Number.isFinite(value))
87
101
  return 0.8;
@@ -200,6 +214,16 @@ function includeSupersededMemories(rankedActive) {
200
214
  /* leave empty */
201
215
  }
202
216
  }
217
+ let structuredFields = null;
218
+ if (typeof row["structured_fields"] === "string" && row["structured_fields"].length > 0) {
219
+ try {
220
+ const parsed = JSON.parse(row["structured_fields"]);
221
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
222
+ structuredFields = parsed;
223
+ }
224
+ }
225
+ catch { /* leave null */ }
226
+ }
203
227
  return {
204
228
  seq: row["seq"],
205
229
  id: row["id"],
@@ -214,6 +238,7 @@ function includeSupersededMemories(rankedActive) {
214
238
  hit_count: row["hit_count"],
215
239
  scope: row["scope"] ?? "project",
216
240
  tags,
241
+ structured_fields: structuredFields,
217
242
  };
218
243
  });
219
244
  }
@@ -0,0 +1,23 @@
1
+ // GSD Resume Tool — returns the contents of .gsd/last-snapshot.md so
2
+ // agents can re-orient after compaction or session resume without
3
+ // re-deriving project memory state.
4
+ import { readCompactionSnapshot } from "../compaction-snapshot.js";
5
+ export function executeResume(_params, opts) {
6
+ const snapshot = readCompactionSnapshot(opts.baseDir);
7
+ if (snapshot == null) {
8
+ return {
9
+ content: [
10
+ {
11
+ type: "text",
12
+ text: "No snapshot found at .gsd/last-snapshot.md. The snapshot is written automatically " +
13
+ "on session_before_compact (enabled by default; set context_mode.enabled=false to opt out).",
14
+ },
15
+ ],
16
+ details: { operation: "gsd_resume", found: false },
17
+ };
18
+ }
19
+ return {
20
+ content: [{ type: "text", text: snapshot }],
21
+ details: { operation: "gsd_resume", found: true, bytes: Buffer.byteLength(snapshot, "utf-8") },
22
+ };
23
+ }
@@ -22,6 +22,23 @@ function hasFileContent(path) {
22
22
  return false;
23
23
  }
24
24
  }
25
+ function getArtifactLookupBases(basePath) {
26
+ const bases = [basePath];
27
+ const projectRoot = process.env.GSD_PROJECT_ROOT;
28
+ if (projectRoot && projectRoot.trim().length > 0 && projectRoot !== basePath) {
29
+ bases.push(projectRoot);
30
+ }
31
+ return bases;
32
+ }
33
+ function hasMilestoneFileContent(basePath, milestoneId, suffix) {
34
+ const bases = getArtifactLookupBases(basePath);
35
+ for (const candidateBase of bases) {
36
+ if (hasFileContent(resolveMilestoneFile(candidateBase, milestoneId, suffix))) {
37
+ return true;
38
+ }
39
+ }
40
+ return false;
41
+ }
25
42
  function countSliceResearchArtifacts(basePath, milestoneId, slices) {
26
43
  let count = 0;
27
44
  for (const slice of slices) {
@@ -43,9 +60,9 @@ export function compileUnitGraphFromState(basePath, state) {
43
60
  const slices = getMilestoneSlices(mid).sort((a, b) => Number(a.sequence ?? 0) - Number(b.sequence ?? 0));
44
61
  const nodes = [];
45
62
  const clarifyRoundLimit = PLAN_V2_CLARIFY_ROUND_LIMIT;
46
- const draftContextIncluded = hasFileContent(resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT"));
47
- const finalizedContextIncluded = hasFileContent(resolveMilestoneFile(basePath, mid, "CONTEXT"));
48
- const researchSynthesized = hasFileContent(resolveMilestoneFile(basePath, mid, "RESEARCH"))
63
+ const draftContextIncluded = hasMilestoneFileContent(basePath, mid, "CONTEXT-DRAFT");
64
+ const finalizedContextIncluded = hasMilestoneFileContent(basePath, mid, "CONTEXT");
65
+ const researchSynthesized = hasMilestoneFileContent(basePath, mid, "RESEARCH")
49
66
  || countSliceResearchArtifacts(basePath, mid, slices) > 0;
50
67
  if (isExecutionEntryPhase(state.phase) && !finalizedContextIncluded) {
51
68
  const reason = draftContextIncluded
@@ -5,6 +5,9 @@ import { fileURLToPath, pathToFileURL } from "node:url";
5
5
  const MCP_WORKFLOW_TOOL_SURFACE = new Set([
6
6
  "ask_user_questions",
7
7
  "gsd_decision_save",
8
+ "gsd_exec",
9
+ "gsd_exec_search",
10
+ "gsd_resume",
8
11
  "gsd_complete_milestone",
9
12
  "gsd_complete_task",
10
13
  "gsd_complete_slice",
@@ -66,5 +66,11 @@ Use for: technology evaluation, architecture decisions, "should we X?" questions
66
66
  - Recommendation with rationale
67
67
  - Next steps if the recommendation is accepted
68
68
  4. **Present** the recommendation to the user for discussion
69
+ 5. **Offer wrap-up:** If the findings are reusable on future work, offer to run
70
+ the `spike-wrap-up` skill to package them as a project-local skill at
71
+ `.claude/skills/<name>/SKILL.md`. That skill will auto-load on future
72
+ similar tasks via `skill-discovery.ts`. If the recommendation is
73
+ decision-only (no reusable guidance), suggest appending a one-liner to
74
+ `.gsd/DECISIONS.md` instead.
69
75
 
70
76
  </process>