proc
Semantic CLI tool for process management. Target by port, PID, name or path.
Why proc?
| Task | proc | Without proc |
|---|---|---|
| What's on port 3000? | proc on :3000 |
lsof -i :3000 -P -n |
| Kill it | proc kill :3000 |
lsof -i :3000 -t | xargs kill -9 |
| Free port 3000 | proc free :3000 |
lsof -i :3000 -t | xargs kill && sleep 1 && lsof -i :3000 |
| Why is port busy? | proc why :3000 |
lsof -i :3000 + ps -o ppid= chain |
| Pause a process | proc freeze node |
kill -STOP $(pgrep node) |
| Find orphans (macOS, Linux) | proc orphans --in . |
No standard tool |
| Wait for process | proc wait node |
while ps aux | grep node | grep -v grep; do sleep 5; done |
| Ports used by node | proc on node |
lsof -i -P -n | grep node |
| Node processes in cwd | proc by node --in . |
ps aux | grep node + manual check |
| Kill mixed targets | proc kill :3000 :8080 node |
Three separate commands |
| Watch in real-time | proc watch node |
watch -n2 'ps aux | grep node' |
| What runs this program? | proc for ./server |
lsof ./server or fuser ./server |
One syntax. One tool. Full comparison →
Install
| Platform | Command |
|---|---|
| macOS | brew install yazeed/proc/proc |
| Windows | scoop bucket add proc https://github.com/yazeed/scoop-bucket-proc && scoop install proc |
| Rust | cargo install proc-cli |
| npm/bun | npm install -g proc-cli |
| Nix | nix profile install github:yazeed/proc-cli |
| Docker | docker run --rm -it --pid=host --net=host yazeed/proc-cli |
| Shell | curl -fsSL https://raw.githubusercontent.com/yazeed/proc-cli/main/install.sh | bash |
| Platform | Method | Command |
|---|---|---|
| macOS/Linux | cargo-binstall | cargo binstall proc-cli |
| Arch Linux | AUR (community-maintained) | yay -S proc-bin |
Manual download:
# macOS (Apple Silicon)
|
# macOS (Intel)
|
# Linux (x86_64)
|
# Linux (ARM64)
|
# Windows (PowerShell)
Agent Skill
Teach your coding agent (Claude Code, Codex, Cursor, Gemini CLI and others) to use proc with structured JSON output:
In Claude Code you can also install it as a plugin:
/plugin marketplace add yazeed/proc-cli
/plugin install proc-cli@proc-cli
The skill lives in skills/proc-cli/ and follows the Agent Skills standard. What it teaches, how to update it, and prompts to try →
Quick Start
What's using port 3000?
Kill it:
What's running this program?
Find all node processes in current directory using >5% CPU:
Kill multiple targets at once:
Watch processes in real-time:
Target Syntax
All commands accept the same target syntax:
| Target | Example | Description |
|---|---|---|
| Port | :3000 |
Every process listening on port 3000 |
| PID | 1234 |
Process with ID 1234 |
| Name | node |
Processes named "node" (the whole name, ignoring case) |
| Multi | :3000 :8080 node |
Separate arguments |
Give several targets as separate arguments, and mix the three kinds freely: proc kill 1234 :3000 node. A process named twice is handled once, except by the read-only on and why, which answer each target under its own header. Comma-separated targets (:3000,:8080,node) still work but are deprecated, and will be removed in a future major release: a command that gets one prints a note: on stderr with the separate-argument form (human output only; --json is unchanged).
A name matches the whole process name, ignoring case, the way killall and pkill read it. proc kill node stops the processes named node and leaves Claude Helper alone, and --by follows the same rule. The file name of the executable and of the first argument count as names too, so a long name that Linux cuts to 15 characters still matches, and on Windows a trailing .exe is optional. Add --cmd to match any part of the name or the full command line instead, like pkill -f: proc kill serve --cmd. A path is not a name: proc kill ./server finds nothing, so use --cmd, or proc for <file> for an executable. proc doesn't expand wildcards; an unquoted node* is expanded by your shell into file names; use --cmd for partial matches.
--in takes the next word as its directory unless that word starts with -, so put it after the targets (proc kill 1234 5678 --in .) or write --in=DIR.
Commands
Discovery
| Command | Alias | Description |
|---|---|---|
on <target> |
: |
Bidirectional port/process lookup |
for <file> |
f |
Find processes by file path |
by <name> |
b |
Filter processes by name |
in <path> |
Filter processes by working directory | |
list |
l, ps |
List all processes |
info <target> |
i, inspect |
Detailed process information |
ports |
p |
List all listening ports |
tree |
t |
Process hierarchy |
watch |
w, top |
Real-time process monitoring |
why <target> |
Trace process ancestry (why is this port busy?) | |
orphans |
o |
Find orphaned processes (Unix only: exits 4 on Windows) |
stuck |
x |
Find hung processes |
Lifecycle
| Command | Alias | Description |
|---|---|---|
kill <target> |
k |
Force kill (SIGKILL) |
stop <target> |
s |
Graceful stop (SIGTERM, --signal for custom) |
freeze <target> |
Pause process (SIGSTOP) | |
thaw <target> |
Resume frozen process (SIGCONT) | |
free <:port> |
Kill process and verify port freed | |
wait <target> |
Block until process(es) exit | |
unstick |
u |
Recover stuck processes |
Filters
Filters can be combined with discovery and lifecycle commands:
| Filter | Description |
|---|---|
--in <path> |
Filter by working directory |
--by <name> |
Filter by process name (the whole name, like a name target) |
--cmd |
Match names and --by as a substring of the process name or full command line (like pkill -f) |
--path <path> |
Filter by executable path (list) |
--min-cpu <n> |
Processes using >n% CPU |
--min-mem <n> |
Processes using >n MB memory |
--min-uptime <s> |
Processes running longer than s seconds |
--parent <pid> |
Children of a specific parent PID |
--status <s> |
Filter by status: running, sleeping, stopped, zombie |
--exposed |
Only network-exposed ports (ports) |
--local |
Only localhost ports (ports) |
--range <s-e> |
Filter ports by range (e.g., 3000-9000) |
--sort/-s <key> |
Sort by: cpu, mem (biggest first); pid, name (ascending). Ties come by PID |
--limit/-n <n> |
Limit number of results (-l on watch, where -n is --interval) |
--ancestors/-a |
Show ancestry path to root (tree) |
--depth/-d <n> |
Maximum tree depth (default: 10) |
--compact/-C |
Show PIDs only in compact view (tree) |
--timeout/-t <s> |
stuck, unstick: only processes running longer than this (default: 300); stuck means busy or blocked for 2 seconds |
Options
| Option | Short | Description |
|---|---|---|
--json |
-j |
JSON output |
--verbose |
-v |
Show paths, cwd, full commands |
--wide |
-w |
Print full directories and arguments instead of fitting the terminal (list, by, in, for, stuck, orphans, watch) |
--yes |
-y |
Skip confirmation; required with --json or without a terminal, where nobody can be asked |
--dry-run |
Preview without executing | |
--force |
-f |
Force termination if recovery fails (unstick) |
--graceful |
-g |
Send SIGTERM instead of SIGKILL (kill) |
--signal |
-S |
Custom signal: HUP, INT, USR1, etc. (stop) |
--kill |
-k |
Kill found processes (stuck, orphans) |
--wait |
Max seconds to wait for port to free (free) |
|
--interval |
-n |
Seconds between status lines (wait) or refreshes (watch) |
--timeout |
-t |
Max seconds to wait before giving up (wait, stop) |
--quiet |
-q |
Suppress periodic status messages (wait) |
Scripting
With --json, every command prints one JSON document with action (the command name) and success. proc watch --json streams one per refresh. Errors, including invalid flags, are JSON too: {"action", "success": false, "error", "exit_code"}. Exit 4 means nothing was done, and its error adds "reason": "invalid_input" (a wrong flag, value or argument), "needs_confirmation" or "not_supported" (this OS cannot do it). A command that changes processes run with --json but without --yes exits 4 and adds "needs_confirmation": true and "processes" (the ones it would have changed), so a script can show them before running it again with --yes. Every command that takes targets also has a "not_found" array in its JSON (the targets that matched nothing, empty when all were found), and it appears in that exit 4 output too.
| Exit code | Meaning |
|---|---|
0 |
Success |
1 |
Error, or not fully successful: a process failed, a port stayed busy, wait timed out |
2 |
Process or port not found |
3 |
Permission denied: proc itself got an I/O permission error. A signal the OS refuses (another user's process) is a failed entry and exit 1 |
4 |
Invalid input (unknown flag, bad argument, invalid signal), an action this OS doesn't support (freeze, thaw, orphans and stop --signal on Windows), or a command that changes processes run without --yes where nobody can confirm (--json, or no terminal): nothing was changed |
"success": false always comes with a non-zero exit code. Full schemas: skills/proc-cli/reference.md.
Examples
# What's on port 3000?
# What ports is node using?
# What's running this program?
# What has this file open?
# Node processes in current directory
# Processes using >10% CPU
# Kill everything on ports 3000 and 8080
# Kill only node processes in current directory
# Process tree filtered by CPU usage
# Watch all processes sorted by CPU (alias: proc top)
# Watch node processes with 1s refresh
# Watch process on port 3000
# Watch processes in cwd sorted by memory
# Freeze/thaw (pause and resume)
# Free a port (kill + verify available)
# Why is port 3000 busy?
# Find orphaned processes
# Wait for processes to exit
# Send custom signal
# Find and recover stuck processes
Platform Support
| Platform | Architecture | Status |
|---|---|---|
| macOS | Apple Silicon (ARM64) | ✅ |
| macOS | Intel (x86_64) | ✅ |
| Linux | x86_64 | ✅ |
| Linux | ARM64 | ✅ |
| Windows | x86_64 | ✅ |
| Docker | linux/amd64, linux/arm64 | ✅ |
The Linux binaries are statically linked (musl) and need no particular glibc version, so they also run on Alpine and on older releases such as Debian 10, Ubuntu 20.04 and Amazon Linux 2.
Shell Completions
Generate completions for your shell:
# Bash
# Zsh
# Fish
Man Page
Generate and install the man page:
|
Building from Source
The binary will be at target/release/proc.
Contributing
See CONTRIBUTING.md.
License
MIT