proc-cli
Health Pass
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 64 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Semantic CLI tool for process management. Target by port, PID, name or path.
proc
Semantic CLI tool for process management. Target by port, PID, name or path.
Find what's on a port, why it's there, and stop it, in one command you'd guess on the first try. On macOS, Linux and Windows.
brew install yazeed/proc/proc
Also on npm, cargo, Scoop, Nix and Docker.
A dev server won't start because port 3000 is taken. proc finds the old tmux session holding it, frees the port, then cleans up what else was left running.
Docs: commands · install · FAQ · agent skill · changelog
One command instead of a pipeline
| Task | proc | Without proc |
|---|---|---|
| What's on port 3000? | proc on :3000 |
lsof -i :3000 -P -n |
| Why is it there? | proc why :3000 |
lsof -i :3000 + ps -o ppid= chain |
| Kill it | proc kill :3000 |
lsof -i :3000 -t | xargs kill -9 |
| Free the port and check | proc free :3000 |
lsof -i :3000 -t | xargs kill && sleep 1 && lsof -i :3000 |
| What ports is node using? | proc on node |
lsof -i -P -n | grep node |
| What runs this program? | proc for ./server |
lsof ./server or fuser ./server |
| Node processes in this project | proc by node --in . |
ps aux | grep node + manual check |
| Pause a process | proc freeze node |
kill -STOP $(pgrep node) |
| Wait for it to exit | proc wait node |
while ps aux | grep node | grep -v grep; do sleep 5; done |
| Watch in real time | proc watch node |
watch -n2 'ps aux | grep node' |
| Find leftovers (macOS, Linux) | proc orphans --in . |
No standard tool |
It asks before it changes anything
- Looking is free.
on,for,by,in,list,info,ports,tree,why,stuckandorphansonly read. - Changing asks first.
kill,stop,freeze,thaw,free,unstick,orphans --killandstuck --killshow what they will touch and wait for a yes.--dry-runshows the list and does nothing else;--yesskips the question. --jsonis not a yes. A script or agent that runs a changing command with--jsonand no--yesgets exit 4, nothing changed, and the list of processes it would have touched. The same goes for running without a terminal.- A name means the whole name.
proc kill nodestops processes namednodeand leavesClaude Helperalone. Add--cmdfor the looser,pkill -fstyle match. - A name is a search, not an approval. Preview a name with
--dry-run, and use--yesonly with a:portor a PID you have seen.
proc kill node --in . --dry-run # what would stop, in this project only
proc kill :3000 # asks first
proc free :3000 --yes # a port target: no question
Install
| Platform | Command |
|---|---|
| macOS | brew install yazeed/proc/proc, or the install script below |
| Linux | curl -fsSL https://raw.githubusercontent.com/yazeed/proc-cli/main/install.sh | bash, or brew install yazeed/proc/proc |
| Windows | scoop bucket add proc https://github.com/yazeed/scoop-bucket-proc && scoop install proc |
| npm / bun | npm install -g proc-cli |
| Rust | cargo install proc-cli (or cargo binstall proc-cli for the pre-built binary) |
| Nix | nix profile install github:yazeed/proc-cli |
| Docker | docker run --rm -it --pid=host --net=host yazeed/proc-cli |
--pid=host lets proc in Docker see the host's processes, and --net=host its ports (for on :port, ports and free).
Check it works:
proc --version
proc ports
More options: Arch Linux, manual download
| Platform | Method | Command |
|---|---|---|
| Arch Linux | AUR (community-maintained) | yay -S proc-bin |
The AUR package is not published by this project and can lag behind releases. Check proc --version after installing: before 2.0, a name matched any part of a command line and --json counted as --yes.
Manual download:
# macOS (Apple Silicon)
curl -fsSL https://github.com/yazeed/proc-cli/releases/latest/download/proc-darwin-aarch64.tar.gz | tar xz
sudo mv proc-darwin-aarch64 /usr/local/bin/proc
# macOS (Intel)
curl -fsSL https://github.com/yazeed/proc-cli/releases/latest/download/proc-darwin-x86_64.tar.gz | tar xz
sudo mv proc-darwin-x86_64 /usr/local/bin/proc
# Linux (x86_64)
curl -fsSL https://github.com/yazeed/proc-cli/releases/latest/download/proc-linux-x86_64.tar.gz | tar xz
sudo mv proc-linux-x86_64 /usr/local/bin/proc
# Linux (ARM64)
curl -fsSL https://github.com/yazeed/proc-cli/releases/latest/download/proc-linux-aarch64.tar.gz | tar xz
sudo mv proc-linux-aarch64 /usr/local/bin/proc
# Windows (PowerShell, run as administrator to write to System32)
Invoke-WebRequest -Uri https://github.com/yazeed/proc-cli/releases/latest/download/proc-windows-x86_64.exe.zip -OutFile proc.zip
Expand-Archive proc.zip -DestinationPath .
Move-Item proc-windows-x86_64.exe C:\Windows\System32\proc.exe
Nineteen commands, three jobs
Every command takes the same targets, and almost all take --in to stay inside one project, --json for scripts, and --dry-run before anything changes (the changing ones).
Find out
Read-only. Nothing is touched.
| Command | Alias | What it does |
|---|---|---|
on <target> |
: |
What's on a port, or which ports a process uses |
why <target> |
Trace what started a process (why is this port busy?) | |
for <file> |
f |
Processes running a program or holding a file open |
by <name> |
b |
Processes with this name |
in <path> |
Processes whose working directory is this path | |
list |
l, ps |
All processes |
info <target> |
i, inspect |
Everything about one process |
ports |
p |
All listening TCP ports |
tree |
t |
Parents and children |
stuck |
x |
Hung processes: busy or blocked for 2 seconds, running longer than --timeout (default 300) |
orphans |
o |
Processes whose parent has exited (Unix only: exits 4 on Windows) |
Change it
Asks first, or previews with --dry-run.
| Command | Alias | What it does |
|---|---|---|
stop <target> |
s |
Ask it to exit (SIGTERM, or --signal), force-kill after --timeout (default 10) |
kill <target> |
k |
End it now (SIGKILL; --graceful sends SIGTERM) |
free <:port> |
Stop what holds the port (SIGTERM, SIGKILL after up to 5 seconds) and check the port is free | |
freeze <target> |
Pause it (SIGSTOP; Unix only: exits 4 on Windows) | |
thaw <target> |
Resume it (SIGCONT; Unix only: exits 4 on Windows) | |
unstick |
u |
Try to recover stuck processes; --force ends them if recovery fails |
Keep watching
| Command | Alias | What it does |
|---|---|---|
watch |
w, top |
Live process table (--json streams one document per refresh) |
wait <target> |
Block until the process(es) exit |
proc on :3000 # what's on port 3000?
proc by node --in . --min-cpu 5 # node in this directory using >5% CPU
proc info :3000 1234 # info for a port and a PID
proc tree --min-cpu 5 # process tree, busy processes only
proc watch node -n 1 # node processes, refreshed every second
proc wait :3000 --timeout 60 # wait up to 60s for the process on port 3000 to exit
proc stop worker --signal INT # ask with SIGINT; force-killed after --timeout
proc orphans --in . # leftovers from this project
Targets
All commands accept the same targets:
| Target | Example | Matches |
|---|---|---|
| Port | :3000 |
Every process listening on port 3000 |
| PID | 1234 |
The process with ID 1234 |
| Name | node |
Processes named "node" (the whole name, ignoring case) |
| Several | :3000 :8080 node |
Separate arguments, any mix of the three |
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).
Names. A name matches the whole process name, ignoring case, the way killall and pkill -x 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, so use --cmd for partial matches.
--in. --in (short -i) takes the next word as its directory unless that word starts with -; a bare --in is the current directory. Put it after the targets (proc kill 1234 5678 --in .) or write --in=DIR.
Filters
Not every filter is on every command. --in, --by (short -b) and --cmd work on nearly all of them, including the ones that change processes. The rest narrow what the finding commands show:
| Filter | Description | On |
|---|---|---|
--in [<path>], -i |
Working directory (bare --in is the current directory) |
nearly every command |
--by <name>, -b |
Process name (the whole name, like a name target) | nearly every command |
--cmd |
Match names and --by as a substring of the name or full command line (like pkill -f) |
every command that takes a name or --by |
--path <path>, -p |
Executable path | list |
--min-cpu <n> |
Processes using >n% CPU | by, in, for, list, tree, watch |
--min-mem <n> |
Processes using >n MB memory | by, in, for, list, tree, watch |
--min-uptime <s> |
Processes running longer than s seconds | by, in, for, list, tree |
--status <s> |
running, sleeping, stopped, zombie | by, in, for, list, tree |
--parent <pid> |
Children of a specific parent PID | by, in, list |
--exposed, -e / --local, -l |
Only network-exposed / localhost ports | ports |
--range <s-e>, -r |
Ports in a range (e.g. 3000-9000) |
ports |
--sort, -s <key> |
list, by, in, for, watch: cpu, mem (biggest first); pid, name (ascending). ports: port, pid, name (ascending). Ties come by PID |
see left |
--limit, -n <n> |
Limit the number of results (-l on watch, where -n is --interval) |
list, by, in, for, ports, watch |
--ancestors, -a |
Show the ancestry path up to the root | tree |
--depth, -d <n> |
Maximum tree depth (default 10) | tree |
--compact, -C |
PIDs only | tree |
--timeout, -t <s> |
Only processes running longer than this (default 300) | stuck, unstick |
Options
| Option | Short | Description |
|---|---|---|
--json |
-j |
JSON output |
--verbose |
-v |
Show paths, cwd, full commands |
--wide |
-w |
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 |
First signal: HUP, INT, USR1, etc. (stop) |
--kill |
-k |
Kill the processes found (stuck, orphans) |
--wait |
Max seconds to wait for the port to free (free, default 10) |
|
--interval |
-n |
Seconds between status lines (wait, default 5) or refreshes (watch, default 2) |
--timeout |
-t |
Max seconds before giving up (wait, default 0 = no limit; stop, default 10 before force-kill) |
--quiet |
-q |
Suppress the periodic still-running lines (wait) |
For scripts and coding agents
Every process and port command prints JSON with --json: one document per command, with action (the command name) and success. proc watch --json streams one per refresh. Exit codes always mean the same thing.
proc on :8080 --json | jq '.process | {pid, cwd, uptime_secs}'
- Errors are JSON too, including invalid flags:
{"action", "success": false, "error", "exit_code"}."success": falsealways comes with a non-zero exit code. - Exit 4 means nothing was done, and the error adds
"reason":"invalid_input"(a wrong flag, value or argument),"needs_confirmation"or"not_supported"(this OS cannot do it). - Preview, then confirm. A command that changes processes, run with
--jsonbut without--yes, exits 4 with"needs_confirmation": trueand"processes"(the ones it would have changed), so a script can show them before running it again with--yes. not_foundlists the targets that matched nothing (empty when all were found). It is in the JSON ofon,why,info,kill,stop,freeze,thaw,unstick,freeandwait, and in the exit 4 output above.listandwatchdo not have it.- A prompt shows what you would approve. The list of processes goes to the terminal beside the
[y/N]question: on stdout, or on stderr when stdout is a file or a pipe. If the list cannot be shown, nothing is asked and nothing is changed (exit 4). - A reader that stops early ends proc quietly.
proc list | head -n1exits 0 without an error, andproc kill 1234 --yes | head -n1still kills the process and exits with the code of the kill (0 if it worked, 2 if there was nothing to kill): a closed pipe never changes the result, on stdout or stderr. A write that fails for another reason (a full disk) is exit 1, with one line on stderr.
| 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 |
129, 130, 131, 143 |
A signal ended proc (SIGHUP, SIGINT or Ctrl+C, SIGQUIT, SIGTERM) while watch had the screen or a prompt was waiting: the terminal is put back first, then proc ends by that signal, so a shell shows 128 plus its number and Ctrl+C stops a script's loop. Nothing was changed by an unanswered prompt |
Full schemas: skills/proc-cli/reference.md.
Agent skill
Teach your coding agent (Claude Code, Codex, Cursor, Gemini CLI and others) when to reach for proc and how to use it safely, with structured JSON output:
npx skills add yazeed/proc-cli
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 →
Platform support
| Platform | Architecture | Status |
|---|---|---|
| macOS | Apple Silicon (ARM64), Intel (x86_64) | ✅ |
| Linux | x86_64, ARM64 | ✅ |
| Windows | x86_64 | ✅ (no freeze, thaw, orphans, stop --signal) |
| 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.
proc is for processes and ports on the machine it runs on. It is not for containers, system services or remote hosts (see PHILOSOPHY.md).
Shell completions and man page
# Bash
proc completions bash > /etc/bash_completion.d/proc
# Zsh
proc completions zsh > ~/.zsh/completions/_proc
# Fish
proc completions fish > ~/.config/fish/completions/proc.fish
# Elvish and PowerShell: proc completions elvish, proc completions powershell
# Man page
proc manpage | sudo tee /usr/local/share/man/man1/proc.1 > /dev/null
Building from source
git clone https://github.com/yazeed/proc-cli
cd proc-cli
cargo build --release
The binary will be at target/release/proc.
Use as a library
proc is also a Rust crate, proc_cli: see the API docs on docs.rs and the Library API entries in the changelog.
Contributing
See CONTRIBUTING.md. The principles behind the commands are in PHILOSOPHY.md.
License
MIT
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found