From fc1634b00799b2c88ef8f02710da1e5e39787e7d Mon Sep 17 00:00:00 2001 From: VERSE Date: Thu, 6 Aug 2026 15:42:56 +0700 Subject: [PATCH] fix: start iris in multiplexer panes instead of inheriting a dead one (#120) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #62 when you open a new Zellij pane, the shell inherits Iris’s environment variables, so it thinks Iris is already running. But the pane doesn't inherit the underlying connection (FD 13), so suggestions silently fail into a dead pipe the old code only checked for `tmux` by process name, ignoring Zellij completely - Fixed: instead of checking for specific app names, Iris now tracks the active terminal device (`IRIS_TTY`) if a new shell detects that it's on a different `TTY` than Iris, it clears the stale environment variables so Iris can start fresh. The `IRIS_PID != PPID` check prevents Iris from trying to re-exec itself when spawning sub-shells. ```sh if [ -n "$IRIS_PID" ] && [ "$IRIS_PID" != "$PPID" ] && [ "${TTY:-$(tty 2>/dev/null)}" != "$IRIS_TTY" ]; then unset IRIS_PID IRIS_IS_CHILD IRIS_FD IRIS_TTY fi ``` --- root/init.go | 25 +++++++++++++++---------- root/wrapper.go | 18 +++++++++++++++--- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/root/init.go b/root/init.go index 2dfb847..edbb30a 100644 --- a/root/init.go +++ b/root/init.go @@ -25,10 +25,10 @@ For example, add this to your ~/.zshrc: case "zsh": fmt.Printf(` # Iris Autostart Hook -if [ -n "$TMUX" ] && [ -n "$IRIS_PID" ]; then - if ps -o comm= -p $PPID 2>/dev/null | grep -q "tmux"; then - unset IRIS_PID IRIS_IS_CHILD IRIS_FD - fi +# a multiplexer pane inherits IRIS_* but runs on its own tty, so those vars +# point at an iris that is not driving this terminal +if [ -n "$IRIS_PID" ] && [ "$IRIS_PID" != "$PPID" ] && [ "${TTY:-$(tty 2>/dev/null)}" != "$IRIS_TTY" ]; then + unset IRIS_PID IRIS_IS_CHILD IRIS_FD IRIS_TTY fi if [ -z "$IRIS_PID" ] && [ -z "$IRIS_RESCUE" ]; then @@ -68,10 +68,10 @@ fi case "bash": fmt.Printf(` # Iris Autostart Hook -if [ -n "$TMUX" ] && [ -n "$IRIS_PID" ]; then - if ps -o comm= -p $PPID 2>/dev/null | grep -q "tmux"; then - unset IRIS_PID IRIS_IS_CHILD IRIS_FD - fi +# a multiplexer pane inherits IRIS_* but runs on its own tty, so those vars +# point at an iris that is not driving this terminal +if [ -n "$IRIS_PID" ] && [ "$IRIS_PID" != "$PPID" ] && [ "$(tty 2>/dev/null)" != "$IRIS_TTY" ]; then + unset IRIS_PID IRIS_IS_CHILD IRIS_FD IRIS_TTY fi if [ -z "$IRIS_PID" ] && [ -z "$IRIS_RESCUE" ]; then @@ -96,11 +96,16 @@ fi case "fish": fmt.Printf(` # Iris Autostart Hook -if set -q TMUX; and set -q IRIS_PID - if ps -o comm= -p $PPID 2>/dev/null | grep -q "tmux" +# a multiplexer pane inherits IRIS_* but runs on its own tty, so those vars +# point at an iris that is not driving this terminal +if set -q IRIS_PID + set -l iris_ppid (ps -o ppid= -p $fish_pid 2>/dev/null | string trim) + set -l iris_cur_tty (tty 2>/dev/null) + if test "$IRIS_PID" != "$iris_ppid"; and test "$iris_cur_tty" != "$IRIS_TTY" set -e IRIS_PID set -e IRIS_IS_CHILD set -e IRIS_FD + set -e IRIS_TTY end end diff --git a/root/wrapper.go b/root/wrapper.go index 94bae49..e8ad519 100644 --- a/root/wrapper.go +++ b/root/wrapper.go @@ -153,15 +153,27 @@ func runWrapper() { c.ExtraFiles = make([]*os.File, 11) // pass write end of pipe to shell as fd 13 (since index 10 maps to 13) c.ExtraFiles[10] = w - c.Env = adapter.GetEnv(13, os.Getpid()) - ptmx, err := pty.Start(c) + ptmx, tts, err := pty.Open() if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "[IRIS] failed to start PTY: %v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "[IRIS] failed to open PTY: %v\n", err) return } defer func() { _ = ptmx.Close() }() + c.Stdin, c.Stdout, c.Stderr = tts, tts, tts + c.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Setctty: true} + // the shell compares its own tty against this to tell whether the inherited + // IRIS_* vars belong to it or leaked in from an outer terminal + c.Env = append(adapter.GetEnv(13, os.Getpid()), "IRIS_TTY="+tts.Name()) + + if err = c.Start(); err != nil { + _ = tts.Close() + _, _ = fmt.Fprintf(os.Stderr, "[IRIS] failed to start shell: %v\n", err) + return + } + _ = tts.Close() + stdinFile := os.Stdin if !term.IsTerminal(int(stdinFile.Fd())) { if tty, ttyErr := os.OpenFile("/dev/tty", os.O_RDWR, 0); ttyErr == nil {