diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index 9e9c5c3b6c..80f9d350c1 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -41,7 +41,7 @@ use crate::config::search_path::SearchPaths; use crate::config::{get_all_extensions, Config}; use crate::oauth::oauth_flow; use crate::prompt_template; -use crate::subprocess::configure_command_no_window; +use crate::subprocess::configure_subprocess; use rmcp::model::{ CallToolRequestParams, Content, ErrorCode, ErrorData, GetPromptResult, Prompt, Resource, ResourceContents, ServerInfo, Tool, @@ -206,9 +206,7 @@ async fn child_process_client( working_dir: Option<&PathBuf>, docker_container: Option, ) -> ExtensionResult { - #[cfg(unix)] - command.process_group(0); - configure_command_no_window(&mut command); + configure_subprocess(&mut command); if let Ok(path) = SearchPaths::builder().path() { command.env("PATH", path); diff --git a/crates/goose/src/providers/claude_code.rs b/crates/goose/src/providers/claude_code.rs index 33ba2f3b33..cbeed18687 100644 --- a/crates/goose/src/providers/claude_code.rs +++ b/crates/goose/src/providers/claude_code.rs @@ -16,7 +16,7 @@ use crate::config::search_path::SearchPaths; use crate::config::{Config, GooseMode}; use crate::conversation::message::{Message, MessageContent}; use crate::model::ModelConfig; -use crate::subprocess::configure_command_no_window; +use crate::subprocess::configure_subprocess; use rmcp::model::Tool; const CLAUDE_CODE_PROVIDER_NAME: &str = "claude-code"; @@ -278,7 +278,7 @@ impl ClaudeCodeProvider { .get_or_try_init(|| async { let mut cmd = Command::new(&self.command); // NO -p flag — persistent mode - configure_command_no_window(&mut cmd); + configure_subprocess(&mut cmd); cmd.arg("--input-format") .arg("stream-json") .arg("--output-format") diff --git a/crates/goose/src/providers/codex.rs b/crates/goose/src/providers/codex.rs index 898355d417..33e0f939f9 100644 --- a/crates/goose/src/providers/codex.rs +++ b/crates/goose/src/providers/codex.rs @@ -19,7 +19,7 @@ use crate::config::search_path::SearchPaths; use crate::config::{Config, GooseMode}; use crate::conversation::message::{Message, MessageContent}; use crate::model::ModelConfig; -use crate::subprocess::configure_command_no_window; +use crate::subprocess::configure_subprocess; use rmcp::model::Role; use rmcp::model::Tool; @@ -155,7 +155,7 @@ impl CodexProvider { } let mut cmd = Command::new(&self.command); - configure_command_no_window(&mut cmd); + configure_subprocess(&mut cmd); // Propagate extended PATH so the codex subprocess can find Node.js // and other dependencies (especially when launched from the desktop app diff --git a/crates/goose/src/providers/cursor_agent.rs b/crates/goose/src/providers/cursor_agent.rs index 9131d9c834..3f782a0003 100644 --- a/crates/goose/src/providers/cursor_agent.rs +++ b/crates/goose/src/providers/cursor_agent.rs @@ -14,7 +14,7 @@ use crate::config::base::CursorAgentCommand; use crate::config::search_path::SearchPaths; use crate::conversation::message::{Message, MessageContent}; use crate::model::ModelConfig; -use crate::subprocess::configure_command_no_window; +use crate::subprocess::configure_subprocess; use futures::future::BoxFuture; use rmcp::model::Tool; @@ -197,7 +197,7 @@ impl CursorAgentProvider { } let mut cmd = Command::new(&self.command); - configure_command_no_window(&mut cmd); + configure_subprocess(&mut cmd); if let Ok(path) = SearchPaths::builder().with_npm().path() { cmd.env("PATH", path); diff --git a/crates/goose/src/providers/gemini_cli.rs b/crates/goose/src/providers/gemini_cli.rs index 9d7186d854..8f7e03e17c 100644 --- a/crates/goose/src/providers/gemini_cli.rs +++ b/crates/goose/src/providers/gemini_cli.rs @@ -15,7 +15,7 @@ use crate::config::Config; use crate::conversation::message::{Message, MessageContent}; use crate::model::ModelConfig; use crate::providers::base::ConfigKey; -use crate::subprocess::configure_command_no_window; +use crate::subprocess::configure_subprocess; use futures::future::BoxFuture; use rmcp::model::Role; use rmcp::model::Tool; @@ -92,7 +92,7 @@ impl GeminiCliProvider { } let mut cmd = Command::new(&self.command); - configure_command_no_window(&mut cmd); + configure_subprocess(&mut cmd); if let Ok(path) = SearchPaths::builder().with_npm().path() { cmd.env("PATH", path); diff --git a/crates/goose/src/subprocess.rs b/crates/goose/src/subprocess.rs index 345a63781b..d7bdfcaede 100644 --- a/crates/goose/src/subprocess.rs +++ b/crates/goose/src/subprocess.rs @@ -4,7 +4,12 @@ use tokio::process::Command; const CREATE_NO_WINDOW_FLAG: u32 = 0x08000000; #[allow(unused_variables)] -pub fn configure_command_no_window(command: &mut Command) { +pub fn configure_subprocess(command: &mut Command) { + // Isolate subprocess into its own process group so it does not receive + // SIGINT when the user presses Ctrl+C in the terminal. + #[cfg(unix)] + command.process_group(0); + #[cfg(windows)] command.creation_flags(CREATE_NO_WINDOW_FLAG); }