@opengsd/gsd-pi 1.0.2-dev.867e002 → 1.0.2-dev.88c4dc5

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 (337) hide show
  1. package/dist/onboarding.js +22 -3
  2. package/dist/resource-loader.js +3 -1
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/context7/index.js +12 -2
  5. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  6. package/dist/resources/extensions/google-cli/index.js +30 -0
  7. package/dist/resources/extensions/google-cli/models.js +55 -0
  8. package/dist/resources/extensions/google-cli/package.json +11 -0
  9. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  10. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +62 -1
  12. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
  13. package/dist/resources/extensions/gsd/auto/phases.js +37 -0
  14. package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
  15. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -11
  18. package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
  19. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  20. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  21. package/dist/resources/extensions/gsd/auto.js +6 -1
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  23. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -7
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
  27. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  29. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  30. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  31. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  32. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  33. package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
  34. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  35. package/dist/resources/extensions/gsd/doctor.js +6 -1
  36. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  37. package/dist/resources/extensions/gsd/guided-flow.js +5 -6
  38. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  39. package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
  40. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  41. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  42. package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
  43. package/dist/resources/extensions/gsd/preferences.js +14 -2
  44. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  45. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  46. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/system.md +1 -3
  48. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  49. package/dist/resources/extensions/gsd/repository-registry.js +3 -1
  50. package/dist/resources/extensions/gsd/safety/evidence-collector.js +11 -4
  51. package/dist/resources/extensions/gsd/skill-activation.js +233 -0
  52. package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
  53. package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
  54. package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
  55. package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
  56. package/dist/resources/extensions/gsd/skill-scope.js +52 -0
  57. package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
  58. package/dist/resources/extensions/gsd/skills.js +60 -0
  59. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
  61. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
  62. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  63. package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
  64. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
  65. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  66. package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
  67. package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
  68. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  69. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  70. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  71. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  72. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  73. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  74. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  75. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  76. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  77. package/dist/web/standalone/.next/BUILD_ID +1 -1
  78. package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
  79. package/dist/web/standalone/.next/build-manifest.json +2 -2
  80. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  81. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.html +1 -1
  98. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app-paths-manifest.json +4 -4
  105. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  106. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  108. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  109. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  110. package/package.json +1 -1
  111. package/packages/cloud-mcp-gateway/package.json +2 -2
  112. package/packages/contracts/dist/rpc.test.js +5 -0
  113. package/packages/contracts/dist/rpc.test.js.map +1 -1
  114. package/packages/contracts/dist/workflow.d.ts +7 -0
  115. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  116. package/packages/contracts/dist/workflow.js +8 -0
  117. package/packages/contracts/dist/workflow.js.map +1 -1
  118. package/packages/contracts/dist/workflow.test.js +1 -0
  119. package/packages/contracts/dist/workflow.test.js.map +1 -1
  120. package/packages/contracts/package.json +1 -1
  121. package/packages/daemon/package.json +4 -4
  122. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
  123. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  124. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
  125. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  138. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  140. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  142. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  144. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  147. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  148. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  149. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  150. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  151. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  152. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  153. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  154. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  155. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  156. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  157. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  158. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  159. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  160. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  161. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  162. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  163. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  164. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  165. package/packages/gsd-agent-modes/package.json +7 -7
  166. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  167. package/packages/mcp-server/dist/workflow-tools.js +28 -5
  168. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  169. package/packages/mcp-server/package.json +3 -3
  170. package/packages/native/package.json +1 -1
  171. package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  172. package/packages/pi-agent-core/dist/harness/skills.js +6 -0
  173. package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
  174. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
  175. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
  176. package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
  177. package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
  178. package/packages/pi-agent-core/package.json +1 -1
  179. package/packages/pi-ai/dist/models.generated.d.ts +8 -59
  180. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  181. package/packages/pi-ai/dist/models.generated.js +21 -72
  182. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  183. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  184. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  185. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  186. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
  188. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  189. package/packages/pi-ai/dist/types.d.ts +2 -0
  190. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  191. package/packages/pi-ai/dist/types.js.map +1 -1
  192. package/packages/pi-ai/package.json +1 -1
  193. package/packages/pi-coding-agent/README.md +1 -1
  194. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
  195. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  198. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  201. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
  203. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/skills.js +3 -0
  205. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  206. package/packages/pi-coding-agent/package.json +7 -7
  207. package/packages/pi-tui/package.json +1 -1
  208. package/packages/rpc-client/package.json +2 -2
  209. package/pkg/package.json +1 -1
  210. package/src/resources/extensions/context7/index.ts +15 -2
  211. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  212. package/src/resources/extensions/google-cli/index.ts +34 -0
  213. package/src/resources/extensions/google-cli/models.ts +57 -0
  214. package/src/resources/extensions/google-cli/package.json +11 -0
  215. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  216. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  217. package/src/resources/extensions/gsd/auto/loop.ts +74 -1
  218. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
  219. package/src/resources/extensions/gsd/auto/phases.ts +46 -0
  220. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
  221. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  222. package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
  223. package/src/resources/extensions/gsd/auto-post-unit.ts +43 -14
  224. package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
  225. package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
  226. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  227. package/src/resources/extensions/gsd/auto.ts +6 -1
  228. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  229. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
  230. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
  231. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
  232. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
  233. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  234. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  235. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  236. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  237. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  238. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  239. package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
  240. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  241. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  242. package/src/resources/extensions/gsd/doctor.ts +6 -1
  243. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  244. package/src/resources/extensions/gsd/guided-flow.ts +5 -6
  245. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  246. package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
  247. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  248. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  249. package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
  250. package/src/resources/extensions/gsd/preferences.ts +17 -2
  251. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  252. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  253. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  254. package/src/resources/extensions/gsd/prompts/system.md +1 -3
  255. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  256. package/src/resources/extensions/gsd/repository-registry.ts +3 -1
  257. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -4
  258. package/src/resources/extensions/gsd/skill-activation.ts +292 -0
  259. package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
  260. package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
  261. package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
  262. package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
  263. package/src/resources/extensions/gsd/skill-scope.ts +63 -0
  264. package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
  265. package/src/resources/extensions/gsd/skills.ts +75 -0
  266. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
  267. package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
  268. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
  269. package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
  270. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
  271. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +21 -0
  272. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
  273. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
  274. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
  275. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +12 -0
  276. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  277. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  278. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  279. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  280. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  281. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  282. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  283. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  284. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
  285. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  286. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
  287. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  288. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
  289. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
  290. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  291. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  292. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  293. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  294. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  295. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
  296. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  297. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
  298. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
  299. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  300. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
  301. package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
  302. package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
  303. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  304. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
  305. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +19 -0
  306. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  307. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  308. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  309. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
  310. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
  311. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  312. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  313. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +74 -1
  314. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
  315. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  316. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  317. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  318. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  319. package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
  320. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
  321. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  322. package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
  323. package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
  324. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  325. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  326. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  327. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  328. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  329. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  330. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  331. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  332. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  333. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  334. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  335. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  336. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → PkhJfy4kKo4yUHj1wY7_q}/_buildManifest.js +0 -0
  337. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → PkhJfy4kKo4yUHj1wY7_q}/_ssgManifest.js +0 -0
