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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/dist/onboarding.js +22 -3
  2. package/dist/resource-loader.js +3 -1
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/context7/index.js +12 -2
  5. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  6. package/dist/resources/extensions/google-cli/index.js +30 -0
  7. package/dist/resources/extensions/google-cli/models.js +55 -0
  8. package/dist/resources/extensions/google-cli/package.json +11 -0
  9. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  10. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  11. package/dist/resources/extensions/gsd/auto/loop.js +62 -1
  12. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
  13. package/dist/resources/extensions/gsd/auto/phases.js +37 -0
  14. package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
  15. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
  17. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -11
  18. package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
  19. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  20. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  21. package/dist/resources/extensions/gsd/auto.js +6 -1
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  23. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -7
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
  27. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  29. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  30. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  31. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  32. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  33. package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
  34. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  35. package/dist/resources/extensions/gsd/doctor.js +6 -1
  36. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  37. package/dist/resources/extensions/gsd/guided-flow.js +5 -6
  38. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  39. package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
  40. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  41. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  42. package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
  43. package/dist/resources/extensions/gsd/preferences.js +14 -2
  44. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  45. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  46. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/system.md +1 -3
  48. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  49. package/dist/resources/extensions/gsd/repository-registry.js +3 -1
  50. package/dist/resources/extensions/gsd/safety/evidence-collector.js +11 -4
  51. package/dist/resources/extensions/gsd/skill-activation.js +233 -0
  52. package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
  53. package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
  54. package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
  55. package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
  56. package/dist/resources/extensions/gsd/skill-scope.js +52 -0
  57. package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
  58. package/dist/resources/extensions/gsd/skills.js +60 -0
  59. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
  61. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
  62. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  63. package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
  64. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
  65. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  66. package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
  67. package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
  68. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  69. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  70. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  71. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  72. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  73. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  74. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  75. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  76. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  77. package/dist/web/standalone/.next/BUILD_ID +1 -1
  78. package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
  79. package/dist/web/standalone/.next/build-manifest.json +2 -2
  80. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  81. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.html +1 -1
  98. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app-paths-manifest.json +4 -4
  105. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  106. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  108. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  109. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  110. package/package.json +1 -1
  111. package/packages/cloud-mcp-gateway/package.json +2 -2
  112. package/packages/contracts/dist/rpc.test.js +5 -0
  113. package/packages/contracts/dist/rpc.test.js.map +1 -1
  114. package/packages/contracts/dist/workflow.d.ts +7 -0
  115. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  116. package/packages/contracts/dist/workflow.js +8 -0
  117. package/packages/contracts/dist/workflow.js.map +1 -1
  118. package/packages/contracts/dist/workflow.test.js +1 -0
  119. package/packages/contracts/dist/workflow.test.js.map +1 -1
  120. package/packages/contracts/package.json +1 -1
  121. package/packages/daemon/package.json +4 -4
  122. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
  123. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  124. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
  125. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  126. package/packages/gsd-agent-core/package.json +5 -5
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  138. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  140. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  141. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  142. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  144. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  145. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  147. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  148. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  149. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  150. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  151. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  152. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  153. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  154. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  155. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  156. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  157. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  158. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  159. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  160. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  161. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  162. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  163. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  164. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  165. package/packages/gsd-agent-modes/package.json +7 -7
  166. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  167. package/packages/mcp-server/dist/workflow-tools.js +28 -5
  168. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  169. package/packages/mcp-server/package.json +3 -3
  170. package/packages/native/package.json +1 -1
  171. package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  172. package/packages/pi-agent-core/dist/harness/skills.js +6 -0
  173. package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
  174. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
  175. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
  176. package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
  177. package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
  178. package/packages/pi-agent-core/package.json +1 -1
  179. package/packages/pi-ai/dist/models.generated.d.ts +8 -59
  180. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  181. package/packages/pi-ai/dist/models.generated.js +21 -72
  182. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  183. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  184. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  185. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  186. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
  188. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  189. package/packages/pi-ai/dist/types.d.ts +2 -0
  190. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  191. package/packages/pi-ai/dist/types.js.map +1 -1
  192. package/packages/pi-ai/package.json +1 -1
  193. package/packages/pi-coding-agent/README.md +1 -1
  194. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
  195. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  198. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  201. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
  203. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/skills.js +3 -0
  205. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  206. package/packages/pi-coding-agent/package.json +7 -7
  207. package/packages/pi-tui/package.json +1 -1
  208. package/packages/rpc-client/package.json +2 -2
  209. package/pkg/package.json +1 -1
  210. package/src/resources/extensions/context7/index.ts +15 -2
  211. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  212. package/src/resources/extensions/google-cli/index.ts +34 -0
  213. package/src/resources/extensions/google-cli/models.ts +57 -0
  214. package/src/resources/extensions/google-cli/package.json +11 -0
  215. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  216. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  217. package/src/resources/extensions/gsd/auto/loop.ts +74 -1
  218. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
  219. package/src/resources/extensions/gsd/auto/phases.ts +46 -0
  220. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
  221. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  222. package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
  223. package/src/resources/extensions/gsd/auto-post-unit.ts +43 -14
  224. package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
  225. package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
  226. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  227. package/src/resources/extensions/gsd/auto.ts +6 -1
  228. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  229. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
  230. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
  231. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
  232. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
  233. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  234. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  235. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  236. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  237. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  238. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  239. package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
  240. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  241. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  242. package/src/resources/extensions/gsd/doctor.ts +6 -1
  243. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  244. package/src/resources/extensions/gsd/guided-flow.ts +5 -6
  245. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  246. package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
  247. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  248. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  249. package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
  250. package/src/resources/extensions/gsd/preferences.ts +17 -2
  251. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  252. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  253. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  254. package/src/resources/extensions/gsd/prompts/system.md +1 -3
  255. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  256. package/src/resources/extensions/gsd/repository-registry.ts +3 -1
  257. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -4
  258. package/src/resources/extensions/gsd/skill-activation.ts +292 -0
  259. package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
  260. package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
  261. package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
  262. package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
  263. package/src/resources/extensions/gsd/skill-scope.ts +63 -0
  264. package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
  265. package/src/resources/extensions/gsd/skills.ts +75 -0
  266. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
  267. package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
  268. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
  269. package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
  270. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
  271. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +21 -0
  272. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
  273. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
  274. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
  275. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +12 -0
  276. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  277. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  278. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  279. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  280. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  281. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  282. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  283. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  284. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
  285. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  286. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
  287. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  288. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
  289. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
  290. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  291. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  292. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  293. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  294. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  295. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
  296. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  297. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
  298. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
  299. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  300. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
  301. package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
  302. package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
  303. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  304. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
  305. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +19 -0
  306. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  307. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  308. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  309. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
  310. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
  311. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  312. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  313. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +74 -1
  314. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
  315. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  316. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  317. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  318. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  319. package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
  320. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
  321. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  322. package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
  323. package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
  324. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  325. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  326. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  327. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  328. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  329. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  330. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  331. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  332. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  333. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  334. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  335. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  336. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → PkhJfy4kKo4yUHj1wY7_q}/_buildManifest.js +0 -0
  337. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → PkhJfy4kKo4yUHj1wY7_q}/_ssgManifest.js +0 -0
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { Theme } from "@gsd/pi-coding-agent";
11
- import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
11
+ import { truncateToWidth, matchesKey, Key } from "@gsd/pi-tui";
12
12
  import { deriveState } from "./state.js";
