@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
@@ -1,6 +1,7 @@
1
1
  import { existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { isDbAvailable, _getAdapter } from "./gsd-db.js";
4
+ import { isAfter, latestExplicitReopenAt } from "./milestone-reopen-events.js";
4
5
  import { resolveGsdPathContract, resolveMilestoneFile } from "./paths.js";
5
6
  import { deriveState } from "./state.js";
6
7
  import { readEvents } from "./workflow-events.js";
@@ -131,6 +132,92 @@ export async function checkEngineHealth(basePath, issues, fixesApplied) {
131
132
  catch {
132
133
  // Non-fatal — duplicate ID check failed
133
134
  }
135
+ // e. Completed milestone dispatch history but DB reopened without an explicit reopen event.
136
+ try {
137
+ const reopened = adapter
138
+ .prepare(`SELECT m.id, m.status, ud.started_at, ud.ended_at
139
+ FROM milestones m
140
+ JOIN unit_dispatches ud ON ud.milestone_id = m.id
141
+ WHERE m.status NOT IN ('complete', 'done', 'skipped', 'closed')
142
+ AND ud.unit_type = 'complete-milestone'
143
+ AND ud.unit_id = m.id
144
+ AND ud.status = 'completed'
145
+ AND ud.id = (
146
+ SELECT latest.id
147
+ FROM unit_dispatches latest
148
+ WHERE latest.milestone_id = m.id
149
+ AND latest.unit_type = 'complete-milestone'
150
+ AND latest.unit_id = m.id
151
+ AND latest.status = 'completed'
152
+ ORDER BY COALESCE(latest.ended_at, latest.started_at) DESC, latest.id DESC
153
+ LIMIT 1
154
+ )
155
+ ORDER BY m.id`)
156
+ .all();
157
+ for (const row of reopened) {
158
+ const completedAt = row.ended_at ?? row.started_at ?? null;
159
+ const reopenAt = latestExplicitReopenAt(basePath, row.id);
160
+ if (reopenAt && (!completedAt || Date.parse(reopenAt) > Date.parse(completedAt)))
161
+ continue;
162
+ issues.push({
163
+ severity: "error",
164
+ code: "completed_milestone_reopened",
165
+ scope: "milestone",
166
+ unitId: row.id,
167
+ message: `Milestone ${row.id} has completed complete-milestone dispatch history but DB status is ${row.status}. Explicitly reopen or recover before planning it again.`,
168
+ fixable: false,
169
+ });
170
+ }
171
+ }
172
+ catch {
173
+ // Non-fatal — completed-milestone reopen check failed
174
+ }
175
+ // f. Completion artifacts disagree with open DB hierarchy rows.
176
+ try {
177
+ const rows = adapter
178
+ .prepare(`SELECT a.path, a.artifact_type, a.milestone_id, a.slice_id, a.task_id, a.imported_at,
179
+ m.status AS milestone_status,
180
+ s.status AS slice_status,
181
+ t.status AS task_status,
182
+ (SELECT COUNT(*) FROM tasks tt WHERE tt.milestone_id = a.milestone_id AND tt.slice_id = a.slice_id) AS task_count
183
+ FROM artifacts a
184
+ JOIN milestones m ON m.id = a.milestone_id
185
+ LEFT JOIN slices s ON s.milestone_id = a.milestone_id AND s.id = a.slice_id
186
+ LEFT JOIN tasks t ON t.milestone_id = a.milestone_id AND t.slice_id = a.slice_id AND t.id = a.task_id
187
+ WHERE a.artifact_type = 'SUMMARY'
188
+ AND m.status NOT IN ('complete', 'done', 'skipped', 'closed')`)
189
+ .all();
190
+ const seen = new Set();
191
+ for (const row of rows) {
192
+ const reopenAt = latestExplicitReopenAt(basePath, row.milestone_id);
193
+ if (!isAfter(row.imported_at, reopenAt))
194
+ continue;
195
+ const isSliceSummary = row.slice_id && !row.task_id && row.slice_status && !["complete", "done", "skipped", "closed"].includes(row.slice_status);
196
+ const isTaskSummary = row.slice_id && row.task_id && (!row.task_status || !["complete", "done", "skipped", "closed"].includes(row.task_status));
197
+ const isTaskArtifactWithoutDbTasks = row.slice_id && row.task_id && Number(row.task_count) === 0;
198
+ if (!isSliceSummary && !isTaskSummary && !isTaskArtifactWithoutDbTasks)
199
+ continue;
200
+ const unitId = row.task_id
201
+ ? `${row.milestone_id}/${row.slice_id}/${row.task_id}`
202
+ : row.slice_id
203
+ ? `${row.milestone_id}/${row.slice_id}`
204
+ : row.milestone_id;
205
+ if (seen.has(unitId))
206
+ continue;
207
+ seen.add(unitId);
208
+ issues.push({
209
+ severity: "error",
210
+ code: "artifact_db_status_divergence",
211
+ scope: row.task_id ? "task" : row.slice_id ? "slice" : "milestone",
212
+ unitId,
213
+ 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.`,
214
+ fixable: false,
215
+ });
216
+ }
217
+ }
218
+ catch {
219
+ // Non-fatal — artifact/DB status drift check failed
220
+ }
134
221
  }
135
222
  }
136
223
  catch {
@@ -15,16 +15,15 @@ import { delimiter, join } from "node:path";
15
15
  import { AuthStorage } from "@gsd/pi-coding-agent";
16
16
  import { getEnvApiKey } from "@gsd/pi-ai";
17
17
  import { loadEffectiveGSDPreferences } from "./preferences.js";
18
- import { getAuthPath, PROVIDER_REGISTRY } from "./key-manager.js";
18
+ import { getAuthPath, PROVIDER_REGISTRY, supportsBrowserOAuth } from "./key-manager.js";
19
19
  import { homedir } from "node:os";
20
20
  // ── Provider routing constants ────────────────────────────────────────────────
21
21
  /**
22
22
  * Providers that use external CLI authentication (not API keys).
23
- * These are always considered "found" the host CLI handles auth.
23
+ * When explicitly selected, the provider's own CLI/session owns auth.
24
24
  */
25
25
  const CLI_AUTH_PROVIDERS = new Set([
26
26
  "claude-code",
27
- "openai-codex",
28
27
  "google-gemini-cli",
29
28
  "google-antigravity",
30
29
  ]);
@@ -126,23 +125,26 @@ function collectConfiguredModelProviders() {
126
125
  * Used for lightweight binary-presence checks (PATH scan, no subprocess).
127
126
  */
128
127
  const CLI_BINARY_MAP = {
129
- "claude-code": "claude",
130
- "openai-codex": "codex",
131
- "google-gemini-cli": "gemini",
132
- "google-antigravity": "antigravity",
128
+ "claude-code": ["claude", "claude-code"],
129
+ "google-gemini-cli": ["gemini"],
130
+ "google-antigravity": ["agy"],
133
131
  };
132
+ const CLI_AUTH_PATH_CHECK_PROVIDERS = new Set([
133
+ "google-gemini-cli",
134
+ "google-antigravity",
135
+ ]);
134
136
  /**
135
137
  * Check if a CLI provider's binary exists anywhere in PATH.
136
138
  * Fast filesystem scan — no subprocess, no network, sub-1ms.
137
139
  */
138
140
  function isCliBinaryInPath(providerId) {
139
- const binary = CLI_BINARY_MAP[providerId];
140
- if (!binary)
141
+ const binaries = CLI_BINARY_MAP[providerId];
142
+ if (!binaries)
141
143
  return false;
142
144
  const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
143
145
  // On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
144
146
  // Scan PATHEXT candidates in addition to the bare binary name.
145
- const executableNames = [binary];
147
+ const executableNames = [...binaries];
146
148
  if (process.platform === "win32") {
147
149
  const rawPathExt = process.env.PATHEXT
148
150
  ?.split(";")
@@ -150,10 +152,12 @@ function isCliBinaryInPath(providerId) {
150
152
  .filter(Boolean) ?? [];
151
153
  const normalizedPathExt = rawPathExt.map(ext => ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`);
152
154
  const defaultExt = [".exe", ".cmd", ".bat", ".com"];
153
- for (const ext of [...normalizedPathExt, ...defaultExt]) {
154
- const candidate = `${binary}${ext}`;
155
- if (!executableNames.includes(candidate))
156
- executableNames.push(candidate);
155
+ for (const binary of binaries) {
156
+ for (const ext of [...normalizedPathExt, ...defaultExt]) {
157
+ const candidate = `${binary}${ext}`;
158
+ if (!executableNames.includes(candidate))
159
+ executableNames.push(candidate);
160
+ }
157
161
  }
158
162
  }
159
163
  return pathDirs.some(dir => executableNames.some(name => existsSync(join(dir, name))));
@@ -185,11 +189,6 @@ function hasModelsJsonApiKey(providerId) {
185
189
  }
186
190
  function resolveKey(providerId) {
187
191
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
188
- // claude-code never stores credentials in auth.json — GSD delegates entirely to
189
- // the local CLI binary. Presence of the binary in PATH is the only signal.
190
- if (providerId === "claude-code") {
191
- return { found: isCliBinaryInPath("claude-code"), source: "env", backedOff: false };
192
- }
193
192
  if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
194
193
  return { found: true, source: "env", backedOff: false };
195
194
  }
@@ -201,7 +200,16 @@ function resolveKey(providerId) {
201
200
  const creds = auth.getCredentialsForProvider(providerId);
202
201
  if (creds.length > 0) {
203
202
  // Filter out empty placeholder keys (from skipped onboarding)
204
- const hasRealKey = creds.some(c => c.type === "oauth" || (c.type === "api_key" && c.key));
203
+ const hasRealKey = creds.some(c => {
204
+ if (c.type === "oauth")
205
+ return true;
206
+ if (c.type !== "api_key")
207
+ return false;
208
+ const key = c.key?.trim();
209
+ if (!key)
210
+ return false;
211
+ return !(CLI_AUTH_PROVIDERS.has(providerId) && key === "cli");
212
+ });
205
213
  if (hasRealKey) {
206
214
  return {
207
215
  found: true,
@@ -229,6 +237,11 @@ function resolveKey(providerId) {
229
237
  if (hasModelsJsonApiKey(providerId)) {
230
238
  return { found: true, source: "models.json", backedOff: false };
231
239
  }
240
+ // Cross-provider routes can use a local CLI when it is installed. Explicit
241
+ // external CLI provider selections are handled in checkLlmProviders() below.
242
+ if (CLI_AUTH_PROVIDERS.has(providerId) && isCliBinaryInPath(providerId)) {
243
+ return { found: true, source: "env", backedOff: false };
244
+ }
232
245
  return { found: false, source: "none", backedOff: false };
233
246
  }
234
247
  // ── Individual check groups ────────────────────────────────────────────────────
@@ -236,15 +249,32 @@ function checkLlmProviders() {
236
249
  const required = collectConfiguredModelProviders();
237
250
  const results = [];
238
251
  for (const providerId of required) {
239
- // CLI-authenticated providers don't need API keys skip key check
252
+ // CLI-authenticated providers don't need API keys. The provider's own
253
+ // request path validates CLI sessions when it is used.
240
254
  if (CLI_AUTH_PROVIDERS.has(providerId)) {
241
255
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
256
+ const label = info?.label ?? providerId;
257
+ if (CLI_AUTH_PATH_CHECK_PROVIDERS.has(providerId) && !isCliBinaryInPath(providerId)) {
258
+ const binaries = CLI_BINARY_MAP[providerId]?.map(binary => `\`${binary}\``).join(" or ");
259
+ results.push({
260
+ name: providerId,
261
+ label,
262
+ category: "llm",
263
+ status: "error",
264
+ message: `${label} — CLI not found`,
265
+ detail: binaries
266
+ ? `Install ${label} and ensure ${binaries} is on PATH`
267
+ : `Install ${label} and ensure its CLI is on PATH`,
268
+ required: true,
269
+ });
270
+ continue;
271
+ }
242
272
  results.push({
243
273
  name: providerId,
244
- label: info?.label ?? providerId,
274
+ label,
245
275
  category: "llm",
246
276
  status: "ok",
247
- message: `${info?.label ?? providerId} — CLI auth (no key needed)`,
277
+ message: `${label} — CLI auth (no key needed)`,
248
278
  required: true,
249
279
  });
250
280
  continue;
@@ -282,7 +312,7 @@ function checkLlmProviders() {
282
312
  message: `${label} — not configured`,
283
313
  detail: providerId === "anthropic-vertex"
284
314
  ? "Set ANTHROPIC_VERTEX_PROJECT_ID and authenticate with Google ADC"
285
- : info?.hasOAuth
315
+ : info && supportsBrowserOAuth(info)
286
316
  ? `Run /gsd keys to authenticate`
287
317
  : `Set ${envVar} or run /gsd keys`,
288
318
  required: true,
@@ -480,13 +480,18 @@ export async function runGSDDoctor(basePath, options) {
480
480
  catch {
481
481
  continue;
482
482
  }
483
+ if (entry === "parallel-research")
484
+ continue;
483
485
  if (!knownSliceIds.has(entry)) {
486
+ const quarantineExample = `.gsd/quarantine/milestones/${milestoneId}/slices/${entry}-manual-review`;
484
487
  issues.push({
485
488
  severity: "warning",
486
489
  code: "orphaned_slice_directory",
487
490
  scope: "milestone",
488
491
  unitId: milestoneId,
489
- message: `Directory "${entry}" exists in ${milestoneId}/slices/ but is not referenced in the roadmap`,
492
+ message: `Directory "${entry}" exists in ${milestoneId}/slices/ but is not referenced in the roadmap or DB. ` +
493
+ `Review it; if stale, move or delete it. To preserve it, move it under ${quarantineExample}. ` +
494
+ "If it contains work to keep, copy or merge that content into a DB-backed slice before resuming.",
490
495
  file: `${relMilestonePath(basePath, milestoneId)}/slices/${entry}`,
491
496
  fixable: false,
492
497
  });
@@ -2,7 +2,7 @@
2
2
  // File Purpose: Detect and reconcile unresolved Git conflict state before automation runs.
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { existsSync } from "node:fs";
5
- import { join } from "node:path";
5
+ import { dirname, join, resolve } from "node:path";
6
6
  import { autoResolveSafeConflictPaths } from "./git-conflict-resolve.js";
7
7
  import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
8
8
  import { abortAndReset } from "./git-self-heal.js";
@@ -10,6 +10,23 @@ import { logWarning } from "./workflow-logger.js";
10
10
  function splitZeroDelimited(output) {
11
11
  return output.split("\0").filter(Boolean);
12
12
  }
13
+ function hasGitMarker(basePath) {
14
+ try {
15
+ let dir = resolve(basePath);
16
+ for (let i = 0; i < 30; i++) {
17
+ if (existsSync(join(dir, ".git")))
18
+ return true;
19
+ const parent = dirname(dir);
20
+ if (parent === dir)
21
+ break;
22
+ dir = parent;
23
+ }
24
+ }
25
+ catch {
26
+ // Fall through to the git probes, which will report unknown on failure.
27
+ }
28
+ return false;
29
+ }
13
30
  export function listUnmergedGitPaths(basePath) {
14
31
  try {
15
32
  const output = spawnSync("git", ["diff", "--name-only", "--diff-filter=U", "-z"], {
@@ -57,6 +74,14 @@ export function listMergeStateBlockers(basePath) {
57
74
  return MERGE_STATE_MARKERS.filter((marker) => existsSync(join(gitDir, marker)));
58
75
  }
59
76
  export function probeGitConflictState(basePath) {
77
+ if (!hasGitMarker(basePath)) {
78
+ return {
79
+ status: "clean",
80
+ unmerged: [],
81
+ checkFailures: [],
82
+ mergeStateBlockers: [],
83
+ };
84
+ }
60
85
  const unmerged = listUnmergedGitPaths(basePath);
61
86
  if (unmerged === null) {
62
87
  return {
@@ -934,7 +934,7 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType,
934
934
  if (unitType)
935
935
  setGuidedUnitContext(projectRoot, unitType);
936
936
  try {
937
- pi.sendMessage({
937
+ await pi.sendMessage({
938
938
  customType,
939
939
  content: buildWorkflowDispatchContent({ workflow, workflowPath, task: note }),
940
940
  display: false,
@@ -946,10 +946,9 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType,
946
946
  }
947
947
  }
948
948
  finally {
949
- // Restore full tool set after the message is queued. The LLM turn has
950
- // already captured the scoped set restoring prevents the narrowed
951
- // tools from leaking into subsequent dispatches (#3628). The finally
952
- // block ensures restoration even if sendMessage throws.
949
+ // Restore full tool/skill surface after the turn completes. Awaiting
950
+ // sendMessage ensures scoped skills stay in _baseSystemPrompt through
951
+ // before_agent_start (#3628, skill token savings).
953
952
  restoreGsdWorkflowTools(pi, savedTools);
954
953
  }
955
954
  }
@@ -1045,7 +1044,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
1045
1044
  * Run preparation phase if enabled, then build the discuss prompt.
1046
1045
  * Preparation analyzes the codebase and prior context, injecting the results
1047
1046
  * as supplementary context into the standard discuss template. The discuss
1048
- * template drives the conversation (asks "What's the vision?" first), while
1047
+ * template drives the conversation with a variable vision opener, while
1049
1048
  * the preparation briefs give the agent grounding in the existing codebase.
1050
1049
  *
1051
1050
  * @param ctx - Extension command context with UI for progress notifications
@@ -12,18 +12,18 @@ import { getErrorMessage } from "./error-utils.js";
12
12
  import { gsdHome } from "./gsd-home.js";
13
13
  export const PROVIDER_REGISTRY = [
14
14
  // LLM Providers
15
- { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], hasOAuth: true, dashboardUrl: "console.anthropic.com" },
15
+ { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], authMode: "apiKey", dashboardUrl: "console.anthropic.com" },
16
16
  // Claude Code CLI: routes through the local `claude` binary — no API key,
17
17
  // authentication is handled by the CLI's own OAuth flow.
18
18
  // Referenced by doctor-providers.ts, auto-model-selection.ts, and others;
19
19
  // must be in the canonical registry so all consumers see the same catalog.
20
20
  // See: https://github.com/open-gsd/gsd-pi/issues/4541
21
- { id: "claude-code", label: "Claude Code CLI", category: "llm", hasOAuth: true },
21
+ { id: "claude-code", label: "Claude Code CLI", category: "llm", authMode: "externalCli" },
22
22
  { id: "openai", label: "OpenAI", category: "llm", envVar: "OPENAI_API_KEY", prefixes: ["sk-"], dashboardUrl: "platform.openai.com/api-keys" },
23
- { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", hasOAuth: true },
24
- { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)", category: "llm", hasOAuth: true },
25
- { id: "google-gemini-cli", label: "Google Gemini CLI", category: "llm", hasOAuth: true },
26
- { id: "google-antigravity", label: "Antigravity", category: "llm", hasOAuth: true },
23
+ { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", authMode: "browserOAuth" },
24
+ { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)", category: "llm", authMode: "browserOAuth" },
25
+ { id: "google-gemini-cli", label: "Google Gemini CLI", category: "llm", authMode: "externalCli" },
26
+ { id: "google-antigravity", label: "Antigravity", category: "llm", authMode: "externalCli" },
27
27
  { id: "google", label: "Google (Gemini)", category: "llm", envVar: "GEMINI_API_KEY", dashboardUrl: "aistudio.google.com/apikey" },
28
28
  { id: "groq", label: "Groq", category: "llm", envVar: "GROQ_API_KEY", dashboardUrl: "console.groq.com" },
29
29
  { id: "xai", label: "xAI (Grok)", category: "llm", envVar: "XAI_API_KEY", dashboardUrl: "console.x.ai" },
@@ -49,6 +49,20 @@ export const PROVIDER_REGISTRY = [
49
49
  { id: "telegram_bot", label: "Telegram Bot", category: "remote", envVar: "TELEGRAM_BOT_TOKEN" },
50
50
  ];
51
51
  // ─── Utilities ──────────────────────────────────────────────────────────────────
52
+ export function getProviderAuthMode(provider) {
53
+ return provider.authMode ?? "apiKey";
54
+ }
55
+ export function supportsBrowserOAuth(provider) {
56
+ return getProviderAuthMode(provider) === "browserOAuth";
57
+ }
58
+ export function supportsStoredApiKey(provider) {
59
+ const mode = getProviderAuthMode(provider);
60
+ if (mode === "externalCli" || mode === "cloudIdentity" || mode === "none")
61
+ return false;
62
+ if (mode === "browserOAuth")
63
+ return Boolean(provider.envVar || provider.prefixes?.length);
64
+ return true;
65
+ }
52
66
  /**
53
67
  * Mask an API key for display: show first 4 + last 4 chars.
54
68
  * Keys shorter than 12 chars show only first 2 + last 2.
@@ -79,11 +93,14 @@ export function formatDuration(ms) {
79
93
  /**
80
94
  * Describe a credential's type and status.
81
95
  */
82
- export function describeCredential(cred) {
96
+ export function describeCredential(cred, provider) {
83
97
  if (cred.type === "api_key") {
84
98
  const apiCred = cred;
85
99
  if (!apiCred.key)
86
100
  return "empty key";
101
+ if (apiCred.key === "cli" && provider && getProviderAuthMode(provider) === "externalCli") {
102
+ return "external CLI";
103
+ }
87
104
  return `API key (${maskKey(apiCred.key)})`;
88
105
  }
89
106
  if (cred.type === "oauth") {
@@ -129,7 +146,7 @@ export function getAllKeyStatuses(auth) {
129
146
  const firstCred = creds[0];
130
147
  const desc = creds.length > 1
131
148
  ? `${creds.length} keys (round-robin)`
132
- : describeCredential(firstCred);
149
+ : describeCredential(firstCred, provider);
133
150
  return {
134
151
  provider,
135
152
  configured: true,
@@ -236,9 +253,20 @@ export async function handleAddKey(providerArg, ctx, auth) {
236
253
  return false;
237
254
  provider = PROVIDER_REGISTRY[idx];
238
255
  }
239
- // If OAuth is available, offer choice
240
- if (provider.hasOAuth) {
241
- const methods = ["API key", "Browser login (OAuth)"];
256
+ const authMode = getProviderAuthMode(provider);
257
+ if (authMode === "externalCli") {
258
+ ctx.ui.notify(`${provider.label} is authenticated by its own local CLI. ` +
259
+ `Sign in with that CLI first, then run /login to activate it in GSD.`, "info");
260
+ return false;
261
+ }
262
+ if (authMode === "cloudIdentity") {
263
+ ctx.ui.notify(`${provider.label} uses cloud identity credentials. Configure the provider's cloud CLI or environment, then restart GSD.`, "info");
264
+ return false;
265
+ }
266
+ if (supportsBrowserOAuth(provider)) {
267
+ const methods = supportsStoredApiKey(provider)
268
+ ? ["API token/key", "Browser login (OAuth)"]
269
+ : ["Browser login (OAuth)"];
242
270
  const method = await ctx.ui.select(`${provider.label} — how do you want to authenticate?`, methods);
243
271
  if (!method || typeof method !== "string")
244
272
  return false;
@@ -248,6 +276,10 @@ export async function handleAddKey(providerArg, ctx, auth) {
248
276
  return false;
249
277
  }
250
278
  }
279
+ if (!supportsStoredApiKey(provider)) {
280
+ ctx.ui.notify(`${provider.label} does not accept a GSD-stored API key. Use /login.`, "info");
281
+ return false;
282
+ }
251
283
  // API key input
252
284
  const input = await ctx.ui.input(`API key for ${provider.label}:`, provider.envVar ? `or set ${provider.envVar} env var` : "paste your key here");
253
285
  if (input === null || input === undefined)
@@ -309,7 +341,7 @@ export async function handleRemoveKey(providerArg, ctx, auth) {
309
341
  }
310
342
  // Multi-key handling
311
343
  if (creds.length > 1) {
312
- const options = creds.map((c, i) => `[${i + 1}] ${describeCredential(c)}`);
344
+ const options = creds.map((c, i) => `[${i + 1}] ${describeCredential(c, provider)}`);
313
345
  options.push("Remove all");
314
346
  const choice = await ctx.ui.select(`${provider.label} has ${creds.length} keys. Remove which?`, options);
315
347
  if (!choice || typeof choice !== "string")
@@ -330,7 +362,7 @@ export async function handleRemoveKey(providerArg, ctx, auth) {
330
362
  }
331
363
  }
332
364
  else {
333
- const confirmed = await ctx.ui.confirm("Remove key?", `Remove ${describeCredential(creds[0])} for ${provider.label}?`);
365
+ const confirmed = await ctx.ui.confirm("Remove key?", `Remove ${describeCredential(creds[0], provider)} for ${provider.label}?`);
334
366
  if (!confirmed)
335
367
  return false;
336
368
  auth.remove(provider.id);
@@ -0,0 +1,28 @@
1
+ import { join } from "node:path";
2
+ import { gsdRoot } from "./paths.js";
3
+ import { readEvents } from "./workflow-events.js";
4
+ export function latestExplicitReopenAt(basePath, milestoneId) {
5
+ const root = gsdRoot(basePath);
6
+ const candidates = [
7
+ join(root, "event-log.jsonl"),
8
+ join(root, `event-log-${milestoneId}.jsonl.archived`),
9
+ ];
10
+ let latest = null;
11
+ for (const file of candidates) {
12
+ for (const event of readEvents(file)) {
13
+ const eventMilestoneId = event.params.milestoneId;
14
+ if (event.cmd !== "reopen-milestone" || eventMilestoneId !== milestoneId)
15
+ continue;
16
+ if (!latest || event.ts > latest)
17
+ latest = event.ts;
18
+ }
19
+ }
20
+ return latest;
21
+ }
22
+ export function isAfter(value, cutoff) {
23
+ if (!cutoff)
24
+ return true;
25
+ if (!value)
26
+ return true;
27
+ return Date.parse(value) > Date.parse(cutoff);
28
+ }
@@ -3,7 +3,7 @@
3
3
  import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
4
4
  import { readNotifications, markAllRead, clearNotifications, onNotificationStoreChange, } from "./notification-store.js";
5
5
  import { formattedShortcutPair } from "./shortcut-defs.js";
6
- import { padRightVisible, renderFrame, renderKeyHints, rightAlign, wrapVisibleText, } from "./tui/render-kit.js";
6
+ import { padRightVisible, renderDialogFrame, renderKeyHints, rightAlign, wrapVisibleText, } from "./tui/render-kit.js";
7
7
  const FILTER_CYCLE = ["all", "error", "warning", "success", "info"];
8
8
  const OVERLAY_WIDTH = "58%";
9
9
  const OVERLAY_MIN_WIDTH = 68;
@@ -163,12 +163,16 @@ export class GSDNotificationOverlay {
163
163
  const terminalRows = process.stdout.rows || 32;
164
164
  const availableRows = Math.max(1, terminalRows - OVERLAY_MARGIN.top - OVERLAY_MARGIN.bottom);
165
165
  const overlayRows = Math.min(availableRows, Math.max(1, Math.floor((terminalRows * OVERLAY_MAX_HEIGHT_PERCENT) / 100)));
166
- const maxVisibleRows = Math.max(5, overlayRows - 2);
166
+ const maxVisibleRows = Math.max(5, overlayRows - 4);
167
167
  const visibleContentRows = Math.min(content.length, maxVisibleRows);
168
168
  const maxScroll = Math.max(0, content.length - visibleContentRows);
169
169
  this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
170
170
  const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
171
- const lines = renderFrame(this.theme, visibleContent, width);
171
+ const footer = renderKeyHints(this.theme, ["↑/↓ scroll", "f filter", "c clear", `Esc/${formattedShortcutPair("notifications")} close`], Math.max(1, width - 4));
172
+ const lines = renderDialogFrame(this.theme, "Notifications", visibleContent, width, {
173
+ footer,
174
+ scroll: { offset: this.scrollOffset, visibleRows: visibleContentRows, totalRows: content.length },
175
+ });
172
176
  this.cachedWidth = width;
173
177
  this.cachedLines = lines;
174
178
  return lines;
@@ -211,8 +215,6 @@ export class GSDNotificationOverlay {
211
215
  };
212
216
  const blank = () => row("");
213
217
  const hr = () => row(th.fg("dim", "─".repeat(contentWidth)));
214
- // Header
215
- const title = th.fg("accent", th.bold("Notifications"));
216
218
  const filterLabel = this.filter === "all"
217
219
  ? th.fg("dim", "all")
218
220
  : th.fg(this.filter === "error" ? "error"
@@ -220,11 +222,8 @@ export class GSDNotificationOverlay {
220
222
  : this.filter === "success" ? "success"
221
223
  : "dim", this.filter);
222
224
  const count = `${this.filteredEntries.length} entries`;
223
- lines.push(row(rightAlign(`${title} ${th.fg("dim", "filter:")} ${filterLabel}`, th.fg("dim", count), contentWidth)));
225
+ lines.push(row(rightAlign(`${th.fg("dim", "filter:")} ${filterLabel}`, th.fg("dim", count), contentWidth)));
224
226
  lines.push(hr());
225
- // Controls
226
- const closeShortcut = formattedShortcutPair("notifications");
227
- lines.push(row(renderKeyHints(th, ["↑/↓ scroll", "f filter", "c clear", `Esc/${closeShortcut} close`], contentWidth)));
228
227
  lines.push(blank());
229
228
  // Entries
230
229
  const filtered = this.filteredEntries;
@@ -7,7 +7,7 @@ import { matchesKey, Key } from "@gsd/pi-tui";
7
7
  import { formatDuration } from "../shared/mod.js";
8
8
  import { formattedShortcutPair } from "./shortcut-defs.js";
9
9
  import { resolveGsdPathContract } from "./paths.js";
10
- import { renderBar, renderKeyHints, renderProgressBar, safeLine, statusGlyph, } from "./tui/render-kit.js";
10
+ import { renderBar, renderDialogFrame, renderKeyHints, renderProgressBar, safeLine, statusGlyph, } from "./tui/render-kit.js";
11
11
  // ─── Data Helpers ─────────────────────────────────────────────────────────
12
12
  function readJsonSafe(filePath) {
13
13
  try {
@@ -306,15 +306,14 @@ export class ParallelMonitorOverlay {
306
306
  const t = this.theme;
307
307
  const lines = [];
308
308
  const w = Math.max(1, width);
309
- // Header
309
+ const contentWidth = Math.max(1, w - 4);
310
310
  const totalCost = this.workers.reduce((s, wk) => s + wk.cost, 0);
311
311
  const aliveCount = this.workers.filter((wk) => wk.alive).length;
312
312
  const now = new Date().toLocaleTimeString();
313
- lines.push(t.bold(t.fg("accent", " GSD Parallel Monitor ")));
314
313
  lines.push(t.fg("muted", ` ${now} │ ${aliveCount}/${this.workers.length} alive │ Total: `) +
315
314
  t.bold(`$${totalCost.toFixed(2)}`) +
316
315
  t.fg("muted", " │ 5s refresh"));
317
- lines.push(renderBar(t, w));
316
+ lines.push(renderBar(t, contentWidth));
318
317
  if (this.workers.length === 0) {
319
318
  lines.push("");
320
319
  lines.push(t.fg("warning", " No parallel workers found."));
@@ -360,7 +359,7 @@ export class ParallelMonitorOverlay {
360
359
  });
361
360
  lines.push(` ${t.fg("muted", "slices")} ${chips.join(" ")}`);
362
361
  // Task progress bar
363
- const barWidth = Math.max(6, Math.min(25, w - 32));
362
+ const barWidth = Math.max(6, Math.min(25, contentWidth - 32));
364
363
  const bar = renderProgressBar(t, wk.doneTasks, wk.totalTasks, barWidth, {
365
364
  filledChar: "█",
366
365
  emptyChar: "░",
@@ -378,7 +377,7 @@ export class ParallelMonitorOverlay {
378
377
  }
379
378
  // Event feed
380
379
  lines.push("");
381
- lines.push(renderBar(t, w));
380
+ lines.push(renderBar(t, contentWidth));
382
381
  lines.push(` ${t.bold("Recent Events")}`);
383
382
  if (this.events.length === 0) {
384
383
  lines.push(t.fg("muted", " No events yet..."));
@@ -389,7 +388,6 @@ export class ParallelMonitorOverlay {
389
388
  lines.push(` ${t.fg("muted", "│")} ${t.fg("accent", mid)} ${evt.replace(/^✓ M\d+\//, "")}`);
390
389
  }
391
390
  }
392
- // Footer
393
391
  lines.push("");
394
392
  const allDone = this.workers.length > 0 && this.workers.every((wk) => !wk.alive);
395
393
  if (allDone) {
@@ -400,16 +398,20 @@ export class ParallelMonitorOverlay {
400
398
  }
401
399
  lines.push(` ${t.bold("Total: $" + this.workers.reduce((s, wk) => s + wk.cost, 0).toFixed(2))}`);
402
400
  }
403
- lines.push(renderKeyHints(t, [`ESC/q/${formattedShortcutPair("parallel")} close`, "↑↓ scroll"], w));
404
401
  // Apply scroll — use terminal rows as height estimate
405
402
  const termHeight = process.stdout.rows || 40;
406
- const maxScroll = Math.max(0, lines.length - termHeight);
403
+ const maxBodyRows = Math.max(1, Math.min(lines.length, termHeight - 12));
404
+ const maxScroll = Math.max(0, lines.length - maxBodyRows);
407
405
  this.scrollOffset = Math.min(Math.max(this.scrollOffset, 0), maxScroll);
408
406
  const visible = lines
409
- .slice(this.scrollOffset, this.scrollOffset + termHeight)
410
- .map((line) => safeLine(line, w));
411
- this.cachedLines = visible;
407
+ .slice(this.scrollOffset, this.scrollOffset + maxBodyRows)
408
+ .map((line) => safeLine(line, contentWidth));
409
+ const footer = renderKeyHints(t, [`ESC/q/${formattedShortcutPair("parallel")} close`, "↑↓ scroll"], contentWidth);
410
+ this.cachedLines = renderDialogFrame(t, "GSD Parallel Monitor", visible, w, {
411
+ footer,
412
+ scroll: { offset: this.scrollOffset, visibleRows: maxBodyRows, totalRows: lines.length },
413
+ });
412
414
  this.cachedWidth = width;
413
- return visible;
415
+ return this.cachedLines;
414
416
  }
415
417
  }