psmon
August 13, 2026 · View on GitHub
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PID PPID USER %CPU %MEM TIME COMMAND
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2201 2176 dumidu 51.6 1.5 48:07 opera
8110 8046 dumidu 47.5 5.0 09:20 chrome
8073 8043 dumidu 23.5 1.2 09:20 chrome
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Sorted by: %cpu | Showing: 3 | Excluded: (ps|ps)
Press 'q' to quit, 'c'=CPU, 'm'=Memory, 't'=Time
A fast, colorful, interactive process monitor built on top of ps.
No ncurses dependency, no compiled binary: just zsh and standard Linux utilities.
Linux only. psmon relies on GNU
psflags and reads/procdirectly, so it does not work on macOS or BSD. See Requirements.
Why psmon?
top and htop are great, but sometimes you just want:
- A quick colorized snapshot of the heaviest processes, straight in your normal terminal scrollback
- A simple live-refreshing view you can switch sort order on with a keypress
- A one-shot way to trace a process's ancestry, or peek at its open file descriptors
psmon combines ps, awk, and other standard Linux utilities into a single small zsh utility.
Features

- 🎨 Colorized output: CPU-heavy processes highlighted in yellow/red
- 📊 Sort by CPU, memory, elapsed time, PID, or user
- 🔴 Live watch mode with in-place key-driven sort switching (
c/m/t/q) - 🔍 Process ancestry / tree view: trace a PID up to
initand list its children (The summary shows the full, unredacted command line, so be mindful when sharing output) - 📂 File descriptor inspector (via
lsof) with type-coded coloring and a summary - 💀 Interactive kill mode: list, pick a PID, confirm, done (SIGTERM or SIGKILL)
- 📦 Sandbox-Aware Kill: traces Flatpak/Bubblewrap containerized apps to their root and terminates the connected process tree.
- 🌐 PWA & Webapp Tagging: automatically extracts URL and Class arguments to help you distinguish between isolated browser instances.
- 🚫 Exclude processes by pattern (regex-friendly, e.g.
-e 'opera|msedge') - ✅ No dependencies beyond zsh,
ps,awk, and (optionally)lsof
Requirements
-
Linux. psmon depends on GNU-specific ps flags (--sort, --ppid, -o field= for header suppression) that don't exist on BSD/macOS ps, and psmon blame reads /proc/loadavg, /proc/meminfo, /proc/net/dev, and /proc/
/io directly along with GNU nproc, none of which exist on macOS. There's no macOS/BSD support and none planned. -
zsh -
ps(GNU procps, standard on Linux) -
lsof(optional, only needed for-d/file descriptor mode)
Installation
Quick install (recommended)
curl -fsSL https://raw.githubusercontent.com/dumidusw/psmon/main/install.sh | zsh
This installs psmon to ~/.local/share/psmon, symlinks the executable into ~/.local/bin/psmon, and installs zsh tab-completion.
Manual install
git clone https://github.com/dumidusw/psmon.git ~/.local/share/psmon
ln -s ~/.local/share/psmon/psmon ~/.local/bin/psmon
chmod +x ~/.local/bin/psmon
Make sure ~/.local/bin is on your $PATH:
export PATH="$HOME/.local/bin:$PATH"
As a zsh plugin
If you use a plugin manager, psmon ships an optional psmon.plugin.zsh wrapper. (Linux only, see Requirements; installing via a plugin manager on macOS will not work.)
zinit
zinit light dumidusw/psmon
oh-my-zsh
git clone https://github.com/dumidusw/psmon.git $ZSH_CUSTOM/plugins/psmon
then add psmon to the plugins=(...) list in ~/.zshrc.
Uninstall
~/.local/share/psmon/install.sh --uninstall
Usage
psmon [OPTIONS]
psmon blame [-l N]
| Flag | Description |
|---|---|
-c | Sort by CPU (default) |
-m | Sort by memory |
-t | Sort by elapsed time |
-p | Sort by PID |
-u | Sort by user |
-l N | Show N lines (default: 16, or 5 for blame) |
-e PATTERN | Exclude processes matching PATTERN (regex, |-joinable) |
-w | Watch mode, live updates |
-i N | Update interval in seconds with -w (default: 2) |
-f PID | Show ancestry/tree view for a PID |
-d PID | Show open file descriptors for a PID (needs lsof) |
-k | Interactive kill mode, prompts before sending SIGTERM |
-K | Interactive kill mode, prompts before sending SIGKILL |
-h | Show help |
Sort direction:
-c,-m, and-tsort descending (heaviest / most recent first).-pand-usort ascending (numeric / alphabetical order).
Note on
-e: the pattern matches against the entire process line: PID, PPID, user, and command, not just the command name. So-e dumiduexcludes every process owned by that user, not only processes literally nameddumidu.
| Subcommand | Description |
|---|---|
blame | Quick "why is my machine slow" diagnostic summary |
Examples
psmon # Top 16 processes by CPU
psmon -m # Top 16 processes by memory
psmon -l 10 # Top 10 processes by CPU
psmon -e snap # Exclude snap processes
psmon -e 'opera|msedge' # Exclude multiple patterns
psmon -w # Live view, refreshes every 2s
psmon -w -i 5 # Live view, refreshes every 5s
psmon -m -e opera # Top memory users, excluding opera
psmon -f 2182 # Process tree / ancestry for PID 2182
psmon -f 1 # Full ancestry from init
psmon -d 2182 # File descriptors for PID 2182
psmon -d 2182 -l 20 # First 20 file descriptors
psmon -k # Interactive kill mode (SIGTERM)
psmon -K -m # Interactive kill mode (SIGKILL), sorted by memory
psmon blame # Quick "why is my machine slow" summary
psmon blame -l 8 # Same, but 8 rows per section
psmon blame: quick diagnostic summary

