diff --git a/crates/nebula-core/src/env.rs b/crates/nebula-core/src/env.rs index 31e071ca..3cfb3e37 100644 --- a/crates/nebula-core/src/env.rs +++ b/crates/nebula-core/src/env.rs @@ -27,6 +27,9 @@ pub const IMPORT_BUNDLE: &str = "NEBULA_IMPORT_BUNDLE"; /// Replaces every agent CLI with one command line, taken verbatim (tests /// stand in `/bin/sh` or a stub script for `claude`). pub const AGENT_CMD: &str = "NEBULA_AGENT_CMD"; +/// Concrete PTY device path captured before a login-shell launch temporarily +/// puts `/dev/null` on stdin for rc-file startup. +pub const PANE_TTY: &str = "NEBULA_PANE_TTY"; /// Idle-session reaper sweep period in ms; tests shorten it. pub const IDLE_REAP_MS: &str = "NEBULA_IDLE_REAP_MS"; /// External-worktree sync probe period in ms; tests shorten it. diff --git a/crates/nebula-daemon/src/registry.rs b/crates/nebula-daemon/src/registry.rs index 0a236698..f66a37de 100644 --- a/crates/nebula-daemon/src/registry.rs +++ b/crates/nebula-daemon/src/registry.rs @@ -66,9 +66,16 @@ const LOGIN_SHELL_ARGS: [&str; 3] = ["-l", "-i", "-c"]; /// give the login shell `/dev/null` as stdin while it sources rc files. /// Shell startup tools that auto-`exec` only when stdin is a TTY (Iris-style /// autocomplete launchers) then leave the `-c` payload alone. The payload -/// reopens `/dev/tty` before starting the agent, so the CLI itself remains -/// fully interactive. -const LOGIN_SHELL_STDIN_SHIM: &str = "exec (String, Vec /// TERMINAL's `.nebula.json` `run`, pipes and `&&` and all — behind the /// same prelude. fn login_shell_line(shell: &str, line: &str) -> (String, Vec) { - let mut cmdline = String::from("exec '{d}/claude-ran'\n", + " echo \"claude $tty stdin=$(tty) pane=${{NEBULA_PANE_TTY-unset}} $*\" > '{d}/claude-ran'\n", " sleep 600\n", "}}\n", ), @@ -3407,10 +3407,12 @@ async fn create_agent_survives_rc_autostart_that_execs_on_tty_stdin() { .await .expect("the rc-defined claude function should run"); let ran = std::fs::read_to_string(&marker).unwrap(); - assert!( - ran.starts_with("claude tty --append-system-prompt "), - "{ran:?}" - ); + let fields: Vec<_> = ran.split_whitespace().collect(); + assert_eq!(fields.first(), Some(&"claude"), "{ran:?}"); + assert_eq!(fields.get(1), Some(&"tty"), "{ran:?}"); + assert_ne!(fields.get(2), Some(&"stdin=/dev/tty"), "{ran:?}"); + assert_eq!(fields.get(3), Some(&"pane=unset"), "{ran:?}"); + assert_eq!(fields.get(4), Some(&"--append-system-prompt"), "{ran:?}"); assert!( !dir.join("iris").exists(), "the rc autostart hook should not have taken over the PTY"