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

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 +6 -6
  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 +6 -6
  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 → QKed8_bmMc7zc3WLM7MW9}/_buildManifest.js +0 -0
  337. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → QKed8_bmMc7zc3WLM7MW9}/_ssgManifest.js +0 -0
@@ -52,6 +52,103 @@ function makeRepoWithUnmergedCompletedMilestone(): string {
52
52
  return base;
53
53
  }
54
54
 
55
+ function makeRepoWithStrandedActiveMilestone(options: { deepPlanning?: boolean } = {}): string {
56
+ const base = mkdtempSync(join(tmpdir(), "gsd-stranded-bootstrap-"));
57
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
58
+ writeFileSync(
59
+ join(base, ".gsd", "PREFERENCES.md"),
60
+ options.deepPlanning
61
+ ? "---\nplanning_depth: deep\ngit:\n isolation: \"none\"\n---\n"
62
+ : "---\ngit:\n isolation: \"none\"\n---\n",
63
+ );
64
+ runGit(base, ["init"]);
65
+ runGit(base, ["config", "user.email", "test@test.com"]);
66
+ runGit(base, ["config", "user.name", "Test"]);
67
+ writeFileSync(join(base, "README.md"), "# test\n");
68
+ runGit(base, ["add", "-A"]);
69
+ runGit(base, ["commit", "-m", "init"]);
70
+ runGit(base, ["branch", "-M", "main"]);
71
+
72
+ runGit(base, ["checkout", "-b", "milestone/M001"]);
73
+ writeFileSync(join(base, "m001.txt"), "in-progress stranded work\n");
74
+ runGit(base, ["add", "-A"]);
75
+ runGit(base, ["commit", "-m", "feat: M001 in progress"]);
76
+ runGit(base, ["checkout", "main"]);
77
+
78
+ openDatabase(join(base, ".gsd", "gsd.db"));
79
+ insertMilestone({ id: "M001", title: "Active milestone", status: "active" });
80
+ closeDatabase();
81
+
82
+ return base;
83
+ }
84
+
85
+ function makeRepoWithMultipleStrandedMilestones(): string {
86
+ const base = mkdtempSync(join(tmpdir(), "gsd-multiple-stranded-bootstrap-"));
87
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
88
+ mkdirSync(join(base, ".gsd", "milestones", "M002"), { recursive: true });
89
+ writeFileSync(
90
+ join(base, ".gsd", "PREFERENCES.md"),
91
+ "---\ngit:\n isolation: \"none\"\n---\n",
92
+ );
93
+ runGit(base, ["init"]);
94
+ runGit(base, ["config", "user.email", "test@test.com"]);
95
+ runGit(base, ["config", "user.name", "Test"]);
96
+ writeFileSync(join(base, "README.md"), "# test\n");
97
+ runGit(base, ["add", "-A"]);
98
+ runGit(base, ["commit", "-m", "init"]);
99
+ runGit(base, ["branch", "-M", "main"]);
100
+
101
+ runGit(base, ["checkout", "-b", "milestone/M001"]);
102
+ writeFileSync(join(base, "m001.txt"), "active stranded work\n");
103
+ runGit(base, ["add", "-A"]);
104
+ runGit(base, ["commit", "-m", "feat: M001 in progress"]);
105
+ runGit(base, ["checkout", "main"]);
106
+
107
+ runGit(base, ["checkout", "-b", "milestone/M002"]);
108
+ writeFileSync(join(base, "m002.txt"), "additional stranded work\n");
109
+ runGit(base, ["add", "-A"]);
110
+ runGit(base, ["commit", "-m", "feat: M002 in progress"]);
111
+ runGit(base, ["checkout", "main"]);
112
+
113
+ openDatabase(join(base, ".gsd", "gsd.db"));
114
+ insertMilestone({ id: "M001", title: "Active milestone", status: "active" });
115
+ insertMilestone({ id: "M002", title: "Pending milestone", status: "pending" });
116
+ closeDatabase();
117
+
118
+ return base;
119
+ }
120
+
121
+ function makeRepoWithRecoveredCleanupAndStrandedMismatch(): string {
122
+ const base = mkdtempSync(join(tmpdir(), "gsd-headless-stranded-bootstrap-"));
123
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
124
+ mkdirSync(join(base, ".gsd", "milestones", "M002"), { recursive: true });
125
+ writeFileSync(
126
+ join(base, ".gsd", "PREFERENCES.md"),
127
+ "---\ngit:\n isolation: \"none\"\n---\n",
128
+ );
129
+ runGit(base, ["init"]);
130
+ runGit(base, ["config", "user.email", "test@test.com"]);
131
+ runGit(base, ["config", "user.name", "Test"]);
132
+ writeFileSync(join(base, "README.md"), "# test\n");
133
+ runGit(base, ["add", "-A"]);
134
+ runGit(base, ["commit", "-m", "init"]);
135
+ runGit(base, ["branch", "-M", "main"]);
136
+
137
+ runGit(base, ["branch", "milestone/M001"]);
138
+ runGit(base, ["checkout", "-b", "milestone/M002"]);
139
+ writeFileSync(join(base, "m002.txt"), "in-progress stranded work\n");
140
+ runGit(base, ["add", "-A"]);
141
+ runGit(base, ["commit", "-m", "feat: M002 in progress"]);
142
+ runGit(base, ["checkout", "main"]);
143
+
144
+ openDatabase(join(base, ".gsd", "gsd.db"));
145
+ insertMilestone({ id: "M001", title: "Completed milestone", status: "complete" });
146
+ insertMilestone({ id: "M002", title: "Stranded milestone", status: "active" });
147
+ closeDatabase();
148
+
149
+ return base;
150
+ }
151
+
55
152
  function makeCtx(notifications: Array<{ message: string; level?: string }>) {
56
153
  const model = { provider: "claude-code", id: "claude-sonnet-4-6", contextWindow: 128000 };
57
154
  return {
@@ -159,3 +256,342 @@ test("bootstrap aborts before starting next milestone when completed orphan merg
159
256
  rmSync(base, { recursive: true, force: true });
160
257
  }
161
258
  });