psmon blame
For when something feels slow and you just want the likely culprit, fast. Shows:
- Load average (1/5/15 min) with a plain-language read against your core count
- Swap usage, with a warning if it's heavily in use (a common sign of memory pressure)
- Memory usage (based on
MemAvailable, not raw free RAM, so it reflects real memory pressure rather than reclaimable disk cache), with a warning as it climbs - Disk usage across real filesystems, flagging any that are nearly full
- Network throughput: aggregate download/upload rate, sampled live across all non-loopback interfaces
- Top CPU processes
- Top Memory processes
- Top I/O processes: cumulative disk bytes read+written since each process started (from
/proc/<pid>/io)
I/O stats are best-effort: Linux only exposes /proc/<pid>/io for your own processes unless you're root, so on a shared or multi-user system you may see fewer I/O rows than CPU/memory rows. Everything else works without root.
Interactive kill mode

Example: resolving a name to multiple PWA instances, then a full sandbox tree.

psmon -k
Shows a compact PID %CPU %MEM COMMAND list. Enter either:
-
a PID: psmon resolves it to the main process of that app by walking up the parent chain.
-
a process name (e.g. brave): psmon finds every matching process and groups them by their application instance.
Advanced sandbox & webapp handling: Modern Linux apps are complicated. Chromium-based browsers spawn dozens of renderers and crashpad handlers, and Flatpak wraps everything in isolated bwrap containers. psmon handles this automatically:
-
Container Grouping: If a process is running inside a Flatpak or Bubblewrap sandbox, psmon traces the tree all the way up to the structural container root.
-
Container Tree Termination: Confirming a kill sends the signal to the entire container tree, so hidden zygotes, renderers, and background workers are terminated along with the process you targeted.
-
PWA Disambiguation: If you have multiple independent webapps running (e.g., a YouTube PWA and a standard browsing session), psmon parses the command arguments and tags them in the UI (e.g., [App: https://youtube.com]) so you know exactly which instance you are targeting.
You will see the resolved target and the total number of connected processes, followed by a single confirmation:
Send SIGTERM to 18 process(es)? [y/N]
psmon -K
Same flow, but sends SIGKILL, useful when something is ignoring SIGTERM.
-kvs-K: Try-k(SIGTERM) first; it asks the process to shut down, giving it a chance to save state and exit cleanly. Some apps, especially Electron/Node-based ones, don't act onSIGTERMright away (or at all) if they're mid-cleanup or waiting on something else, in which case the process will still show up when you check again.-K(SIGKILL) is the fallback for that: it's handled by the kernel directly and can't be caught, delayed, or ignored, so it always terminates immediately. That also means it skips whatever graceful shutdown/state-save the app would normally do, so treat it as a last resort rather than a default.
Safety notes:
- Always confirms before sending anything; nothing happens from typing a PID/name alone.
- Refuses to signal PID 1 (
init) or psmon's own process/shell, and bails out entirely if the resolved tree contains any protected PID. - After signaling, re-checks each PID (polling for up to ~2s) and reports how many actually terminated (correctly treating zombie processes as terminated, since a zombie is already dead and just awaiting reap by its parent).
Known quirk: For heavy multi-process apps (browsers especially), a full graceful shutdown can occasionally take a bit longer than the ~2s poll window + list refresh. If the very next screen inside kill mode still shows one of the processes you just killed, that's usually just teardown finishing a moment late; it's already gone by the time you check again with a fresh psmon invocation a few seconds later. This is cosmetic and doesn't affect whether the kill actually worked.
Interactive keys (watch mode)
| Key | Action |
|---|---|
q | Quit |
c | Switch to CPU sort |
m | Switch to memory sort |
t | Switch to time sort |
Project structure
psmon/
├── psmon # entrypoint: arg parsing + dispatch
├── lib/
│ ├── display.zsh # colors, help text, process list rendering
│ ├── watch.zsh # live watch-mode loop
│ ├── descriptors.zsh # lsof-based file descriptor inspector
│ ├── tree.zsh # process ancestry / tree view
│ ├── kill.zsh # interactive kill mode
│ └── blame.zsh # quick diagnostic summary (`psmon blame`)
├── completions/
│ └── _psmon # zsh tab-completion
├── psmon.plugin.zsh # optional plugin-manager wrapper
└── install.sh # installer / uninstaller
The entrypoint resolves its own real path (following symlinks), so it works whether you run it directly, symlink it into ~/.local/bin, or load it via a plugin manager, as long as lib/ sits alongside the real psmon file.
Contributing
Issues and PRs welcome. A few ideas if you're looking for something to add:
- Additional sort fields (e.g.
-ofor open files count) - A
--jsonoutput mode for scripting - Per-user filtering (
-U username)
License
MIT. See LICENSE.