13
13
  import { loadFile } from "./files.js";
14
14
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
@@ -29,6 +29,7 @@ import { estimateTimeRemaining } from "./auto-dashboard.js";
29
29
  import { computeProgressScore, formatProgressLine } from "./progress-score.js";
30
30
  import { runEnvironmentChecks, type EnvironmentCheckResult } from "./doctor-environment.js";
31
31
  import { formattedShortcutPair } from "./shortcut-defs.js";
32
+ import { renderDialogFrame, renderKeyHints } from "./tui/render-kit.js";
32
33
 
33
34
  export function unitLabel(type: string): string {
34
35
  switch (type) {
@@ -258,35 +259,26 @@ export class GSDDashboardOverlay {
258
259
 
259
260
  const content = this.buildContentLines(width);
260
261
  const viewportHeight = Math.max(5, process.stdout.rows ? process.stdout.rows - 8 : 24);
261
- const chromeHeight = 2;
262
- const visibleContentRows = Math.max(1, viewportHeight - chromeHeight);
262
+ const visibleContentRows = Math.max(1, viewportHeight - 4);
263
263
  const maxScroll = Math.max(0, content.length - visibleContentRows);
264
264
  this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
265
265
  const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
266
-
267
- const lines = this.wrapInBox(visibleContent, width);
266
+ const contentWidth = Math.max(1, width - 4);
267
+ const footer = renderKeyHints(
268
+ this.theme,
269
+ ["↑↓ scroll", "g/G top/end", `Esc/${formattedShortcutPair("dashboard")} close`],
270
+ contentWidth,
271
+ );
272
+ const lines = renderDialogFrame(this.theme, "GSD Dashboard", visibleContent, width, {
273
+ footer,
274
+ scroll: { offset: this.scrollOffset, visibleRows: visibleContentRows, totalRows: content.length },
275
+ });
268
276
 
269
277
  this.cachedWidth = width;
270
278
  this.cachedLines = lines;
271
279
  return lines;
272
280
  }
273
281
 
274
- private wrapInBox(inner: string[], width: number): string[] {
275
- const th = this.theme;
276
- const border = (s: string) => th.fg("borderAccent", s);
277
- const innerWidth = width - 4;
278
- const lines: string[] = [];
279
-
280
- lines.push(border("╭" + "─".repeat(width - 2) + "╮"));
281
- for (const line of inner) {
282
- const truncated = truncateToWidth(line, innerWidth);
283
- const padWidth = Math.max(0, innerWidth - visibleWidth(truncated));
284
- lines.push(border("│") + " " + truncated + " ".repeat(padWidth) + " " + border("│"));
285
- }
286
- lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
287
- return lines;
288
- }
289
-
290
282
  private buildContentLines(width: number): string[] {
291
283
  const th = this.theme;
292
284
  const shellWidth = width - 4;
@@ -303,7 +295,6 @@ export class GSDDashboardOverlay {
303
295
  const hr = () => row(th.fg("dim", "─".repeat(contentWidth)));
304
296
  const centered = (content: string) => row(centerLine(content, contentWidth));
305
297
 
306
- const title = th.fg("accent", th.bold("GSD Dashboard"));
307
298
  const isRemote = !!this.dashData.remoteSession;
308
299
  const status = this.dashData.active
309
300
  ? `${Date.now() % 2000 < 1000 ? th.fg("success", "●") : th.fg("dim", "○")} ${th.fg("success", "AUTO")}`
@@ -328,7 +319,7 @@ export class GSDDashboardOverlay {
328
319
  } else if (isRemote) {
329
320
  elapsedParts = th.fg("dim", `since ${this.dashData.remoteSession!.startedAt.replace("T", " ").slice(0, 19)}`);
330
321
  }
331
- lines.push(row(joinColumns(`${title} ${status}${worktreeTag}`, elapsedParts, contentWidth)));
322
+ lines.push(row(joinColumns(`${status}${worktreeTag}`, elapsedParts, contentWidth)));
332
323
 
333
324
  // Progress score — traffic light indicator (#1221)
334
325
  if (this.dashData.active || this.dashData.paused) {
@@ -610,10 +601,6 @@ export class GSDDashboardOverlay {
610
601
  }
611
602
  }
612
603
 
613
- lines.push(blank());
614
- lines.push(hr());
615
- lines.push(centered(th.fg("dim", `↑↓ scroll · g/G top/end · Esc/${formattedShortcutPair("dashboard")} close`)));
616
-
617
604
  return lines;
618
605
  }
619
606
 
@@ -3,6 +3,7 @@ import { join } from "node:path";
3
3
 
4
4
  import type { DoctorIssue } from "./doctor-types.js";
5
5
  import { isDbAvailable, _getAdapter } from "./gsd-db.js";
6
+ import { isAfter, latestExplicitReopenAt } from "./milestone-reopen-events.js";
6
7
  import { resolveGsdPathContract, resolveMilestoneFile } from "./paths.js";
7
8
  import { deriveState } from "./state.js";
8
9
  import { readEvents } from "./workflow-events.js";
@@ -151,6 +152,104 @@ export async function checkEngineHealth(
151
152
  } catch {
152
153
  // Non-fatal — duplicate ID check failed
153
154
  }
155
+
156
+ // e. Completed milestone dispatch history but DB reopened without an explicit reopen event.
157
+ try {
158
+ const reopened = adapter
159
+ .prepare(
160
+ `SELECT m.id, m.status, ud.started_at, ud.ended_at
161
+ FROM milestones m
162
+ JOIN unit_dispatches ud ON ud.milestone_id = m.id
163
+ WHERE m.status NOT IN ('complete', 'done', 'skipped', 'closed')
164
+ AND ud.unit_type = 'complete-milestone'
165
+ AND ud.unit_id = m.id
166
+ AND ud.status = 'completed'
167
+ AND ud.id = (
168
+ SELECT latest.id
169
+ FROM unit_dispatches latest
170
+ WHERE latest.milestone_id = m.id
171
+ AND latest.unit_type = 'complete-milestone'
172
+ AND latest.unit_id = m.id
173
+ AND latest.status = 'completed'
174
+ ORDER BY COALESCE(latest.ended_at, latest.started_at) DESC, latest.id DESC
175
+ LIMIT 1
176
+ )
177
+ ORDER BY m.id`,
178
+ )
179
+ .all() as Array<{ id: string; status: string; started_at: string | null; ended_at: string | null }>;
180
+
181
+ for (const row of reopened) {
182
+ const completedAt = row.ended_at ?? row.started_at ?? null;
183
+ const reopenAt = latestExplicitReopenAt(basePath, row.id);
184
+ if (reopenAt && (!completedAt || Date.parse(reopenAt) > Date.parse(completedAt))) continue;
185
+ issues.push({
186
+ severity: "error",
187
+ code: "completed_milestone_reopened",
188
+ scope: "milestone",
189
+ unitId: row.id,
190
+ message: `Milestone ${row.id} has completed complete-milestone dispatch history but DB status is ${row.status}. Explicitly reopen or recover before planning it again.`,
191
+ fixable: false,
192
+ });
193
+ }
194
+ } catch {
195
+ // Non-fatal — completed-milestone reopen check failed
196
+ }
197
+
198
+ // f. Completion artifacts disagree with open DB hierarchy rows.
199
+ try {
200
+ const rows = adapter
201
+ .prepare(
202
+ `SELECT a.path, a.artifact_type, a.milestone_id, a.slice_id, a.task_id, a.imported_at,
203
+ m.status AS milestone_status,
204
+ s.status AS slice_status,
205
+ t.status AS task_status,
206
+ (SELECT COUNT(*) FROM tasks tt WHERE tt.milestone_id = a.milestone_id AND tt.slice_id = a.slice_id) AS task_count
207
+ FROM artifacts a
208
+ JOIN milestones m ON m.id = a.milestone_id
209
+ LEFT JOIN slices s ON s.milestone_id = a.milestone_id AND s.id = a.slice_id
210
+ LEFT JOIN tasks t ON t.milestone_id = a.milestone_id AND t.slice_id = a.slice_id AND t.id = a.task_id
211
+ WHERE a.artifact_type = 'SUMMARY'
212
+ AND m.status NOT IN ('complete', 'done', 'skipped', 'closed')`,
213
+ )
214
+ .all() as Array<{
215
+ path: string;
216
+ milestone_id: string;
217
+ slice_id: string | null;
218
+ task_id: string | null;
219
+ imported_at: string | null;
220
+ slice_status: string | null;
221
+ task_status: string | null;
222
+ task_count: number;
223
+ }>;
224
+
225
+ const seen = new Set<string>();
226
+ for (const row of rows) {
227
+ const reopenAt = latestExplicitReopenAt(basePath, row.milestone_id);
228
+ if (!isAfter(row.imported_at, reopenAt)) continue;
229
+ const isSliceSummary = row.slice_id && !row.task_id && row.slice_status && !["complete", "done", "skipped", "closed"].includes(row.slice_status);
230
+ const isTaskSummary = row.slice_id && row.task_id && (!row.task_status || !["complete", "done", "skipped", "closed"].includes(row.task_status));
231
+ const isTaskArtifactWithoutDbTasks = row.slice_id && row.task_id && Number(row.task_count) === 0;
232
+ if (!isSliceSummary && !isTaskSummary && !isTaskArtifactWithoutDbTasks) continue;
233
+
234
+ const unitId = row.task_id
235
+ ? `${row.milestone_id}/${row.slice_id}/${row.task_id}`
236
+ : row.slice_id
237
+ ? `${row.milestone_id}/${row.slice_id}`
238
+ : row.milestone_id;
239
+ if (seen.has(unitId)) continue;
240
+ seen.add(unitId);
241
+ issues.push({
242
+ severity: "error",
243
+ code: "artifact_db_status_divergence",
244
+ scope: row.task_id ? "task" : row.slice_id ? "slice" : "milestone",
245
+ unitId,
246
+ message: `Completion artifact ${row.path} exists while DB state for ${unitId} is still open or missing. Runtime will not import it silently; run explicit recovery/repair after review.`,
247
+ fixable: false,
248
+ });
249
+ }
250
+ } catch {
251
+ // Non-fatal — artifact/DB status drift check failed
252
+ }
154
253
  }
155
254
  } catch {
156
255
  // Non-fatal — DB constraint checks failed entirely
@@ -16,7 +16,7 @@ import { delimiter, join } from "node:path";
16
16
  import { AuthStorage } from "@gsd/pi-coding-agent";
17
17
  import { getEnvApiKey } from "@gsd/pi-ai";
18
18
  import { loadEffectiveGSDPreferences } from "./preferences.js";
19
- import { getAuthPath, PROVIDER_REGISTRY, type ProviderCategory } from "./key-manager.js";
19
+ import { getAuthPath, PROVIDER_REGISTRY, supportsBrowserOAuth, type ProviderCategory } from "./key-manager.js";
20
20
  import { homedir } from "node:os";
21
21
 
22
22
  // ── Types ──────────────────────────────────────────────────────────────────────
@@ -42,11 +42,10 @@ export interface ProviderCheckResult {
42
42
 
43
43
  /**
44
44
  * Providers that use external CLI authentication (not API keys).
45
- * These are always considered "found" the host CLI handles auth.
45
+ * When explicitly selected, the provider's own CLI/session owns auth.
46
46
  */
47
47
  const CLI_AUTH_PROVIDERS = new Set([
48
48
  "claude-code",
49
- "openai-codex",
50
49
  "google-gemini-cli",
51
50
  "google-antigravity",
52
51
  ]);
@@ -156,26 +155,30 @@ interface KeyLookup {
156
155
  * Map of CLI provider IDs to their binary names on disk.
157
156
  * Used for lightweight binary-presence checks (PATH scan, no subprocess).
158
157
  */
159
- const CLI_BINARY_MAP: Record<string, string> = {
160
- "claude-code": "claude",
161
- "openai-codex": "codex",
162
- "google-gemini-cli": "gemini",
163
- "google-antigravity": "antigravity",
158
+ const CLI_BINARY_MAP: Record<string, string[]> = {
159
+ "claude-code": ["claude", "claude-code"],
160
+ "google-gemini-cli": ["gemini"],
161
+ "google-antigravity": ["agy"],
164
162
  };
165
163
 
164
+ const CLI_AUTH_PATH_CHECK_PROVIDERS = new Set([
165
+ "google-gemini-cli",
166
+ "google-antigravity",
167
+ ]);
168
+
166
169
  /**
167
170
  * Check if a CLI provider's binary exists anywhere in PATH.
168
171
  * Fast filesystem scan — no subprocess, no network, sub-1ms.
169
172
  */
170
173
  function isCliBinaryInPath(providerId: string): boolean {
171
- const binary = CLI_BINARY_MAP[providerId];
172
- if (!binary) return false;
174
+ const binaries = CLI_BINARY_MAP[providerId];
175
+ if (!binaries) return false;
173
176
 
174
177
  const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
175
178
 
176
179
  // On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
177
180
  // Scan PATHEXT candidates in addition to the bare binary name.
178
- const executableNames: string[] = [binary];
181
+ const executableNames: string[] = [...binaries];
179
182
  if (process.platform === "win32") {
180
183
  const rawPathExt = process.env.PATHEXT
181
184
  ?.split(";")
@@ -185,9 +188,11 @@ function isCliBinaryInPath(providerId: string): boolean {
185
188
  ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`,
186
189
  );
187
190
  const defaultExt = [".exe", ".cmd", ".bat", ".com"];
188
- for (const ext of [...normalizedPathExt, ...defaultExt]) {
189
- const candidate = `${binary}${ext}`;
190
- if (!executableNames.includes(candidate)) executableNames.push(candidate);
191
+ for (const binary of binaries) {
192
+ for (const ext of [...normalizedPathExt, ...defaultExt]) {
193
+ const candidate = `${binary}${ext}`;
194
+ if (!executableNames.includes(candidate)) executableNames.push(candidate);
195
+ }
191
196
  }
192
197
  }
193
198
 
@@ -224,12 +229,6 @@ function hasModelsJsonApiKey(providerId: string): boolean {
224
229
  function resolveKey(providerId: string): KeyLookup {
225
230
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
226
231
 
227
- // claude-code never stores credentials in auth.json — GSD delegates entirely to
228
- // the local CLI binary. Presence of the binary in PATH is the only signal.
229
- if (providerId === "claude-code") {
230
- return { found: isCliBinaryInPath("claude-code"), source: "env", backedOff: false };
231
- }
232
-
233
232
  if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
234
233
  return { found: true, source: "env", backedOff: false };
235
234
  }
@@ -242,9 +241,15 @@ function resolveKey(providerId: string): KeyLookup {
242
241
  const creds = auth.getCredentialsForProvider(providerId);
243
242
  if (creds.length > 0) {
244
243
  // Filter out empty placeholder keys (from skipped onboarding)
245
- const hasRealKey = creds.some(c =>
246
- c.type === "oauth" || (c.type === "api_key" && (c as { key?: string }).key)
247
- );
244
+ const hasRealKey = creds.some(c => {
245
+ if (c.type === "oauth") return true;
246
+ if (c.type !== "api_key") return false;
247
+
248
+ const key = (c as { key?: string }).key?.trim();
249
+ if (!key) return false;
250
+
251
+ return !(CLI_AUTH_PROVIDERS.has(providerId) && key === "cli");
252
+ });
248
253
  if (hasRealKey) {
249
254
  return {
250
255
  found: true,
@@ -275,6 +280,12 @@ function resolveKey(providerId: string): KeyLookup {
275
280
  return { found: true, source: "models.json", backedOff: false };
276
281
  }
277
282
 
283
+ // Cross-provider routes can use a local CLI when it is installed. Explicit
284
+ // external CLI provider selections are handled in checkLlmProviders() below.
285
+ if (CLI_AUTH_PROVIDERS.has(providerId) && isCliBinaryInPath(providerId)) {
286
+ return { found: true, source: "env", backedOff: false };
287
+ }
288
+
278
289
  return { found: false, source: "none", backedOff: false };
279
290
  }
280
291
 
@@ -285,15 +296,32 @@ function checkLlmProviders(): ProviderCheckResult[] {
285
296
  const results: ProviderCheckResult[] = [];
286
297
 
287
298
  for (const providerId of required) {
288
- // CLI-authenticated providers don't need API keys skip key check
299
+ // CLI-authenticated providers don't need API keys. The provider's own
300
+ // request path validates CLI sessions when it is used.
289
301
  if (CLI_AUTH_PROVIDERS.has(providerId)) {
290
302
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
303
+ const label = info?.label ?? providerId;
304
+ if (CLI_AUTH_PATH_CHECK_PROVIDERS.has(providerId) && !isCliBinaryInPath(providerId)) {
305
+ const binaries = CLI_BINARY_MAP[providerId]?.map(binary => `\`${binary}\``).join(" or ");
306
+ results.push({
307
+ name: providerId,
308
+ label,
309
+ category: "llm",
310
+ status: "error",
311
+ message: `${label} — CLI not found`,
312
+ detail: binaries
313
+ ? `Install ${label} and ensure ${binaries} is on PATH`
314
+ : `Install ${label} and ensure its CLI is on PATH`,
315
+ required: true,
316
+ });
317
+ continue;
318
+ }
291
319
  results.push({
292
320
  name: providerId,
293
- label: info?.label ?? providerId,
321
+ label,
294
322
  category: "llm",
295
323
  status: "ok",
296
- message: `${info?.label ?? providerId} — CLI auth (no key needed)`,
324
+ message: `${label} — CLI auth (no key needed)`,
297
325
  required: true,
298
326
  });
299
327
  continue;
@@ -333,7 +361,7 @@ function checkLlmProviders(): ProviderCheckResult[] {
333
361
  message: `${label} — not configured`,
334
362
  detail: providerId === "anthropic-vertex"
335
363
  ? "Set ANTHROPIC_VERTEX_PROJECT_ID and authenticate with Google ADC"
336
- : info?.hasOAuth
364
+ : info && supportsBrowserOAuth(info)
337
365
  ? `Run /gsd keys to authenticate`
338
366
  : `Set ${envVar} or run /gsd keys`,
339
367
  required: true,
@@ -83,6 +83,8 @@ export type DoctorIssueCode =
83
83
  | "db_orphaned_task"
84
84
  | "db_orphaned_slice"
85
85
  | "db_done_task_no_summary"
86
+ | "artifact_db_status_divergence"
87
+ | "completed_milestone_reopened"
86
88
  | "db_duplicate_id"
87
89
  | "db_unavailable"
88
90
  | "projection_drift"
@@ -514,13 +514,18 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
514
514
  try {
515
515
  if (!lstatSync(join(slicesDir, entry)).isDirectory()) continue;
516
516
  } catch { continue; }
517
+ if (entry === "parallel-research") continue;
517
518
  if (!knownSliceIds.has(entry)) {
519
+ const quarantineExample = `.gsd/quarantine/milestones/${milestoneId}/slices/${entry}-manual-review`;
518
520
  issues.push({
519
521
  severity: "warning",
520
522
  code: "orphaned_slice_directory",
521
523
  scope: "milestone",
522
524
  unitId: milestoneId,
523
- message: `Directory "${entry}" exists in ${milestoneId}/slices/ but is not referenced in the roadmap`,
525
+ message:
526
+ `Directory "${entry}" exists in ${milestoneId}/slices/ but is not referenced in the roadmap or DB. ` +
527
+ `Review it; if stale, move or delete it. To preserve it, move it under ${quarantineExample}. ` +
528
+ "If it contains work to keep, copy or merge that content into a DB-backed slice before resuming.",
524
529
  file: `${relMilestonePath(basePath, milestoneId)}/slices/${entry}`,
525
530
  fixable: false,
526
531
  });
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { existsSync } from "node:fs";
6
- import { join } from "node:path";
6
+ import { dirname, join, resolve } from "node:path";
7
7
 
8
8
  import { autoResolveSafeConflictPaths } from "./git-conflict-resolve.js";
9
9
  import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
@@ -14,6 +14,21 @@ function splitZeroDelimited(output: string): string[] {
14
14
  return output.split("\0").filter(Boolean);
15
15
  }
16
16
 
17
+ function hasGitMarker(basePath: string): boolean {
18
+ try {
19
+ let dir = resolve(basePath);
20
+ for (let i = 0; i < 30; i++) {
21
+ if (existsSync(join(dir, ".git"))) return true;
22
+ const parent = dirname(dir);
23
+ if (parent === dir) break;
24
+ dir = parent;
25
+ }
26
+ } catch {
27
+ // Fall through to the git probes, which will report unknown on failure.
28
+ }
29
+ return false;
30
+ }
31
+
17
32
  export function listUnmergedGitPaths(basePath: string): string[] | null {
18
33
  try {
19
34
  const output = spawnSync("git", ["diff", "--name-only", "--diff-filter=U", "-z"], {
@@ -75,6 +90,15 @@ export interface GitConflictProbeResult {
75
90
  }
76
91
 
77
92
  export function probeGitConflictState(basePath: string): GitConflictProbeResult {
93
+ if (!hasGitMarker(basePath)) {
94
+ return {
95
+ status: "clean",
96
+ unmerged: [],
97
+ checkFailures: [],
98
+ mergeStateBlockers: [],
99
+ };
100
+ }
101
+
78
102
  const unmerged = listUnmergedGitPaths(basePath);
79
103
  if (unmerged === null) {
80
104
  return {
@@ -1218,7 +1218,7 @@ async function dispatchWorkflow(
1218
1218
 
1219
1219
  if (unitType) setGuidedUnitContext(projectRoot, unitType);
1220
1220
  try {
1221
- pi.sendMessage(
1221
+ await pi.sendMessage(
1222
1222
  {
1223
1223
  customType,
1224
1224
  content: buildWorkflowDispatchContent({ workflow, workflowPath, task: note }),
@@ -1231,10 +1231,9 @@ async function dispatchWorkflow(
1231
1231
  throw err;
1232
1232
  }
1233
1233
  } finally {
1234
- // Restore full tool set after the message is queued. The LLM turn has
1235
- // already captured the scoped set restoring prevents the narrowed
1236
- // tools from leaking into subsequent dispatches (#3628). The finally
1237
- // block ensures restoration even if sendMessage throws.
1234
+ // Restore full tool/skill surface after the turn completes. Awaiting
1235
+ // sendMessage ensures scoped skills stay in _baseSystemPrompt through
1236
+ // before_agent_start (#3628, skill token savings).
1238
1237
  restoreGsdWorkflowTools(pi, savedTools);
1239
1238
  }
1240
1239
  }
@@ -1358,7 +1357,7 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
1358
1357
  * Run preparation phase if enabled, then build the discuss prompt.
1359
1358
  * Preparation analyzes the codebase and prior context, injecting the results
1360
1359
  * as supplementary context into the standard discuss template. The discuss
1361
- * template drives the conversation (asks "What's the vision?" first), while
1360
+ * template drives the conversation with a variable vision opener, while
1362
1361
  * the preparation briefs give the agent grounding in the existing codebase.
1363
1362
  *
1364
1363
  * @param ctx - Extension command context with UI for progress notifications
@@ -22,6 +22,7 @@ import { gsdHome } from "./gsd-home.js";
22
22
  // ─── Provider Registry ─────────────────────────────────────────────────────────
23
23
 
24
24
  export type ProviderCategory = "llm" | "tool" | "search" | "remote";
25
+ export type ProviderAuthMode = "apiKey" | "browserOAuth" | "externalCli" | "cloudIdentity" | "none";
25
26
 
26
27
  export interface ProviderInfo {
27
28
  id: string;
@@ -29,24 +30,24 @@ export interface ProviderInfo {
29
30
  category: ProviderCategory;
30
31
  envVar?: string;
31
32
  prefixes?: string[];
32
- hasOAuth?: boolean;
33
+ authMode?: ProviderAuthMode;
33
34
  dashboardUrl?: string;
34
35
  }
35
36
 
36
37
  export const PROVIDER_REGISTRY: ProviderInfo[] = [
37
38
  // LLM Providers
38
- { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], hasOAuth: true, dashboardUrl: "console.anthropic.com" },
39
+ { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], authMode: "apiKey", dashboardUrl: "console.anthropic.com" },
39
40
  // Claude Code CLI: routes through the local `claude` binary — no API key,
40
41
  // authentication is handled by the CLI's own OAuth flow.
41
42
  // Referenced by doctor-providers.ts, auto-model-selection.ts, and others;
42
43
  // must be in the canonical registry so all consumers see the same catalog.
43
44
  // See: https://github.com/open-gsd/gsd-pi/issues/4541
44
- { id: "claude-code", label: "Claude Code CLI", category: "llm", hasOAuth: true },
45
+ { id: "claude-code", label: "Claude Code CLI", category: "llm", authMode: "externalCli" },
45
46
  { id: "openai", label: "OpenAI", category: "llm", envVar: "OPENAI_API_KEY", prefixes: ["sk-"], dashboardUrl: "platform.openai.com/api-keys" },
46
- { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", hasOAuth: true },
47
- { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)",category: "llm", hasOAuth: true },
48
- { id: "google-gemini-cli",label: "Google Gemini CLI", category: "llm", hasOAuth: true },
49
- { id: "google-antigravity",label: "Antigravity", category: "llm", hasOAuth: true },
47
+ { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", authMode: "browserOAuth" },
48
+ { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)",category: "llm", authMode: "browserOAuth" },
49
+ { id: "google-gemini-cli",label: "Google Gemini CLI", category: "llm", authMode: "externalCli" },
50
+ { id: "google-antigravity",label: "Antigravity", category: "llm", authMode: "externalCli" },
50
51
  { id: "google", label: "Google (Gemini)", category: "llm", envVar: "GEMINI_API_KEY", dashboardUrl: "aistudio.google.com/apikey" },
51
52
  { id: "groq", label: "Groq", category: "llm", envVar: "GROQ_API_KEY", dashboardUrl: "console.groq.com" },
52
53
  { id: "xai", label: "xAI (Grok)", category: "llm", envVar: "XAI_API_KEY", dashboardUrl: "console.x.ai" },
@@ -77,6 +78,21 @@ export const PROVIDER_REGISTRY: ProviderInfo[] = [
77
78
 
78
79
  // ─── Utilities ──────────────────────────────────────────────────────────────────
79
80
 
81
+ export function getProviderAuthMode(provider: ProviderInfo): ProviderAuthMode {
82
+ return provider.authMode ?? "apiKey";
83
+ }
84
+
85
+ export function supportsBrowserOAuth(provider: ProviderInfo): boolean {
86
+ return getProviderAuthMode(provider) === "browserOAuth";
87
+ }
88
+
89
+ export function supportsStoredApiKey(provider: ProviderInfo): boolean {
90
+ const mode = getProviderAuthMode(provider);
91
+ if (mode === "externalCli" || mode === "cloudIdentity" || mode === "none") return false;
92
+ if (mode === "browserOAuth") return Boolean(provider.envVar || provider.prefixes?.length);
93
+ return true;
94
+ }
95
+
80
96
  /**
81
97
  * Mask an API key for display: show first 4 + last 4 chars.
82
98
  * Keys shorter than 12 chars show only first 2 + last 2.
@@ -104,10 +120,13 @@ export function formatDuration(ms: number): string {
104
120
  /**
105
121
  * Describe a credential's type and status.
106
122
  */
107
- export function describeCredential(cred: AuthCredential): string {
123
+ export function describeCredential(cred: AuthCredential, provider?: ProviderInfo): string {
108
124
  if (cred.type === "api_key") {
109
125
  const apiCred = cred as ApiKeyCredential;
110
126
  if (!apiCred.key) return "empty key";
127
+ if (apiCred.key === "cli" && provider && getProviderAuthMode(provider) === "externalCli") {
128
+ return "external CLI";
129
+ }
111
130
  return `API key (${maskKey(apiCred.key)})`;
112
131
  }
113
132
  if (cred.type === "oauth") {
@@ -171,7 +190,7 @@ export function getAllKeyStatuses(auth: AuthStorage): KeyStatus[] {
171
190
  const desc =
172
191
  creds.length > 1
173
192
  ? `${creds.length} keys (round-robin)`
174
- : describeCredential(firstCred);
193
+ : describeCredential(firstCred, provider);
175
194
  return {
176
195
  provider,
177
196
  configured: true,
@@ -289,9 +308,28 @@ export async function handleAddKey(
289
308
  provider = PROVIDER_REGISTRY[idx];
290
309
  }
291
310
 
292
- // If OAuth is available, offer choice
293
- if (provider.hasOAuth) {
294
- const methods = ["API key", "Browser login (OAuth)"];
311
+ const authMode = getProviderAuthMode(provider);
312
+ if (authMode === "externalCli") {
313
+ ctx.ui.notify(
314
+ `${provider.label} is authenticated by its own local CLI. ` +
315
+ `Sign in with that CLI first, then run /login to activate it in GSD.`,
316
+ "info",
317
+ );
318
+ return false;
319
+ }
320
+
321
+ if (authMode === "cloudIdentity") {
322
+ ctx.ui.notify(
323
+ `${provider.label} uses cloud identity credentials. Configure the provider's cloud CLI or environment, then restart GSD.`,
324
+ "info",
325
+ );
326
+ return false;
327
+ }
328
+
329
+ if (supportsBrowserOAuth(provider)) {
330
+ const methods = supportsStoredApiKey(provider)
331
+ ? ["API token/key", "Browser login (OAuth)"]
332
+ : ["Browser login (OAuth)"];
295
333
  const method = await ctx.ui.select(
296
334
  `${provider.label} — how do you want to authenticate?`,
297
335
  methods,
@@ -308,6 +346,11 @@ export async function handleAddKey(
308
346
  }
309
347
  }
310
348
 
349
+ if (!supportsStoredApiKey(provider)) {
350
+ ctx.ui.notify(`${provider.label} does not accept a GSD-stored API key. Use /login.`, "info");
351
+ return false;
352
+ }
353
+
311
354
  // API key input
312
355
  const input = await ctx.ui.input(
313
356
  `API key for ${provider.label}:`,
@@ -387,7 +430,7 @@ export async function handleRemoveKey(
387
430
 
388
431
  // Multi-key handling
389
432
  if (creds.length > 1) {
390
- const options = creds.map((c, i) => `[${i + 1}] ${describeCredential(c)}`);
433
+ const options = creds.map((c, i) => `[${i + 1}] ${describeCredential(c, provider)}`);
391
434
  options.push("Remove all");
392
435
 
393
436
  const choice = await ctx.ui.select(
@@ -411,7 +454,7 @@ export async function handleRemoveKey(
411
454
  } else {
412
455
  const confirmed = await ctx.ui.confirm(
413
456
  "Remove key?",
414
- `Remove ${describeCredential(creds[0])} for ${provider.label}?`,
457
+ `Remove ${describeCredential(creds[0], provider)} for ${provider.label}?`,
415
458
  );
416
459
  if (!confirmed) return false;
417
460
  auth.remove(provider.id);
@@ -0,0 +1,28 @@
1
+ import { join } from "node:path";
2
+
3
+ import { gsdRoot } from "./paths.js";
4
+ import { readEvents } from "./workflow-events.js";
5
+
6
+ export function latestExplicitReopenAt(basePath: string, milestoneId: string): string | null {
7
+ const root = gsdRoot(basePath);
8
+ const candidates = [
9
+ join(root, "event-log.jsonl"),
10
+ join(root, `event-log-${milestoneId}.jsonl.archived`),
11
+ ];
12
+
13
+ let latest: string | null = null;
14
+ for (const file of candidates) {
15
+ for (const event of readEvents(file)) {
16
+ const eventMilestoneId = (event.params as { milestoneId?: unknown }).milestoneId;
17
+ if (event.cmd !== "reopen-milestone" || eventMilestoneId !== milestoneId) continue;
18
+ if (!latest || event.ts > latest) latest = event.ts;
19
+ }
20
+ }
21
+ return latest;
22
+ }
23
+
24
+ export function isAfter(value: string | null | undefined, cutoff: string | null): boolean {
25
+ if (!cutoff) return true;
26
+ if (!value) return true;
27
+ return Date.parse(value) > Date.parse(cutoff);
28
+ }