snag / docs

Snag · open source

snag-run/treetop

treetop

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

FlagDescription
-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).

The live-session scan runs on Linux and macOS; on Windows (run under WSL) or elsewhere the marker file still works. The included Claude Code and Codex hooks drop and remove the marker as sessions and subagents start and stop, so worktrees an agent is working in light up even though they leave no other footprint — see the repo README for the hook installer.