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 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-27 00:42:01 -08:00
parent 3e8a0e1fb7
commit 1c7346b05e

View File

@@ -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
}