Fix quoted command rendering in tui_app_server (#15825)

When `tui_app_server` is enabled, shell commands in the transcript
render as fully quoted invocations like `/bin/zsh -lc "..."`. The
non-app-server TUI correctly shows the parsed command body.

Root cause:
The app-server stores `ThreadItem::CommandExecution.command` as a
shell-quoted string. When `tui_app_server` bridges that item back into
the exec renderer, it was passing `vec![command]` unchanged instead of
splitting the string back into argv. That prevented
`strip_bash_lc_and_escape()` from recognizing the shell wrapper, so the
renderer displayed the wrapper literally.

Solution:
Add a shared command-string splitter that round-trips shell-quoted
commands back into argv when it is safe to do so, while preserving
non-roundtrippable inputs as a single string. Use that helper everywhere
`tui_app_server` reconstructs exec commands from app-server payloads,
including live command-execution items, replayed thread items, and exec
approval requests. This restores the same command display behavior as
the direct TUI path without breaking Windows-style commands that cannot
be safely round-tripped.
This commit is contained in:
Eric Traut
2026-03-25 22:03:29 -06:00
committed by GitHub
Unverified
parent 4b50446ffa
commit b565f05d79
6 changed files with 191 additions and 22 deletions
@@ -16,6 +16,22 @@ pub(crate) fn strip_bash_lc_and_escape(command: &[String]) -> String {
escape_command(command)
}
pub(crate) fn split_command_string(command: &str) -> Vec<String> {
let Some(parts) = shlex::split(command) else {
return vec![command.to_string()];
};
match shlex::try_join(parts.iter().map(String::as_str)) {
Ok(round_trip)
if round_trip == command
|| (!command.contains(":\\")
&& shlex::split(&round_trip).as_ref() == Some(&parts)) =>
{
parts
}
_ => vec![command.to_string()],
}
}
/// If `path` is absolute and inside $HOME, return the part *after* the home
/// directory; otherwise, return the path as-is. Note if `path` is the homedir,
/// this will return and empty path.
@@ -67,4 +83,25 @@ mod tests {
let cmdline = strip_bash_lc_and_escape(&args);
assert_eq!(cmdline, "echo hello");
}
#[test]
fn split_command_string_round_trips_shell_wrappers() {
let command =
shlex::try_join(["/bin/zsh", "-lc", r#"python3 -c 'print("Hello, world!")'"#])
.expect("round-trippable command");
assert_eq!(
split_command_string(&command),
vec![
"/bin/zsh".to_string(),
"-lc".to_string(),
r#"python3 -c 'print("Hello, world!")'"#.to_string(),
]
);
}
#[test]
fn split_command_string_preserves_non_roundtrippable_windows_commands() {
let command = r#"C:\Program Files\Git\bin\bash.exe -lc "echo hi""#;
assert_eq!(split_command_string(command), vec![command.to_string()]);
}
}