devreap

Automatically detect and kill orphaned developer processes.
MCP servers, dev servers, headless browsers, and ffmpeg instances that survive after their parent IDE or terminal crashes. Uses multi-signal scoring to avoid false positives. Zero config to start.
Why This Exists
When your IDE crashes or you force-quit a terminal, child processes survive as orphans. This isn't a minor annoyance — it's a systemic problem affecting every developer who uses AI coding agents:
84% of developers now use AI tools (Stack Overflow 2025). Each AI coding session spawns 3-10 background processes. None reliably clean up on crash or force-quit.
macOS makes this worse: there's no PR_SET_PDEATHSIG (Linux's mechanism to auto-kill children when parents die), no PR_SET_CHILD_SUBREAPER, no kernel-level safety net. When your IDE dies on macOS, orphans survive indefinitely.
kill-port gets 1.16M weekly npm downloads — that's a million developers manually killing port-squatting processes every week. fkill-cli has 6,900 GitHub stars. Both are reactive (you find the problem, you kill it). Nothing proactively detects and cleans up orphans.
devreap does.
Install
Homebrew (macOS)
brew install tjp2021/devreap/devreap
Pre-built binaries
Download from GitHub Releases.
From source
go install github.com/tjp2021/devreap/cmd/devreap@latest
Quick Start
# See what's orphaned right now
devreap scan
# Start the daemon (scans every 30s, kills orphans automatically)
devreap start
# Install as LaunchAgent (auto-start on login)
devreap install
# Check status
devreap status
# Run diagnostics
devreap doctor
How It Works
Multi-Signal Orphan Scoring
devreap doesn't use binary "is orphan" / "isn't orphan" detection. A process that matches a known pattern gets scored across multiple signals:
| Signal |
Weight |
Description |
ppid_is_init |
0.40 |
PPID is 1 (parent died, reparented to launchd) |
parent_ide_dead |
0.30 |
No IDE process running (VS Code, Cursor, Claude Code, Zed, JetBrains) |
exceeded_duration |
0.25 |
Running longer than the pattern's max duration |
has_listener |
0.20 |
Bound to a listening port (potential orphaned server) |
no_tty |
0.15 |
No controlling terminal attached |
Default threshold: 0.6 — a process needs multiple signals to be flagged. This eliminates false positives.
Examples:
- MCP server, PPID=1, no Cursor running → 0.70 → killed
- MCP server, PPID=1, Cursor IS running → 0.40 → safe
- Dev server, PPID=1, running 48 hours → 0.65 → killed
- Your Postgres, running as
_postgres user → 0.00 → ignored (wrong user)
IDE Detection
devreap uses path-based signatures, not substring matching. CursorUIViewService (Apple's text cursor macOS service) won't trigger a false positive — only /Applications/Cursor.app/Contents/MacOS/Cursor counts.
Detected IDEs: VS Code, Cursor, Claude Code CLI, Windsurf, Zed, IntelliJ IDEA, WebStorm, GoLand, PyCharm, PhpStorm, RustRover, Xcode.
MCP Config Cross-Referencing
devreap reads your IDE's MCP configuration files:
~/.claude.json (Claude Code)
~/.cursor/mcp.json (Cursor)
~/.vscode/mcp.json (VS Code)
It knows which MCP servers should be running. If servers are running but no IDE is active, they're flagged.
Pattern-Aware Signal Strategy
Each process type gets the right shutdown signal:
- ffmpeg →
SIGINT first (writes moov atom for clean MP4, then SIGTERM, then SIGKILL)
- Node.js →
SIGTERM → SIGKILL
- Chrome →
SIGTERM with extended grace period
Safety
- PID reuse protection — verifies process name before sending signals
- User isolation — only kills your processes, never another user's
- Blocklist — postgres, redis, nginx, sshd, and 20+ system processes are protected
- PID 1 / self / parent protection — hardcoded, can't be overridden
- Config validation — rejects invalid thresholds, weights, and intervals at startup
Commands
devreap scan # One-shot scan, print orphan candidates
devreap scan --json # Machine-readable output
devreap scan -v # Show all pattern matches (including safe ones)
devreap start # Start background daemon
devreap start --foreground # Foreground mode (used by LaunchAgent)
devreap stop # Stop daemon
devreap status # Daemon status + config
devreap kill <pid> # Manual graceful kill
devreap kill --port 3000 # Kill by port
devreap logs # View recent daemon log entries
devreap logs -n 100 # Show last 100 entries
devreap logs --level error # Filter by severity
devreap logs --json # Raw JSON (pipe to jq)
devreap install # Install macOS LaunchAgent
devreap uninstall # Remove LaunchAgent
devreap doctor # Run diagnostics
devreap patterns # List all 18 built-in patterns
devreap version # Print version
Configuration
Optional. devreap works with zero config using sensible defaults.
Create ~/.config/devreap/config.yaml to customize:
scan_interval: 30s # How often to scan (min: 1s, max: 24h)
kill_threshold: 0.6 # Score threshold to kill (0.1 - 1.0)
grace_period: 5s # Time between signals
dry_run: false # Log what would be killed without killing
notify:
enabled: true # macOS notifications on kill
# Tune signal weights (each 0.0 - 1.0)
weights:
ppid_is_init: 0.4
no_tty: 0.15
parent_ide_dead: 0.3
exceeded_duration: 0.25
has_listener: 0.2
# Never kill these
blocklist:
- postgres
- redis-server
- nginx
# Processes to skip even if they match patterns
allowlist:
- my-persistent-mcp-server
# Additional pattern files beyond built-ins
extra_patterns:
- ~/.config/devreap/my-patterns.yaml
Setting one weight preserves all others at their defaults — no silent zeroing.
Built-in Patterns
18 patterns across 4 categories:
| Category |
Patterns |
Max Duration |
Signal |
| MCP servers |
node, python, npx, uvx, docker |
4h |
SIGTERM |
| Dev servers |
Next.js, Vite, Webpack, Expo, CRA, esbuild |
24h |
SIGTERM |
| Headless browsers |
Chrome (headless + remote debugging), Firefox |
2-4h |
SIGTERM |
| Media tools |
ffmpeg, ffprobe, sox, ImageMagick |
30m-2h |
SIGINT/SIGTERM |
See all with devreap patterns.
Architecture
cmd/devreap/main.go → entry point
internal/
cli/ → 10 cobra commands
scanner/
process.go → gopsutil process enumeration + port mapping
scorer.go → multi-signal scoring engine + IDE detection
orphan.go → threshold filtering + parent-first kill ordering
mcp.go → MCP config cross-referencing
patterns/
registry.go → go:embed YAML pattern loading
matcher.go → compiled regex caching (18 compiles, not 12,600)
killer/
killer.go → PID-reuse-safe signal delivery
safety.go → blocklist + ownership checks
signals.go → per-pattern signal sequences
daemon/
daemon.go → scan loop with 30s timeout, sync.Once stop
launchagent.go → macOS plist generation + bootstrap/bootout
config/ → YAML config with validation + partial merge
logger/ → structured JSON logging with rotation
notify/ → macOS notifications via osascript
Single static binary. No runtime dependencies. Cross-compiles to macOS (arm64/amd64) and Linux.
License
MIT