@opengsd/gsd-pi 1.0.2-dev.5961fbf → 1.0.2-dev.5f7864c

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 (223) hide show
  1. package/README.md +63 -12
  2. package/dist/onboarding.js +22 -3
  3. package/dist/resource-loader.d.ts +2 -0
  4. package/dist/resource-loader.js +18 -1
  5. package/dist/resources/.managed-resources-content-hash +1 -1
  6. package/dist/resources/extensions/context7/index.js +12 -2
  7. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  8. package/dist/resources/extensions/google-cli/index.js +30 -0
  9. package/dist/resources/extensions/google-cli/models.js +55 -0
  10. package/dist/resources/extensions/google-cli/package.json +11 -0
  11. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  12. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  13. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  14. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  15. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  16. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -15
  17. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  18. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  19. package/dist/resources/extensions/gsd/commands-handlers.js +3 -0
  20. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  21. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  22. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  23. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  24. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  25. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  26. package/dist/resources/extensions/gsd/guided-flow.js +1 -1
  27. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  28. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  29. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  30. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  31. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  32. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  33. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  34. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  35. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -1
  36. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  37. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  38. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  39. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  40. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  41. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  42. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  43. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  44. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  45. package/dist/resources/shared/package-manager-detection.js +36 -0
  46. package/dist/update-check.d.ts +6 -2
  47. package/dist/update-check.js +7 -3
  48. package/dist/web/standalone/.next/BUILD_ID +1 -1
  49. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  50. package/dist/web/standalone/.next/build-manifest.json +2 -2
  51. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  69. package/dist/web/standalone/.next/server/app/index.html +1 -1
  70. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  77. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  78. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  80. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  81. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  82. package/package.json +1 -1
  83. package/packages/cloud-mcp-gateway/package.json +2 -2
  84. package/packages/contracts/package.json +1 -1
  85. package/packages/daemon/package.json +4 -4
  86. package/packages/gsd-agent-core/package.json +5 -5
  87. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  88. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  89. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  90. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  91. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  92. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  93. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  94. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  95. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  96. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  97. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  98. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  99. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  100. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  101. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  102. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  103. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  104. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  105. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  106. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  108. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  110. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  112. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  113. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  114. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  115. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  116. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  117. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  118. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  119. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  120. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  121. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  122. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  123. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  124. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  125. package/packages/gsd-agent-modes/package.json +7 -7
  126. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  127. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  128. package/packages/mcp-server/package.json +3 -3
  129. package/packages/native/package.json +1 -1
  130. package/packages/pi-agent-core/dist/agent-loop.js +13 -13
  131. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  132. package/packages/pi-agent-core/package.json +1 -1
  133. package/packages/pi-ai/dist/models.generated.d.ts +57 -17
  134. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  135. package/packages/pi-ai/dist/models.generated.js +64 -28
  136. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  137. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  138. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  139. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  140. package/packages/pi-ai/dist/types.d.ts +2 -0
  141. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  142. package/packages/pi-ai/dist/types.js.map +1 -1
  143. package/packages/pi-ai/package.json +1 -1
  144. package/packages/pi-coding-agent/package.json +7 -7
  145. package/packages/pi-tui/package.json +1 -1
  146. package/packages/rpc-client/package.json +2 -2
  147. package/pkg/package.json +1 -1
  148. package/scripts/install/detect-existing.js +17 -3
  149. package/scripts/install/npm-global.js +103 -33
  150. package/scripts/install.js +1 -0
  151. package/src/resources/extensions/context7/index.ts +15 -2
  152. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  153. package/src/resources/extensions/google-cli/index.ts +34 -0
  154. package/src/resources/extensions/google-cli/models.ts +57 -0
  155. package/src/resources/extensions/google-cli/package.json +11 -0
  156. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  157. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  158. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  159. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  160. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  161. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -15
  162. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  163. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  164. package/src/resources/extensions/gsd/commands-handlers.ts +2 -0
  165. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  166. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  167. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  168. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  169. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  170. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  171. package/src/resources/extensions/gsd/guided-flow.ts +1 -1
  172. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  173. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  174. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  175. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  176. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  177. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  178. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  179. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  180. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  181. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  182. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  183. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  184. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  185. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  186. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  187. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  188. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  189. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  190. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  191. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  192. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  193. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  194. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  195. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  196. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  197. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  198. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  199. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  200. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  201. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  202. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +60 -0
  203. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +54 -0
  204. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  205. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  206. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  207. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  208. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +56 -4
  209. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  210. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  211. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  212. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  213. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  214. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  215. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  216. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  217. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  218. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  219. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  220. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  221. package/src/resources/shared/package-manager-detection.ts +39 -0
  222. /package/dist/web/standalone/.next/static/{spUYLkQXoHJyxYOMH9VQy → IjxvcC7sl_MHNKXsUZrAy}/_buildManifest.js +0 -0
  223. /package/dist/web/standalone/.next/static/{spUYLkQXoHJyxYOMH9VQy → IjxvcC7sl_MHNKXsUZrAy}/_ssgManifest.js +0 -0
