# Show HN: A local merge queue for parallel Claude Code agents

> Source: <https://github.com/funador/claude-code-merge-queue>
> Published: 2026-07-30 00:20:16+00:00

**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](#%EF%B8%8F-configuration)[🆚 vs. GitHub's Merge Queue](#-vs-githubs-merge-queue)[🧰 What's in the box](#-whats-in-the-box)[🚨 The emergency hatch](#-the-emergency-hatch)[🔍 Know the limits](#-know-the-limits)[📄 License](#-license)

Everything lives in one file — see
[ examples/claude-code-merge-queue.config.mjs](/funador/claude-code-merge-queue/blob/main/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`

and`checkCommand`

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`

*if*you already have Husky. If you don't,`init`

tells you rather than silently writing to the untracked`.git/hooks/pre-push`

.—`package.json`

scripts`land`

,`sync`

,`promote`

,`preview`

,`preview:restore`

, skipping any you've already defined yourself.— a self-contained safety net that runs before`claude-code-merge-queue-preflight.mjs`

`land`

/`sync`

, so a stale branch fails with a real diagnosis instead of a bare`command 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 and`echo 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 means`git 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-run`land`

.

MIT. Fork it, rename it, argue with the config shape — that's the point.
