From a179c60ca78c2d330cef1d3aa9b4b5fc2a153d23 Mon Sep 17 00:00:00 2001 From: suda Date: Fri, 10 Apr 2026 01:26:07 +0800 Subject: [PATCH] fix: launch Ghostty via shell command Use Ghostty's shell execution path instead of injecting raw terminal input so Claude resume commands run reliably when opening a session terminal. Co-Authored-By: Claude Opus 4.6 --- src-tauri/src/session_manager/terminal/mod.rs | 84 ++++--------------- 1 file changed, 17 insertions(+), 67 deletions(-) diff --git a/src-tauri/src/session_manager/terminal/mod.rs b/src-tauri/src/session_manager/terminal/mod.rs index ab13466ba..c8901ffa2 100644 --- a/src-tauri/src/session_manager/terminal/mod.rs +++ b/src-tauri/src/session_manager/terminal/mod.rs @@ -77,10 +77,19 @@ end tell"# } fn launch_ghostty(command: &str, cwd: Option<&str>) -> Result<(), String> { - let args = build_ghostty_args(command, cwd); + let full_command = build_shell_command(command, cwd); + let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/zsh".to_string()); let status = Command::new("open") - .args(args.iter().map(String::as_str)) + .arg("-na") + .arg("Ghostty") + .arg("--args") + .arg("--quit-after-last-window-closed=true") + .arg("-e") + .arg(&shell) + .arg("-l") + .arg("-c") + .arg(&full_command) .status() .map_err(|e| format!("Failed to launch Ghostty: {e}"))?; @@ -91,40 +100,6 @@ fn launch_ghostty(command: &str, cwd: Option<&str>) -> Result<(), String> { } } -fn build_ghostty_args(command: &str, cwd: Option<&str>) -> Vec { - let input = ghostty_raw_input(command); - - let mut args = vec![ - "-na".to_string(), - "Ghostty".to_string(), - "--args".to_string(), - "--quit-after-last-window-closed=true".to_string(), - ]; - - if let Some(dir) = cwd { - if !dir.trim().is_empty() { - args.push(format!("--working-directory={dir}")); - } - } - - args.push(format!("--input={input}")); - args -} - -fn ghostty_raw_input(command: &str) -> String { - let mut escaped = String::from("raw:"); - for ch in command.chars() { - match ch { - '\\' => escaped.push_str("\\\\"), - '\n' => escaped.push_str("\\n"), - '\r' => escaped.push_str("\\r"), - _ => escaped.push(ch), - } - } - escaped.push_str("\\n"); - escaped -} - fn launch_kitty(command: &str, cwd: Option<&str>) -> Result<(), String> { let full_command = build_shell_command(command, cwd); @@ -266,43 +241,18 @@ mod tests { use super::*; #[test] - fn ghostty_uses_shell_mode_for_resume_commands() { - let args = build_ghostty_args("claude --resume abc-123", Some("/tmp/project dir")); - - assert_eq!( - args, - vec![ - "-na", - "Ghostty", - "--args", - "--quit-after-last-window-closed=true", - "--working-directory=/tmp/project dir", - "--input=raw:claude --resume abc-123\\n", - ] - ); - } - - #[test] - fn ghostty_keeps_command_without_cwd_prefix_when_not_provided() { - let args = build_ghostty_args("claude --resume abc-123", None); - + fn build_shell_command_prefixes_cwd_for_ghostty_shell_execution() { assert_eq!( - args, - vec![ - "-na", - "Ghostty", - "--args", - "--quit-after-last-window-closed=true", - "--input=raw:claude --resume abc-123\\n", - ] + build_shell_command("claude --resume abc-123", Some("/tmp/project dir")), + "cd \"/tmp/project dir\" && claude --resume abc-123" ); } #[test] - fn ghostty_escapes_newlines_and_backslashes_in_input() { + fn build_shell_command_keeps_command_without_cwd_prefix_when_not_provided() { assert_eq!( - ghostty_raw_input("echo foo\\\\bar\npwd"), - "raw:echo foo\\\\\\\\bar\\npwd\\n" + build_shell_command("claude --resume abc-123", None), + "claude --resume abc-123" ); } }