π Spawning Parallel Agents Across Multiple Repos
𧬠Each spawn creates
a fully isolated copy
of every repo viagit clone --local
(copy-on-write, near
instant on macOS).
The biggest friction in modern AI-assisted development isnβt the models β itβs the workspace problem. Youβre shipping a feature that touches a backend service, a client library, and an infrastructure repo. You need the agent to reason across all three simultaneously, make consistent changes, and open PRs that actually cohere.
The usual answer? Open three terminal windows, manually create matching branches, context-switch constantly. It works, but it doesnβt flow.
This post is about a small script I wrote called spawn that solves exactly this. It creates a clean, isolated workspace with all your repos cloned and pre-branched, then opens everything in a single Cursor window. One command, zero context-switching.
The idea
git clone --local
uses hard-links on
the same filesystem,
so the clone is
nearly instantaneous
and uses minimal
extra disk space.
Every agent gets its own sandbox directory β a folder that contains one git clone --local of each repo you care about. All clones are put on the same branch. A .code-workspace file stitches them into a single Cursor multi-root workspace so the AI can see across repo boundaries as if they were one project.
Think of it like spawning a new process: isolated state, clean branch, no side-effects on your main working copies.
Installation
The script lives at ~/gitlab.com/angi/anchor/spawn. Drop a symlink so itβs on your PATH:
ln -s ~/gitlab.com/angi/anchor/spawn ~/.local/bin/spawn
chmod +x ~/.local/bin/spawn
Dependencies: bash β₯ 5, git, python3 (stdlib only), and cursor on your PATH.
Configuration
π‘ The config file is
created with sensible
defaults on first use
if it doesnβt exist.
On first run spawn creates ~/.config/spawn/config.json. You can also pass a custom path via SPAWN_CONFIG=/path/to/file.json.
Build your config interactively below β then copy the JSON into ~/.config/spawn/config.json.
The keys:
| Key | Required | Description |
|---|---|---|
workspace_root |
β | Parent directory for all agent workspaces |
primary_repos |
β | Always cloned (your core services) |
secondary_repos |
β | Only cloned with --all flag |
Usage
spawn without args
prints the full help
text β good habit
to check after updates.
Hit Run below to watch a simulated spawn session:
The four commands youβll actually use:
# create a new isolated workspace on branch feat/my-feature
spawn feat/my-feature
# same, but also clone secondary repos (docs, infra, etc.)
spawn --all feat/my-feature
# list all your agents and their current branches
spawn --list
# open an existing agent in Cursor
spawn --open feat-my-feature__a3f9c21b
# delete a finished agent (asks for confirmation)
spawn --remove feat-my-feature__a3f9c21b
Watching agents work across repos
π€ In practice I run
2β4 agents in parallel
on separate branches.
Each has its own
Cursor window with
the branch name in
the title bar.
Once Cursor opens the workspace file, the agent sees every repo as a top-level folder in the Explorer. You can:
- Ask it to trace a type across
backend/βclient-lib/βinfra/in one message - Have it create matching migration + schema + client changes atomically
- Review all diffs in one
git statusacross all roots
The simulation below shows three parallel agents running on separate spawned workspaces. Toggle them on/off to see what each is doing:
How the workspace file works
The window.title
setting embeds the
branch name so you
always know which
agent is in which
Cursor window.
spawn writes two files into each agent directory:
agent.code-workspace
{
"folders": [
{ "path": ".", "name": "feat/payments-v2" }
],
"settings": {
"window.title": "${dirty}${separator}feat/payments-v2${separator}${activeEditorShort}${separator}${appName}"
}
}
.vscode/settings.json (same window.title β Cursor reads both).
When you cursor agent.code-workspace, Cursor opens all subdirectories as workspace roots and shows the branch in the title bar, so you always know which agent is which at a glance.
Tips & tricks
π SPAWN_FORCE=1
skips the confirmation
prompt for --remove.
Useful in scripts.
The mental model
π― One branch per task.
One Cursor window
per branch. Let the
agent run. Review
the diff. Merge.
Delete the workspace.
A useful way to think about spawn is as git worktree with superpowers. Regular worktrees let you check out a different branch into a second directory β spawn does the same but for N repos at once, wires them into a Cursor workspace, and adds metadata so you can manage the lifecycle (--list, --open, --remove).
The workflow:
spawn feat/my-featureβ agent opens in Cursor- Describe the cross-repo feature in Cursorβs Composer
- Agent edits files across all repos
git diffin each repo, review, pushspawn --remove feat-my-feature__<id>β clean up
Each task is a clean slate. No lingering uncommitted changes. No branch confusion. Just: spawn, build, ship, remove.