Stacked pull requests your coding agent can actually drive — without hanging, and without silently breaking the stack.
A Claude Code plugin for GitHub's native stacked pull requests.
/plugin marketplace add XingLiu0923/pr-stack-skill
/plugin install pr-stack@xing-plugins
Instead of one 40-file pull request nobody wants to review, a chain of small ones — each built on the last, each reviewable on its own.
main ── auth ─────────── api ─────────── ui
PR #1 PR #2 PR #3
5 files 8 files 12 files
▲ ▲
merges first merges last
└────── each PR's diff = only that layer ──────┘
GitHub shipped native stacked PRs in July 2026 with a gh stack CLI. Two things
go wrong the moment an agent starts using it.
gh stack opens a prompt or a full-screen TUI whenever stdout is a terminal.
Eight of its commands block forever in an agent session.
without pr-stack with pr-stack
agent agent
│ gh stack view │ gh stack view
▼ ▼
╔═══════════════╗ ┌───────────────┐
║ TUI opens ║ │ DENIED │
╚═══════════════╝ └───────────────┘
│ │ ~3 ms
▼ ▼
blocks forever "use --json"
(until you notice) agent retries, works
GitHub documents this as a table in prose. A prose table stops nothing.
One gh pr merge merges a single PR without retargeting the layers above it —
and GitHub auto-closes them. It does not fail loudly. You find out later.
gh pr merge 1 gh stack merge 1 --yes
main ── auth ── api ── ui main ── auth ── api ── ui
#1 #2 #3 #1 #2 #3
│ │
▼ merge ▼ merge
main ───────── ✗ ✗ main ─────────── api ── ui
#2 #3 #2 #3
AUTO-CLOSED base retargeted ✓
A plain git rebase is the same story: it moves one branch and leaves every
branch above pointing at abandoned commits.
Every session-blocking command is denied before it runs, with the working form handed back to the agent:
pr-stack: blocked `gh stack view`
A bare `gh stack view` opens a full-screen TUI under a PTY and blocks forever.
to parse : gh stack view --json
to display : gh stack view --short
| Instead of | It runs |
|---|---|
bare view |
gh stack view --json |
bare submit |
gh stack submit --auto |
bare init / add / checkout |
the form with an explicit argument |
switch |
gh stack up / down / top / bottom |
modify |
unstack --local then init — modify is TUI-only, with no non-interactive path at all |
| Blocked | Because |
|---|---|
| force-push | a restack rewrites every branch above; the lease is the only protection → --force-with-lease |
plain git rebase |
moves one branch and orphans the rest → gh stack rebase --upstack |
gh pr merge |
cannot merge a stack (see above) → gh stack merge <n> --yes |
git reset --hard, branch delete |
destructive; needs your explicit say-so |
Only inside a repository that gh stack manages. Everywhere else this
plugin is completely silent — git commit, git push, git rebase and the
rest behave exactly as they always have. It is a guard, not a cage.
GitHub's advice is to plan the layers before writing code. Its own stack-design reference puts it bluntly:
The failure mode to avoid is writing everything on one branch and trying to split it afterwards.
True — and useless once the 40-file branch exists.
before
main ── feature ─────────────────────── 40 files, 1 PR
"LGTM 👍" (nobody read it)
after /pr-stack:split
main ── refactor ── schema ── api ── ui ── tests
#1 #2 #3 #4 #5
5 files 3 8 12 12
proof git diff <anchor> HEAD → empty
nothing lost, nothing invented — or it stops and tells you
Stacked PRs aren't enabled on every repository, and gh stack only says so via
exit code 9 — after a command has already failed halfway through your workflow.
This checks first, every session:
gh=installed
ext=installed
auth=authenticated
repo=/Users/you/code/project
stacks=available
stack=main <- auth <- api* (2 layers, 2 PRs, 1 needs rebase)
gh stack already ships an excellent agent skill, and this plugin doesn't
duplicate it — it defers to gh stack <command> --help rather than restating
flags that a v0.1.0 preview will change.
It's a plugin because a skill cannot stop a command from running. Skills advise; hooks enforce.
| Skills | pr-stack:stack · pr-stack:stack-setup · pr-stack:split-pr |
| Commands | /pr-stack:status · /pr-stack:split |
| Hooks | preflight check · the guard |
Hooks run harness-side, so they cost zero model context.
gh extension install github/gh-stackPlus gh itself, authenticated. If anything's
missing the plugin says so and walks you through it — run /pr-stack:status.
gh stackis v0.1.0 public preview; expect its flags to move.- Stacks are strictly linear — parallel work needs separate stacks, and layer order can't be changed non-interactively after the fact.
- The guard covers the
Bashtool. Quoted text and heredoc bodies are treated as data, so documentation mentioning a blocked command isn't itself blocked. Where a command is genuinely ambiguous it errs toward allowing — a parser quirk must never wedge a session. - Blocked-but-legitimate command? Approve it in plain words, then it may be
re-run once prefixed with
PR_STACK_ALLOW=1. The agent must never add that itself. - Every timeout and command path is an environment override — see
scripts/config.sh. Nothing is hardcoded to a machine, a trunk name, or a build system. - No review/CI iteration skill yet. Planned for v0.2.
bash tests/run-tests.sh # 307 assertions, hermetic — no real repo or PR is touched
claude plugin validate .MIT — see LICENSE.