Skip to main content

pidlens

Everything macOS knows about a running process, in one readable report.

CI PyPI npm License: MIT

ps tells you a process exists. lsof tells you what it has open. codesign tells you who signed it. launchctl tells you who keeps it alive. pidlens asks all of them and prints one report you can actually read — decoded into English, wrapped to your terminal, no flags to memorise.

sthnaqvi@macbook ~ % pidlens 594

╭──────────────────────────────────────────────────────────────────────────╮
│                            Finder  (pid 594)                             │
╰──────────────────────────────────────────────────────────────────────────╯
── IDENTITY ────────────────────────────────────────────────────────────────
Name            : Finder
PID             : 594
Parent PID      : 1
Owner           : sthnaqvi (uid 501)
Terminal        : none — not attached to a terminal
State           : sleeping (waiting, idle under 20s)
Scheduling      : priority 46, nice 0 (normal)
Process group   : 594
Session         : 0

── WHEN IT STARTED ─────────────────────────────────────────────────────────
Started at      : Wednesday, 5 August 2026 at 9:38:15 PM
Running for     : 23 days, 18 hours, 31 min
CPU time used   : 38 min, 26 sec (0.1% of one core over its lifetime)
Booted at       : Wednesday, 5 August 2026 at 9:37:59 PM
Vs. boot        : started 16 sec after boot → a startup service, not
                  something you launched
Unix epoch      : 1785946095

── WHAT IS RUNNING ─────────────────────────────────────────────────────────
Executable      : /System/Library/CoreServices/Finder.app/Contents/MacOS
                  /Finder
Arguments       : none
Working dir     : /
── WHO STARTED IT ──────────────────────────────────────────────────────────
[1] root
  /sbin/launchd
  └─ [594] sthnaqvi
       /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder

── RESOURCE USAGE ──────────────────────────────────────────────────────────
CPU now         : 0.0%
Memory          : 26.6 MB resident (0.1% of RAM)
Virtual size    : 398.8 GB
                  virtual size is address space reserved, not memory in use
Threads         : 10
Open handles    : 249

── NETWORK ─────────────────────────────────────────────────────────────────
  no network sockets open
  plus 1 unix-domain socket(s) for local IPC

── THE BINARY ON DISK ──────────────────────────────────────────────────────
Path            : /System/Library/CoreServices/Finder.app/Contents/MacOS
                  /Finder
Size            : 23.6 MB
Modified        : 17 August 2025 at 12:14 AM
Owner           : root, mode 755
Architectures   : arm64e, x86_64
Signed by       : Software Signing
Chain           : Apple Code Signing Certification Authority → Apple Root CA
Bundle ID       : com.apple.finder
Team ID         : —
Signed on       : 12 Jul 2025 at 2:00:36 PM
Entitlements    : 88 granted
  · com.apple.accounts.appleaccount.fullaccess
  · com.apple.amp.devices.client
  · com.apple.application-identifier
── SERVICE / LAUNCHD ───────────────────────────────────────────────────────
Managed job     : com.apple.Finder
Last exit       : 0
  launchd owns this — it is restarted automatically when it exits


Install

Homebrew

brew tap sthnaqvi/tap
brew install pidlens

npm

npm install -g pidlens

pip (or pipx, which keeps it out of your system Python)

pipx install pidlens     # recommended
pip install pidlens

From source

git clone https://github.com/sthnaqvi/pidlens.git
cd pidlens && make venv && make test && pip install .

The npm package bundles the same Python program and only needs Python 3.9+ on your PATH — nothing is compiled or downloaded at install time. Set PIDLENS_PYTHON to pick a specific interpreter.

However you installed it, pidlens --version says which channel that copy came from, so a bug report can name it:

sthnaqvi@macbook ~ % pidlens --version
pidlens 0.1.0 (installed via homebrew)

Usage

pidlens 594                  # by pid
pidlens Finder                 # by name — exact first, then loose, then argv
pidlens node --all             # don't truncate long lists
pidlens 594 --json | jq .    # same data, machine-readable
pidlens 594 -s timing,binary # only the sections you want
sudo pidlens 594             # also reveals environment variables
Flag What it does
-a, --all Print every open file, environment variable and entitlement instead of the first few
-j, --json Emit the collected data as JSON — the same values the report renders from
-s, --sections LIST Render only these sections, comma separated
-w, --width N Wrap to N columns instead of detecting the terminal
--no-color Disable ANSI colour (also honours NO_COLOR and non-tty output)
-V, --version Print the version

Exit codes: 0 report produced · 2 no target given · 3 no such process · 4 not macOS.

What each section answers

Section The question it answers
identity Who owns it, what state it's in, and what that state actually means
timing When did it start, how long has it run, how much CPU has it burned, and did it start at boot (so: a system service, not something you launched)
command Which binary, which arguments, from which working directory
environment Its environment variables, with credential-shaped values masked
ancestry The chain of parents up to launchd, plus its children
resources CPU, resident memory, virtual size, threads, open handles
network Every socket in English — listening ports, established peers, unbound sockets
files Open files grouped into libraries, real files, pipes and devices
binary Size, architectures, signing authority and chain, Team ID, signing date, entitlements, Gatekeeper verdict, quarantine flag, installing package
launchd Whether launchd owns it and will restart it
commands Copy-paste fs_usage, nettop, log stream, sample, spindump, lldb lines with the pid already filled in

