diff --git a/crates/tty7-cli/src/cli.rs b/crates/tty7-cli/src/cli.rs index 27de76a6..ce8e152e 100644 --- a/crates/tty7-cli/src/cli.rs +++ b/crates/tty7-cli/src/cli.rs @@ -67,7 +67,14 @@ pub enum Command { The environment comes from the server too, for the same reason, so \ `FOO=bar tty7 run -- ...` does not reach the command. Set it where \ the command can see it:\n\n \ - tty7 run -- sh -c 'FOO=bar cargo test'" + tty7 run -- sh -c 'FOO=bar cargo test'\n\n\ + Nor is your stdin forwarded: the command reads from the pane's \ + terminal, which nothing is typing into. Output is streamed back, \ + input does not go the other way, so `echo hi | tty7 run -- cat` \ + waits for input that cannot arrive — with no error, because as far \ + as the pane is concerned a terminal is simply idle. Give the \ + command its input directly:\n\n \ + tty7 run -- sh -c 'cat < input.txt'" )] Run(RunArgs), diff --git a/crates/tty7-core/src/daemon/pane.rs b/crates/tty7-core/src/daemon/pane.rs index aeafdafc..8c58a049 100644 --- a/crates/tty7-core/src/daemon/pane.rs +++ b/crates/tty7-core/src/daemon/pane.rs @@ -122,10 +122,16 @@ struct SpawnConfig { shell: Option, } -/// Why a configured shell cannot be run, in one sentence, before anything -/// tries. Without it a missing program arrives at the window wrapped four -/// deep — "daemon refused Spawn: spawn failed: Unable to spawn … (ENOENT: No -/// such file or directory)" — and the one fact that matters is buried in it. +/// Why a program cannot be run, in one sentence, before anything tries. +/// Without it a missing program arrives at the window wrapped four deep — +/// "daemon refused Spawn: spawn failed: Unable to spawn … (ENOENT: No such +/// file or directory)" — and the one fact that matters is buried in it. +/// +/// "Program", not "shell": the same check stands in front of the configured +/// shell and of a command handed to `tty7 run`, and calling the latter a shell +/// tells someone who typed `tty7 run -- ./build.sh` that tty7 misunderstood +/// what they asked for. The caller supplies the context — the CLI prefixes +/// "spawning `…`" — so the sentence only has to carry the fact. /// /// Only a program given as a path can be checked here; a bare name is resolved /// through PATH by the OS, and guessing at that would be worse than silence. @@ -135,16 +141,16 @@ fn shell_program_problem(program: &str) -> Option { return None; } let Ok(meta) = std::fs::metadata(path) else { - return Some(format!("no such shell on this machine: {program}")); + return Some(format!("no such program on this machine: {program}")); }; if meta.is_dir() { - return Some(format!("the configured shell is a directory: {program}")); + return Some(format!("that program is a directory: {program}")); } #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; if meta.permissions().mode() & 0o111 == 0 { - return Some(format!("the configured shell is not executable: {program}")); + return Some(format!("that program is not executable: {program}")); } } None @@ -3191,7 +3197,7 @@ mod tests { let dir = tempfile::tempdir().expect("tempdir"); let missing = dir.path().join("not-a-shell"); let problem = shell_program_problem(&missing.to_string_lossy()).expect("a problem"); - assert!(problem.contains("no such shell"), "{problem}"); + assert!(problem.contains("no such program"), "{problem}"); assert!(problem.contains("not-a-shell"), "{problem}"); // One sentence: none of the layers this used to arrive wrapped in. assert!(!problem.contains("ENOENT"), "{problem}");