From 3a61140084725c85f6940ade873b3df334ee2429 Mon Sep 17 00:00:00 2001 From: weytani Date: Thu, 12 Mar 2026 22:57:14 -0600 Subject: [PATCH] fix: detect Claude Code native binary via resolved Exe path The native Claude Code binary installs to ~/.local/share/claude/versions/ and is symlinked from ~/.local/bin/claude. On macOS, gopsutil reports the cmdline as just "claude" (the symlink name) without the full path, so the existing node_modules-based IDE signatures don't match. Add Exe field to ProcessInfo (resolved binary path from gopsutil p.Exe()) and check it alongside Cmdline in IDE signature matching. Add a /.local/share/claude/ path signature for the native install. Without this fix, parent_ide_dead (0.30) fires on all MCP servers even when Claude Code is running, pushing servers with has_listener above the kill threshold. Closes #8 Co-Authored-By: Claude Opus 4.6 --- internal/scanner/process.go | 3 +++ internal/scanner/scorer.go | 3 ++- internal/scanner/scorer_test.go | 16 ++++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/internal/scanner/process.go b/internal/scanner/process.go index 88f1cda..cbcfe5d 100644 --- a/internal/scanner/process.go +++ b/internal/scanner/process.go @@ -15,6 +15,7 @@ type ProcessInfo struct { PID int32 PPID int32 Name string + Exe string Cmdline string Args string CreateTime time.Time @@ -66,6 +67,7 @@ func processToInfo(p *process.Process, portMap map[int32][]uint32) *ProcessInfo } ppid, _ := p.Ppid() + exe, _ := p.Exe() cmdline, _ := p.Cmdline() createMs, _ := p.CreateTime() terminal, _ := p.Terminal() @@ -92,6 +94,7 @@ func processToInfo(p *process.Process, portMap map[int32][]uint32) *ProcessInfo PID: p.Pid, PPID: ppid, Name: name, + Exe: exe, Cmdline: cmdline, Args: args, CreateTime: createTime, diff --git a/internal/scanner/scorer.go b/internal/scanner/scorer.go index 57b2d71..68ba9d6 100644 --- a/internal/scanner/scorer.go +++ b/internal/scanner/scorer.go @@ -29,6 +29,7 @@ var ideSignatures = []ideSignature{ // Claude Code CLI — match the actual binary, not anything with "claude" in it {pathContains: "/node_modules/.bin/claude"}, {pathContains: "/@anthropic-ai/claude-code"}, + {pathContains: "/.local/share/claude/"}, // Windsurf {pathContains: "/Windsurf.app/"}, @@ -145,7 +146,7 @@ func checkIDERunningFromList(procs []ProcessInfo) bool { for _, p := range procs { for _, sig := range ideSignatures { - if sig.pathContains != "" && strings.Contains(p.Cmdline, sig.pathContains) { + if sig.pathContains != "" && (strings.Contains(p.Cmdline, sig.pathContains) || strings.Contains(p.Exe, sig.pathContains)) { return true } if sig.exactName != "" && p.Name == sig.exactName { diff --git a/internal/scanner/scorer_test.go b/internal/scanner/scorer_test.go index 04b819f..d3bc465 100644 --- a/internal/scanner/scorer_test.go +++ b/internal/scanner/scorer_test.go @@ -229,7 +229,7 @@ func TestIDEDetectionClaude(t *testing.T) { t.Error("Random claude-helper should NOT be detected as an IDE") } - // Actual Claude Code CLI should be detected + // Actual Claude Code CLI (node_modules install) should be detected procs = []ProcessInfo{ { Name: "node", @@ -237,7 +237,19 @@ func TestIDEDetectionClaude(t *testing.T) { }, } if !checkIDERunningFromList(procs) { - t.Error("Claude Code CLI should be detected as an IDE") + t.Error("Claude Code CLI (node_modules) should be detected as an IDE") + } + + // Claude Code native binary should be detected via Exe path + procs = []ProcessInfo{ + { + Name: "claude", + Exe: "/Users/testuser/.local/share/claude/versions/2.1.74", + Cmdline: "claude --dangerously-skip-permissions", + }, + } + if !checkIDERunningFromList(procs) { + t.Error("Claude Code native binary should be detected as an IDE via Exe path") } }