Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions crates/jcode-config-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -892,3 +892,17 @@ impl Default for GatewayConfig {
}
}
}

/// Terminal / shell execution configuration (issue #260 follow-up).
///
/// When `shell` is set, the bash tool spawns the named shell instead
/// of the platform default (`bash` on Unix, `cmd.exe` on Windows).
/// Useful for users on `nu`, `zsh`, `fish`, or PowerShell who want
/// shell-specific syntax to work in agent-spawned commands.
///
/// Overridden by the `JCODE_SHELL` env var.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct TerminalConfig {
pub shell: Option<String>,
}
7 changes: 6 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -383,6 +385,9 @@ pub struct Config {

/// Auto-judge configuration
pub autojudge: AutoJudgeConfig,

/// Terminal / shell execution configuration (issue #260)
pub terminal: TerminalConfig,
}

/// Agent Client Protocol adapter configuration.
Expand Down
9 changes: 9 additions & 0 deletions src/config/env_overrides.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,15 @@ impl Config {
}
}

// Terminal (issue #260 follow-up): JCODE_SHELL overrides
// the bash tool's default shell selection.
if let Ok(v) = std::env::var("JCODE_SHELL") {
let trimmed = v.trim().to_string();
if !trimmed.is_empty() {
self.terminal.shell = Some(trimmed);
}
}

// Provider
if let Ok(v) = std::env::var("JCODE_MODEL") {
self.provider.default_model = Some(v);
Expand Down
27 changes: 23 additions & 4 deletions src/tool/bash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,23 @@ 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 {
// Issue #260 follow-up: when the user configured a custom shell
// via `[terminal] shell = "..."` or JCODE_SHELL, just spawn that
// shell directly with the command string. The shell handles its
// own arg parsing (most shells accept `-c` for inline commands;
// PowerShell uses `-Command`; nu uses `-c`). Falls back to the
// platform default when shell is None.
if let Some(shell_name) = shell {
let mut cmd = TokioCommand::new(shell_name);
// Use `-c` as a reasonable default. Shells that don't accept
// it (e.g. fish-only) should be configured at the script
// level. PowerShell users should set shell = "pwsh" and the
// tool's input will be POSIX-shell-like; for full PowerShell
// syntax, wrap in `pwsh -Command` at the input level.
cmd.arg("-c").arg(cmd_str);
return cmd;
}
#[cfg(windows)]
{
let mut cmd = TokioCommand::new("cmd.exe");
Expand Down Expand Up @@ -488,7 +504,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");
Expand Down Expand Up @@ -627,7 +643,10 @@ impl BashTool {

let has_stdin_channel = ctx.stdin_request_tx.is_some();

let mut command = build_shell_command(&params.command);
let mut command = build_shell_command(
crate::config::config().terminal.shell.as_deref(),
&params.command,
);
command
.kill_on_drop(true)
.stdout(Stdio::piped())
Expand Down Expand Up @@ -900,7 +919,7 @@ impl BashTool {
notify,
wake,
move |output_path| async move {
let mut cmd = build_shell_command(&command);
let mut cmd = build_shell_command(crate::config::config().terminal.shell.as_deref(), &command);
#[cfg(unix)]
unsafe {
cmd.pre_exec(|| {
Expand Down