@@ -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,11 +1,12 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
 
7
7
  import { registerHooks } from "../bootstrap/register-hooks.ts";
8
8
  import { GSD_WORKFLOW_MCP_SERVER_NAME } from "../mcp-project-config.ts";
9
+ import { clearSkillSnapshot, snapshotSkills } from "../skill-discovery.js";
9
10
  import { prepareWorkflowMcpForProject, shouldAutoPrepareWorkflowMcp } from "../workflow-mcp-auto-prep.ts";
10
11
 
11
12
  test("shouldAutoPrepareWorkflowMcp enables prep for externalCli local transport", () => {
@@ -134,3 +135,75 @@ test("before_agent_start auto-prepares project workflow MCP for Claude Code CLI"
134
135
  assert.ok(parsed.mcpServers?.[GSD_WORKFLOW_MCP_SERVER_NAME]);
135
136
  assert.match(notifications.join("\n"), /Claude Code MCP prepared/);
136
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" });
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { test } from "node:test";
16
16
  import assert from "node:assert/strict";
17
- import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
17
+ import { existsSync, mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
18
18
  import { join } from "node:path";
19
19
  import { tmpdir } from "node:os";
20
20
 
@@ -79,3 +79,47 @@ test("#2942: injected existsFn — milestones/ alone is enough", () => {
79
79
  p === "/proj/.gsd" || p === "/proj/.gsd/milestones";
80
80
  assert.equal(hasGsdBootstrapArtifacts("/proj/.gsd", existsFn), true);
81
81
  });
82
+
83
+ test("bare /gsd routes zombie .gsd folders to project init before closeout/db checks", async (t) => {
84
+ const base = mkdtempSync(join(tmpdir(), "gsd-zombie-bare-command-"));
85
+ t.after(() => rmSync(base, { recursive: true, force: true }));
86
+ mkdirSync(join(base, ".gsd", "runtime"), { recursive: true });
87
+
88
+ const previousCwd = process.cwd();
89
+ const previousGsdHome = process.env.GSD_HOME;
90
+ const previousProjectRoot = process.env.GSD_PROJECT_ROOT;
91
+ try {
92
+ process.chdir(base);
93
+ process.env.GSD_HOME = join(base, ".test-gsd-home");
94
+ delete process.env.GSD_PROJECT_ROOT;
95
+
96
+ const notifications: string[] = [];
97
+ const ctx = {
98
+ hasUI: false,
99
+ ui: {
100
+ notify: (content: unknown) => notifications.push(String(content)),
101
+ setStatus: () => {},
102
+ setWidget: () => {},
103
+ },
104
+ };
105
+ const { handleAutoCommand } = await import("../commands/handlers/auto.ts");
106
+
107
+ await handleAutoCommand("", ctx as any, {} as any);
108
+
109
+ assert.ok(
110
+ notifications.some((message) => message.includes("/gsd init did not start")),
111
+ "bare /gsd should route unbootstrapped zombie folders to the init wizard",
112
+ );
113
+ assert.equal(
114
+ existsSync(join(base, ".gsd", "gsd.db")),
115
+ false,
116
+ "bare /gsd should not create the project DB before init has bootstrapped .gsd/",
117
+ );
118
+ } finally {
119
+ process.chdir(previousCwd);
120
+ if (previousGsdHome === undefined) delete process.env.GSD_HOME;
121
+ else process.env.GSD_HOME = previousGsdHome;
122
+ if (previousProjectRoot === undefined) delete process.env.GSD_PROJECT_ROOT;
123
+ else process.env.GSD_PROJECT_ROOT = previousProjectRoot;
124
+ }
125
+ });
@@ -173,6 +173,15 @@ export async function handleCompleteTask(
173
173
  if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
174
174
  return { error: "milestoneId is required and must be a non-empty string" };
175
175
  }
176
+ if (!params.oneLiner || typeof params.oneLiner !== "string" || params.oneLiner.trim() === "") {
177
+ return { error: "oneLiner is required and must be a non-empty string" };
178
+ }
179
+ if (!params.narrative || typeof params.narrative !== "string" || params.narrative.trim() === "") {
180
+ return { error: "narrative is required and must be a non-empty string" };
181
+ }
182
+ if (!params.verification || typeof params.verification !== "string" || params.verification.trim() === "") {
183
+ return { error: "verification is required and must be a non-empty string" };
184
+ }
176
185
 
177
186
  const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, params.milestoneId);
178
187
 
@@ -14,8 +14,11 @@ import { isContextModeEnabled, type ContextModeConfig } from "../preferences-typ
14
14
  import { contextModeDisabledResult, type ToolExecutionResult } from "./context-mode-tool-result.js";
15
15
 
16
16
  export interface ExecToolParams {
17
- runtime: ExecSandboxRequest["runtime"];
18
- script: string;
17
+ runtime?: unknown;
18
+ script?: unknown;
19
+ command?: unknown;
20
+ cmd?: unknown;
21
+ code?: unknown;
19
22
  purpose?: string;
20
23
  timeout_ms?: number;
21
24
  }
@@ -80,6 +83,39 @@ function paramError(message: string): ToolExecutionResult {
80
83
  };
81
84
  }
82
85
 
86
+ function normalizeRuntime(value: unknown): ExecSandboxRequest["runtime"] | ToolExecutionResult {
87
+ if (value === undefined || value === null || value === "") return "bash";
88
+ if (typeof value !== "string") {
89
+ return paramError(`invalid runtime "${String(value)}" — must be bash | node | python`);
90
+ }
91
+ const normalized = value.trim().toLowerCase();
92
+ if (normalized === "" || normalized === "bash" || normalized === "sh" || normalized === "shell") return "bash";
93
+ if (normalized === "node" || normalized === "nodejs" || normalized === "js" || normalized === "javascript") return "node";
94
+ if (normalized === "python" || normalized === "python3" || normalized === "py") return "python";
95
+ return paramError(`invalid runtime "${value}" — must be bash | node | python`);
96
+ }
97
+
98
+ function normalizeScript(params: ExecToolParams): string | ToolExecutionResult {
99
+ const candidates = [params.script, params.command, params.cmd, params.code];
100
+ let sawNonString = false;
101
+ for (const candidate of candidates) {
102
+ if (candidate === undefined || candidate === null) continue;
103
+ if (typeof candidate !== "string") {
104
+ sawNonString = true;
105
+ continue;
106
+ }
107
+ if (candidate.trim().length > 0) return candidate;
108
+ }
109
+ if (sawNonString) {
110
+ return paramError("script/command must be a non-empty string");
111
+ }
112
+ return paramError("script is required and must be a non-empty string");
113
+ }
114
+
115
+ function isToolExecutionResult(value: unknown): value is ToolExecutionResult {
116
+ return typeof value === "object" && value !== null && Array.isArray((value as { content?: unknown }).content);
117
+ }
118
+
83
119
  function escapeRegExp(value: string): string {
84
120
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
85
121
  }
@@ -201,14 +237,10 @@ export async function executeGsdExec(
201
237
  ): Promise<ToolExecutionResult> {
202
238
  if (!isEnabled(deps.preferences)) return contextModeDisabledResult("gsd_exec");
203
239
 
204
- const runtime = params.runtime;
205
- if (runtime !== "bash" && runtime !== "node" && runtime !== "python") {
206
- return paramError(`invalid runtime "${String(runtime)}" — must be bash | node | python`);
207
- }
208
- const script = typeof params.script === "string" ? params.script : "";
209
- if (script.trim().length === 0) {
210
- return paramError("script is required and must be a non-empty string");
211
- }
240
+ const runtime = normalizeRuntime(params.runtime);
241
+ if (isToolExecutionResult(runtime)) return runtime;
242
+ const script = normalizeScript(params);
243
+ if (isToolExecutionResult(script)) return script;
212
244
  if (Buffer.byteLength(script, "utf8") > 200_000) {
213
245
  return paramError("script exceeds the 200 KB length limit");
214
246
  }
@@ -15,8 +15,10 @@ import {
15
15
  } from "../gsd-db.js";
16
16
  import { GATE_REGISTRY } from "../gate-registry.js";
17
17
  import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
18
- import { resolveMilestoneFile, resolveSliceFile } from "../paths.js";
18
+ import { clearPathCache, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
19
+ import { saveFile, clearParseCache } from "../files.js";
19
20
  import { unlinkSync } from "node:fs";
21
+ import { join } from "node:path";
20
22
  import type { CompleteMilestoneParams } from "./complete-milestone.js";
21
23
  import { handleCompleteMilestone } from "./complete-milestone.js";
22
24
  import { handleCompleteTask } from "./complete-task.js";
@@ -96,6 +98,28 @@ function registerProjectMilestoneSequence(content: string): string[] {
96
98
  return registered;
97
99
  }
98
100
 
101
+ async function mirrorArtifactToActiveWorktreeProjection(
102
+ basePath: string,
103
+ relativePath: string,
104
+ content: string,
105
+ ): Promise<void> {
106
+ const contract = resolveGsdPathContract(basePath);
107
+ if (!contract.worktreeGsd) return;
108
+ if (contract.worktreeGsd === contract.projectGsd) return;
109
+
110
+ const fullPath = join(contract.worktreeGsd, relativePath);
111
+ try {
112
+ await saveFile(fullPath, content);
113
+ clearPathCache();
114
+ clearParseCache();
115
+ invalidateStateCache();
116
+ } catch (err) {
117
+ logWarning("tool", `gsd_summary_save worktree projection mirror failed: ${(err as Error).message}`, {
118
+ path: relativePath,
119
+ });
120
+ }
121
+ }
122
+
99
123
  export async function executeSummarySave(
100
124
  params: SummarySaveParams,
101
125
  basePath: string = process.cwd(),
@@ -197,6 +221,7 @@ export async function executeSummarySave(
197
221
  },
198
222
  basePath,
199
223
  );
224
+ await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
200
225
 
201
226
  let registeredMilestones: string[] = [];
202
227
  if (params.artifact_type === "PROJECT") {
@@ -306,7 +331,7 @@ export interface TaskCompleteParams {
306
331
  milestoneId: string;
307
332
  oneLiner: string;
308
333
  narrative: string;
309
- verification: string;
334
+ verification?: string;
310
335
  deviations?: string;
311
336
  knownIssues?: string;
312
337
  keyFiles?: string[];
@@ -315,6 +340,40 @@ export interface TaskCompleteParams {
315
340
  verificationEvidence?: VerificationEvidenceInput[];
316
341
  }
317
342
 
343
+ type NormalizedVerificationEvidence = {
344
+ command: string;
345
+ exitCode: number;
346
+ verdict: string;
347
+ durationMs: number;
348
+ };
349
+
350
+ function normalizeVerificationEvidence(
351
+ evidence: VerificationEvidenceInput[] | undefined,
352
+ ): NormalizedVerificationEvidence[] {
353
+ return (evidence ?? []).map((entry) =>
354
+ typeof entry === "string"
355
+ ? { command: entry, exitCode: -1, verdict: "unknown (coerced from string)", durationMs: 0 }
356
+ : entry,
357
+ );
358
+ }
359
+
360
+ function deriveVerificationSummary(
361
+ evidence: NormalizedVerificationEvidence[],
362
+ ): string | null {
363
+ if (evidence.length === 0) return null;
364
+
365
+ const rendered = evidence.slice(0, 3).map((entry) => {
366
+ const command = entry.command.trim() || "(unspecified command)";
367
+ const verdict = entry.verdict.trim() || "recorded";
368
+ return `\`${command}\` exited ${entry.exitCode} (${verdict})`;
369
+ });
370
+ const suffix = evidence.length > rendered.length
371
+ ? `; ${evidence.length - rendered.length} more check(s) recorded`
372
+ : "";
373
+
374
+ return `Verification evidence recorded: ${rendered.join("; ")}${suffix}.`;
375
+ }
376
+
318
377
  export type CompleteMilestoneExecutorParams = Partial<CompleteMilestoneParams> & Record<string, unknown>;
319
378
  export type SliceCompleteExecutorParams = CompleteSliceParams;
320
379
  export type PlanMilestoneExecutorParams = PlanMilestoneParams;
@@ -350,9 +409,27 @@ export async function executeTaskComplete(
350
409
  }
351
410
  try {
352
411
  const coerced = { ...params };
353
- coerced.verificationEvidence = (params.verificationEvidence ?? []).map((v) =>
354
- typeof v === "string" ? { command: v, exitCode: -1, verdict: "unknown (coerced from string)", durationMs: 0 } : v,
355
- );
412
+ const verificationEvidence = normalizeVerificationEvidence(params.verificationEvidence);
413
+ coerced.verificationEvidence = verificationEvidence;
414
+
415
+ const verification = typeof params.verification === "string" ? params.verification.trim() : "";
416
+ if (verification.length === 0) {
417
+ const derived = deriveVerificationSummary(verificationEvidence);
418
+ if (derived) {
419
+ coerced.verification = derived;
420
+ } else if (params.blockerDiscovered === true) {
421
+ coerced.verification = "Not run: blocker discovered before verification.";
422
+ } else {
423
+ return {
424
+ content: [{
425
+ type: "text",
426
+ text: "Error completing task: verification is required unless verificationEvidence is provided or blockerDiscovered is true.",
427
+ }],
428
+ details: { operation: "complete_task", error: "verification_required" },
429
+ isError: true,
430
+ };
431
+ }
432
+ }
356
433
 
357
434
  const result = await handleCompleteTask(coerced as any, basePath);
358
435
  if ("error" in result) {
@@ -151,3 +151,85 @@ export function renderFrame(
151
151
  lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
152
152
  return lines.map((line) => safeLine(line, width, ""));
153
153
  }
154
+
155
+ export interface DialogFrameOptions {
156
+ borderColor?: string;
157
+ paddingX?: number;
158
+ footer?: string | string[];
159
+ scroll?: {
160
+ offset: number;
161
+ visibleRows: number;
162
+ totalRows: number;
163
+ trackOffset?: number;
164
+ trackRows?: number;
165
+ };
166
+ }
167
+
168
+ function renderTitledTopBorder(
169
+ theme: ThemeLike,
170
+ title: string,
171
+ width: number,
172
+ border: (text: string) => string,
173
+ ): string {
174
+ const trimmedTitle = title.trim();
175
+ if (!trimmedTitle || width < 10) {
176
+ return border("╭" + "─".repeat(width - 2) + "╮");
177
+ }
178
+
179
+ const maxTitleWidth = Math.max(0, width - 7);
180
+ const safeTitle = safeLine(trimmedTitle, maxTitleWidth);
181
+ const fill = Math.max(0, width - visibleWidth(safeTitle) - 5);
182
+ return border("╭─ ") + theme.bold(theme.fg("accent", safeTitle)) + border(" " + "─".repeat(fill) + "╮");
183
+ }
184
+
185
+ export function renderDialogFrame(
186
+ theme: ThemeLike,
187
+ title: string,
188
+ inner: string[],
189
+ width: number,
190
+ options: DialogFrameOptions = {},
191
+ ): string[] {
192
+ if (width < 4) return inner.map((line) => safeLine(line, width));
193
+
194
+ const borderColor = options.borderColor ?? "borderAccent";
195
+ const paddingX = Math.max(0, options.paddingX ?? 1);
196
+ const contentWidth = Math.max(0, width - 2 - paddingX * 2);
197
+ const border = (text: string) => theme.fg(borderColor, text);
198
+ const pad = " ".repeat(paddingX);
199
+ const lines = [renderTitledTopBorder(theme, title, width, border)];
200
+
201
+ const scroll = options.scroll;
202
+ const bodyRows = inner.length;
203
+ const trackOffset = Math.max(0, Math.min(scroll?.trackOffset ?? 0, bodyRows));
204
+ const trackRows = Math.max(0, Math.min(scroll?.trackRows ?? bodyRows, bodyRows - trackOffset));
205
+ const scrollable = !!scroll && scroll.totalRows > scroll.visibleRows && trackRows > 0;
206
+ const thumbLen = scrollable
207
+ ? Math.max(1, Math.round((scroll.visibleRows / scroll.totalRows) * trackRows))
208
+ : 0;
209
+ const maxThumbStart = Math.max(0, trackRows - thumbLen);
210
+ const maxScrollOffset = scrollable ? Math.max(1, scroll.totalRows - scroll.visibleRows) : 1;
211
+ const thumbStart = scrollable
212
+ ? trackOffset + Math.min(maxThumbStart, Math.round((scroll.offset / maxScrollOffset) * maxThumbStart))
213
+ : -1;
214
+
215
+ for (let i = 0; i < inner.length; i++) {
216
+ const line = inner[i] ?? "";
217
+ const rightBorder = scrollable && i >= thumbStart && i < thumbStart + thumbLen ? "┃" : "│";
218
+ lines.push(border("│") + pad + padRightVisible(line, contentWidth) + pad + border(rightBorder));
219
+ }
220
+
221
+ const footer = Array.isArray(options.footer)
222
+ ? options.footer
223
+ : options.footer
224
+ ? [options.footer]
225
+ : [];
226
+ if (footer.length > 0) {
227
+ lines.push(border("├" + "─".repeat(width - 2) + "┤"));
228
+ for (const line of footer) {
229
+ lines.push(border("│") + pad + padRightVisible(line, contentWidth) + pad + border("│"));
230
+ }
231
+ }
232
+
233
+ lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
234
+ return lines.map((line) => safeLine(line, width, ""));
235
+ }