Snag · open source
snag-run/treetoptreetop
A top-style tracker for your git worktrees across projects — see every
worktree, its branch, which ones are in use, and when each last changed, in one view.
If you juggle lots of worktrees (multiple branches in flight, agents working in
parallel), it's easy to lose track of what's where. treetop scans your
repos, groups worktrees by project, marks the ones with a session running inside them,
and shows how recently each one changed.
$ treetop snag
snag
● ~/snag-wt1 fix/234-surface-truncation-error edited 8s · changed 15h
● ~/snag-docs feat/renderer-host-brand edited 23m · changed 23m
~/snag-wt-deps chore/deps-batch-majors edited 14h · changed 14h
~/snag (bare) edited — · changed 1d
● marks a worktree that is in use (a live session is running there); a worktree with no marker is open. The two time columns are distinct signals: edited is the newest working-tree file change (including unstaged edits — e.g. an agent mid-task), while changed is the last git activity (commit / checkout / stage).
Install
Download a binary
Prebuilt binaries for Linux
and macOS
(Intel + Apple Silicon) are attached to each release. Grab the one for
your platform, drop it on your PATH, and make it executable:
# Detect your platform (linux/darwin, amd64/arm64) and the latest release:
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
VERSION=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
https://github.com/snag-run/treetop/releases/latest | sed 's#.*/tag/v##')
curl -fsSL -o treetop \
"https://github.com/snag-run/treetop/releases/download/v${VERSION}/treetop_${VERSION}_${OS}_${ARCH}"
chmod +x treetop
sudo mv treetop /usr/local/bin/
On macOS, Gatekeeper may quarantine the binary on first run; clear it with xattr -d com.apple.quarantine /usr/local/bin/treetop.
Verify the download against the published checksums (optional):
curl -fsSL -O "https://github.com/snag-run/treetop/releases/latest/download/SHA256SUMS"
shasum -a 256 -c SHA256SUMS --ignore-missing
Install with Go
go install github.com/snag-run/treetop@latest
Build from source
git clone https://github.com/snag-run/treetop
cd treetop
go build -o treetop .
Two modes
Snapshot (default)
Print once and exit — the plain CLI. treetop snag
Live · -w / --watch
A full-screen, scrollable dashboard that refreshes in place, like top. It
uses the terminal's alternate screen (your scrollback is left untouched on exit) and
shows a header with live counts. Scroll with the mouse wheel, ↑/↓ (or j/k),
PgUp/PgDn, and g/G; quit with q or Ctrl-C.
Usage
treetop # snapshot of every project's worktrees
treetop snag # filter to projects whose name matches "snag"
treetop 'snag|athanor' # match either project (regex alternation)
treetop -e snag -e athanor # same, grep-style; -e is repeatable
treetop -p # collapse to one line per project
treetop -w # live mode, refreshing every 2s
treetop -w -i 5 # live mode, every 5s
treetop --pr # show each worktree's open-PR check status (needs gh)
treetop -w --notify # live + desktop alerts on PR review / CI changes
treetop --in-use # only worktrees with a live session
treetop --open # only worktrees with no session
treetop --root ~/code # scan a specific directory (repeatable)
treetop config show # inspect your effective config (path + values)
treetop bug # open a prefilled bug report in your browser
The project filter is a case-insensitive regular expression
matched against project names. Pass it positionally or with -e/--regexp;
give several patterns (or use |
alternation) to match more than one project.
Quote any pattern containing |
so your shell doesn't treat it as a pipe.
Flags
| Flag | Description |
|---|---|
-w, --watch |
Live mode: refresh continuously (also --live) |
-i, --interval N |
Refresh interval in seconds (with --watch, default 2) |
-p, --projects |
Collapse to one line per project (no worktrees) |
-e, --regexp |
Project-name pattern to match (repeatable; also positional) |
--pr |
Show a PR check-status glyph per worktree (needs gh; polls only when filtered, max 5 projects)
|
--checks |
Expand --pr
into one row per CI check under each worktree (implies --pr)
|
--notify |
With --watch, raise a desktop notification when a PR is approved or sent
back for changes, or CI fails (implies --pr)
|
--in-use |
Show only worktrees with a live session |
--open |
Show only worktrees with no session |
--root DIR |
Directory to scan for repos (repeatable; default $HOME) |
--depth N |
Levels below each root to scan for repos (default 1, max 3) |
--no-color |
Disable ANSI color (also honors NO_COLOR) |
--no-watch, --no-pr, --no-checks, --no-notify,
--no-projects
|
Negation flags — turn a config-enabled boolean off for a single run
(CLI › config). The last form on the line wins; --no-pr
can't combine with --checks/--notify.
|
By default treetop
scans $HOME
one level deep for git worktrees
and groups them by repository. A bare repo is discovered via any of its linked worktrees.
For nested layouts like ~/src/<host>/<org>/<repo>, raise
--depth
(a repo is never descended into, so a deeper scan only costs directory
stats).
PR status & desktop notifications
With --pr, each worktree shows a glyph for the rolled-up CI status of the open
pull request on its branch (via the gh
CLI) —
green when checks pass, red when one fails, yellow while they run. To stay cheap it polls only
the worktrees you're filtered to (max 5 projects), and stays quietly blank where
gh
is missing or there's no open PR. --checks
expands the rollup into one row per CI check so you can see which
one is red; in watch mode the c
key cycles that expansion (collapse → all checks → non-skipped) without relaunching.
Watch mode is passive — you have to be looking at it to catch a PR get "changes requested" or
CI go red. --notify
(with --watch) pushes just the events that need a
human as desktop notifications, so treetop
can sit in a pane and tap you on the
shoulder. Alerts fire only on a real change
in rolled-up status,
and are delivered as OSC 9
escape sequences — terminal-native, with tmux
passthrough — so no notification daemon is required.
Config file
If you find yourself retyping the same flags (treetop -w --pr --checks), set them
once in a config file. treetop
reads JSON from $XDG_CONFIG_HOME/treetop/config.json, falling back to
~/.config/treetop/config.json
when $XDG_CONFIG_HOME
is unset.
{
"watch": true,
"pr": true,
"checks": true,
"notify": false,
"projects": false,
"color": true,
"interval": 2
}
These keys mirror the persistent mode/display flags (-w, --pr, --checks, --notify, -p,
--no-color
as "color", and -i). Per-run inputs (--in-use/--open,
patterns) and the scan surface (--root/--depth) stay on the command
line — they're situational, not defaults. Precedence is CLI flag › config › built-in default: a flag you pass for a single
run wins, but flags you leave off fall back to the config. A missing file is fine; a malformed
one warns to stderr and falls back to the built-in defaults rather than failing.
To disable
a boolean your config turns on, pass its negation flag for the run (--no-watch, --no-pr, --no-checks, --no-notify, --no-projects, --no-color). To find or inspect your settings without hunting for the path or schema:
treetop config path |
Print the resolved config file path (even if it doesn't exist yet). |
treetop config show |
Print the path, then each key's effective value (built-in default overlaid by the file),
before any CLI flag. Bare treetop config
is the same as config show.
|
Vocabulary
| in use | A worktree with a live session running inside it (marked ●). |
| open | A worktree with no session. |
| edited |
The newest working-tree file change, including unstaged edits (so an agent editing
files shows up immediately), in compact wording (12s, 5m, 2d). Shown as
—
when nothing is present.
|
| changed | The most recent git activity in the worktree (commit / checkout / stage). |
How in-use detection works
treetop decides a worktree is in use from two independent signals:
1 · A live-session scan (best-effort; Linux + macOS)
On Linux it reads /proc; on macOS it uses ps
+ lsof. Either
way it finds live Claude Code
and Codex
processes and marks a worktree if the process is working
at or below it — either by its working directory, or by a file it currently holds open. The
open-file check is what lets treetop
catch subagents, which run in-process and
never chdir
into the worktree they target. A worktree stays marked for 30s after
the signal last appeared, so the ●
doesn't flicker.
2 · A .treetop-inuse marker file
At the worktree root. This is the deterministic, cross-platform signal: whatever drops the marker reports the activity. Its first line may be the owning process's PID, in which case it's honoured only while that process is alive (a stale marker from a crashed writer is ignored).