v
( o)> p e r c h d
//\ ─────────────────────────────────
~~~~~~~~~~V_/_~~~~~ the one branch you actually land on
│ > feature/auth next :3000 ACTIVE
│ fix/payments vite :5173
│ spike/ai-search fastapi :8000
│ chore/bump-deps next :3000
│
│ your agents build every branch · one server on the one you watch
Eight agents. Eight branches. One perch.
perchd is npm run dev, pointed at any worktree —
switch which branch is live without the port-collision dance.
1 live server · 0 port collisions · 0 daemons
It's the agentic era. You don't write one branch at a time anymore.
You open six git worktrees, point a coding agent at each — Claude Code here, Cursor there, Codex on the spike — and they all start typing at once. Six dev servers want port 3000. You want to look at one of them.
perchd is the perch. One dev server, on its native port, following whichever
branch you're looking at. No cd. No remembering the command. No port roulette.
You don't have to learn perchd to use perchd. Run perchd and you get a menu of
your worktrees with the one you're standing in pre-selected — press Enter and it's
your npm run dev: foreground, logs streaming, on the native port. Arrow to a different
worktree and that's the switch. Or name one directly and skip the menu:
perchd # menu of worktrees, current one pre-selected → Enter runs it
perchd feature/auth # skip the menu — run that worktree by name
perchd main # the main tree, without leaving your branchSame muscle memory, all the way down to Ctrl-C: it stops the server, exactly like
npm run dev. The menu is the point, not overhead — it's how you switch which branch
you're previewing without ever cd-ing between worktrees.
That one live server actually runs in the background, and your terminal is just a viewport onto its log stream — so you can move it, leave it running, or watch it from somewhere else:
perchd # pick a worktree → attach, live logs, like npm run dev
^C # → stops it, just like npm run dev
perchd feature/auth -d # switch to another branch, but DON'T attach — keep your prompt
perchd attach # come watch that one; here ^C only detaches, it keeps running
perchd stop # terminate whatever's active, from anywhereCtrl-C stops the server you started — like
npm run dev. The one exception isperchd attach: you're peeking at a server that's meant to persist, so there Ctrl-C only detaches and leaves it running (the banner tells you which mode you're in). Either way,perchd stopis the definitive kill.
So there are two ways to keep a server alive while you move on: start it detached with
-d, or attach to it and Ctrl-C back out. Closing the terminal window on a server you
started stops it too — no orphans.
Without perchd, switching what you're previewing is a small ritual every time:
$ cd ../repo-feature-auth && pnpm dev # wait — which port was this one?
^C # kill the vite server from the other worktree
$ lsof -ti tcp:3000 | xargs kill # something's *still* on 3000
$ cd ../repo-fix-payments # ...was it `npm run dev` or `make dev` here?With perchd:
$ perchd feature/auth
# old server stops, frees its port, auth starts on :3000, logs stream
# → http://localhost:3000That was it.
The bottleneck moved. Agents are cheap and parallel; your attention is not.
Agent concurrency (8 worktrees) is not preview concurrency (1 pair of eyes).
So perchd keeps exactly one dev server alive — the worktree you're actually looking at — and that one assumption pays for everything:
- No port collisions. Only one server runs, so every project starts on its own conventional default port. Two Next apps both want 3000? Fine — they never run at the same time.
- No proxy, no port hashing, no
.envrewriting. Your bookmarks never move. It's alwayslocalhost:<the default for that framework>. - No daemon. perchd reconciles lazily the next time you run it. Delete the active
worktree and the server gets stopped and cleared on your next
statusor switch — self-healing, no watcher. - No orphans. Dev servers spawn children (Turbopack, webpack, esbuild). perchd starts each server as its own process group and tears down the whole group, so rapid switches never leave zombies on a port.
If a worktree needs several processes at once, point its runner at mprocs /
make dev / a dev:all script. perchd supervises one process group; that group can
fan out all it likes.
Install it globally — perchd is a CLI you run from any worktree, in any terminal,
so it belongs on your PATH, not in one project's node_modules:
npm i -g perchd # or: pnpm add -g perchd · bun add -g perchdRequires Node ≥ 20, on macOS or Linux (Windows is out of scope for v1).
If fzf is on your PATH, the picker uses it;
otherwise a clean built-in prompt takes over.
cd any/worktree/of/your/repo
perchd # menu (current worktree pre-selected) → Enter runs it
^C # stop it — just like npm run dev
perchd feature/auth -d # switch to another branch by name, keep it in the background
perchd status # table: worktree, runner, port, which is ACTIVE
perchd stop # stop the active server| Command | What it does |
|---|---|
perchd |
Menu of worktrees, current one pre-selected — Enter runs it (drop-in), or pick another to switch. Then attaches. |
perchd <branch|path> |
Skip the menu: switch the active dev server to that worktree and attach. |
perchd … -d / --detach |
Switch but stay in the background — print the URL and return the prompt. |
perchd switch [branch|path] |
Explicit synonym for perchd [branch|path]. Note it now attaches by default; add -d for the old background behavior. |
perchd attach [branch] |
Attach to the active server (or switch to branch, then attach). |
perchd stop |
Stop the active server. |
perchd status / perchd ls |
Table: worktree, branch, runner, port, ACTIVE?, pid, uptime. |
perchd restart |
Restart the active server in place. |
perchd logs [-f] |
Print (or follow with -f) the active server's log. |
perchd open |
Open the active server's URL in the browser. |
perchd path [branch] |
Print a worktree's absolute path (for the shell cd function). |
perchd gc |
Stop + clear a deleted active worktree; reap stale pids. |
perchd doctor |
Diagnose stale pids, dead ports, undetected worktrees, foreign port holders. |
perchd config |
Print the resolved config and detected runner per worktree. |
perchd watch |
Foreground watcher: auto-stops the active server the instant its worktree is deleted. |
perchd dev [target] |
Deprecated — an alias for perchd [target]. See migrating. |
Flags on a switch: -d/--detach (don't attach), --cmd <str> and --port <n>
(one-off overrides), --no-wait (skip the readiness wait), --force (kill a foreign
process holding the target port).
Passthrough is runner-agnostic — perchd appends your -- <args> verbatim to
whatever command it resolved, and does not assume npm. For npm's own script
forwarding, include npm's separator yourself: perchd -- -- --host runs
npm run dev -- --host.
perchd --port 4000 # override the port
perchd -- --host # append args to the underlying runnerTwo honest caveats about attached output. Because the server writes to a log stream that outlives your terminal rather than to the terminal itself:
- The dev server's own keypress shortcuts (e.g. vite's
r/q) are inactive — useperchd restartandperchd stop. perchd's servers don't read stdin by design; it's what keeps them from being suspended once they outlive your terminal.- Colour comes through, but interactive progress re-rendering (spinners, bars that redraw in place) isn't faithfully reproduced. Logs, URLs, and errors are all exactly what you'd see from
npm run dev.
Through 0.3.x, the foreground drop-in was a subcommand: perchd dev. It's now just
perchd. perchd dev still works and behaves identically — it prints a deprecation
hint and will be removed in a future major.
Before (≤ 0.3.x) |
Now |
|---|---|
perchd dev |
perchd |
perchd dev feature/auth |
perchd feature/auth |
perchd (background switch) |
perchd -d |
perchd switch feature/auth (background) |
perchd feature/auth -d |
| Ctrl-C stopped the server | Ctrl-C still stops it — same as before |
perchd switch still exists as an explicit synonym, but like every switch it now
attaches by default — pass -d to get the old background behavior.
If you used
0.4.0specifically: that release briefly made Ctrl-C detach instead of stop.0.5.0reverts to the intuitivenpm run devbehavior — Ctrl-C stops the server you started. Onlyperchd attachdetaches on Ctrl-C now.
Auto-detected with zero config — perchd reads the worktree and figures out how it wants to run:
- JS/TS: Next.js, Vite (React/Vue/Svelte), Nuxt, Remix, Astro, SvelteKit, generic
Node (
dev/start/servescript). Package manager inferred from the lockfile (pnpm/bun/yarn/npm). - Python: Django, FastAPI, Flask. Toolchain inferred from the lockfile
(
uv/poetry/.venv/system). - Convention runners:
mprocs,process-compose,Procfile(overmind/hivemind/foreman),Makefiledev:target,justfiledevrecipe.
Your agent invented a framework perchd doesn't know? Adding one is a single detector module — see CONTRIBUTING.md.
Everything is auto-detected; .perchd.toml at the repo root is only for overrides and
non-standard projects:
ready_timeout = 30 # seconds to wait for the port to listen
stop_timeout = 8 # seconds before SIGTERM escalates to SIGKILL
[runner] # force the runner for the whole repo
command = "make dev"
port = 3000
[worktrees."feature/auth"] # per-branch override
command = "pnpm dev"
cwd = "apps/web" # relative to the worktree root
port = 4000
env = { DEBUG = "1" }A child process can't change your shell's directory, so perchd ships a path
subcommand and a small shell function. Add this to your ~/.zshrc:
perchd() {
if [[ "$1" == "cd" ]]; then
cd "$(command perchd path "$2")"
else
command perchd "$@"
fi
}Then perchd cd feature/auth drops you right where your agent has been working.
Do I have to learn a new tool?
Barely. Run perchd, press Enter on the pre-selected current worktree, and it's your
npm run dev — same foreground, same logs, same port. The only new thing is that the
menu lets you arrow to another worktree instead of cd-ing there first.
Does Ctrl-C stop the server, like npm run dev?
Yes — when you started it (perchd / perchd <branch>), Ctrl-C stops it, and so does
closing the terminal. The one exception is perchd attach: there you're looking at a
server that's meant to keep running, so Ctrl-C just detaches and leaves it alive. The
banner tells you which mode you're in, and perchd stop always terminates.
How do I keep a server running while I move on?
Two ways: start it detached with perchd <branch> -d (never attaches), or perchd attach
to a running one and Ctrl-C back out.
Can't it just run all eight servers at once? No. You have two eyes. It runs the one you're looking at. That's not a limitation — it's the whole design.
My agent spawns Turbopack / webpack / esbuild children. Orphans on the port? No. perchd signals the whole process group, not the bare pid, and waits for the port to free before starting the next one.
Do I need a daemon running in the background? No daemon. perchd does its bookkeeping lazily, the next time you run it.
Does perchd phone home?
Only to check for its own updates: at most once a day it asks the npm registry for the
latest perchd version (in a detached background process, so it never slows a command),
and prints a one-line "you're outdated" notice to stderr if you're behind. Set
NO_UPDATE_NOTIFIER=1 (or run under CI) to turn it off. Nothing else leaves your
machine.
Does it manage my agents (Claude Code, Cursor, Codex)? No. It manages dev servers. The agents are your problem — perchd just gives you a clean window onto whatever they've built.
I deleted the active worktree out from under it.
Your next status or switch notices, stops the server, and clears the state.
Self-healing.
MIT © Irwan Setiawan