gt, a stacked PRs CLI
I wanted to see what it feels like to build a DIY Graphite clone. So I spent a weekend building gt, a CLI for stacked PRs on GitHub.
$ gt create new-user-mailer
? Include untracked file '.session.key'? (You chose: no)
? Include untracked file 'app/views/admin_mailer/new_user.text.erb'? (You chose: yes)
? Include untracked file 'logfile'? (You chose: no)
...
✓ Creating branch new-user-mailer
Opening PR for new-user-mailer → postmark
https://github.com/grodowski/undercover-ci/pull/807
✓ Updating stack comments
It also posts a stack comment on each PR:

Squash merges
When GitHub squash-merges a PR the resulting commit SHA doesn’t exist in your local history, so a naive rebase breaks. gt stores a fork-point (the parent branch tip at the time you branched) in git config and uses git rebase --onto to skip already-merged commits and replay only yours. All state in .git/config.
What it does
gt create stages your changes, creates a new branch from the current one, commits, pushes, and opens a PR targeting the parent branch.
gt create auth -m "add authentication"
# ... make changes ...
gt create profile -m "add profile page"
# ... make changes ...
gt create settings -m "add settings"
gt log shows the stack:
gt log
# main
# └─ auth
# └─ profile
# └─ settings *
gt sync pulls the latest main and rebases the whole stack on top of it. gt restack does the same after a PR gets merged and prompts to clean up the branch.
Navigation:
gt up # toward the tip
gt down # toward main
gt top # jump to tip
All commands:
$ gt help
Usage: gt <command> [options]
Commands:
create <name> -m <msg> [-p] Create a new stacked branch and PR
log (ls) Show the current stack
restack Rebase the stack onto updated parents
(prompts to delete if bottom PR was merged)
modify (m) [-m <msg>] [-p] Amend the current branch and restack
sync Pull main and restack
Navigation:
up Move up one level in the stack
down Move down one level in the stack
top Jump to the top of the stack
Options for restack:
--continue Continue after resolving conflicts
--abort Abort an in-progress restack
Building with Claude
I designed the commands, the stack model, and the edge cases. Claude did the implementation. Under the hood it leans on gh for all GitHub operations and cli-ui for the interactive prompts. I used the undercover-claude skill to keep test coverage tight as features landed. How much that actually matters is the subject of the follow-up post.
gem install gt-cli