Show setup instructions for ACP providers in settings modal (#8065)

Signed-off-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
Michael Neale
2026-03-23 16:28:56 +11:00
committed by GitHub
parent c2a02d73c8
commit b4422a6862
4 changed files with 103 additions and 5 deletions
+7 -1
View File
@@ -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(
+7 -1
View File
@@ -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(
+7 -1
View File
@@ -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(
@@ -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 <code> and newlines into <br/>. */
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 (
<code
key={i}
className="px-1 py-0.5 rounded bg-background-secondary text-xs font-mono break-all"
>
{part.slice(1, -1)}
</code>
);
}
// Handle newlines within text
const lines = part.split('\n');
return (
<Fragment key={i}>
{lines.map((line, j) => (
<Fragment key={j}>
{j > 0 && <br />}
{line}
</Fragment>
))}
</Fragment>
);
});
}
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.
</p>
</div>
) : provider.metadata.config_keys.length === 0 &&
provider.metadata.setup_steps &&
provider.metadata.setup_steps.length > 0 ? (
<div className="space-y-3">
<p className="text-sm text-text-secondary">
This provider is configured outside of goose. Follow these steps:
</p>
<ol className="ml-5 list-decimal text-sm text-text-primary space-y-2">
{provider.metadata.setup_steps.map((step, i) => (
<li key={i}>{renderSetupStep(step)}</li>
))}
</ol>
{provider.metadata.model_doc_link && (
<p className="text-sm text-text-secondary mt-4">
See the{' '}
<a
href="#"
onClick={(e) => {
e.preventDefault();
window.electron.openExternal(provider.metadata.model_doc_link);
}}
className="underline hover:text-text-primary"
>
documentation
</a>{' '}
for more details.
</p>
)}
</div>
) : (
<>
{/* Contains information used to set up each provider */}
@@ -271,6 +337,20 @@ export default function ProviderConfigurationModal({
</Button>
)}
</div>
) : provider.metadata.config_keys.length === 0 &&
provider.metadata.setup_steps &&
provider.metadata.setup_steps.length > 0 &&
!showDeleteConfirmation ? (
<div className="w-full">
<Button
type="button"
variant="ghost"
onClick={handleCancel}
className="w-full h-[60px] rounded-none border-t border-border-primary text-md hover:bg-background-secondary text-text-primary font-medium"
>
Close
</Button>
</div>
) : (
<ProviderSetupActions
primaryParameters={primaryParameters}