Answering "when did this start?"

The timing section is the one most people come for:

Started at      : Wednesday, 5 August 2026 at 9:38:15 PM
Running for     : 23 days, 18 hours, 31 min
CPU time used   : 38 min, 26 sec (0.1% of one core over its lifetime)
Booted at       : Wednesday, 5 August 2026 at 9:37:59 PM
Vs. boot        : started 16 sec after boot → a startup service, not
                  something you launched
Unix epoch      : 1785946095

ps -o lstart gives you the first line. The value is in the rest: elapsed time in units you can hold in your head, CPU time as a share of one core, and the comparison against boot that tells you whether anyone ever chose to run this.

JSON

Every value in the report comes from one dict, and --json prints it:

sthnaqvi@macbook ~ % pidlens 594 --json | jq '{started: .timing.started_human,
    age: .timing.elapsed_human, signed_by: .binary.signature.authority,
    launchd: .launchd.label}'
{
  "started": "Wednesday, 5 August 2026 at 9:38:15 PM",
  "age": "23 days, 18 hours, 31 min",
  "signed_by": "Software Signing",
  "launchd": "com.apple.Finder"
}

More recipes — every field is documented in docs/json-schema.md:

pidlens node --json | jq -r '.sockets[].description'
pidlens Safari --json | jq -r '.binary.entitlements[]'
pidlens 594 --json | jq '.timing.started_at_boot'

How it works

pidlens is read-only. It never attaches to, signals, or modifies the process it describes — it shells out to tools already on your Mac and parses what they print:

Tool Used for
ps identity, state, priority, start time, elapsed and CPU time, argv, environment
lsof open files, sockets, working directory, handle count
pgrep name matching and children
sysctl boot time, to place the start time in context
codesign signing authority, Team ID, signing date, entitlements
spctl the Gatekeeper verdict (assessed on the app bundle, not the inner executable)
xattr, pkgutil, file quarantine flag, installing package, architectures
launchctl whether it's a managed job

Two consequences worth knowing:

  • Environment variables need root. macOS only exposes another process's environment to root, even your own processes. sudo pidlens PID shows them. Names that look like credentials (*_TOKEN, *_SECRET, *PASSWORD*, …) are masked before printing either way.
  • Gatekeeper "not assessed" is normal. spctl only rates app bundles, so a plain executable comes back unassessable even when its signature is perfectly valid. pidlens says that, instead of reporting a scary "rejected".

Requirements

macOS (tested on Sonoma and Sequoia) and Python 3.9 or newer — the version macOS itself ships. No third-party dependencies — the whole tool is the standard library plus the system tools above.

Development

make venv     # local .venv with dev dependencies
make test     # 550+ tests, no live processes required for most of them
make demo     # run pidlens against your shell
make build    # sdist + wheel
make npm-pack # build the npm tarball

The code is split so that almost all of it is testable without a running process:

src/pidlens/
  humanize.py  bytes, durations, ps state codes → English      (pure)
  layout.py    width-aware wrapping and the Renderer           (pure)
  parse.py     one parser per tool's output format             (pure)
  system.py    the only module that shells out
  collect.py   builds the data model for one pid
  report.py    turns the data model into sections
  cli.py       arguments, exit codes, JSON

Tests use fixtures captured from real lsof, codesign, spctl and launchctl output, plus a FakeRunner that answers commands from a dict — so the parsers and the entire report are verified without touching a live process. The layout suite asserts that no line ever exceeds the terminal width and that wrapping loses no characters, across widths from 38 to 110 columns.

Contributing

Issues and pull requests are welcome — see CONTRIBUTING.md.

License

MIT © Sayed Tauseef Naqvi

Release files for pidlens 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pidlens 0.1.1
File Size Uploaded
pidlens-0.1.1.tar.gz 35.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pidlens 0.1.1
File Interpreter ABI Platform
pidlens-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 60.5 kB

Release files / pidlens-0.1.1.tar.gz

Download URL pidlens-0.1.1.tar.gz
Size 35.1 kB
Tags Source
SHA-256 checksum
How to use checksums
6765108ab60f518b98a367c69e3f403ccda7aa362193effcb5ab19e79fe9e6f2
BLAKE2b-256 checksum
How to use checksums
7b4abe0edcad2c41ddae69d448fbc3d84d7dd0d315b628e45ee3fadc7bb83aee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.

Transparency log

Release files / pidlens-0.1.1-py3-none-any.whl

Download URL pidlens-0.1.1-py3-none-any.whl
Size 25.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f24e291624cc264f77e03f432d175cb11f32db733ee45364cfa034b91a021a89
BLAKE2b-256 checksum
How to use checksums
c878bf091fc790c8bf3d901d233e1f18ab6d753d30839fc7e80680144840ad09
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.3

2 release files

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page