I got tired of writing the same 80-line setup script for every AI worktree
Source: Dev.to
Parallel AI Agents & Isolated Worktrees

If you’ve been running parallel AI coding agents — Claude Code, Aider, Codex, anything — you’ve hit this wall.
- Each agent needs its own worktree.
- Each worktree needs its own environment.
That means every time you spin one up you either:
- Manually edit
.envfiles, or - Run a setup script like the one below.
#!/bin/bash
# conductor.json worktree script — set up isolated environment
BRANCH_NAME="${CONDUCTOR_BRANCH_NAME}"
SLUG="${BRANCH_NAME//\//_}"
SLUG="${SLUG//-/_}"
BASE_PORT=3000
USED_PORTS=$(cat ~/.worktree-ports 2>/dev/null || echo "")
PORT=$BASE_PORT
while echo "$USED_PORTS" | grep -q "^$PORT$"; do
PORT=$((PORT + 1))
done
echo "$PORT" >> ~/.worktree-ports
DB_NAME="myapp_${SLUG}"
COMPOSE_PROJECT="myapp_${SLUG}"
REDIS_PORT=$((PORT + 1000))
cat > .env.local /dev/null || true
echo "Set up: PORT=$PORT DB=$DB_NAME"
That’s 30 lines of Bash, with no error handling, no cleanup when you tear the worktree down, and it breaks the moment two worktrees race to write ~/.worktree-ports.
I’ve seen this pattern everywhere – the Phoenix community, 10play, etc. – everyone reinvents the same wheel.
What I built instead
I maintain workz – a Rust CLI for Git worktrees that handles zero‑config dependency syncing (symlinks node_modules, target, .venv automatically) and AI‑agent launching.
Last week I shipped the --isolated flag:
workz start feature/auth --isolated
Output
creating worktree for branch 'feature/auth'
worktree created at /home/you/myapp--feature-auth
symlinked node_modules
isolated environment:
PORT=3001 → .env.local
DB_NAME=feature_auth
COMPOSE_PROJECT_NAME=feature_auth
REDIS_URL=redis://localhost:4001
ready!
What it does
- Picks the next available port starting from 3000 (atomic, no race conditions).
- Sanitizes the branch name into a safe slug (
feature/auth → feature_auth). - Writes
.env.localwithPORT,DB_NAME,DATABASE_URL,COMPOSE_PROJECT_NAME,REDIS_URL. - Registers the allocation in
~/.config/workz/ports.jsonso no two worktrees ever collide.
When you’re done:
workz done feature/auth --cleanup-db
- Releases the port.
- Removes the worktree.
- Optionally drops the database.
No orphaned ports. No stale registry entries.
Running 3 isolated agents in parallel
The reason I care about this: I run multiple Claude Code agents in parallel, each on a different task. Before --isolated they all tried to bind to port 3000. The first one won; the others crashed.
Now:
workz fleet start \
--task "add OAuth2 login" \
--task "write integration tests" \
--task "refactor database layer" \
--agent claude \
--isolated
Three isolated worktrees, three unique ports, three separate databases, three .env.local files — all spun up in parallel.
workz status shows the full picture:
main /home/you/myapp [clean] 342K 2h ago
feature/auth /home/you/myapp--feature-auth 89M 5m ago PORT:3001
fix/tests /home/you/myapp--fix-tests 91M 5m ago PORT:3002
refactor/db /home/you/myapp--refactor-db 88M 5m ago PORT:3003
The Linux angle
I built this partly because Conductor.build – the GUI for parallel Claude Code agents that’s been getting a lot of attention – is Mac‑only (Apple Silicon required).
Every Linux developer who wants to run parallel AI agents has no Conductor. Even on Mac, Conductor’s worktree setup is a Bash script you write yourself – there’s no environment engine built in.
workz fills that gap:
| Feature | Details |
|---|---|
| Platforms | Linux, macOS (Windows planned) |
| License | MIT – single Rust binary |
| Installation | cargo install workz (or Homebrew) |
| Zero‑config | Auto‑detects Node/Rust/Python/Go/Java projects, symlinks deps automatically |
--isolated | The environment engine Conductor lacks |
| Not a GUI | Terminal‑native, exactly what Linux developers want |
How the port registry works
A simple JSON file at ~/.config/workz/ports.json:
{
"base_port": 3000,
"allocations": {
"feature_auth": {
"port": 3001,
"branch": "feature/auth",
"db_name": "feature_auth",
"compose_project": "feature_auth",
"allocated_at": "2026-03-03T20:00:00Z"
}
}
}
workz start --isolated– scans allocations for the next unused port, writes the entry, writes.env.local.workz done– removes the entry.
All reads/writes are atomic, so concurrent workz start calls never clash.
The branch‑slug sanitizer handles edge cases (feature/add-auth → feature_add_auth), collapses repeated separators, and lower‑cases everything.
Install
Homebrew
brew tap rohansx/tap
brew install workz
Cargo
cargo install workz
Add shell integration to your ~/.zshrc / ~/.bashrc:
eval "$(workz init zsh)" # or `zsh` → `bash` as appropriate
This provides a workz shell function that cds into the new worktree automatically after workz start.
Repository:
If you’re on macOS and already using Conductor — workz works as your setup script, giving you the same isolation without the GUI constraints.
Tip: Drop workz sync --isolated into your conductor.json and you get the environment engine without changing your workflow.