259
+
260
+ test("headless bootstrap checks stranded work before recovered-complete shortcut", async () => {
261
+ const base = makeRepoWithRecoveredCleanupAndStrandedMismatch();
262
+ const previousCwd = process.cwd();
263
+ const previousHeadless = process.env.GSD_HEADLESS;
264
+ const previousParallelWorker = process.env.GSD_PARALLEL_WORKER;
265
+ const previousMilestoneLock = process.env.GSD_MILESTONE_LOCK;
266
+ const s = new AutoSession();
267
+ const notifications: Array<{ message: string; level?: string }> = [];
268
+
269
+ try {
270
+ process.env.GSD_HEADLESS = "1";
271
+ process.env.GSD_PARALLEL_WORKER = "1";
272
+ process.env.GSD_MILESTONE_LOCK = "M001";
273
+
274
+ const ready = await bootstrapAutoSession(
275
+ s,
276
+ makeCtx(notifications) as any,
277
+ {
278
+ getThinkingLevel: () => "medium",
279
+ getActiveTools: () => [],
280
+ events: { emit: () => {} },
281
+ } as any,
282
+ base,
283
+ false,
284
+ false,
285
+ {
286
+ shouldUseWorktreeIsolation: () => false,
287
+ registerSigtermHandler: () => {},
288
+ registerAutoWorkerForSession: () => {},
289
+ lockBase: () => base,
290
+ buildLifecycle: () => ({
291
+ adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
292
+ s.basePath = sessionBase;
293
+ if (originalBase !== undefined) {
294
+ s.originalBasePath = originalBase;
295
+ } else if (!s.originalBasePath) {
296
+ s.originalBasePath = sessionBase;
297
+ }
298
+ },
299
+ enterMilestone: () => ({ ok: true, mode: "none", path: base }),
300
+ adoptOrphanWorktree: <T extends { merged: boolean }>(
301
+ _mid: string,
302
+ _base: string,
303
+ run: () => T,
304
+ ): T => run(),
305
+ }) as any,
306
+ },
307
+ {
308
+ classification: "none",
309
+ lock: null,
310
+ pausedSession: null,
311
+ state: null,
312
+ recovery: null,
313
+ recoveryPrompt: null,
314
+ recoveryToolCallCount: 0,
315
+ artifactSatisfied: false,
316
+ hasResumableDiskState: false,
317
+ isBootstrapCrash: false,
318
+ },
319
+ );
320
+
321
+ const messages = notifications.map((entry) => entry.message).join("\n");
322
+ assert.equal(ready, false);
323
+ assert.match(messages, /Stranded work for M002 blocks auto-mode/);
324
+ assert.doesNotMatch(messages, /all milestones complete/);
325
+ } finally {
326
+ if (previousHeadless === undefined) {
327
+ delete process.env.GSD_HEADLESS;
328
+ } else {
329
+ process.env.GSD_HEADLESS = previousHeadless;
330
+ }
331
+ if (previousParallelWorker === undefined) {
332
+ delete process.env.GSD_PARALLEL_WORKER;
333
+ } else {
334
+ process.env.GSD_PARALLEL_WORKER = previousParallelWorker;
335
+ }
336
+ if (previousMilestoneLock === undefined) {
337
+ delete process.env.GSD_MILESTONE_LOCK;
338
+ } else {
339
+ process.env.GSD_MILESTONE_LOCK = previousMilestoneLock;
340
+ }
341
+ try {
342
+ closeDatabase();
343
+ } catch {}
344
+ process.chdir(previousCwd);
345
+ rmSync(base, { recursive: true, force: true });
346
+ }
347
+ });
348
+
349
+ test("bootstrap blocks active stranded recovery when another open milestone also has stranded work", async () => {
350
+ const base = makeRepoWithMultipleStrandedMilestones();
351
+ const previousCwd = process.cwd();
352
+ const s = new AutoSession();
353
+ const adoptCalls: string[] = [];
354
+ const notifications: Array<{ message: string; level?: string }> = [];
355
+
356
+ try {
357
+ const ready = await bootstrapAutoSession(
358
+ s,
359
+ makeCtx(notifications) as any,
360
+ {
361
+ getThinkingLevel: () => "medium",
362
+ getActiveTools: () => [],
363
+ events: { emit: () => {} },
364
+ } as any,
365
+ base,
366
+ false,
367
+ false,
368
+ {
369
+ shouldUseWorktreeIsolation: () => false,
370
+ registerSigtermHandler: () => {},
371
+ registerAutoWorkerForSession: () => {},
372
+ lockBase: () => base,
373
+ buildLifecycle: () => ({
374
+ adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
375
+ s.basePath = sessionBase;
376
+ if (originalBase !== undefined) {
377
+ s.originalBasePath = originalBase;
378
+ } else if (!s.originalBasePath) {
379
+ s.originalBasePath = sessionBase;
380
+ }
381
+ },
382
+ enterMilestone: () => ({ ok: true, mode: "none", path: base }),
383
+ adoptStrandedMilestone: (milestoneId: string) => {
384
+ adoptCalls.push(milestoneId);
385
+ return { ok: true, mode: "branch", path: base };
386
+ },
387
+ adoptOrphanWorktree: <T extends { merged: boolean }>(
388
+ _mid: string,
389
+ _base: string,
390
+ run: () => T,
391
+ ): T => run(),
392
+ }) as any,
393
+ },
394
+ {
395
+ classification: "none",
396
+ lock: null,
397
+ pausedSession: null,
398
+ state: null,
399
+ recovery: null,
400
+ recoveryPrompt: null,
401
+ recoveryToolCallCount: 0,
402
+ artifactSatisfied: false,
403
+ hasResumableDiskState: false,
404
+ isBootstrapCrash: false,
405
+ },
406
+ );
407
+
408
+ const messages = notifications.map((entry) => entry.message).join("\n");
409
+ assert.equal(ready, false);
410
+ assert.deepEqual(adoptCalls, []);
411
+ assert.match(messages, /Stranded work for M002 blocks auto-mode before M001/);
412
+ } finally {
413
+ try {
414
+ closeDatabase();
415
+ } catch {}
416
+ process.chdir(previousCwd);
417
+ rmSync(base, { recursive: true, force: true });
418
+ }
419
+ });
420
+
421
+ test("bootstrap adopts stranded active branch even when isolation is none", async () => {
422
+ const base = makeRepoWithStrandedActiveMilestone();
423
+ const previousCwd = process.cwd();
424
+ const s = new AutoSession();
425
+ const adoptCalls: Array<{ milestoneId: string; mode: string }> = [];
426
+ const enterCalls: string[] = [];
427
+ const notifications: Array<{ message: string; level?: string }> = [];
428
+
429
+ try {
430
+ const ready = await bootstrapAutoSession(
431
+ s,
432
+ makeCtx(notifications) as any,
433
+ {
434
+ getThinkingLevel: () => "medium",
435
+ getActiveTools: () => [],
436
+ events: { emit: () => {} },
437
+ } as any,
438
+ base,
439
+ false,
440
+ false,
441
+ {
442
+ shouldUseWorktreeIsolation: () => false,
443
+ registerSigtermHandler: () => {},
444
+ registerAutoWorkerForSession: () => {},
445
+ lockBase: () => base,
446
+ buildLifecycle: () => ({
447
+ adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
448
+ s.basePath = sessionBase;
449
+ if (originalBase !== undefined) {
450
+ s.originalBasePath = originalBase;
451
+ } else if (!s.originalBasePath) {
452
+ s.originalBasePath = sessionBase;
453
+ }
454
+ },
455
+ enterMilestone: (milestoneId: string) => {
456
+ enterCalls.push(milestoneId);
457
+ return { ok: true, mode: "none", path: base };
458
+ },
459
+ adoptStrandedMilestone: (
460
+ milestoneId: string,
461
+ sessionBase: string,
462
+ _ctx: unknown,
463
+ opts: { mode: "worktree" | "branch" },
464
+ ) => {
465
+ adoptCalls.push({ milestoneId, mode: opts.mode });
466
+ s.basePath = sessionBase;
467
+ s.originalBasePath = sessionBase;
468
+ s.strandedRecoveryIsolationMode = opts.mode;
469
+ return { ok: true, mode: opts.mode, path: sessionBase };
470
+ },
471
+ adoptOrphanWorktree: <T extends { merged: boolean }>(
472
+ _mid: string,
473
+ _base: string,
474
+ run: () => T,
475
+ ): T => run(),
476
+ }) as any,
477
+ },
478
+ {
479
+ classification: "none",
480
+ lock: null,
481
+ pausedSession: null,
482
+ state: null,
483
+ recovery: null,
484
+ recoveryPrompt: null,
485
+ recoveryToolCallCount: 0,
486
+ artifactSatisfied: false,
487
+ hasResumableDiskState: false,
488
+ isBootstrapCrash: false,
489
+ },
490
+ );
491
+
492
+ assert.equal(ready, true);
493
+ assert.deepEqual(adoptCalls, [{ milestoneId: "M001", mode: "branch" }]);
494
+ assert.deepEqual(enterCalls, []);
495
+ assert.equal(s.currentMilestoneId, "M001");
496
+ assert.equal(s.strandedRecoveryIsolationMode, "branch");
497
+ assert.match(
498
+ notifications.map((entry) => entry.message).join("\n"),
499
+ /Recovering stranded work for M001/,
500
+ );
501
+ } finally {
502
+ try {
503
+ closeDatabase();
504
+ } catch {}
505
+ process.chdir(previousCwd);
506
+ rmSync(base, { recursive: true, force: true });
507
+ }
508
+ });
509
+
510
+ test("bootstrap adopts stranded active branch before deep project setup", async () => {
511
+ const base = makeRepoWithStrandedActiveMilestone({ deepPlanning: true });
512
+ const previousCwd = process.cwd();
513
+ const s = new AutoSession();
514
+ const adoptCalls: Array<{ milestoneId: string; mode: string }> = [];
515
+ const enterCalls: string[] = [];
516
+ const notifications: Array<{ message: string; level?: string }> = [];
517
+
518
+ try {
519
+ const ready = await bootstrapAutoSession(
520
+ s,
521
+ makeCtx(notifications) as any,
522
+ {
523
+ getThinkingLevel: () => "medium",
524
+ getActiveTools: () => [],
525
+ events: { emit: () => {} },
526
+ } as any,
527
+ base,
528
+ false,
529
+ false,
530
+ {
531
+ shouldUseWorktreeIsolation: () => false,
532
+ registerSigtermHandler: () => {},
533
+ registerAutoWorkerForSession: () => {},
534
+ lockBase: () => base,
535
+ buildLifecycle: () => ({
536
+ adoptSessionRoot: (sessionBase: string, originalBase?: string) => {
537
+ s.basePath = sessionBase;
538
+ if (originalBase !== undefined) {
539
+ s.originalBasePath = originalBase;
540
+ } else if (!s.originalBasePath) {
541
+ s.originalBasePath = sessionBase;
542
+ }
543
+ },
544
+ enterMilestone: (milestoneId: string) => {
545
+ enterCalls.push(milestoneId);
546
+ return { ok: true, mode: "none", path: base };
547
+ },
548
+ adoptStrandedMilestone: (
549
+ milestoneId: string,
550
+ sessionBase: string,
551
+ _ctx: unknown,
552
+ opts: { mode: "worktree" | "branch" },
553
+ ) => {
554
+ adoptCalls.push({ milestoneId, mode: opts.mode });
555
+ s.basePath = sessionBase;
556
+ s.originalBasePath = sessionBase;
557
+ s.strandedRecoveryIsolationMode = opts.mode;
558
+ return { ok: true, mode: opts.mode, path: sessionBase };
559
+ },
560
+ adoptOrphanWorktree: <T extends { merged: boolean }>(
561
+ _mid: string,
562
+ _base: string,
563
+ run: () => T,
564
+ ): T => run(),
565
+ }) as any,
566
+ },
567
+ {
568
+ classification: "none",
569
+ lock: null,
570
+ pausedSession: null,
571
+ state: null,
572
+ recovery: null,
573
+ recoveryPrompt: null,
574
+ recoveryToolCallCount: 0,
575
+ artifactSatisfied: false,
576
+ hasResumableDiskState: false,
577
+ isBootstrapCrash: false,
578
+ },
579
+ );
580
+
581
+ assert.equal(ready, true);
582
+ assert.deepEqual(adoptCalls, [{ milestoneId: "M001", mode: "branch" }]);
583
+ assert.deepEqual(enterCalls, []);
584
+ assert.equal(s.currentMilestoneId, "M001");
585
+ assert.equal(s.strandedRecoveryIsolationMode, "branch");
586
+ assert.match(
587
+ notifications.map((entry) => entry.message).join("\n"),
588
+ /Recovering stranded work for M001/,
589
+ );
590
+ } finally {
591
+ try {
592
+ closeDatabase();
593
+ } catch {}
594
+ process.chdir(previousCwd);
595
+ rmSync(base, { recursive: true, force: true });
596
+ }
597
+ });
@@ -9,6 +9,7 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
 