@@ -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);
@@ -15,7 +15,7 @@ import {
15
15
  import { formattedShortcutPair } from "./shortcut-defs.js";
16
16
  import {
17
17
  padRightVisible,
18
- renderFrame,
18
+ renderDialogFrame,
19
19
  renderKeyHints,
20
20
  rightAlign,
21
21
  wrapVisibleText,
@@ -200,13 +200,20 @@ export class GSDNotificationOverlay {
200
200
  availableRows,
201
201
  Math.max(1, Math.floor((terminalRows * OVERLAY_MAX_HEIGHT_PERCENT) / 100)),
202
202
  );
203
- const maxVisibleRows = Math.max(5, overlayRows - 2);
203
+ const maxVisibleRows = Math.max(5, overlayRows - 4);
204
204
  const visibleContentRows = Math.min(content.length, maxVisibleRows);
205
205
  const maxScroll = Math.max(0, content.length - visibleContentRows);
206
206
  this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
207
207
  const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
208
-
209
- const lines = renderFrame(this.theme, visibleContent, width);
208
+ const footer = renderKeyHints(
209
+ this.theme,
210
+ ["↑/↓ scroll", "f filter", "c clear", `Esc/${formattedShortcutPair("notifications")} close`],
211
+ Math.max(1, width - 4),
212
+ );
213
+ const lines = renderDialogFrame(this.theme, "Notifications", visibleContent, width, {
214
+ footer,
215
+ scroll: { offset: this.scrollOffset, visibleRows: visibleContentRows, totalRows: content.length },
216
+ });
210
217
 
211
218
  this.cachedWidth = width;
212
219
  this.cachedLines = lines;
@@ -256,8 +263,6 @@ export class GSDNotificationOverlay {
256
263
  const blank = () => row("");
257
264
  const hr = () => row(th.fg("dim", "─".repeat(contentWidth)));
258
265
 
259
- // Header
260
- const title = th.fg("accent", th.bold("Notifications"));
261
266
  const filterLabel = this.filter === "all"
262
267
  ? th.fg("dim", "all")
263
268
  : th.fg(
@@ -269,15 +274,11 @@ export class GSDNotificationOverlay {
269
274
  );
270
275
  const count = `${this.filteredEntries.length} entries`;
271
276
  lines.push(row(rightAlign(
272
- `${title} ${th.fg("dim", "filter:")} ${filterLabel}`,
277
+ `${th.fg("dim", "filter:")} ${filterLabel}`,
273
278
  th.fg("dim", count),
274
279
  contentWidth,
275
280
  )));
276
281
  lines.push(hr());
277
-
278
- // Controls
279
- const closeShortcut = formattedShortcutPair("notifications");
280
- lines.push(row(renderKeyHints(th, ["↑/↓ scroll", "f filter", "c clear", `Esc/${closeShortcut} close`], contentWidth)));
281
282
  lines.push(blank());
282
283
 
283
284
  // Entries
@@ -13,6 +13,7 @@ import { formattedShortcutPair } from "./shortcut-defs.js";
13
13
  import { resolveGsdPathContract } from "./paths.js";
14
14
  import {
15
15
  renderBar,
16
+ renderDialogFrame,
16
17
  renderKeyHints,
17
18
  renderProgressBar,
18
19
  safeLine,
@@ -378,19 +379,18 @@ export class ParallelMonitorOverlay {
378
379
  const t = this.theme;
379
380
  const lines: string[] = [];
380
381
  const w = Math.max(1, width);
382
+ const contentWidth = Math.max(1, w - 4);
381
383
 
382
- // Header
383
384
  const totalCost = this.workers.reduce((s, wk) => s + wk.cost, 0);
384
385
  const aliveCount = this.workers.filter((wk) => wk.alive).length;
385
386
  const now = new Date().toLocaleTimeString();
386
387
 
387
- lines.push(t.bold(t.fg("accent", " GSD Parallel Monitor ")));
388
388
  lines.push(
389
389
  t.fg("muted", ` ${now} │ ${aliveCount}/${this.workers.length} alive │ Total: `) +
390
390
  t.bold(`$${totalCost.toFixed(2)}`) +
391
391
  t.fg("muted", " │ 5s refresh"),
392
392
  );
393
- lines.push(renderBar(t, w));
393
+ lines.push(renderBar(t, contentWidth));
394
394
 
395
395
  if (this.workers.length === 0) {
396
396
  lines.push("");
@@ -444,7 +444,7 @@ export class ParallelMonitorOverlay {
444
444
  lines.push(` ${t.fg("muted", "slices")} ${chips.join(" ")}`);
445
445
 
446
446
  // Task progress bar
447
- const barWidth = Math.max(6, Math.min(25, w - 32));
447
+ const barWidth = Math.max(6, Math.min(25, contentWidth - 32));
448
448
  const bar = renderProgressBar(t, wk.doneTasks, wk.totalTasks, barWidth, {
449
449
  filledChar: "█",
450
450
  emptyChar: "░",
@@ -466,7 +466,7 @@ export class ParallelMonitorOverlay {
466
466
 
467
467
  // Event feed
468
468
  lines.push("");
469
- lines.push(renderBar(t, w));
469
+ lines.push(renderBar(t, contentWidth));
470
470
  lines.push(` ${t.bold("Recent Events")}`);
471
471
 
472
472
  if (this.events.length === 0) {
@@ -478,7 +478,6 @@ export class ParallelMonitorOverlay {
478
478
  }
479
479
  }
480
480
 
481
- // Footer
482
481
  lines.push("");
483
482
  const allDone = this.workers.length > 0 && this.workers.every((wk) => !wk.alive);
484
483
  if (allDone) {
@@ -491,17 +490,22 @@ export class ParallelMonitorOverlay {
491
490
  }
492
491
  lines.push(` ${t.bold("Total: $" + this.workers.reduce((s, wk) => s + wk.cost, 0).toFixed(2))}`);
493
492
  }
494
- lines.push(renderKeyHints(t, [`ESC/q/${formattedShortcutPair("parallel")} close`, "↑↓ scroll"], w));
495
493
 
496
494
  // Apply scroll — use terminal rows as height estimate
497
495
  const termHeight = process.stdout.rows || 40;
498
- const maxScroll = Math.max(0, lines.length - termHeight);
496
+ const maxBodyRows = Math.max(1, Math.min(lines.length, termHeight - 12));
497
+ const maxScroll = Math.max(0, lines.length - maxBodyRows);
499
498
  this.scrollOffset = Math.min(Math.max(this.scrollOffset, 0), maxScroll);
500
499
  const visible = lines
501
- .slice(this.scrollOffset, this.scrollOffset + termHeight)
502
- .map((line) => safeLine(line, w));
503
- this.cachedLines = visible;
500
+ .slice(this.scrollOffset, this.scrollOffset + maxBodyRows)
501
+ .map((line) => safeLine(line, contentWidth));
502
+ const footer = renderKeyHints(t, [`ESC/q/${formattedShortcutPair("parallel")} close`, "↑↓ scroll"], contentWidth);
503
+
504
+ this.cachedLines = renderDialogFrame(t, "GSD Parallel Monitor", visible, w, {
505
+ footer,
506
+ scroll: { offset: this.scrollOffset, visibleRows: maxBodyRows, totalRows: lines.length },
507
+ });
504
508
  this.cachedWidth = width;
505
- return visible;
509
+ return this.cachedLines;
506
510
  }
507
511
  }
@@ -22,6 +22,7 @@ import { join, dirname } from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
23
  import { logWarning } from "./workflow-logger.js";
24
24
  import { gsdHome } from "./gsd-home.js";
25
+ import { chooseVisionAskVariant } from "./vision-ask.js";
25
26
 
26
27
  type ExistsFn = (path: string) => boolean;
27
28
 
@@ -184,6 +185,7 @@ export function loadPrompt(name: string, vars: Record<string, string> = {}): str
184
185
  planTemplatePath: join(getTemplatesDir(), "plan.md"),
185
186
  taskPlanTemplatePath: join(getTemplatesDir(), "task-plan.md"),
186
187
  taskSummaryTemplatePath: join(getTemplatesDir(), "task-summary.md"),
188
+ visionAsk: chooseVisionAskVariant(),
187
189
  skillActivation: "If a `GSD Skill Preferences` block is present in system context, use it and the `<available_skills>` catalog in your system prompt to decide which skills to load and follow for this unit, without relaxing required verification or artifact rules.",
188
190
  ...vars,
189
191
  };
@@ -1,8 +1,10 @@
1
1
  {{preamble}}
2
2
 
3
- Ask: "What's the vision?" once, then use the reply as vision input.
3
+ Ask exactly this once: "{{visionAsk}}" Then use the user's reply as vision input.
4
4
 
5
- **Special handling:** If the **user's** message is status, branch state, clarification, or any other non-project-description, treat it as vision input and proceed instead of repeating "What's the vision?" Do **not** treat the system preamble above (e.g. "New milestone M00X.") as vision input — wait for the user.
5
+ The opener is intentionally variable so GSD feels alive across project starts. Keep it natural, easy to answer, and assistant/developer shaped: plain language, light guidance, no corporate wording, no roleplay.
6
+
7
+ **Special handling:** If the **user's** message is status, branch state, clarification, or any other non-project-description, treat it as vision input and proceed instead of repeating the opener. Do **not** treat the system preamble above (e.g. "New milestone M00X.") as vision input — wait for the user.
6
8
 
7
9
  ## Reflection Step
8
10
 
@@ -4,6 +4,8 @@ Discuss milestone {{milestoneId}} ("{{milestoneTitle}}"). Identify real gray are
4
4
 
5
5
  **Structured questions available: {{structuredQuestionsAvailable}}**
6
6
 
7
+ **Conversation opener:** For the first user-facing question round, if you need a broad starting question, use: "{{visionAsk}}" Pair it with investigation-shaped follow-ups. Keep the tone natural and easy to answer, like an assistant/developer helping the user shape the next piece of work.
8
+
7
9
  {{inlinedTemplates}}
8
10
 
9
11
  ---
@@ -11,9 +11,9 @@
11
11
  import type { ExtensionContext } from "@gsd/pi-coding-agent";
12
12
  import { type Theme } from "@gsd/pi-coding-agent";
13
13
  import { Key, matchesKey, truncateToWidth, type TUI } from "@gsd/pi-tui";
14
- import { makeUI } from "../shared/tui.js";
15
14
  import { GLYPH } from "../shared/mod.js";
16
15
  import { validateQueueOrder, type DependencyValidation } from "./queue-order.js";
16
+ import { renderDialogFrame, renderKeyHints } from "./tui/render-kit.js";
17
17
 
18
18
  export interface ReorderItem {
19
19
  id: string;
@@ -45,6 +45,7 @@ export async function showQueueReorder(
45
45
  let grabbed = false;
46
46
  let scrollOffset = 0;
47
47
  let cachedLines: string[] | undefined;
48
+ let cachedWidth: number | undefined;
48
49
  let validation: DependencyValidation;
49
50
 
50
51
  // Mutable deps map — tracks removals during this session
@@ -66,6 +67,7 @@ export async function showQueueReorder(
66
67
 
67
68
  function refresh() {
68
69
  cachedLines = undefined;
70
+ cachedWidth = undefined;
69
71
  tui.requestRender();
70
72
  }
71
73
 
@@ -151,17 +153,15 @@ export async function showQueueReorder(
151
153
  }
152
154
 
153
155
  function render(width: number): string[] {
154
- if (cachedLines) return cachedLines;
156
+ if (cachedLines && cachedWidth === width) return cachedLines;
155
157
 
156
- const ui = makeUI(theme, width);
158
+ const contentWidth = Math.max(1, width - 4);
157
159
  const lines: string[] = [];
158
160
  const queueRows: string[] = [];
159
- const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
160
- const add = (s: string) => truncateToWidth(s, width);
161
+ const add = (s: string) => truncateToWidth(s, contentWidth);
161
162
  let cursorQueueRow = 0;
162
163
 
163
- const headerText = grabbed ? " Queue Reorder Moving Item" : " Queue Reorder";
164
- push(ui.bar(), ui.blank(), ui.header(headerText), ui.blank());
164
+ const headerText = grabbed ? "Queue Reorder - Moving Item" : "Queue Reorder";
165
165
 
166
166
  // Completed milestones (dimmed)
167
167
  if (completed.length > 0) {
@@ -170,7 +170,7 @@ export async function showQueueReorder(
170
170
  const label = m.title && m.title !== m.id ? `${m.id} ${m.title}` : m.id;
171
171
  lines.push(add(` ${theme.fg("dim", `${GLYPH.statusDone} ${label}`)}`));
172
172
  }
173
- push(ui.blank());
173
+ lines.push("");
174
174
  }
175
175
 
176
176
  // Pending milestones
@@ -223,7 +223,7 @@ export async function showQueueReorder(
223
223
  // Removed deps feedback
224
224
  const trailingLines: string[] = [];
225
225
  if (removedDeps.length > 0) {
226
- trailingLines.push(...ui.blank());
226
+ trailingLines.push("");
227
227
  for (const r of removedDeps) {
228
228
  trailingLines.push(add(` ${theme.fg("success", `${GLYPH.statusDone} Removed: ${r.milestone} depends_on ${r.dep}`)}`));
229
229
  }
@@ -232,12 +232,10 @@ export async function showQueueReorder(
232
232
  // Circular warning
233
233
  const circ = validation.violations.find(v => v.type === 'circular');
234
234
  if (circ) {
235
- trailingLines.push(...ui.blank());
235
+ trailingLines.push("");
236
236
  trailingLines.push(add(` ${theme.fg("error", `${GLYPH.statusWarning} ${circ.message}`)}`));
237
237
  }
238
238
 
239
- trailingLines.push(...ui.blank());
240
-
241
239
  // Hints — context-sensitive based on grab state
242
240
  const hints: string[] = [];
243
241
  if (grabbed) {
@@ -256,10 +254,9 @@ export async function showQueueReorder(
256
254
  }
257
255
  hints.push("esc");
258
256
 
259
- trailingLines.push(...ui.hints(hints), ...ui.bar());
260
-
261
257
  const maxOverlayRows = Math.max(10, process.stdout.rows ? Math.floor(process.stdout.rows * 0.8) : 24);
262
- const availableQueueRows = Math.max(1, maxOverlayRows - lines.length - trailingLines.length);
258
+ const frameRows = 4;
259
+ const availableQueueRows = Math.max(1, maxOverlayRows - frameRows - lines.length - trailingLines.length);
263
260
  const maxScroll = Math.max(0, queueRows.length - availableQueueRows);
264
261
  if (cursorQueueRow < scrollOffset) {
265
262
  scrollOffset = cursorQueueRow;
@@ -268,13 +265,25 @@ export async function showQueueReorder(
268
265
  }
269
266
  scrollOffset = Math.min(Math.max(scrollOffset, 0), maxScroll);
270
267
 
271
- lines.push(...queueRows.slice(scrollOffset, scrollOffset + availableQueueRows), ...trailingLines);
272
-
273
- cachedLines = lines;
274
- return lines;
268
+ const queueStartRow = lines.length;
269
+ const visibleQueueRows = queueRows.slice(scrollOffset, scrollOffset + availableQueueRows);
270
+ lines.push(...visibleQueueRows, ...trailingLines);
271
+
272
+ cachedLines = renderDialogFrame(theme, headerText, lines, width, {
273
+ footer: renderKeyHints(theme, hints, contentWidth),
274
+ scroll: {
275
+ offset: scrollOffset,
276
+ visibleRows: availableQueueRows,
277
+ totalRows: queueRows.length,
278
+ trackOffset: queueStartRow,
279
+ trackRows: visibleQueueRows.length,
280
+ },
281
+ });
282
+ cachedWidth = width;
283
+ return cachedLines;
275
284
  }
276
285
 
277
- return { render, invalidate: () => { cachedLines = undefined; }, handleInput };
286
+ return { render, invalidate: () => { cachedLines = undefined; cachedWidth = undefined; }, handleInput };
278
287
  }, {
279
288
  overlay: true,
280
289
  overlayOptions: { width: "70%", minWidth: 50, maxHeight: "80%", anchor: "center" },