From 0e8d937a3f3b8fb756f8ce0b6492f30fa9a4a6e7 Mon Sep 17 00:00:00 2001 From: hxreborn <32096750+hxreborn@users.noreply.github.com> Date: Mon, 20 Oct 2025 19:24:39 +0200 Subject: [PATCH] Strip zsh -lc wrapper from TUI command headers (#5374) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends shell wrapper stripping in TUI to handle `zsh -lc` in addition to `bash -lc`. Currently, Linux users (and macOS users with zsh profiles) see cluttered command headers like `• Ran zsh -lc "echo hello"` instead of `• Ran echo hello`. This happens because `codex-rs/tui/src/exec_command.rs` only checks for literal `"bash"`, ignoring `zsh` and absolute paths like `/usr/bin/zsh`. **Changes:** - Added `is_login_shell_with_lc` helper that extracts shell basename and matches against `bash` or `zsh` - Updated pattern matching to use the helper instead of hardcoded check - Added test coverage for zsh and absolute paths (`/usr/bin/zsh`, `/bin/bash`) **Testing:** ```bash cd codex-rs cargo test strip_bash_lc_and_escape -p codex-tui ``` All 4 test cases pass (bash, zsh, and absolute paths for both). Closes #4201 --- .../tui/src/bottom_pane/approval_overlay.rs | 9 +++---- codex-rs/tui/src/exec_command.rs | 26 ++++++++++++++++++- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/approval_overlay.rs b/codex-rs/tui/src/bottom_pane/approval_overlay.rs index ce0e3c9c..e8ecc084 100644 --- a/codex-rs/tui/src/bottom_pane/approval_overlay.rs +++ b/codex-rs/tui/src/bottom_pane/approval_overlay.rs @@ -485,11 +485,10 @@ mod tests { }) .collect(); let expected = vec![ - "✔ You approved codex to".to_string(), - " run /bin/zsh -lc 'git add".to_string(), - " tui/src/render/mod.rs tui/".to_string(), - " src/render/renderable.rs'".to_string(), - " this time".to_string(), + "✔ You approved codex to run".to_string(), + " git add tui/src/render/".to_string(), + " mod.rs tui/src/render/".to_string(), + " renderable.rs this time".to_string(), ]; assert_eq!(rendered, expected); } diff --git a/codex-rs/tui/src/exec_command.rs b/codex-rs/tui/src/exec_command.rs index 5c9a0d03..70ec4e84 100644 --- a/codex-rs/tui/src/exec_command.rs +++ b/codex-rs/tui/src/exec_command.rs @@ -8,9 +8,17 @@ pub(crate) fn escape_command(command: &[String]) -> String { try_join(command.iter().map(String::as_str)).unwrap_or_else(|_| command.join(" ")) } +fn is_login_shell_with_lc(shell: &str) -> bool { + let shell_name = std::path::Path::new(shell) + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(shell); + matches!(shell_name, "bash" | "zsh") +} + pub(crate) fn strip_bash_lc_and_escape(command: &[String]) -> String { match command { - [first, second, third] if first == "bash" && second == "-lc" => third.clone(), + [first, second, third] if is_login_shell_with_lc(first) && second == "-lc" => third.clone(), _ => escape_command(command), } } @@ -46,8 +54,24 @@ mod tests { #[test] fn test_strip_bash_lc_and_escape() { + // Test bash let args = vec!["bash".into(), "-lc".into(), "echo hello".into()]; let cmdline = strip_bash_lc_and_escape(&args); assert_eq!(cmdline, "echo hello"); + + // Test zsh + let args = vec!["zsh".into(), "-lc".into(), "echo hello".into()]; + let cmdline = strip_bash_lc_and_escape(&args); + assert_eq!(cmdline, "echo hello"); + + // Test absolute path to zsh + let args = vec!["/usr/bin/zsh".into(), "-lc".into(), "echo hello".into()]; + let cmdline = strip_bash_lc_and_escape(&args); + assert_eq!(cmdline, "echo hello"); + + // Test absolute path to bash + let args = vec!["/bin/bash".into(), "-lc".into(), "echo hello".into()]; + let cmdline = strip_bash_lc_and_escape(&args); + assert_eq!(cmdline, "echo hello"); } }