11
11
  import {
12
+ getCloseoutManualResolveBlocker,
12
13
  listUnresolvedCloseoutFailures,
13
14
  markLatestCloseoutFailureResolved,
14
15
  retryLatestCloseoutFailure,
@@ -99,3 +100,17 @@ test("closeout manual resolve refuses a dirty worktree", () => {
99
100
  rmSync(base, { recursive: true, force: true });
100
101
  }
101
102
  });
103
+
104
+ test("closeout manual resolve blocks non-git project roots without throwing", () => {
105
+ const base = mkdtempSync(join(tmpdir(), "gsd-closeout-recovery-non-git-"));
106
+ try {
107
+ mkdirSync(join(base, ".gsd"), { recursive: true });
108
+
109
+ assert.equal(
110
+ getCloseoutManualResolveBlocker(base),
111
+ `Could not inspect git status in ${base}.`,
112
+ );
113
+ } finally {
114
+ rmSync(base, { recursive: true, force: true });
115
+ }
116
+ });
@@ -16,8 +16,34 @@ import assert from "node:assert/strict";
16
16
  import { mkdirSync, writeFileSync, readFileSync, rmSync } from "node:fs";
17
17
  import { join } from "node:path";
18
18
  import { tmpdir } from "node:os";
19
+ import { visibleWidth } from "@gsd/pi-tui";
19
20
  import type { SecretsManifest, SecretsManifestEntry } from "../types.ts";
