@opengsd/gsd-pi 1.0.2-dev.e70300c → 1.0.2-dev.fb7ddf1

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 (480) hide show
  1. package/README.md +63 -12
  2. package/dist/headless-answers.js +2 -1
  3. package/dist/headless-events.d.ts +1 -0
  4. package/dist/headless-events.js +8 -1
  5. package/dist/onboarding.js +22 -3
  6. package/dist/resource-loader.d.ts +7 -0
  7. package/dist/resource-loader.js +44 -9
  8. package/dist/resources/.managed-resources-content-hash +1 -1
  9. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +34 -11
  10. package/dist/resources/extensions/context7/index.js +12 -2
  11. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  12. package/dist/resources/extensions/google-cli/index.js +30 -0
  13. package/dist/resources/extensions/google-cli/models.js +55 -0
  14. package/dist/resources/extensions/google-cli/package.json +11 -0
  15. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  16. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  17. package/dist/resources/extensions/gsd/auto/loop.js +81 -1
  18. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
  19. package/dist/resources/extensions/gsd/auto/phases.js +38 -1
  20. package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
  21. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  22. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
  23. package/dist/resources/extensions/gsd/auto-post-unit.js +65 -16
  24. package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
  25. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  26. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  27. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -1
  28. package/dist/resources/extensions/gsd/auto-verification.js +14 -2
  29. package/dist/resources/extensions/gsd/auto-worktree.js +34 -1
  30. package/dist/resources/extensions/gsd/auto.js +40 -2
  31. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  32. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
  33. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
  34. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +107 -27
  35. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
  36. package/dist/resources/extensions/gsd/bootstrap/tool-search-shim.js +4 -4
  37. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  38. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  39. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  40. package/dist/resources/extensions/gsd/commands-handlers.js +3 -0
  41. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  42. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  43. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  44. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  45. package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
  46. package/dist/resources/extensions/gsd/doctor-git-checks.js +70 -5
  47. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  48. package/dist/resources/extensions/gsd/doctor.js +7 -2
  49. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  50. package/dist/resources/extensions/gsd/guided-flow.js +5 -6
  51. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  52. package/dist/resources/extensions/gsd/mcp-filter.js +57 -18
  53. package/dist/resources/extensions/gsd/mcp-project-config.js +15 -9
  54. package/dist/resources/extensions/gsd/migration-auto-check.js +5 -1
  55. package/dist/resources/extensions/gsd/milestone-actions.js +3 -0
  56. package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
  57. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  58. package/dist/resources/extensions/gsd/parallel-merge.js +6 -4
  59. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  60. package/dist/resources/extensions/gsd/post-execution-checks.js +5 -4
  61. package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
  62. package/dist/resources/extensions/gsd/preferences.js +14 -2
  63. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  64. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  65. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  67. package/dist/resources/extensions/gsd/prompts/system.md +3 -20
  68. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  69. package/dist/resources/extensions/gsd/repo-identity.js +36 -6
  70. package/dist/resources/extensions/gsd/repository-registry.js +3 -1
  71. package/dist/resources/extensions/gsd/safety/evidence-collector.js +13 -6
  72. package/dist/resources/extensions/gsd/skill-activation.js +233 -0
  73. package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
  74. package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
  75. package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
  76. package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
  77. package/dist/resources/extensions/gsd/skill-scope.js +52 -0
  78. package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
  79. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +1 -1
  80. package/dist/resources/extensions/gsd/skills.js +60 -0
  81. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
  82. package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
  83. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
  84. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  85. package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
  86. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
  87. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  88. package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
  89. package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
  90. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  91. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  92. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  93. package/dist/resources/extensions/gsd/worktree-state-projection.js +29 -0
  94. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  95. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  96. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  97. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  98. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  99. package/dist/resources/extensions/subagent/index.js +8 -15
  100. package/dist/resources/shared/package-manager-detection.js +36 -0
  101. package/dist/resources/skills/agent-browser/SKILL.md +1 -1
  102. package/dist/resources/skills/api-design/SKILL.md +1 -1
  103. package/dist/resources/skills/code-optimizer/SKILL.md +6 -11
  104. package/dist/resources/skills/create-gsd-extension/SKILL.md +1 -1
  105. package/dist/resources/skills/create-mcp-server/SKILL.md +1 -1
  106. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  107. package/dist/resources/skills/create-skill/workflows/verify-skill.md +2 -10
  108. package/dist/resources/skills/debug-like-expert/references/when-to-research.md +1 -5
  109. package/dist/resources/skills/decompose-into-slices/SKILL.md +3 -3
  110. package/dist/resources/skills/dependency-upgrade/SKILL.md +1 -1
  111. package/dist/resources/skills/forensics/SKILL.md +2 -2
  112. package/dist/resources/skills/grill-me/SKILL.md +1 -1
  113. package/dist/resources/skills/handoff/SKILL.md +1 -1
  114. package/dist/resources/skills/make-interfaces-feel-better/SKILL.md +1 -1
  115. package/dist/resources/skills/observability/SKILL.md +1 -1
  116. package/dist/resources/skills/security-review/SKILL.md +1 -1
  117. package/dist/resources/skills/spike-wrap-up/SKILL.md +1 -1
  118. package/dist/resources/skills/tdd/SKILL.md +1 -1
  119. package/dist/resources/skills/write-docs/SKILL.md +1 -1
  120. package/dist/resources/skills/write-milestone-brief/SKILL.md +1 -1
  121. package/dist/update-check.d.ts +6 -2
  122. package/dist/update-check.js +7 -3
  123. package/dist/web/standalone/.next/BUILD_ID +1 -1
  124. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  125. package/dist/web/standalone/.next/build-manifest.json +2 -2
  126. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  127. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  128. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/index.html +1 -1
  145. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  148. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  152. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  153. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  155. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  156. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  157. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  158. package/package.json +5 -2
  159. package/packages/cloud-mcp-gateway/package.json +2 -2
  160. package/packages/contracts/dist/rpc.test.js +5 -0
  161. package/packages/contracts/dist/rpc.test.js.map +1 -1
  162. package/packages/contracts/dist/workflow.d.ts +15 -0
  163. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  164. package/packages/contracts/dist/workflow.js +16 -0
  165. package/packages/contracts/dist/workflow.js.map +1 -1
  166. package/packages/contracts/dist/workflow.test.js +1 -0
  167. package/packages/contracts/dist/workflow.test.js.map +1 -1
  168. package/packages/contracts/package.json +1 -1
  169. package/packages/daemon/package.json +4 -4
  170. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
  171. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  172. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
  173. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  174. package/packages/gsd-agent-core/package.json +5 -5
  175. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  176. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  177. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  178. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  179. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  180. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  181. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  182. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  183. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  184. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  186. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  193. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  194. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  196. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  197. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  198. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  199. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  201. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  202. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  203. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  204. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
  205. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  206. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  207. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -2
  208. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  209. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
  210. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  211. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
  212. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  213. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  214. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
  215. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  216. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  217. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  218. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  219. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  220. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  221. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  222. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  223. package/packages/gsd-agent-modes/package.json +7 -7
  224. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  225. package/packages/mcp-server/dist/server.js +7 -1
  226. package/packages/mcp-server/dist/server.js.map +1 -1
  227. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  228. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  229. package/packages/mcp-server/dist/workflow-tools.js +47 -8
  230. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  231. package/packages/mcp-server/package.json +3 -3
  232. package/packages/native/package.json +1 -1
  233. package/packages/pi-agent-core/dist/agent-loop.js +16 -14
  234. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  235. package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  236. package/packages/pi-agent-core/dist/harness/skills.js +6 -0
  237. package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
  238. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
  239. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
  240. package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
  241. package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
  242. package/packages/pi-agent-core/package.json +1 -1
  243. package/packages/pi-ai/dist/models.generated.d.ts +48 -206
  244. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  245. package/packages/pi-ai/dist/models.generated.js +67 -220
  246. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  247. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  248. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  249. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  250. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  251. package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
  252. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  253. package/packages/pi-ai/dist/types.d.ts +2 -0
  254. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  255. package/packages/pi-ai/dist/types.js.map +1 -1
  256. package/packages/pi-ai/dist/utils/tests/tool-search-shim.test.js +29 -1
  257. package/packages/pi-ai/dist/utils/tests/tool-search-shim.test.js.map +1 -1
  258. package/packages/pi-ai/dist/utils/tool-search-shim.d.ts +4 -1
  259. package/packages/pi-ai/dist/utils/tool-search-shim.d.ts.map +1 -1
  260. package/packages/pi-ai/dist/utils/tool-search-shim.js +58 -10
  261. package/packages/pi-ai/dist/utils/tool-search-shim.js.map +1 -1
  262. package/packages/pi-ai/dist/utils/tool-shims.d.ts +1 -1
  263. package/packages/pi-ai/dist/utils/tool-shims.d.ts.map +1 -1
  264. package/packages/pi-ai/dist/utils/tool-shims.js.map +1 -1
  265. package/packages/pi-ai/package.json +1 -1
  266. package/packages/pi-coding-agent/README.md +1 -1
  267. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
  268. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  269. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  271. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  272. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  273. package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  274. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  275. package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
  276. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  277. package/packages/pi-coding-agent/dist/core/skills.js +3 -0
  278. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  280. package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -7
  281. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  282. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
  283. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  284. package/packages/pi-coding-agent/dist/core/tools/read.js +5 -4
  285. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  286. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/tools/write.js +0 -1
  288. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  289. package/packages/pi-coding-agent/package.json +8 -8
  290. package/packages/pi-tui/package.json +1 -1
  291. package/packages/rpc-client/package.json +2 -2
  292. package/pkg/package.json +1 -1
  293. package/scripts/install/detect-existing.js +17 -3
  294. package/scripts/install/npm-global.js +103 -33
  295. package/scripts/install.js +1 -0
  296. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +36 -11
  297. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +86 -19
  298. package/src/resources/extensions/context7/index.ts +15 -2
  299. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  300. package/src/resources/extensions/google-cli/index.ts +34 -0
  301. package/src/resources/extensions/google-cli/models.ts +57 -0
  302. package/src/resources/extensions/google-cli/package.json +11 -0
  303. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  304. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  305. package/src/resources/extensions/gsd/auto/loop.ts +96 -1
  306. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
  307. package/src/resources/extensions/gsd/auto/phases.ts +47 -1
  308. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
  309. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  310. package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
  311. package/src/resources/extensions/gsd/auto-post-unit.ts +101 -18
  312. package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
  313. package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
  314. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  315. package/src/resources/extensions/gsd/auto-tool-tracking.ts +3 -1
  316. package/src/resources/extensions/gsd/auto-verification.ts +18 -2
  317. package/src/resources/extensions/gsd/auto-worktree.ts +45 -1
  318. package/src/resources/extensions/gsd/auto.ts +50 -2
  319. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  320. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
  321. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
  322. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +124 -25
  323. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
  324. package/src/resources/extensions/gsd/bootstrap/tool-search-shim.ts +4 -4
  325. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  326. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  327. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  328. package/src/resources/extensions/gsd/commands-handlers.ts +2 -0
  329. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  330. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  331. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  332. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  333. package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
  334. package/src/resources/extensions/gsd/doctor-git-checks.ts +72 -5
  335. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  336. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  337. package/src/resources/extensions/gsd/doctor.ts +7 -2
  338. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  339. package/src/resources/extensions/gsd/guided-flow.ts +5 -6
  340. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  341. package/src/resources/extensions/gsd/mcp-filter.ts +64 -17
  342. package/src/resources/extensions/gsd/mcp-project-config.ts +24 -9
  343. package/src/resources/extensions/gsd/migration-auto-check.ts +6 -0
  344. package/src/resources/extensions/gsd/milestone-actions.ts +2 -0
  345. package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
  346. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  347. package/src/resources/extensions/gsd/parallel-merge.ts +6 -4
  348. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  349. package/src/resources/extensions/gsd/post-execution-checks.ts +7 -4
  350. package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
  351. package/src/resources/extensions/gsd/preferences.ts +17 -2
  352. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  353. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  354. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  355. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  356. package/src/resources/extensions/gsd/prompts/system.md +3 -20
  357. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  358. package/src/resources/extensions/gsd/repo-identity.ts +35 -7
  359. package/src/resources/extensions/gsd/repository-registry.ts +3 -1
  360. package/src/resources/extensions/gsd/safety/evidence-collector.ts +13 -6
  361. package/src/resources/extensions/gsd/skill-activation.ts +292 -0
  362. package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
  363. package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
  364. package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
  365. package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
  366. package/src/resources/extensions/gsd/skill-scope.ts +63 -0
  367. package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
  368. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +1 -1
  369. package/src/resources/extensions/gsd/skills.ts +75 -0
  370. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
  371. package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
  372. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
  373. package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
  374. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
  375. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +75 -0
  376. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
  377. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
  378. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
  379. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +24 -0
  380. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  381. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +53 -0
  382. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  383. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  384. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  385. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  386. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  387. package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +40 -3
  388. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  389. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
  390. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  391. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
  392. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  393. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
  394. package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +71 -1
  395. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  396. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
  397. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
  398. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  399. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +22 -3
  400. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +43 -0
  401. package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +8 -0
  402. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  403. package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +19 -1
  404. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +24 -0
  405. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +56 -1
  406. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  407. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  408. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  409. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +9 -0
  410. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +127 -10
  411. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +44 -0
  412. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
  413. package/src/resources/extensions/gsd/tests/project-relocation-recovery.test.ts +101 -0
  414. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  415. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
  416. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +27 -0
  417. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
  418. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  419. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
  420. package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
  421. package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
  422. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  423. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
  424. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +67 -1
  425. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  426. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  427. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  428. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
  429. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
  430. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  431. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  432. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +133 -0
  433. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
  434. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  435. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  436. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +38 -1
  437. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +10 -0
  438. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  439. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  440. package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
  441. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
  442. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  443. package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
  444. package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
  445. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  446. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  447. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  448. package/src/resources/extensions/gsd/worktree-state-projection.ts +33 -0
  449. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  450. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  451. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  452. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  453. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  454. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  455. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  456. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  457. package/src/resources/extensions/subagent/index.ts +8 -15
  458. package/src/resources/shared/package-manager-detection.ts +39 -0
  459. package/src/resources/skills/agent-browser/SKILL.md +1 -1
  460. package/src/resources/skills/api-design/SKILL.md +1 -1
  461. package/src/resources/skills/code-optimizer/SKILL.md +6 -11
  462. package/src/resources/skills/create-gsd-extension/SKILL.md +1 -1
  463. package/src/resources/skills/create-mcp-server/SKILL.md +1 -1
  464. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  465. package/src/resources/skills/create-skill/workflows/verify-skill.md +2 -10
  466. package/src/resources/skills/debug-like-expert/references/when-to-research.md +1 -5
  467. package/src/resources/skills/decompose-into-slices/SKILL.md +3 -3
  468. package/src/resources/skills/dependency-upgrade/SKILL.md +1 -1
  469. package/src/resources/skills/forensics/SKILL.md +2 -2
  470. package/src/resources/skills/grill-me/SKILL.md +1 -1
  471. package/src/resources/skills/handoff/SKILL.md +1 -1
  472. package/src/resources/skills/make-interfaces-feel-better/SKILL.md +1 -1
  473. package/src/resources/skills/observability/SKILL.md +1 -1
  474. package/src/resources/skills/security-review/SKILL.md +1 -1
  475. package/src/resources/skills/spike-wrap-up/SKILL.md +1 -1
  476. package/src/resources/skills/tdd/SKILL.md +1 -1
  477. package/src/resources/skills/write-docs/SKILL.md +1 -1
  478. package/src/resources/skills/write-milestone-brief/SKILL.md +1 -1
  479. /package/dist/web/standalone/.next/static/{szb-HAt0IoSx3docUZO-E → tH1tnDYt1E0hK9Ien73Z0}/_buildManifest.js +0 -0
  480. /package/dist/web/standalone/.next/static/{szb-HAt0IoSx3docUZO-E → tH1tnDYt1E0hK9Ien73Z0}/_ssgManifest.js +0 -0
