diff --git a/crates/goose/src/providers/claude_acp.rs b/crates/goose/src/providers/claude_acp.rs index 468929c2ce..3d8c6a95fa 100644 --- a/crates/goose/src/providers/claude_acp.rs +++ b/crates/goose/src/providers/claude_acp.rs @@ -25,12 +25,18 @@ impl ProviderDef for ClaudeAcpProvider { ProviderMetadata::new( CLAUDE_ACP_PROVIDER_NAME, "Claude Code", - "ACP wrapper for Anthropic's Claude. Install: npm install -g @zed-industries/claude-agent-acp", + "Use goose with your Claude Code subscription via the claude-agent-acp adapter.", ACP_CURRENT_MODEL, vec![], CLAUDE_ACP_DOC_URL, vec![], ) + .with_setup_steps(vec![ + "Install the ACP adapter: `npm install -g @zed-industries/claude-agent-acp`", + "Ensure your Claude CLI is authenticated (run `claude` to verify)", + "Set in your goose config file (`~/.config/goose/config.yaml` on macOS/Linux):\n GOOSE_PROVIDER: claude-acp\n GOOSE_MODEL: current", + "Restart goose for changes to take effect", + ]) } fn from_env( diff --git a/crates/goose/src/providers/codex_acp.rs b/crates/goose/src/providers/codex_acp.rs index 232f8145f8..bdecb7db9e 100644 --- a/crates/goose/src/providers/codex_acp.rs +++ b/crates/goose/src/providers/codex_acp.rs @@ -24,12 +24,18 @@ impl ProviderDef for CodexAcpProvider { ProviderMetadata::new( CODEX_ACP_PROVIDER_NAME, "Codex CLI", - "ACP adapter for OpenAI's coding assistant. Install: npm install -g @zed-industries/codex-acp", + "Use goose with your ChatGPT Plus/Pro subscription via the codex-acp adapter.", ACP_CURRENT_MODEL, vec![], CODEX_ACP_DOC_URL, vec![], ) + .with_setup_steps(vec![ + "Install the ACP adapter: `npm install -g @zed-industries/codex-acp`", + "Run `codex` once to authenticate with your OpenAI account", + "Set in your goose config file (`~/.config/goose/config.yaml` on macOS/Linux):\n GOOSE_PROVIDER: codex-acp\n GOOSE_MODEL: current", + "Restart goose for changes to take effect", + ]) } fn from_env( diff --git a/crates/goose/src/providers/gemini_acp.rs b/crates/goose/src/providers/gemini_acp.rs index 6b996e0509..603a519ca9 100644 --- a/crates/goose/src/providers/gemini_acp.rs +++ b/crates/goose/src/providers/gemini_acp.rs @@ -24,12 +24,18 @@ impl ProviderDef for GeminiAcpProvider { ProviderMetadata::new( GEMINI_ACP_PROVIDER_NAME, "Gemini CLI (ACP)", - "ACP provider for Google's Gemini CLI. Install: npm install -g @google/gemini-cli", + "Use goose with your Google Gemini subscription via the Gemini CLI.", ACP_CURRENT_MODEL, vec![], GEMINI_ACP_DOC_URL, vec![], ) + .with_setup_steps(vec![ + "Install the Gemini CLI: `npm install -g @google/gemini-cli`", + "Run `gemini` once to authenticate with your Google account", + "Set in your goose config file (`~/.config/goose/config.yaml` on macOS/Linux):\n GOOSE_PROVIDER: gemini-acp\n GOOSE_MODEL: current", + "Restart goose for changes to take effect", + ]) } fn from_env( diff --git a/ui/desktop/src/components/settings/providers/modal/ProviderConfiguationModal.tsx b/ui/desktop/src/components/settings/providers/modal/ProviderConfiguationModal.tsx index b46b91600e..b98ea32e32 100644 --- a/ui/desktop/src/components/settings/providers/modal/ProviderConfiguationModal.tsx +++ b/ui/desktop/src/components/settings/providers/modal/ProviderConfiguationModal.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, Fragment } from 'react'; import { Dialog, DialogContent, @@ -26,6 +26,36 @@ import { import { Button } from '../../../../components/ui/button'; import { errorMessage } from '../../../../utils/conversionUtils'; +/** Render a setup step string, turning `backtick` spans into and newlines into
. */ +function renderSetupStep(text: string) { + // Split on backtick-wrapped segments + const parts = text.split(/(`[^`]+`)/g); + return parts.map((part, i) => { + if (part.startsWith('`') && part.endsWith('`')) { + return ( + + {part.slice(1, -1)} + + ); + } + // Handle newlines within text + const lines = part.split('\n'); + return ( + + {lines.map((line, j) => ( + + {j > 0 &&
} + {line} +
+ ))} +
+ ); + }); +} + interface ProviderConfigurationModalProps { provider: ProviderDetails; onClose: () => void; @@ -59,13 +89,20 @@ export default function ProviderConfigurationModal({ ? `Delete configuration for ${provider.metadata.display_name}` : `Configure ${provider.metadata.display_name}`; + const isExternalSetup = + provider.metadata.config_keys.length === 0 && + provider.metadata.setup_steps && + provider.metadata.setup_steps.length > 0; + const descriptionText = showDeleteConfirmation ? isActiveProvider ? `You cannot delete this provider while it's currently in use. Please switch to a different model first.` : 'This will permanently delete the current provider configuration.' : isOAuthProvider ? `Sign in with your ${provider.metadata.display_name} account to use this provider` - : `Add your API key(s) for this provider to integrate into goose`; + : isExternalSetup + ? provider.metadata.description + : `Add your API key(s) for this provider to integrate into goose`; const handleOAuthLogin = async () => { setIsOAuthLoading(true); @@ -241,6 +278,35 @@ export default function ProviderConfigurationModal({ A browser window will open for you to complete the login.

+ ) : provider.metadata.config_keys.length === 0 && + provider.metadata.setup_steps && + provider.metadata.setup_steps.length > 0 ? ( +
+

+ This provider is configured outside of goose. Follow these steps: +

+
    + {provider.metadata.setup_steps.map((step, i) => ( +
  1. {renderSetupStep(step)}
  2. + ))} +
+ {provider.metadata.model_doc_link && ( +

+ See the{' '} + { + e.preventDefault(); + window.electron.openExternal(provider.metadata.model_doc_link); + }} + className="underline hover:text-text-primary" + > + documentation + {' '} + for more details. +

+ )} +
) : ( <> {/* Contains information used to set up each provider */} @@ -271,6 +337,20 @@ export default function ProviderConfigurationModal({ )} + ) : provider.metadata.config_keys.length === 0 && + provider.metadata.setup_steps && + provider.metadata.setup_steps.length > 0 && + !showDeleteConfirmation ? ( +
+ +
) : (