fix(crew): detect running sessions started with shell compound commands

IsClaudeRunning now checks for child processes when the pane command is
a shell (bash/zsh). This fixes gt crew start --all killing running crew
members that were started with "export ... && claude ..." commands.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
dennis
2026-01-10 16:32:59 -08:00
committed by Steve Yegge
parent d13922523a
commit ad6386809c
2 changed files with 120 additions and 1 deletions

View File

@@ -406,6 +406,43 @@ func (t *Tmux) GetPaneWorkDir(session string) (string, error) {
return strings.TrimSpace(out), nil
}
// GetPanePID returns the PID of the pane's main process.
func (t *Tmux) GetPanePID(session string) (string, error) {
out, err := t.run("list-panes", "-t", session, "-F", "#{pane_pid}")
if err != nil {
return "", err
}
return strings.TrimSpace(out), nil
}
// hasClaudeChild checks if a process has a child running claude/node.
// Used when the pane command is a shell (bash, zsh) that launched claude.
func hasClaudeChild(pid string) bool {
// Use pgrep to find child processes
cmd := exec.Command("pgrep", "-P", pid, "-l")
out, err := cmd.Output()
if err != nil {
return false
}
// Check if any child is node or claude
lines := strings.Split(string(out), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
// Format: "PID name" e.g., "29677 node"
parts := strings.Fields(line)
if len(parts) >= 2 {
name := parts[1]
if name == "node" || name == "claude" {
return true
}
}
}
return false
}
// FindSessionByWorkDir finds tmux sessions where the pane's current working directory
// matches or is under the target directory. Returns session names that match.
// If processNames is provided, only returns sessions that match those processes.
@@ -597,6 +634,7 @@ func (t *Tmux) IsAgentRunning(session string, expectedPaneCommands ...string) bo
// IsClaudeRunning checks if Claude appears to be running in the session.
// Only trusts the pane command - UI markers in scrollback cause false positives.
// Claude can report as "node", "claude", or a version number like "2.0.76".
// Also checks for child processes when the pane is a shell running claude via "bash -c".
func (t *Tmux) IsClaudeRunning(session string) bool {
// Check for known command names first
if t.IsAgentRunning(session, "node", "claude") {
@@ -608,7 +646,21 @@ func (t *Tmux) IsClaudeRunning(session string) bool {
return false
}
matched, _ := regexp.MatchString(`^\d+\.\d+\.\d+`, cmd)
return matched
if matched {
return true
}
// If pane command is a shell, check for claude/node child processes.
// This handles the case where sessions are started with "bash -c 'export ... && claude ...'"
for _, shell := range constants.SupportedShells {
if cmd == shell {
pid, err := t.GetPanePID(session)
if err == nil && pid != "" {
return hasClaudeChild(pid)
}
break
}
}
return false
}
// IsRuntimeRunning checks if a runtime appears to be running in the session.