20
21
 
22
+ const ANSI_PATTERN = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
23
+
24
+ function stripAnsi(text: string): string {
25
+ return text.replace(ANSI_PATTERN, "");
26
+ }
27
+
28
+ function assertFullOuterBorder(lines: string[], width: number): void {
29
+ assert.ok(lines.length >= 2, "dialog must include top and bottom borders");
30
+
31
+ for (const [index, line] of lines.entries()) {
32
+ assert.equal(visibleWidth(line), width, `line ${index} must fill dialog width`);
33
+ }
34
+
35
+ const top = stripAnsi(lines[0] ?? "");
36
+ const bottom = stripAnsi(lines.at(-1) ?? "");
37
+ assert.match(top, /^╭.*╮$/, `top border missing full corners: ${top}`);
38
+ assert.match(bottom, /^╰.*╯$/, `bottom border missing full corners: ${bottom}`);
39
+
40
+ for (let index = 1; index < lines.length - 1; index++) {
41
+ const line = stripAnsi(lines[index] ?? "");
42
+ assert.match(line, /^[│├]/, `line ${index} missing left border: ${line}`);
43
+ assert.match(line, /[│┤]$/, `line ${index} missing right border: ${line}`);
44
+ }
45
+ }
46
+
21
47
  // Dynamic imports for files.ts functions to avoid cascading failure
