From 1c7346b05e7b60de7202228e4f91afc460b9a282 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Sat, 27 Dec 2025 00:42:01 -0800 Subject: [PATCH] fix: Orphan process check now correctly detects tmux server on macOS The orphan-processes check was incorrectly killing active crew sessions because pgrep -x tmux does not reliably find the tmux server on macOS. Root cause: - pgrep -x tmux was finding tmux attach-session processes but missing the actual tmux server process - Claude processes running in tmux panes were incorrectly flagged as orphaned because their parent (tmux server) was not in the allowed list Fixes: 1. Use ps + awk instead of pgrep to find tmux processes more reliably 2. Exclude Claude.app desktop processes from orphan detection (they are not Gas Town CLI processes) Closes: gt-ronyn Generated with Claude Code Co-Authored-By: Claude Opus 4.5 --- internal/doctor/orphan_check.go | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/internal/doctor/orphan_check.go b/internal/doctor/orphan_check.go index 12cdfc31..91face23 100644 --- a/internal/doctor/orphan_check.go +++ b/internal/doctor/orphan_check.go @@ -324,13 +324,15 @@ type processInfo struct { cmd string } -// getTmuxSessionPIDs returns PIDs of all tmux server processes. +// getTmuxSessionPIDs returns PIDs of all tmux server processes and pane shell PIDs. func (c *OrphanProcessCheck) getTmuxSessionPIDs() (map[int]bool, error) { // Get tmux server PID and all pane PIDs pids := make(map[int]bool) - // Find tmux server process - out, err := exec.Command("pgrep", "-x", "tmux").Output() + // Find tmux server processes using ps instead of pgrep. + // pgrep -x tmux is unreliable on macOS - it often misses the actual server. + // We use ps with awk to find processes where comm is exactly "tmux". + out, err := exec.Command("sh", "-c", `ps ax -o pid,comm | awk '$2 == "tmux" || $2 ~ /\/tmux$/ { print $1 }'`).Output() if err != nil { // No tmux server running return pids, nil @@ -363,7 +365,8 @@ func (c *OrphanProcessCheck) getTmuxSessionPIDs() (map[int]bool, error) { return pids, nil } -// findClaudeProcesses finds all running claude/claude-code processes. +// findClaudeProcesses finds all running claude/claude-code CLI processes. +// Excludes Claude.app desktop application and its helpers. func (c *OrphanProcessCheck) findClaudeProcesses() ([]processInfo, error) { var procs []processInfo @@ -374,8 +377,12 @@ func (c *OrphanProcessCheck) findClaudeProcesses() ([]processInfo, error) { return nil, err } - // Regex to match claude processes - claudePattern := regexp.MustCompile(`(?i)claude`) + // Regex to match claude CLI processes (not Claude.app) + // Match: "claude" or paths ending in "/claude" + claudePattern := regexp.MustCompile(`(?i)(^claude$|/claude$)`) + + // Pattern to exclude Claude.app and related desktop processes + excludePattern := regexp.MustCompile(`(?i)(Claude\.app|claude-native|chrome-native)`) for _, line := range strings.Split(string(out), "\n") { fields := strings.Fields(line) @@ -383,8 +390,15 @@ func (c *OrphanProcessCheck) findClaudeProcesses() ([]processInfo, error) { continue } - // Check if command contains "claude" + // Check if command matches claude CLI cmd := strings.Join(fields[2:], " ") + + // Skip desktop app processes + if excludePattern.MatchString(cmd) { + continue + } + + // Only match CLI claude processes if !claudePattern.MatchString(cmd) { continue }