@@ -5,7 +5,8 @@ import assert from "node:assert/strict";
5
5
  import test from "node:test";
6
6
 
7
7
  import { DISCUSS_TOOLS_ALLOWLIST } from "../constants.ts";
8
- import { buildMinimalAutoGsdToolSet, buildMinimalGsdToolSet, buildMinimalGsdWorkflowToolSet, buildRequestScopedGsdToolSet, MINIMAL_AUTO_BASE_TOOL_NAMES, MINIMAL_GSD_TOOL_NAMES, restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch } from "../bootstrap/register-hooks.ts";
8
+ import { buildMinimalAutoGsdToolSet, buildMinimalGsdToolSet, buildMinimalGsdWorkflowToolSet, buildRequestScopedGsdToolSet, MINIMAL_AUTO_BASE_TOOL_NAMES, MINIMAL_GSD_TOOL_NAMES, requestHasGsdCustomType, restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch } from "../bootstrap/register-hooks.ts";
9
+ import { applyUnitSkillVisibility } from "../skill-scope.ts";
9
10
 
10
11
  test("buildMinimalGsdToolSet preserves non-GSD tools and replaces broad GSD surface", () => {
11
12
  const result = buildMinimalGsdToolSet([
@@ -54,6 +55,17 @@ test("buildMinimalGsdToolSet always preserves ToolSearch shim", () => {
54
55
  assert.ok(result.includes("ToolSearch"));
55
56
  });
56
57
 
58
+ test("requestHasGsdCustomType detects GSD-driven requests (drives interactive default scoping)", () => {
59
+ // Plain interactive chat → no gsd-* customType → scoped to the minimal set.
60
+ assert.equal(requestHasGsdCustomType(undefined), false);
61
+ assert.equal(requestHasGsdCustomType([]), false);
62
+ assert.equal(requestHasGsdCustomType([{ customType: "user" }, {}]), false);
63
+ // GSD workflow commands carry a gsd-* customType → keep their full surface.
64
+ assert.equal(requestHasGsdCustomType([{ customType: "gsd-quick-task" }]), true);
65
+ assert.equal(requestHasGsdCustomType([{}, { customType: "gsd-workflow-template" }]), true);
66
+ assert.equal(requestHasGsdCustomType([{ customType: "gsd-run" }]), true);
67
+ });
68
+
57
69
  test("buildMinimalAutoGsdToolSet keeps unit-specific completion tools without aliases", () => {
58
70
  const result = buildMinimalAutoGsdToolSet([
59
71
  "ask_user_questions",
@@ -86,6 +98,24 @@ test("buildMinimalAutoGsdToolSet keeps unit-specific completion tools without al
86
98
  assert.ok(!result.includes("gsd_complete_slice"));
87
99
  });
88
100
 
101
+ test("buildMinimalAutoGsdToolSet re-resolves run-uat browser tools from the registry when dropped from the active set", () => {
102
+ // B2 drops the browser surface from the advertised interactive set, but the
103
+ // tools stay registered. run-uat must still get them: resolution reads the
104
+ // full registry, not just the (browser-stripped) active set.
105
+ const active = ["ask_user_questions", "bash", "read", "gsd_summary_save"];
106
+ const registered = [
107
+ ...active,
108
+ "browser_navigate",
109
+ "browser_click",
110
+ "browser_snapshot_refs",
111
+ "gsd_exec",
112
+ ];
113
+ const result = buildMinimalAutoGsdToolSet(active, "run-uat", registered);
114
+ assert.ok(result.includes("browser_navigate"), "run-uat needs browser_navigate");
115
+ assert.ok(result.includes("browser_click"), "run-uat needs browser_click");
116
+ assert.ok(result.includes("gsd_summary_save"));
117
+ });
118
+
89
119
  test("buildMinimalAutoGsdToolSet keeps only the auto base non-GSD tools", () => {
90
120
  const result = buildMinimalAutoGsdToolSet([
91
121
  "ask_user_questions",
@@ -171,6 +201,24 @@ test("buildMinimalAutoGsdToolSet preserves browser tools for run-uat", () => {
171
201
  assert.ok(!result.includes("gsd_task_complete"));
172
202
  });
173
203
 
204
+ test("buildMinimalAutoGsdToolSet includes discuss-slice persistence tools", () => {
205
+ const result = buildMinimalAutoGsdToolSet([
206
+ "bash",
207
+ "read",
208
+ "gsd_summary_save",
209
+ "gsd_decision_save",
210
+ "gsd_plan_slice",
211
+ "gsd_task_complete",
212
+ "memory_query",
213
+ "capture_thought",
214
+ ], "discuss-slice");
215
+
216
+ assert.ok(result.includes("gsd_summary_save"));
217
+ assert.ok(result.includes("gsd_decision_save"));
218
+ assert.ok(!result.includes("gsd_plan_slice"));
219
+ assert.ok(!result.includes("gsd_task_complete"));
220
+ });
221
+
174
222
  test("buildMinimalAutoGsdToolSet includes closeout tool for complete-slice", () => {
175
223
  const result = buildMinimalAutoGsdToolSet([
176
224
  "bash",
@@ -435,3 +483,21 @@ test("scopeGsdWorkflowToolsForDispatch applies and restores per-unit skill visib
435
483
  assert.deepEqual(visibleSkills, ["previous-skill"]);
436
484
  assert.equal(calls.filter((call) => call.kind === "skills").length, 2);
437
485
  });
486
+
487
+ test("applyUnitSkillVisibility sets manifest or clears for wildcard", () => {
488
+ const calls: Array<string[] | undefined> = [];
489
+ applyUnitSkillVisibility({
490
+ setVisibleSkills: (names) => {
491
+ calls.push(names);
492
+ },
493
+ }, "plan-milestone");
494
+ assert.ok(Array.isArray(calls[0]));
495
+ assert.ok(calls[0]!.includes("tdd"));
496
+
497
+ applyUnitSkillVisibility({
498
+ setVisibleSkills: (names) => {
499
+ calls.push(names);
500
+ },
501
+ }, "execute-task");
502
+ assert.equal(calls[1], undefined);
503
+ });
@@ -235,12 +235,16 @@ test("gsd_task_complete — enrichment arrays are optional", () => {
235
235
  "milestoneId",
236
236
  "oneLiner",
237
237
  "narrative",
238
- "verification",
239
238
  ];
240
239
  for (const field of coreRequired) {
241
240
  assert.ok(required.has(field), `core field "${field}" must be required`);
242
241
  }
243
242
 
243
+ assert.ok(
244
+ !required.has("verification"),
245
+ "verification must be optional at the schema layer so step-mode can recover when verificationEvidence is present",
246
+ );
247
+
244
248
  // Enrichment fields must be optional
245
249
  const enrichmentFields = [
246
250
  "keyFiles",
@@ -272,6 +276,25 @@ test("gsd_task_complete — validates with only core params", () => {
272
276
  assert.strictEqual(errors.length, 0, `Minimal params should validate but got errors: ${errors.join(", ")}`);
273
277
  });
274
278
 
279
+ test("gsd_task_complete — accepts evidence-only verification at schema layer", () => {
280
+ const tool = getTool("gsd_task_complete");
281
+ assert.ok(tool, "gsd_task_complete must be registered");
282
+
283
+ const params = {
284
+ taskId: "T01",
285
+ sliceId: "S01",
286
+ milestoneId: "M001",
287
+ oneLiner: "Implemented the feature",
288
+ narrative: "Created the module and wired it up.",
289
+ verificationEvidence: [
290
+ { command: "npm test", exitCode: 0, verdict: "pass", durationMs: 1234 },
291
+ ],
292
+ };
293
+
294
+ const errors = validateSchema(tool, params);
295
+ assert.strictEqual(errors.length, 0, `Evidence-only params should validate but got errors: ${errors.join(", ")}`);
296
+ });
297
+
275
298
  // ─── gsd_complete_milestone: enrichment arrays must be optional ──────────────
276
299
 
277
300
  test("gsd_complete_milestone — enrichment arrays are optional", () => {
@@ -0,0 +1,28 @@
1
+ import assert from "node:assert/strict";
2
+
3
+ import { visibleWidth } from "@gsd/pi-tui";
4
+
5
+ const ANSI_PATTERN = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
6
+
7
+ function stripAnsi(text: string): string {
8
+ return text.replace(ANSI_PATTERN, "");
9
+ }
10
+
11
+ export function assertFullOuterBorder(lines: string[], width: number): void {
12
+ assert.ok(lines.length >= 2, "dialog must include top and bottom borders");
13
+
14
+ for (const [index, line] of lines.entries()) {
15
+ assert.equal(visibleWidth(line), width, `line ${index} must fill dialog width`);
16
+ }
17
+
18
+ const top = stripAnsi(lines[0] ?? "");
19
+ const bottom = stripAnsi(lines.at(-1) ?? "");
20
+ assert.match(top, /^[╭┌].*[╮┐]$/, `top border missing full corners: ${top}`);
21
+ assert.match(bottom, /^[╰└].*[╯┘]$/, `bottom border missing full corners: ${bottom}`);
22
+
23
+ for (let index = 1; index < lines.length - 1; index++) {
24
+ const line = stripAnsi(lines[index] ?? "");
25
+ assert.match(line, /^[│┃├]/, `line ${index} missing left border: ${line}`);
26
+ assert.match(line, /[│┃┤]$/, `line ${index} missing right border: ${line}`);
27
+ }
28
+ }
@@ -5,8 +5,10 @@ import { describe, test } from "node:test";
5
5
  import assert from "node:assert/strict";
6
6
 
7
7
  import { visibleWidth } from "@gsd/pi-tui";
8
+ import { assertFullOuterBorder } from "./tui-border-assertions.ts";
8
9
  import {
9
10
  padRightVisible,
11
+ renderDialogFrame,
10
12
  renderFrame,
11
13
  renderKeyHints,
12
14
  renderPanel,
@@ -60,6 +62,18 @@ describe("tui render kit", () => {
60
62
  }
61
63
  });
62
64
 
65
+ test("renderDialogFrame draws a full titled modal border with footer", () => {
66
+ const lines = renderDialogFrame(theme, "Dialog", ["row", "long ".repeat(40)], 40, {
67
+ footer: renderKeyHints(theme, ["esc close"], 36),
68
+ });
69
+ assertWidth(lines, 40);
70
+ assertFullOuterBorder(lines, 40);
71
+ assert.match(lines[0] ?? "", /^╭─ Dialog ─+╮$/);
72
+ assert.ok(lines.some((line) => line.startsWith("│") && line.endsWith("│")));
73
+ assert.ok(lines.some((line) => line.startsWith("├") && line.endsWith("┤")));
74
+ assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
75
+ });
76
+
63
77
  test("renderPanel stays within width and draws no vertical borders", () => {
64
78
  for (const width of [3, 40, 80]) {
65
79
  const lines = renderPanel(theme, "Title", ["row", "long ".repeat(40)], width);
@@ -14,6 +14,7 @@ import {
14
14
  type SkillsPolicy,
15
15
  type UnitContextManifest,
16
16
  } from "../unit-context-manifest.ts";
17
+ import { resolveSkillManifest } from "../skill-manifest.ts";
17
18
  import {
18
19
  ALLOWED_PLANNING_DISPATCH_AGENTS,
19
20
  shouldBlockPlanningUnit,
@@ -148,6 +149,23 @@ test("#4782 phase 1: skills policy shapes are valid discriminated-union members"
148
149
  }
149
150
  });
150
151
 
152
+ test("#4782 phase 1b: allowlist skills policy matches skill-manifest resolver", () => {
153
+ for (const unitType of KNOWN_UNIT_TYPES) {
154
+ const manifest = UNIT_MANIFESTS[unitType];
155
+ const allowlist = resolveSkillManifest(unitType);
156
+ if (allowlist) {
157
+ assert.strictEqual(manifest.skills.mode, "allowlist", unitType);
158
+ assert.deepEqual(
159
+ [...(manifest.skills as Extract<SkillsPolicy, { mode: "allowlist" }>).skills].sort(),
160
+ [...allowlist].sort(),
161
+ unitType,
162
+ );
163
+ } else {
164
+ assert.notStrictEqual(manifest.skills.mode, "allowlist", unitType);
165
+ }
166
+ }
167
+ });
168
+
151
169
  // ─── Lookup helper ────────────────────────────────────────────────────────
152
170
 
153
171
  test("#4782 phase 1: resolveManifest returns null for an unknown unit type", () => {
@@ -0,0 +1,26 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import * as userInputBoundary from "../user-input-boundary.ts";
5
+
6
+ test("lastAssistantText extracts the latest assistant text block content", () => {
7
+ const lastAssistantText = (userInputBoundary as {
8
+ lastAssistantText?: (messages: unknown[] | null | undefined) => string;
9
+ }).lastAssistantText;
10
+
11
+ assert.equal(typeof lastAssistantText, "function");
12
+ assert.equal(
13
+ lastAssistantText?.([
14
+ { role: "assistant", content: "Older message" },
15
+ {
16
+ role: "assistant",
17
+ content: [
18
+ { type: "text", text: "First line" },
19
+ { type: "text", text: "Second line" },
20
+ ],
21
+ },
22
+ ]),
23
+ "First line\nSecond line",
24
+ );
25
+ assert.equal(lastAssistantText?.(null), "");
26
+ });
@@ -0,0 +1,23 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { chooseVisionAskVariant, VISION_ASK_VARIANTS } from "../vision-ask.ts";
4
+
5
+ test("vision ask variants stay varied and conversational", () => {
6
+ assert.ok(VISION_ASK_VARIANTS.length >= 6, "keep enough openers to avoid repetition");
7
+ assert.equal(new Set(VISION_ASK_VARIANTS).size, VISION_ASK_VARIANTS.length, "openers should be unique");
8
+
9
+ for (const opener of VISION_ASK_VARIANTS) {
10
+ assert.ok(opener.length <= 72, `opener should stay short: ${opener}`);
11
+ assert.doesNotMatch(opener, /\n/, "opener should be a single line");
12
+ assert.doesNotMatch(opener, /\bstakeholders?|key success metrics?|business objectives?\b/i, "avoid corporate wording");
13
+ assert.notEqual(opener, "What's the vision?", "do not keep the old fixed opener in rotation");
14
+ }
15
+ });
16
+
17
+ test("chooseVisionAskVariant picks from the configured opener list", () => {
18
+ assert.equal(chooseVisionAskVariant(() => 0), VISION_ASK_VARIANTS[0]);
19
+ assert.equal(
20
+ chooseVisionAskVariant((exclusiveMax) => exclusiveMax - 1),
21
+ VISION_ASK_VARIANTS[VISION_ASK_VARIANTS.length - 1],
22
+ );
23
+ });
@@ -14,6 +14,7 @@ import { test } from "node:test";
14
14
  import assert from "node:assert/strict";
15
15
 
16
16
  import { GSDVisualizerOverlay, TAB_COUNT } from "../visualizer-overlay.ts";
17
+ import { assertFullOuterBorder } from "./tui-border-assertions.ts";
17
18
 
18
19
  function makeTui() {
19
20
  const renders: number[] = [];
@@ -50,7 +51,11 @@ test("overlay renders 10 tabs (Progress, Timeline, Deps, Metrics, Health, Agent,
50
51
  overlay.loading = true; // body shows loading text, but tab bar renders regardless
51
52
 
52
53
  // Use a very wide terminal so the tab bar is not truncated.
53
- const lines = overlay.render(200).map(stripAnsi);
54
+ const rawLines = overlay.render(200);
55
+ assertFullOuterBorder(rawLines, 200);
56
+ const lines = rawLines.map(stripAnsi);
57
+ assert.match(lines[0] ?? "", /^╭─ GSD Visualizer /);
58
+ assert.match(lines.at(-1) ?? "", /^╰─+╯$/);
54
59
  const tabBar = lines.find((l) => l.includes("Progress") && l.includes("Export"));
55
60
  assert.ok(tabBar, `expected a tab-bar line containing all labels, got:\n${lines.slice(0, 5).join("\n")}`);
56
61
  for (const label of ["Progress", "Timeline", "Deps", "Metrics", "Health", "Agent", "Changes", "Knowledge", "Captures", "Export"]) {
@@ -1,6 +1,12 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
3
6
 
7
+ import { registerHooks } from "../bootstrap/register-hooks.ts";
8
+ import { GSD_WORKFLOW_MCP_SERVER_NAME } from "../mcp-project-config.ts";
9
+ import { clearSkillSnapshot, snapshotSkills } from "../skill-discovery.js";
4
10
  import { prepareWorkflowMcpForProject, shouldAutoPrepareWorkflowMcp } from "../workflow-mcp-auto-prep.ts";
5
11
 
6
12
  test("shouldAutoPrepareWorkflowMcp enables prep for externalCli local transport", () => {
@@ -74,3 +80,130 @@ test("prepareWorkflowMcpForProject warns with /gsd mcp init guidance when prep f
74
80
  assert.equal(notifications[0].level, "warning");
75
81
  assert.match(notifications[0].message, /Please run \/gsd mcp init \./);
76
82
  });
83
+
84
+ test("before_agent_start auto-prepares project workflow MCP for Claude Code CLI", async (t) => {
85
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-mcp-before-agent-"));
86
+ const originalCwd = process.cwd();
87
+ const notifications: string[] = [];
88
+ const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<any> | any>>();
89
+ const pi = {
90
+ on(event: string, handler: (event: any, ctx?: any) => Promise<any> | any) {
91
+ const existing = handlers.get(event) ?? [];
92
+ existing.push(handler);
93
+ handlers.set(event, existing);
94
+ },
95
+ getActiveTools: () => [],
96
+ getAllTools: () => [],
97
+ setActiveTools() {},
98
+ };
99
+
100
+ t.after(() => {
101
+ process.chdir(originalCwd);
102
+ rmSync(projectRoot, { recursive: true, force: true });
103
+ });
104
+
105
+ process.chdir(projectRoot);
106
+ registerHooks(pi as any, []);
107
+
108
+ const beforeAgentStart = handlers.get("before_agent_start")?.[0];
109
+ assert.ok(beforeAgentStart, "before_agent_start hook should be registered");
110
+
111
+ await beforeAgentStart(
112
+ { prompt: "hello", systemPrompt: "base" },
113
+ {
114
+ cwd: projectRoot,
115
+ model: { provider: "claude-code", baseUrl: "local://claude-code" },
116
+ modelRegistry: {
117
+ getProviderAuthMode: () => "externalCli",
118
+ isProviderRequestReady: () => true,
119
+ },
120
+ ui: {
121
+ notify(message: string) {
122
+ notifications.push(message);
123
+ },
124
+ setWidget() {},
125
+ },
126
+ },
127
+ );
128
+
129
+ const configPath = join(projectRoot, ".mcp.json");
130
+ assert.equal(existsSync(configPath), true, "Claude Code CLI turns should create project MCP config");
131
+
132
+ const parsed = JSON.parse(readFileSync(configPath, "utf-8")) as {
133
+ mcpServers?: Record<string, unknown>;
134
+ };
135
+ assert.ok(parsed.mcpServers?.[GSD_WORKFLOW_MCP_SERVER_NAME]);
136
+ assert.match(notifications.join("\n"), /Claude Code MCP prepared/);
137
+ });
138
+
139
+ test("before_agent_start returns discovered skill fallback without project .gsd", async (t) => {
140
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-skill-before-agent-"));
141
+ const skillHome = mkdtempSync(join(tmpdir(), "gsd-skill-home-"));
142
+ const originalCwd = process.cwd();
143
+ const originalHome = process.env.HOME;
144
+ const originalGsdHome = process.env.GSD_HOME;
145
+ const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<any> | any>>();
146
+ const pi = {
147
+ on(event: string, handler: (event: any, ctx?: any) => Promise<any> | any) {
148
+ const existing = handlers.get(event) ?? [];
149
+ existing.push(handler);
150
+ handlers.set(event, existing);
151
+ },
152
+ getActiveTools: () => [],
153
+ getAllTools: () => [],
154
+ setActiveTools() {},
155
+ };
156
+
157
+ t.after(() => {
158
+ process.chdir(originalCwd);
159
+ clearSkillSnapshot();
160
+ if (originalHome === undefined) {
161
+ delete process.env.HOME;
162
+ } else {
163
+ process.env.HOME = originalHome;
164
+ }
165
+ if (originalGsdHome === undefined) {
166
+ delete process.env.GSD_HOME;
167
+ } else {
168
+ process.env.GSD_HOME = originalGsdHome;
169
+ }
170
+ rmSync(projectRoot, { recursive: true, force: true });
171
+ rmSync(skillHome, { recursive: true, force: true });
172
+ });
173
+
174
+ process.env.HOME = skillHome;
175
+ process.env.GSD_HOME = join(skillHome, ".gsd");
176
+ process.chdir(projectRoot);
177
+ snapshotSkills();
178
+
179
+ const skillDir = join(skillHome, ".agents", "skills", "late-skill");
180
+ mkdirSync(skillDir, { recursive: true });
181
+ const skillPath = join(skillDir, "SKILL.md");
182
+ writeFileSync(skillPath, "---\nname: late-skill\ndescription: Use for late skill.\n---\n\n# late-skill\n");
183
+
184
+ registerHooks(pi as any, []);
185
+ const beforeAgentStart = handlers.get("before_agent_start")?.[0];
186
+ assert.ok(beforeAgentStart, "before_agent_start hook should be registered");
187
+
188
+ const result = await beforeAgentStart(
189
+ { prompt: "hello", systemPrompt: "event system prompt" },
190
+ {
191
+ cwd: projectRoot,
192
+ model: { provider: "openai", baseUrl: "https://api.openai.com" },
193
+ modelRegistry: {
194
+ getProviderAuthMode: () => "apiKey",
195
+ isProviderRequestReady: () => false,
196
+ },
197
+ getSystemPrompt: () => "context system prompt",
198
+ reload: async () => {},
199
+ ui: {
200
+ notify() {},
201
+ setWidget() {},
202
+ },
203
+ },
204
+ );
205
+
206
+ assert.match(result?.systemPrompt ?? "", /<newly_discovered_skills>/);
207
+ assert.match(result?.systemPrompt ?? "", /late-skill/);
208
+ assert.equal(result?.systemPrompt?.includes(skillPath), true);
209
+ });
@@ -108,6 +108,34 @@ test("executeSummarySave persists artifact and returns computed path", async ()
108
108
  }
109
109
  });
110
110
 
111
+ test("executeSummarySave mirrors milestone artifacts into the active worktree projection", async () => {
112
+ const base = makeTmpBase();
113
+ const worktree = join(base, ".gsd", "worktrees", "M001");
114
+ try {
115
+ mkdirSync(join(worktree, ".gsd"), { recursive: true });
116
+ writeFileSync(join(worktree, ".git"), "gitdir: ../../../.git/worktrees/M001\n");
117
+ openTestDb(base);
118
+
119
+ const result = await inProjectDir(worktree, () => executeSummarySave({
120
+ milestone_id: "M001",
121
+ slice_id: "S02",
122
+ artifact_type: "RESEARCH",
123
+ content: "# S02 Research\n\ncanonical and worktree",
124
+ }, worktree));
125
+
126
+ assert.equal(result.details.operation, "save_summary");
127
+ const relPath = "milestones/M001/slices/S02/S02-RESEARCH.md";
128
+ const projectPath = join(base, ".gsd", relPath);
129
+ const worktreePath = join(worktree, ".gsd", relPath);
130
+ assert.equal(existsSync(projectPath), true, "canonical artifact should be written");
131
+ assert.equal(existsSync(worktreePath), true, "active worktree projection should be mirrored");
132
+ assert.match(readFileSync(worktreePath, "utf-8"), /S02 Research/);
133
+ } finally {
134
+ closeDatabase();
135
+ cleanup(base);
136
+ }
137
+ });
138
+
111
139
  test("executeTaskComplete coerces string verificationEvidence entries", async () => {
112
140
  const base = makeTmpBase();
113
141
  try {
@@ -148,6 +176,60 @@ test("executeTaskComplete coerces string verificationEvidence entries", async ()
148
176
  }
149
177
  });
150
178
 
179
+ test("executeTaskComplete derives missing verification from evidence", async () => {
180
+ const base = makeTmpBase();
181
+ try {
182
+ openTestDb(base);
183
+ const planDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
184
+ mkdirSync(planDir, { recursive: true });
185
+ writeFileSync(join(planDir, "S01-PLAN.md"), "# S01\n\n- [ ] **T01: Demo** `est:5m`\n");
186
+
187
+ const result = await inProjectDir(base, () => executeTaskComplete({
188
+ milestoneId: "M001",
189
+ sliceId: "S01",
190
+ taskId: "T01",
191
+ oneLiner: "Completed task",
192
+ narrative: "Did the work",
193
+ verificationEvidence: [
194
+ { command: "npm test", exitCode: 0, verdict: "pass", durationMs: 1234 },
195
+ ],
196
+ }, base));
197
+
198
+ assert.equal(result.details.operation, "complete_task");
199
+ const db = _getAdapter();
200
+ assert.ok(db, "DB should be open");
201
+ const row = db!.prepare(
202
+ "SELECT verification_result FROM tasks WHERE milestone_id = ? AND slice_id = ? AND id = ?",
203
+ ).get("M001", "S01", "T01") as Record<string, unknown> | undefined;
204
+
205
+ assert.match(String(row?.verification_result), /Verification evidence recorded/);
206
+ assert.match(String(row?.verification_result), /`npm test` exited 0 \(pass\)/);
207
+ } finally {
208
+ closeDatabase();
209
+ cleanup(base);
210
+ }
211
+ });
212
+
213
+ test("executeTaskComplete returns a tool error when verification cannot be derived", async () => {
214
+ const base = makeTmpBase();
215
+ try {
216
+ openTestDb(base);
217
+ const result = await inProjectDir(base, () => executeTaskComplete({
218
+ milestoneId: "M001",
219
+ sliceId: "S01",
220
+ taskId: "T01",
221
+ oneLiner: "Completed task",
222
+ narrative: "Did the work",
223
+ }, base));
224
+
225
+ assert.equal(result.isError, true);
226
+ assert.match(String(result.content[0]?.text), /verification is required/);
227
+ } finally {
228
+ closeDatabase();
229
+ cleanup(base);
230
+ }
231
+ });
232
+
151
233
  test("executeSliceComplete preserves omitted optional requirement arrays", async () => {
152
234
  const base = makeTmpBase();
153
235
  try {
@@ -9,7 +9,7 @@ import { join } from "node:path";
9
9
  import { probeGitConflictState } from "../git-conflict-state.js";
10
10
  import { ensureWorkspaceGitReadyForPath } from "../workspace-git-preflight.js";
11
11
  import { isWorkspaceGitAllowedCommand } from "../workspace-git-guard.js";
12
- import { cleanup, git, makeTempRepo } from "./test-utils.ts";
12
+ import { cleanup, git, makeTempDir, makeTempRepo } from "./test-utils.ts";
13
13
 
14
14
  function seedGsdConflict(base: string): void {
15
15
  mkdirSync(join(base, ".gsd"), { recursive: true });
@@ -60,6 +60,21 @@ test("probeGitConflictState reports clean repo", () => {
60
60
  }
61
61
  });
62
62
 
63
+ test("ensureWorkspaceGitReadyForPath allows fresh non-git project setup folders", async () => {
64
+ const base = makeTempDir("gsd-ws-git-non-repo-");
65
+ try {
66
+ mkdirSync(join(base, ".gsd"), { recursive: true });
67
+
68
+ const probe = probeGitConflictState(base);
69
+ assert.equal(probe.status, "clean");
70
+
71
+ const ready = await ensureWorkspaceGitReadyForPath(base);
72
+ assert.equal(ready.ok, true);
73
+ } finally {
74
+ cleanup(base);
75
+ }
76
+ });
77
+
63
78
  test("ensureWorkspaceGitReadyForPath auto-resolves .gsd/ conflicts", async () => {
64
79
  const base = makeTempRepo("gsd-ws-git-heal-");
65
80
  try {
@@ -212,6 +212,34 @@ test("enterMilestone returns ok:true mode:none when isolation disabled", () => {
212
212
  assert.equal(s.basePath, "/project");
213
213
  });
214
214
 
215
+ test("adoptStrandedMilestone forces branch recovery even when normal preferences differ", (t) => {
216
+ const previousCwd = process.cwd();
217
+ const base = makeGitRepoBase({ isolation: "worktree" });
218
+ t.after(() => cleanupRepoBase(base, previousCwd));
219
+
220
+ const s = makeSession({ basePath: base, originalBasePath: base });
221
+ const deps = makeDeps();
222
+ const ctx = makeCtx();
223
+ const lifecycle = new WorktreeLifecycle(s, deps);
224
+
225
+ const result = lifecycle.adoptStrandedMilestone("M001", base, ctx, {
226
+ mode: "branch",
227
+ });
228
+
229
+ assert.equal(result.ok, true, `expected ok:true, got: ${JSON.stringify(result)}`);
230
+ if (result.ok) {
231
+ assert.equal(result.mode, "branch");
232
+ assert.equal(result.path, base);
233
+ }
234
+ assert.equal(s.basePath, base);
235
+ assert.equal(s.strandedRecoveryIsolationMode, "branch");
236
+ const currentBranch = execFileSync("git", ["branch", "--show-current"], {
237
+ cwd: base,
238
+ encoding: "utf-8",
239
+ }).trim();
240
+ assert.equal(currentBranch, "milestone/M001");
241
+ });
242
+
215
243
  test("enterMilestone returns ok:false reason:isolation-degraded when session degraded", () => {
216
244
  const s = makeSession({ isolationDegraded: true });
217
245
  const deps = makeDeps({ getIsolationMode: () => "branch" });
@@ -2,7 +2,7 @@
2
2
  // File Purpose: Worktree State Projection Module — typed-Interface contract tests for projectRootToWorktree (ADR-016).
3
3
  import test from "node:test";
4
4
  import assert from "node:assert/strict";
5
- import { mkdtempSync, rmSync, mkdirSync } from "node:fs";
5
+ import { existsSync, mkdtempSync, readFileSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
6
6
  import { join } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
  import { WorktreeStateProjection } from "../worktree-state-projection.js";
@@ -60,6 +60,43 @@ test("projectRootToWorktree is idempotent — repeated calls do not throw", () =
60
60
  }
61
61
  });
62
62
 
63
+ test("projectRootToWorktree forwards root PROJECT.md into isolated worktrees", () => {
64
+ const { dir, cleanup } = makeProjectRoot();
65
+ try {
66
+ const worktree = join(dir, ".gsd", "worktrees", "M001");
67
+ mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
68
+ mkdirSync(join(worktree, ".gsd"), { recursive: true });
69
+
70
+ const projectContent = [
71
+ "# Project",
72
+ "",
73
+ "## Milestone Sequence",
74
+ "",
75
+ "- [ ] M001: Foundation — Establish the runnable slice.",
76
+ "",
77
+ ].join("\n");
78
+ writeFileSync(join(dir, ".gsd", "PROJECT.md"), projectContent);
79
+ writeFileSync(join(dir, ".gsd", "REQUIREMENTS.md"), "# Requirements\n");
80
+ writeFileSync(join(dir, ".gsd", "milestones", "M001", "M001-ROADMAP.md"), "# M001\n");
81
+
82
+ const workspace = createWorkspace(worktree);
83
+ const scope = scopeMilestone(workspace, "M001");
84
+ const projection = new WorktreeStateProjection();
85
+
86
+ projection.projectRootToWorktree(scope);
87
+
88
+ const projectedProject = join(worktree, ".gsd", "PROJECT.md");
89
+ assert.ok(existsSync(projectedProject), "PROJECT.md is available to worktree-bound units");
90
+ assert.equal(readFileSync(projectedProject, "utf-8"), projectContent);
91
+ assert.ok(
92
+ existsSync(join(worktree, ".gsd", "milestones", "M001", "M001-ROADMAP.md")),
93
+ "milestone artifacts still project into the worktree",
94
+ );
95
+ } finally {
96
+ cleanup();
97
+ }
98
+ });
99
+
63
100
  // ─── projectWorktreeToRoot — Module contract ────────────────────────────────
64
101
 
65
102
  test("projectWorktreeToRoot exists and accepts a MilestoneScope", () => {