22
48
  // when paths.js isn't available (files.ts statically imports paths.js)
23
49
  async function loadFilesExports(): Promise<{
@@ -300,6 +326,7 @@ test("showSecretsSummary: produces lines with correct status glyphs for each ent
300
326
 
301
327
  assert.ok(renderFn, "render function should have been captured from factory");
302
328
  const lines = renderFn!(80);
329
+ assertFullOuterBorder(lines, 80);
303
330
 
304
331
  // Verify each key appears in the output
305
332
  const output = lines.join("\n");
@@ -341,6 +368,7 @@ test("showSecretsSummary: existing keys shown with distinct status indicator", a
341
368
 
342
369
  assert.ok(renderFn, "render function should have been captured");
343
370
  const lines = renderFn!(80);
371
+ assertFullOuterBorder(lines, 80);
344
372
  const output = lines.join("\n");
345
373
 
346
374
  assert.ok(output.includes("NEW_KEY"), "should include NEW_KEY");
@@ -380,6 +408,7 @@ test("collectOneSecret: guidance lines appear in render output when guidance is
380
408
 
381
409
  assert.ok(renderFn, "render function should have been captured");
382
410
  const lines = renderFn!(80);
411
+ assertFullOuterBorder(lines, 80);
383
412
  const output = lines.join("\n");
384
413
 
385
414
  // Verify guidance steps appear in the output
@@ -417,6 +446,7 @@ test("collectOneSecret: guidance lines wrap long URLs instead of truncating", as
417
446
  assert.ok(renderFn, "render function should have been captured");
418
447
  // Render at narrow width to force wrapping
419
448
  const lines = renderFn!(50);
449
+ assertFullOuterBorder(lines, 50);
420
450
  const output = lines.join("\n");
421
451
 
422
452
  // The full URL should be present (wrapped, not truncated)
@@ -449,6 +479,7 @@ test("collectOneSecret: no guidance provided — render output has no guidance s
449
479
 
450
480
  assert.ok(renderFn, "render function should have been captured");
451
481
  const lines = renderFn!(80);
482
+ assertFullOuterBorder(lines, 80);
452
483
  const output = lines.join("\n");
453
484
 
454
485
  // Should include the key name and hint but no numbered guidance steps
@@ -156,9 +156,11 @@ test("handleContext writes open reports under the command project root", async (
156
156
  process.chdir(processProject);
157
157
  try {
158
158
  const binDir = mkdtempSync(join(tmpdir(), "gsd-context-bin-"));
159
- const xdgOpen = join(binDir, "xdg-open");
160
- writeFileSync(xdgOpen, "#!/bin/sh\nexit 0\n");
161
- chmodSync(xdgOpen, 0o755);
159
+ for (const opener of ["open", "xdg-open"]) {
160
+ const openerPath = join(binDir, opener);
161
+ writeFileSync(openerPath, "#!/bin/sh\nexit 0\n");
162
+ chmodSync(openerPath, 0o755);
163
+ }
162
164
  process.env.PATH = `${binDir}:${originalPath ?? ""}`;
163
165
 
164
166
  const notifications: string[] = [];
@@ -3,11 +3,11 @@
3
3
 
4
4
  import test from "node:test";
5
5
  import assert from "node:assert/strict";
6
- import { writeFileSync } from "node:fs";
6
+ import { mkdirSync, writeFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
 
9
9
  import { getWorkspaceGitBlockMessageForBase } from "../workspace-git-guard.js";
10
- import { cleanup, git, makeTempRepo } from "./test-utils.ts";
10
+ import { cleanup, git, makeTempDir, makeTempRepo } from "./test-utils.ts";
11
11
 
12
12
  function seedProductConflict(base: string): void {
13
13
  writeFileSync(join(base, "app.ts"), "root\n");
@@ -41,6 +41,19 @@ test("getWorkspaceGitBlockMessageForBase blocks auto when product conflicts rema
41
41
  }
42
42
  });
43
43
 
44
+ test("getWorkspaceGitBlockMessageForBase does not block project setup in non-git folders", async () => {
45
+ const base = makeTempDir("gsd-dispatch-ws-git-new-project-");
46
+ try {
47
+ mkdirSync(join(base, ".gsd"), { recursive: true });
48
+
49
+ assert.equal(await getWorkspaceGitBlockMessageForBase(base, ""), null);
50
+ assert.equal(await getWorkspaceGitBlockMessageForBase(base, "init"), null);
51
+ assert.equal(await getWorkspaceGitBlockMessageForBase(base, "new-project"), null);
52
+ } finally {
53
+ cleanup(base);
54
+ }
55
+ });
56
+
44
57
  test("getWorkspaceGitBlockMessageForBase allows doctor on product conflicts", async () => {
45
58
  const base = makeTempRepo("gsd-dispatch-ws-git-doctor-");
46
59
  try {