Running several coding agents at once is normal now. You give Claude Code the auth work in one git worktree, point Codex at a risky refactor in another, and let Cursor chew through a bug on main. They all type at the same time.
Then two of them finish within a few minutes of each other. And you want to actually see what they built.
That’s where it gets clumsy.
The routine
Here’s what checking a second branch looked like for me for most of this year:
^C
cd ../app-auth
lsof -ti tcp:3000 | xargs kill
npm run devEvery line is there for a reason.
The kill line is there because Ctrl-C doesn’t always take everything down. Next.js with Turbopack sometimes leaves a child process behind, and it keeps holding port 3000. So the new server starts, finds 3000 taken, and quietly moves to 3001. Meanwhile I’m staring at the old branch in the browser, wondering why my change isn’t showing up.
And npm run dev is a guess. This repo uses pnpm. The Python service starts from a Makefile. A worktree an agent opened last week could be any of those. Half the time I’m opening package.json to find out how to start my own project.
I counted once. About forty switches a day. Each one takes a minute or two, and together they were most of the reason review felt slower than the agents.
Everything expects localhost:3000
The obvious move is to stop switching and run every branch at once. That’s when you find out how far localhost:3000 has spread through a normal web project.
The OAuth app has a callback URL registered for it. The .env has it in a public API URL. The browser holds a session cookie for it, and my bookmarks point at it.
Playwright is the sneaky one. The config uses 3000 as its base URL, and with reuseExistingServer turned on it’ll happily run your tests against whatever’s already on that port. Which might be a different branch.
So whatever’s running on 3000 is the branch you’re testing, whether you meant it or not.
What people use instead
There’s a lot out there for this already. Most of it falls into three groups.
Give every worktree its own port. Conductor gives each workspace a block of ports through a CONDUCTOR_PORT variable. Paseo, Emdash and worktrunk each have their own version. Some go further. workz gives each worktree its own port range, database and Docker Compose project. ataegina does it from a single bash file, where your main checkout keeps the usual ports and every other worktree gets an offset and its own database. The worktree features inside Claude Code, Cursor and the Codex app mostly copy your .env, run a setup script, and leave ports to you.
Give every branch a name. portless, from Vercel Labs, puts each app behind a local HTTPS proxy, so a worktree called fix-ui shows up at fix-ui.myapp.localhost.
Bring one branch back to the usual port. Conductor’s Spotlight copies one workspace’s files into your main checkout, so the dev server already running there picks them up. The Codex app can hand a branch back to your local checkout.
Good tools, all of them. They just didn’t fit how I work.
Where they didn’t fit for me
Separate ports broke everything pointing at 3000. Google matches OAuth redirect URIs exactly, so a callback registered for localhost:3000 won’t take 3417. And the quick fix, PORT in each worktree’s .env.local, doesn’t work for Next.js at all. Its docs say PORT can’t be set in .env. Even the tools that keep main on the usual port only give it to main. The branch an agent just finished is the one on the offset.
Named URLs fix the bookmarks, but the OAuth problem just moves. portless’s own docs say strict providers reject .localhost and suggest a domain you own. Then Next.js blocks dev requests from that domain until you add it to allowedDevOrigins. So it’s a line of config in every repo, plus a proxy that has to stay up.
The one-at-a-time options came closest. But they belong to the app that made the worktree. Spotlight only works on Conductor workspaces, and it syncs one way over my main checkout. My worktrees come from all over: Claude Code’s --worktree flag in a terminal, the Codex app, a plain git worktree add.
And somewhere in all that, I noticed I never had more than one of those branches open in a tab.
The part that didn’t get faster
Agent concurrency isn’t preview concurrency.
The agents can work on eight branches at the same time. I still read one diff, click through one login flow, and keep one “what was this supposed to do” in my head.
What I built: perchd
So I went the other way. One dev server, on the port the framework already uses, running inside the worktree I’m looking at. Switching branches moves it.
I wrote a small tool for that, called perchd. Run perchd, pick a worktree, hit Enter. It stops the old server along with anything it spawned, works out how the new branch starts from its lockfile and scripts, and brings it up on localhost:3000. Ctrl-C stops it, same as npm run dev. If you already know the branch, perchd feature/auth skips the picker.
It doesn’t care which agent made the worktree. Your callback URL, .env, Playwright config and bookmarks never change, because the port never does. No proxy, nothing running in the background.
It’s MIT and on npm: npm i -g perchd. The repo’s at github.com/irwansetiawan/perchd.
I’m not sure one server is the right answer for everyone. If one of the tools above works for you, I’d honestly like to know which, and what you did about the callback URL.





