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

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 (318) hide show
  1. package/dist/onboarding.js +22 -3
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/context7/index.js +12 -2
  4. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  5. package/dist/resources/extensions/google-cli/index.js +30 -0
  6. package/dist/resources/extensions/google-cli/models.js +55 -0
  7. package/dist/resources/extensions/google-cli/package.json +11 -0
  8. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  9. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +62 -1
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
  12. package/dist/resources/extensions/gsd/auto/phases.js +37 -0
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
  14. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
  16. package/dist/resources/extensions/gsd/auto-post-unit.js +18 -2
  17. package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
  18. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  19. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  20. package/dist/resources/extensions/gsd/auto.js +6 -1
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  22. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -7
  24. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
  25. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  26. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  27. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  28. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  29. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  30. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  31. package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
  32. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  33. package/dist/resources/extensions/gsd/doctor.js +6 -1
  34. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  35. package/dist/resources/extensions/gsd/guided-flow.js +5 -6
  36. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  37. package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
  38. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  39. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  40. package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
  41. package/dist/resources/extensions/gsd/preferences.js +14 -2
  42. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  43. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  44. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  45. package/dist/resources/extensions/gsd/prompts/system.md +1 -3
  46. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  47. package/dist/resources/extensions/gsd/repository-registry.js +3 -1
  48. package/dist/resources/extensions/gsd/skill-activation.js +233 -0
  49. package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
  50. package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
  51. package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
  52. package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
  53. package/dist/resources/extensions/gsd/skill-scope.js +52 -0
  54. package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
  55. package/dist/resources/extensions/gsd/skills.js +60 -0
  56. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
  57. package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
  58. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
  59. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  60. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
  61. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  62. package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
  63. package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
  64. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  65. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  66. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  67. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  68. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  69. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  70. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  71. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  72. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  73. package/dist/web/standalone/.next/BUILD_ID +1 -1
  74. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  75. package/dist/web/standalone/.next/build-manifest.json +2 -2
  76. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  77. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.html +1 -1
  94. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  101. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  102. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  104. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  105. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  106. package/package.json +1 -1
  107. package/packages/cloud-mcp-gateway/package.json +2 -2
  108. package/packages/contracts/package.json +1 -1
  109. package/packages/daemon/package.json +4 -4
  110. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
  111. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  112. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
  113. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  114. package/packages/gsd-agent-core/package.json +5 -5
  115. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  116. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  117. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  118. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  119. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  120. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  121. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  122. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  123. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  124. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  125. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  126. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  138. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  140. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  142. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  144. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  146. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  147. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  148. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  149. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  150. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  151. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  152. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  153. package/packages/gsd-agent-modes/package.json +7 -7
  154. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  155. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  156. package/packages/mcp-server/package.json +3 -3
  157. package/packages/native/package.json +1 -1
  158. package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  159. package/packages/pi-agent-core/dist/harness/skills.js +6 -0
  160. package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
  161. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
  162. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
  163. package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
  164. package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
  165. package/packages/pi-agent-core/package.json +1 -1
  166. package/packages/pi-ai/dist/models.generated.d.ts +8 -59
  167. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  168. package/packages/pi-ai/dist/models.generated.js +21 -72
  169. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  170. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  171. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  172. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  173. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  174. package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
  175. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  176. package/packages/pi-ai/dist/types.d.ts +2 -0
  177. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  178. package/packages/pi-ai/dist/types.js.map +1 -1
  179. package/packages/pi-ai/package.json +1 -1
  180. package/packages/pi-coding-agent/README.md +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
  182. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  185. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  188. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
  190. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/skills.js +3 -0
  192. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  193. package/packages/pi-coding-agent/package.json +7 -7
  194. package/packages/pi-tui/package.json +1 -1
  195. package/packages/rpc-client/package.json +2 -2
  196. package/pkg/package.json +1 -1
  197. package/src/resources/extensions/context7/index.ts +15 -2
  198. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  199. package/src/resources/extensions/google-cli/index.ts +34 -0
  200. package/src/resources/extensions/google-cli/models.ts +57 -0
  201. package/src/resources/extensions/google-cli/package.json +11 -0
  202. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  203. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  204. package/src/resources/extensions/gsd/auto/loop.ts +74 -1
  205. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
  206. package/src/resources/extensions/gsd/auto/phases.ts +46 -0
  207. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
  208. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  209. package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
  210. package/src/resources/extensions/gsd/auto-post-unit.ts +37 -2
  211. package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
  212. package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
  213. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  214. package/src/resources/extensions/gsd/auto.ts +6 -1
  215. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  216. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
  217. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
  218. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
  219. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  220. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  221. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  222. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  223. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  224. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  225. package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
  226. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  227. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  228. package/src/resources/extensions/gsd/doctor.ts +6 -1
  229. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  230. package/src/resources/extensions/gsd/guided-flow.ts +5 -6
  231. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  232. package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
  233. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  234. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  235. package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
  236. package/src/resources/extensions/gsd/preferences.ts +17 -2
  237. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  238. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  239. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  240. package/src/resources/extensions/gsd/prompts/system.md +1 -3
  241. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  242. package/src/resources/extensions/gsd/repository-registry.ts +3 -1
  243. package/src/resources/extensions/gsd/skill-activation.ts +292 -0
  244. package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
  245. package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
  246. package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
  247. package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
  248. package/src/resources/extensions/gsd/skill-scope.ts +63 -0
  249. package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
  250. package/src/resources/extensions/gsd/skills.ts +75 -0
  251. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
  252. package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
  253. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
  254. package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
  255. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
  256. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +21 -0
  257. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
  258. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
  259. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  260. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  261. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  262. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  263. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  264. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  265. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  266. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  267. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
  268. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  269. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
  270. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  271. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
  272. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  273. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  274. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  275. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  276. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  277. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
  278. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  279. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
  280. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
  281. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  282. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
  283. package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
  284. package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
  285. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  286. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
  287. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +19 -0
  288. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  289. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  290. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  291. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
  292. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
  293. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  294. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  295. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +74 -1
  296. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
  297. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  298. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  299. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  300. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  301. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
  302. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  303. package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
  304. package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
  305. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  306. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  307. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  308. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  309. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  310. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  311. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  312. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  313. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  314. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  315. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  316. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  317. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → orfEoZqDIo6Be_Z9ZFipD}/_buildManifest.js +0 -0
  318. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → orfEoZqDIo6Be_Z9ZFipD}/_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 {