From f0fbe6ba5514b7e384108360daa3553f890c244f Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 15 Sep 2025 09:25:06 -0700 Subject: [PATCH 1/3] adding in goose acp --- crates/agent_servers/src/agent_servers.rs | 2 + crates/agent_servers/src/goose.rs | 80 +++++++++++++++++++++++ crates/agent_ui/src/agent_panel.rs | 50 +++++++++++++- crates/agent_ui/src/agent_ui.rs | 3 + crates/feature_flags/src/flags.rs | 10 +++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 crates/agent_servers/src/goose.rs diff --git a/crates/agent_servers/src/agent_servers.rs b/crates/agent_servers/src/agent_servers.rs index 2c2900cb793282..4d21711f5a55a0 100644 --- a/crates/agent_servers/src/agent_servers.rs +++ b/crates/agent_servers/src/agent_servers.rs @@ -2,6 +2,7 @@ mod acp; mod claude; mod custom; mod gemini; +mod goose; #[cfg(any(test, feature = "test-support"))] pub mod e2e_tests; @@ -10,6 +11,7 @@ pub use claude::*; pub use custom::*; use fs::Fs; pub use gemini::*; +pub use goose::*; use project::agent_server_store::AgentServerStore; use acp_thread::AgentConnection; diff --git a/crates/agent_servers/src/goose.rs b/crates/agent_servers/src/goose.rs new file mode 100644 index 00000000000000..54f2dd2f98b24a --- /dev/null +++ b/crates/agent_servers/src/goose.rs @@ -0,0 +1,80 @@ +use std::rc::Rc; +use std::{any::Any, path::Path}; + +use crate::{AgentServer, AgentServerDelegate}; +use acp_thread::AgentConnection; +use anyhow::Result; +use gpui::{App, SharedString, Task}; + +#[derive(Clone)] +pub struct GooseAcp; + +impl AgentServer for GooseAcp { + fn telemetry_id(&self) -> &'static str { + "goose-acp" + } + + fn name(&self) -> SharedString { + "Goose ACP".into() + } + + fn logo(&self) -> ui::IconName { + ui::IconName::Terminal + } + + fn connect( + &self, + root_dir: Option<&Path>, + delegate: AgentServerDelegate, + cx: &mut App, + ) -> Task, Option)>> { + let name = self.name(); + let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string()); + let is_remote = delegate.project.read(cx).is_via_remote_server(); + let default_mode = self.default_mode(cx); + + cx.spawn(async move |cx| { + // Create a command to run "goose acp" + let command = project::agent_server_store::AgentServerCommand { + path: "goose".into(), + args: vec!["acp".to_string()], + env: None, + }; + + let root_dir = std::path::PathBuf::from( + root_dir.as_deref().unwrap_or(".") + ); + + let connection = crate::acp::connect( + name, + command, + &root_dir, + default_mode, + is_remote, + cx, + ) + .await?; + Ok((connection, None)) + }) + } + + fn into_any(self: Rc) -> Rc { + self + } +} + +#[cfg(test)] +pub(crate) mod tests { + use project::agent_server_store::AgentServerCommand; + + use super::*; + use std::path::Path; + + pub fn local_command() -> AgentServerCommand { + AgentServerCommand { + path: "goose".into(), + args: vec!["acp".to_string()], + env: None, + } + } +} diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 2ace7096c25886..23236427c53cec 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -53,7 +53,7 @@ use assistant_tool::ToolWorkingSet; use client::{UserStore, zed_urls}; use cloud_llm_client::{CompletionIntent, Plan, PlanV1, PlanV2, UsageLimit}; use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer}; -use feature_flags::{self, ClaudeCodeFeatureFlag, FeatureFlagAppExt, GeminiAndNativeFeatureFlag}; +use feature_flags::{self, ClaudeCodeFeatureFlag, FeatureFlagAppExt, GeminiAndNativeFeatureFlag, GooseAcpFeatureFlag}; use fs::Fs; use gpui::{ Action, Animation, AnimationExt as _, AnyElement, App, AsyncWindowContext, ClipboardItem, @@ -263,6 +263,7 @@ pub enum AgentType { TextThread, Gemini, ClaudeCode, + GooseAcp, NativeAgent, Custom { name: SharedString, @@ -277,6 +278,7 @@ impl AgentType { Self::NativeAgent => "Agent 2".into(), Self::Gemini => "Gemini CLI".into(), Self::ClaudeCode => "Claude Code".into(), + Self::GooseAcp => "Goose".into(), Self::Custom { name, .. } => name.into(), } } @@ -286,6 +288,7 @@ impl AgentType { Self::Zed | Self::NativeAgent | Self::TextThread => None, Self::Gemini => Some(IconName::AiGemini), Self::ClaudeCode => Some(IconName::AiClaude), + Self::GooseAcp => Some(IconName::Terminal), Self::Custom { .. } => Some(IconName::Terminal), } } @@ -296,6 +299,7 @@ impl From for AgentType { match value { ExternalAgent::Gemini => Self::Gemini, ExternalAgent::ClaudeCode => Self::ClaudeCode, + ExternalAgent::GooseAcp => Self::GooseAcp, ExternalAgent::Custom { name, command } => Self::Custom { name, command }, ExternalAgent::NativeAgent => Self::NativeAgent, } @@ -1166,6 +1170,11 @@ impl AgentPanel { return; } } + crate::ExternalAgent::GooseAcp => { + if !cx.has_flag::() { + return; + } + } } let selected_agent = ext_agent.into(); @@ -1936,6 +1945,17 @@ impl AgentPanel { cx, ) } + AgentType::GooseAcp => { + self.selected_agent = AgentType::GooseAcp; + self.serialize(cx); + self.external_thread( + Some(crate::ExternalAgent::GooseAcp), + None, + None, + window, + cx, + ) + } AgentType::Custom { name, command } => self.external_thread( Some(crate::ExternalAgent::Custom { name, command }), None, @@ -2688,6 +2708,34 @@ impl AgentPanel { }), ) }) + .when(cx.has_flag::(), |menu| { + menu.item( + ContextMenuEntry::new("New Goose ACP Thread") + .icon(IconName::Terminal) + .disabled(is_via_collab) + .icon_color(Color::Muted) + .handler({ + let workspace = workspace.clone(); + move |window, cx| { + if let Some(workspace) = workspace.upgrade() { + workspace.update(cx, |workspace, cx| { + if let Some(panel) = + workspace.panel::(cx) + { + panel.update(cx, |panel, cx| { + panel.new_agent_thread( + AgentType::GooseAcp, + window, + cx, + ); + }); + } + }); + } + } + }), + ) + }) .when(cx.has_flag::(), |mut menu| { let agent_names = agent_server_store .read(cx) diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index 09d2179fc3a2ec..f3d8431b113219 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/crates/agent_ui/src/agent_ui.rs @@ -175,6 +175,7 @@ enum ExternalAgent { #[default] Gemini, ClaudeCode, + GooseAcp, NativeAgent, Custom { name: SharedString, @@ -196,6 +197,7 @@ impl ExternalAgent { Self::NativeAgent => "zed", Self::Gemini => "gemini-cli", Self::ClaudeCode => "claude-code", + Self::GooseAcp => "goose-acp", Self::Custom { .. } => "custom", } } @@ -208,6 +210,7 @@ impl ExternalAgent { match self { Self::Gemini => Rc::new(agent_servers::Gemini), Self::ClaudeCode => Rc::new(agent_servers::ClaudeCode), + Self::GooseAcp => Rc::new(agent_servers::GooseAcp), Self::NativeAgent => Rc::new(agent2::NativeAgentServer::new(fs, history)), Self::Custom { name, command: _ } => { Rc::new(agent_servers::CustomAgentServer::new(name.clone())) diff --git a/crates/feature_flags/src/flags.rs b/crates/feature_flags/src/flags.rs index 2278848a194671..8de3a1e7b37869 100644 --- a/crates/feature_flags/src/flags.rs +++ b/crates/feature_flags/src/flags.rs @@ -54,3 +54,13 @@ impl FeatureFlag for ClaudeCodeFeatureFlag { true } } + +pub struct GooseAcpFeatureFlag; + +impl FeatureFlag for GooseAcpFeatureFlag { + const NAME: &'static str = "goose-acp"; + + fn enabled_for_all() -> bool { + true + } +} From f1f8bbbead969cc8d4c6f532e779eec8c4ef37d4 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 15 Sep 2025 10:32:11 -0700 Subject: [PATCH 2/3] wording --- crates/agent_servers/src/goose.rs | 17 ++++------------- crates/agent_ui/src/agent_panel.rs | 14 +++++--------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/crates/agent_servers/src/goose.rs b/crates/agent_servers/src/goose.rs index 54f2dd2f98b24a..6417525b5c67a2 100644 --- a/crates/agent_servers/src/goose.rs +++ b/crates/agent_servers/src/goose.rs @@ -15,7 +15,7 @@ impl AgentServer for GooseAcp { } fn name(&self) -> SharedString { - "Goose ACP".into() + "Goose".into() } fn logo(&self) -> ui::IconName { @@ -41,19 +41,10 @@ impl AgentServer for GooseAcp { env: None, }; - let root_dir = std::path::PathBuf::from( - root_dir.as_deref().unwrap_or(".") - ); + let root_dir = std::path::PathBuf::from(root_dir.as_deref().unwrap_or(".")); - let connection = crate::acp::connect( - name, - command, - &root_dir, - default_mode, - is_remote, - cx, - ) - .await?; + let connection = + crate::acp::connect(name, command, &root_dir, default_mode, is_remote, cx).await?; Ok((connection, None)) }) } diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 23236427c53cec..1b01dca5f11a49 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -53,7 +53,9 @@ use assistant_tool::ToolWorkingSet; use client::{UserStore, zed_urls}; use cloud_llm_client::{CompletionIntent, Plan, PlanV1, PlanV2, UsageLimit}; use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer}; -use feature_flags::{self, ClaudeCodeFeatureFlag, FeatureFlagAppExt, GeminiAndNativeFeatureFlag, GooseAcpFeatureFlag}; +use feature_flags::{ + self, ClaudeCodeFeatureFlag, FeatureFlagAppExt, GeminiAndNativeFeatureFlag, GooseAcpFeatureFlag, +}; use fs::Fs; use gpui::{ Action, Animation, AnimationExt as _, AnyElement, App, AsyncWindowContext, ClipboardItem, @@ -1948,13 +1950,7 @@ impl AgentPanel { AgentType::GooseAcp => { self.selected_agent = AgentType::GooseAcp; self.serialize(cx); - self.external_thread( - Some(crate::ExternalAgent::GooseAcp), - None, - None, - window, - cx, - ) + self.external_thread(Some(crate::ExternalAgent::GooseAcp), None, None, window, cx) } AgentType::Custom { name, command } => self.external_thread( Some(crate::ExternalAgent::Custom { name, command }), @@ -2710,7 +2706,7 @@ impl AgentPanel { }) .when(cx.has_flag::(), |menu| { menu.item( - ContextMenuEntry::new("New Goose ACP Thread") + ContextMenuEntry::new("New Goose Thread") .icon(IconName::Terminal) .disabled(is_via_collab) .icon_color(Color::Muted) From a7e5ea82ec4c9dd4be60c778a31019a225839bb7 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Mon, 15 Sep 2025 12:35:27 -0700 Subject: [PATCH 3/3] conflict resolution --- crates/agent_ui/src/agent_panel.rs | 64 ++++++++++++------------------ 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 77c6b2bebc6962..81c7e463b9f404 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -1956,50 +1956,36 @@ impl AgentPanel { } }); } -<<<<<<< HEAD - }), - ) - }) - .when(cx.has_flag::(), |menu| { - menu.item( - ContextMenuEntry::new("New Goose Thread") - .icon(IconName::Terminal) - .disabled(is_via_collab) - .icon_color(Color::Muted) - .handler({ - let workspace = workspace.clone(); - move |window, cx| { - if let Some(workspace) = workspace.upgrade() { - workspace.update(cx, |workspace, cx| { - if let Some(panel) = - workspace.panel::(cx) - { - panel.update(cx, |panel, cx| { - panel.new_agent_thread( - AgentType::GooseAcp, - window, - cx, - ); - }); - } - }); - } + } + }), + ) + .item( + ContextMenuEntry::new("New Goose Thread") + .icon(IconName::Terminal) + .disabled(is_via_collab) + .icon_color(Color::Muted) + .handler({ + let workspace = workspace.clone(); + move |window, cx| { + if let Some(workspace) = workspace.upgrade() { + workspace.update(cx, |workspace, cx| { + if let Some(panel) = + workspace.panel::(cx) + { + panel.update(cx, |panel, cx| { + panel.new_agent_thread( + AgentType::GooseAcp, + window, + cx, + ); + }); + } + }); } - }), - ) - }) - .when(cx.has_flag::(), |mut menu| { -||||||| bdf44e55aa - }), - ) - }) - .when(cx.has_flag::(), |mut menu| { -======= } }), ) .map(|mut menu| { ->>>>>>> main let agent_names = agent_server_store .read(cx) .external_agents()