feat(tmux): add KillSessionWithProcesses for explicit process termination

Before calling tmux kill-session, explicitly kill the pane's process tree
using pkill. This ensures claude processes don't survive session termination
due to SIGHUP being caught/ignored.

Implementation:
- Add KillSessionWithProcesses() to tmux.go
- Update killSessionsInOrder() in start.go to use new method
- Update stopSession() in down.go to use new method

Fixes: gt-5r7zr

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nux
2026-01-13 11:42:16 -08:00
committed by beads/crew/emma
parent 87fde4b4fd
commit e043f4a16c
3 changed files with 38 additions and 5 deletions

View File

@@ -134,6 +134,39 @@ func (t *Tmux) KillSession(name string) error {
return err
}
// KillSessionWithProcesses explicitly kills all processes in a session before terminating it.
// This prevents orphaned processes that can occur when Claude catches/ignores SIGHUP.
// The kill sequence is:
// 1. Get the pane's PID
// 2. Send SIGTERM to the process group (all children)
// 3. Wait 100ms for graceful exit
// 4. Send SIGKILL if processes still alive
// 5. Kill the tmux session
func (t *Tmux) KillSessionWithProcesses(name string) error {
// Get the pane PID
pid, err := t.GetPanePID(name)
if err != nil {
// Session might not exist or be in bad state, try direct kill
return t.KillSession(name)
}
if pid != "" {
// Send SIGTERM to process group (all children of the pane process)
termCmd := exec.Command("pkill", "-TERM", "-P", pid)
_ = termCmd.Run() // Ignore errors - process might already be dead
// Wait for graceful termination
time.Sleep(100 * time.Millisecond)
// Send SIGKILL to any survivors
killCmd := exec.Command("pkill", "-KILL", "-P", pid)
_ = killCmd.Run() // Ignore errors - process might already be dead
}
// Now kill the tmux session
return t.KillSession(name)
}
// KillServer terminates the entire tmux server and all sessions.
func (t *Tmux) KillServer() error {
_, err := t.run("kill-server")