fix(pty): always set TERM/COLORTERM for spawned shells

A GUI/launchd-spawned daemon has no TERM in its environment, so child shells
inherited none and tput/zsh/ncurses failed ('tput: No value for $TERM').
The PTY now defaults TERM=xterm-256color and COLORTERM=truecolor (matching
xterm.js) unless the caller already provides them. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 11:47:21 +07:00
parent a929c166a3
commit 07cf7f9ed4
+23
View File
@@ -38,6 +38,16 @@ impl PtyHandle {
cmd.arg(a);
}
cmd.cwd(&spec.cwd);
// Guarantee a terminal environment even when the daemon was launched
// without one (GUI/launchd have no TERM, which breaks tput/zsh/ncurses).
// xterm.js renders an xterm-256color/truecolor terminal. Caller-provided
// values in spec.env win.
if !spec.env.iter().any(|(k, _)| k == "TERM") {
cmd.env("TERM", "xterm-256color");
}
if !spec.env.iter().any(|(k, _)| k == "COLORTERM") {
cmd.env("COLORTERM", "truecolor");
}
for (k, v) in &spec.env {
cmd.env(k, v);
}
@@ -125,6 +135,19 @@ mod tests {
assert!(text.contains("SPACESH_OK"), "got: {text:?}");
}
#[tokio::test]
async fn term_is_set_even_without_inherited_env() {
// Clear TERM in the parent to emulate a GUI/launchd-spawned daemon.
std::env::remove_var("TERM");
let mut handle = PtyHandle::spawn(shell_spec("printf %s \"$TERM\"")).unwrap();
let mut collected = Vec::new();
while let Some(chunk) = handle.output.recv().await {
collected.extend_from_slice(&chunk);
}
let text = String::from_utf8_lossy(&collected);
assert!(text.contains("xterm-256color"), "got: {text:?}");
}
#[tokio::test]
async fn resize_does_not_error() {
let handle = PtyHandle::spawn(shell_spec("sleep 0.2")).unwrap();