From 8162639a1627bb36d47f4533c29571bc2e2dc9bf Mon Sep 17 00:00:00 2001 From: Odonno Date: Sun, 24 May 2026 11:39:08 +0200 Subject: [PATCH] Allow shell configuration --- crates/jcode-config-types/src/lib.rs | 10 ++++++++++ src/config.rs | 7 ++++++- src/config/env_overrides.rs | 5 +++++ src/tool/bash.rs | 17 ++++++++++++----- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/crates/jcode-config-types/src/lib.rs b/crates/jcode-config-types/src/lib.rs index daf32dd24..1cb5efb07 100644 --- a/crates/jcode-config-types/src/lib.rs +++ b/crates/jcode-config-types/src/lib.rs @@ -865,3 +865,13 @@ impl Default for GatewayConfig { } } } + +/// Terminal / shell execution configuration +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default)] +pub struct TerminalConfig { + /// Custom shell to use for executing commands (e.g. "nu", "zsh", "fish"). + /// Overridden by the JCODE_SHELL environment variable. + /// On Unix, defaults to "bash". On Windows, defaults to "cmd.exe". + pub shell: Option, +} diff --git a/src/config.rs b/src/config.rs index 5469f66f9..640fe5068 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,7 +9,8 @@ pub use jcode_config_types::{ DiffDisplayMode, DisplayConfig, FeatureConfig, GatewayConfig, KeybindingsConfig, MarkdownSpacingMode, NamedProviderAuth, NamedProviderConfig, NamedProviderModelConfig, NamedProviderType, NativeScrollbarConfig, ProviderConfig, SafetyConfig, - SessionPickerResumeAction, SwarmSpawnMode, UpdateChannel, WebSearchConfig, WebSearchEngine, + SessionPickerResumeAction, SwarmSpawnMode, TerminalConfig, UpdateChannel, WebSearchConfig, + WebSearchEngine, }; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashSet}; @@ -104,6 +105,7 @@ const CONFIG_ENV_KEYS: &[&str] = &[ "JCODE_SCROLL_PROMPT_UP_KEY", "JCODE_SCROLL_UP_FALLBACK_KEY", "JCODE_SCROLL_UP_KEY", + "JCODE_SHELL", "JCODE_SHOW_DIFFS", "JCODE_SHOW_THINKING", "JCODE_SIDE_PANEL_NATIVE_SCROLLBAR", @@ -383,6 +385,9 @@ pub struct Config { /// Auto-judge configuration pub autojudge: AutoJudgeConfig, + + /// Terminal / shell execution configuration + pub terminal: TerminalConfig, } /// Agent Client Protocol adapter configuration. diff --git a/src/config/env_overrides.rs b/src/config/env_overrides.rs index f3dd4a913..3721e0c8e 100644 --- a/src/config/env_overrides.rs +++ b/src/config/env_overrides.rs @@ -513,6 +513,11 @@ impl Config { crate::env::set_var("JCODE_COPILOT_PREMIUM", env_val); } } + + // Terminal + if let Ok(v) = std::env::var("JCODE_SHELL") { + self.terminal.shell = Some(v); + } } } diff --git a/src/tool/bash.rs b/src/tool/bash.rs index bf1e61832..b750220ff 100644 --- a/src/tool/bash.rs +++ b/src/tool/bash.rs @@ -428,7 +428,10 @@ async fn handle_background_output_line( file.flush().await.ok(); } -fn build_shell_command(cmd_str: &str) -> TokioCommand { +fn build_shell_command(shell: Option<&str>, cmd_str: &str) -> TokioCommand { + if let Some(shell_name) = shell { + return TokioCommand::new(shell_name); + } #[cfg(windows)] { let mut cmd = TokioCommand::new("cmd.exe"); @@ -488,7 +491,7 @@ mod utf8_truncation_tests { #[cfg(windows)] #[tokio::test] async fn build_shell_command_uses_cmd_and_executes_command() { - let output = build_shell_command("echo hello-from-cmd") + let output = build_shell_command(None, "echo hello-from-cmd") .output() .await .expect("run cmd command"); @@ -627,7 +630,10 @@ impl BashTool { let has_stdin_channel = ctx.stdin_request_tx.is_some(); - let mut command = build_shell_command(¶ms.command); + let mut command = build_shell_command( + crate::config::config().terminal.shell.as_deref(), + ¶ms.command, + ); command .kill_on_drop(true) .stdout(Stdio::piped()) @@ -899,8 +905,9 @@ impl BashTool { &ctx.session_id, notify, wake, - move |output_path| async move { - let mut cmd = build_shell_command(&command); + move |output_path| async move { + let shell = crate::config::config().terminal.shell.clone(); + let mut cmd = build_shell_command(shell.as_deref(), &command); #[cfg(unix)] unsafe { cmd.pre_exec(|| {