The local, zero-cost merge queue for parallel Claude Code agents. Several agents land, build, and test at the same time β this serializes it so push races, redundant heavy builds, and shared-resource test flakiness can't happen.
npm install --save-dev claude-code-merge-queue # or: pnpm add -D / yarn add -D / bun add -d
npx claude-code-merge-queue init
βοΈ Configurationπ vs. GitHub's Merge Queueπ§° What's in the boxπ¨ The emergency hatchπ Know the limitsπ License
Everything lives in one file β see examples/claude-code-merge-queue.config.mjs for every field with comments. The short version:
export default {
branchPrefix: "lane/", // lane/1, lane/2, ...
worktreeSuffix: "-lane-", // ../your-repo-lane-1
portBase: 3000, // lane n gets portBase + n
integrationBranch: "main", // where agents land β see below
productionBranch: null, // set this for a two-stage model β see below
protectedBranches: [], // extra branches beyond the two above; most repos need none
regenerableFiles: [], // files a build tool rewrites β never block a rebase on these
symlinks: [".env", ".env.local", "node_modules"],
buildOutputDirs: ["dist", "build", ".next"], // preview never copies these onto your checkout
checkCommand: "npm run check", // what actually gates a landing β see below
checksRequired: true, // false = deliberately run with none; see below
};
A malformed config (empty branch names, a negative port, productionBranch
equal to integrationBranch
, ...) fails loud with every problem listed, the moment any command loads it β not a mysterious failure three steps later.
| GitHub Merge Queue | Claude Code Merge Queue | |
|---|---|---|
| Private repo | Enterprise Cloud only | |
| Any plan, any repo | ||
| Cost per landing | GitHub Actions minutes, every queue attempt | $0 β runs on your own machine |
| Requires | A pull request | Nothing β direct rebase + push |
Same idea β serialize landings, test before merge, keep history clean β run locally instead of in someone else's billed cloud.
| Command | What it does |
|---|---|
claude-code-merge-queue hook worktree-create |
|
A Claude Code WorktreeCreate hook. Plugs Claude Code Merge Queue's numbered lanes into Claude's native worktree creation. |
|
claude-code-merge-queue build-lock -- <cmd> |
|
Runs <cmd> β your build β serialized across every lane, machine-wide. |
|
claude-code-merge-queue land |
|
| Rebases and pushes your lane onto the integration branch through a FIFO queue, so two lanes are never mid-push at once. Agents run this themselves. | |
claude-code-merge-queue sync |
|
| Fast-forwards your main checkout so a dev server actually sees what just landed β and re-installs dependencies if the lockfile changed. | |
claude-code-merge-queue promote |
|
| Ships the integration branch to production. Human-only β never in an agent's instructions, never automated. | |
claude-code-merge-queue preview |
|
| Instantly mirrors a lane's live working tree β uncommitted changes included β onto the main checkout, so you can look at it without a build. | |
claude-code-merge-queue port |
|
| Prints a lane's dev-server port, derived from its own directory name. | |
claude-code-merge-queue prune |
|
| Removes already-landed sibling lane worktrees on demand. |
A pre-push hook makes land
non-optional: a direct git push
straight to
the integration branch is rejected, with the actual command to run
instead, and the same hook runs checkCommand
before allowing a landing through β no checkCommand configured means every push fails by default. There's a way out for every block (see π¨ The emergency hatch), but it takes naming the specific branch, not a generic flag.
βclaude-code-merge-queue.config.mjs
integrationBranch
andcheckCommand
auto-detected.(or appends to yours) β tells Claude Code to land its own work once green, without being asked.CLAUDE.md
β the.claude/settings.json
WorktreeCreate
hook wired in, without touching anything else already there.β created or appended to,.husky/pre-push
ifyou already have Husky. If you don't,init
tells you rather than silently writing to the untracked.git/hooks/pre-push
.βpackage.json
scriptsland
,sync
,promote
,preview
,preview:restore
, skipping any you've already defined yourself.β a self-contained safety net that runs beforeclaude-code-merge-queue-preflight.mjs
land
/sync
, so a stale branch fails with a real diagnosis instead of a barecommand not found
.
Every blocked push β the integration branch, productionBranch
, anything
in protectedBranches
β has a real way through it. One env var, no prompts, no second factor to remember:
CLAUDE_CODE_MERGE_QUEUE_EMERGENCY_PUSH=1 git push origin HEAD:main
This is a convention, not a hard guarantee: it stops mistakes and stray pushes, not an adversarial agent that sets the var itself.
No human reviews any of this before it lands.checkCommand
passing is the only gate β a real test suite andecho ok
look identical to this tool. Want a human on every change? This is missing that step on purpose.Locks are crash-safe by PID liveness, not a timeout.kill -9
anything mid-claim and the next process notices the PID is dead and reclaims it β no stale locks, no timeout to tune.One machine, not a fleet. The FIFO queue lives in local temp storage β two machines landing at once just get git's ordinary non-fast-forward rejection.Not a security boundary. Every guardrail here stops mistakes and convention drift, not an adversarial agent β shell access always meansgit push --no-verify
or editing the config on purpose.A slow The FIFO lock holds for its entire duration β a 3β4 minute suite caps you well under 20 landings/hour.checkCommand
is a real throughput ceiling.Rebase conflicts abort, they never guess.git rebase --abort
on any conflict, working tree left clean β CLAUDE.md tells the agent to resolve it and re-runland
.
MIT. Fork it, rename it, argue with the config shape β that's the point.