https://github.com/cesarferreira/stax
The fastest stacked-branch workflow for Git. Interactive TUI, smart PRs, safe undo. Written in Rust.
https://github.com/cesarferreira/stax
freephite git graphite prs stack stacked stacks
Last synced: 20 days ago
JSON representation
The fastest stacked-branch workflow for Git. Interactive TUI, smart PRs, safe undo. Written in Rust.
- Host: GitHub
- URL: https://github.com/cesarferreira/stax
- Owner: cesarferreira
- License: mit
- Created: 2025-12-24T01:43:15.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-04-27T18:05:42.000Z (about 1 month ago)
- Last Synced: 2026-04-27T20:29:18.250Z (about 1 month ago)
- Topics: freephite, git, graphite, prs, stack, stacked, stacks
- Language: Rust
- Homepage: https://cesarferreira.com/stax/
- Size: 5.62 MB
- Stars: 85
- Watchers: 1
- Forks: 9
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
stax
Stacked Git branches and PRs — fast, safe, and built for humans and AI agents.
Install
·
Quickstart
·
Commands
·
Docs
---
## Why stax
One giant PR is slow to review and risky to merge. A stack of small PRs is the answer — but managing stacks by hand with `git rebase --onto` is a footgun. **stax** makes stacks a first-class Git primitive.
- **Stack, don't wait.** Keep shipping on top of in-review PRs. `st create`, `st ss`, done.
- **Native-fast.** A single Rust binary that starts in ~25ms. `st ls` benches ~70× faster than Graphite and ~215× faster than Freephite on this repo.
- **Agent-native.** Run parallel AI agents on isolated branches (`st lane`), auto-resolve rebase conflicts (`st resolve`), and generate branch names, commit messages, and PR details from real diffs.
- **Undo-first.** Every destructive op snapshots state. `st undo` / `st redo` rescue risky rebases instantly.
- **Batteries-included TUI.** Run bare `st` to browse the stack, inspect diffs, and watch CI hydrate live.
> `stax` installs two binaries: `stax` and the short alias `st`. This README uses `st`.
## Install
The shortest path on macOS and Linux:
```bash
brew install cesarferreira/tap/stax
```
Other installation methods — cargo-binstall, prebuilt binaries, Windows, from source
### cargo-binstall
```bash
cargo binstall stax
```
### Prebuilt binaries
Download the latest binary from [GitHub Releases](https://github.com/cesarferreira/stax/releases):
```bash
# macOS (Apple Silicon)
curl -fsSL https://github.com/cesarferreira/stax/releases/latest/download/stax-aarch64-apple-darwin.tar.gz | tar xz
# macOS (Intel)
curl -fsSL https://github.com/cesarferreira/stax/releases/latest/download/stax-x86_64-apple-darwin.tar.gz | tar xz
# Linux (x86_64)
curl -fsSL https://github.com/cesarferreira/stax/releases/latest/download/stax-x86_64-unknown-linux-gnu.tar.gz | tar xz
# Linux (arm64)
curl -fsSL https://github.com/cesarferreira/stax/releases/latest/download/stax-aarch64-unknown-linux-gnu.tar.gz | tar xz
mkdir -p ~/.local/bin
mv stax st ~/.local/bin/
# Ensure ~/.local/bin is on your PATH:
# echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
```
**Windows (x86_64):** download `stax-x86_64-pc-windows-msvc.zip` from [Releases](https://github.com/cesarferreira/stax/releases), extract `stax.exe` and `st.exe`, and place them on your `PATH`. See [Windows notes](#windows-notes).
### Build from source
Prereqs:
- Debian/Ubuntu: `sudo apt-get install libssl-dev pkg-config`
- Fedora/RHEL: `sudo dnf install openssl-devel`
- Arch: `sudo pacman -S openssl pkg-config`
- macOS: OpenSSL included
Then:
```bash
cargo install --path . --locked
# or
make install
# or
task install
```
No system OpenSSL? Use the vendored feature:
```bash
cargo install --path . --locked --features vendored-openssl
```
Verify the install:
```bash
st --version
```
`st setup` handles shell integration, AI agent skills, and GitHub auth in a single step:
```bash
st setup --yes
```
Alternative auth options
```bash
# Import from GitHub CLI
gh auth login && st auth --from-gh
# Enter a token interactively
st auth
# Or via env var
export STAX_GITHUB_TOKEN="ghp_xxxx"
```
By default stax ignores ambient `GITHUB_TOKEN`. Opt in with `auth.allow_github_token_env = true`.
Now ship a two-branch stack end-to-end:
```bash
# 1. Stack two branches on trunk
st create auth-api
st create auth-ui
# 2. See the stack
st ls
# ◉ auth-ui 1↑
# ○ auth-api 1↑
# ○ main
# 3. Submit the whole stack as linked PRs
st ss
# 4. After the bottom PR merges on GitHub…
st update # sync trunk, restack this stack, update PRs
```
Picked the wrong trunk? Run `st trunk main` or `st init --trunk ` to reconfigure.
Next: [Quick Start guide](docs/getting-started/quick-start.md) · [Merge & cascade workflow](docs/workflows/merge-and-cascade.md)
## Highlights
### Parallel AI lanes
Spin up multiple AI agents on isolated branches, all tracked as normal stax branches:
```bash
st lane fix-auth-refresh "Fix the token refresh edge case from #142"
st lane stabilize-ci "Stabilize the 3 flaky tests in the checkout flow"
st lane api-docs "Update API docs for the /users endpoint"
```
Each lane is a real Git worktree with normal stax metadata — it appears in `st ls`, participates in restack/sync/undo, and re-attaches via tmux any time. No hidden scratch directories, no lost work.
```bash
st wt # open the worktree dashboard
st wt rs # restack every lane at once when trunk moves
st ss # submit PRs for the ones that are ready
```
→ [Agent worktrees](docs/workflows/agent-worktrees.md) · [Multi-worktree workflow](docs/workflows/multi-worktree.md)
### Cascade stack merge
Merge from the bottom of the stack up to your current branch, with CI and readiness checks:
```bash
st merge # local cascade merge
st merge --when-ready # wait/poll until PRs are mergeable
st merge --ds # merge ancestors, rebase current branch
st merge --remote # merge remotely on GitHub while you keep working
st merge --all # merge the whole stack regardless of position
```
→ [Merge and cascade](docs/workflows/merge-and-cascade.md)
### AI conflict resolution
When a rebase stops on a conflict, `st resolve` sends only the conflicted text files to your configured AI agent, applies the result, and resumes the rebase automatically. If the AI returns invalid output, touches a non-conflicted file, or leaves extra conflicts behind, stax bails out and preserves the in-progress rebase so you can inspect or continue manually.
```bash
st resolve
st resolve --agent codex --model gpt-5.3-codex
```
Before each rebase, stax also runs a **preflight repair** that compares the
stored parent boundary against `merge-base(parent, branch)`. When they diverge
sharply — the “my restack hit conflicts on files I never touched” case — stax
automatically uses the merge-base boundary for that rebase and prints a
one-line notice. Silence the notice with `[restack] preflight_warn = false` or
`--quiet`; disable the automatic correction with
`[restack] preflight_auto_repair = false`.
### Undo / redo
`restack`, `submit`, and `reorder` each snapshot branch state before they touch anything. Recovery is one command away.
```bash
st restack
st undo
st redo
```
→ [Undo/redo safety](docs/safety/undo-redo.md)
### Interactive TUI
Bare `st` launches a full-screen TUI for browsing stacks, inspecting branch summaries and cached patches, watching live CI hydrate, and running common ops without leaving the terminal.
→ [TUI guide](docs/interface/tui.md)
### AI branch names, PR details, and standups
```bash
st create --ai -a --yes # generate branch name + first commit message
st ss --ai --yes # generate PR titles/bodies during submit
st gen # interactive: PR body, PR title, or commit message (AI)
st generate --pr-body # non-interactive: refresh PR body from branch diff + context
st generate --pr-title # non-interactive: refresh PR title from branch diff
st generate --commit-msg # non-interactive: amend HEAD commit message with AI
st standup --ai # spoken-style daily engineering summary
st standup --ai --style slack # Slack-ready Yesterday/Today bullets
```
Each AI feature (`generate`, `standup`, `resolve`, `lane`) can use a different agent/model. `st create --ai`, `st submit --ai`, and `st generate` / `st gen` (PR body/title, commit message) share the `generate` setting. Configure with:
```bash
st config --set-ai
```
→ [PR templates & AI](docs/integrations/pr-templates-and-ai.md) · [Reporting](docs/workflows/reporting.md)
| Command | What it does |
|---|---|
| `st` | Launch interactive TUI |
| `st ls` / `st ll` | Show stack health and PR status (`st ll` adds PR URLs/details) |
| `st watch` | Live auto-refreshing stack status with CI and PR state (adaptive polling: 15s active CI → 60s open PRs → 120s idle) |
| `st watch --current` | Watch only the current stack |
| `st create ` / `st add ` | Create a branch stacked on current |
| `st create --ai -a --yes` | Generate branch name + first commit message |
| `st create --below` | Insert a new branch below current, carrying tracked/untracked prepared changes with it |
| `st ss` | Submit the full stack, open/update linked PRs |
| `st merge` | Cascade-merge from bottom to current (`--when-ready`, `--downstack-only`/`--ds`, `--remote`, `--all`) |
| `st ci -w --alert` | Watch CI until all checks finish, then play success/error sounds |
| `st ci -w --strict` | Watch CI but exit as soon as any check fails |
| `st rs` / `st rs --restack` | Sync trunk, clean merged branches, optionally rebase |
| `st update` | Sync trunk without merged-branch cleanup, restack current stack, then push/update PRs |
| `st update --force --yes --no-prompt` | Run update without sync or submit prompts |
| `st update --verbose` | Include detailed sync/restack/submit timing |
| `st restack` | Rebase current stack onto parents locally |
| `st cascade` | Restack + push + open/update PRs |
| `st split` | Split a branch into stacked branches (by commit or `--hunk`) |
| `st lane ""` | Spawn an AI agent on a new lane |
| `st wt` | Open the worktree dashboard |
| `st resolve` | AI-resolve an in-progress rebase conflict |
| `st create --ai` | Generate a branch name from local changes |
| `st gen` / `st generate` | AI: interactive picker, or `--pr-body` / `--pr-title` / `--commit-msg` |
| `st ss --ai` | Submit with AI-generated PR title/body suggestions |
| `st standup` | Summarize recent engineering activity |
| `st tmux status` | Print a tmux-formatted status string (branch, stack position, PR, CI) for `status-right` |
| `st tmux popup` | Open `stax watch --current` in a floating tmux panel |
| `st undo` / `st redo` | Recover / reapply risky operations |
| `st run ` | Run a command on each branch in the stack |
| `st draft [branch]` / `st undraft [branch]` | Toggle a PR between draft and ready-for-review |
| `st pr` / `st pr body` / `st pr list` / `st issue list` | Open current PR · view/edit PR body · list PRs · list issues |
Full reference: [docs/commands/core.md](docs/commands/core.md) · [docs/commands/reference.md](docs/commands/reference.md)
## Performance
Benchmarked with `hyperfine` on this repo. Absolute times vary by repo and machine; the ratios do not.
| Benchmark | stax | vs [Freephite](https://github.com/bradymadden97/freephite) | vs [Graphite](https://github.com/withgraphite/graphite-cli) |
|----------------|----------|-----------------|----------------|
| `st ls` | baseline | **214.76×** faster | **69.72×** faster |
| `st rs` (sync) | baseline | **2.41×** faster | — |
stax is wire-compatible with Freephite/Graphite for common stacked-branch workflows.
→ [Full benchmarks](docs/reference/benchmarks.md) · [Compatibility notes](docs/compatibility/freephite-graphite.md)
## Configuration
```bash
st config # open the config editor
st config --set-ai # pick AI agent + model
st config --reset-ai # clear saved AI pairing and re-prompt
```
Config lives at `~/.config/stax/config.toml`:
```toml
[submit]
stack_links = "body" # "comment" | "body" | "both" | "off"
single_stack = "on" # "on" | "off" — when "off", skip stack-link sync while only one PR exists
```
→ [Full config reference](docs/configuration/index.md)
## Integrations
### tmux
[**stax.tmux**](https://github.com/cesarferreira/stax.tmux) is a TPM-compatible plugin that puts your stack in the tmux status bar and adds keybindings for common actions:
- Live status bar — branch, stack position, PR state, CI state; auto-refreshes in the background
- Keybindings — `prefix + S` popup, `prefix + ]`/`[` up/down, `prefix + M-s` sync
- Window auto-rename — tmux window title follows the current branch
Install via TPM:
```tmux
set -g @plugin 'cesarferreira/stax.tmux'
```
See the [stax.tmux README](https://github.com/cesarferreira/stax.tmux) for full setup and configuration options.
---
AI and editor integration guides:
- [Claude Code](docs/integrations/claude-code.md)
- [Codex](docs/integrations/codex.md)
- [Gemini CLI](docs/integrations/gemini-cli.md)
- [OpenCode](docs/integrations/opencode.md)
- [PR templates + AI generation](docs/integrations/pr-templates-and-ai.md)
Shared skill/instruction file used across agents: [skills.md](skills.md)
`st changelog` can generate notes between refs, and `st changelog find [query]`
or `st changelog --find [query]` fuzzy-finds entries in `CHANGELOG.md` while
showing the release and section that contain each match.
Windows notes — shell integration, worktrees, tmux
stax runs on Windows (x86_64) with prebuilt binaries on [Releases](https://github.com/cesarferreira/stax/releases). Most commands work identically, with these limitations:
- **Shell integration is not available.** `st setup` supports bash/zsh/fish only. On Windows:
- `st wt c` / `st wt go` create and navigate worktrees but cannot auto-`cd` the parent shell. Manually `cd` to the printed path.
- The `sw` quick alias is not available.
- `st wt rm` (bare) cannot relocate the shell. Specify: `st wt rm `.
- **Worktree commands still work.** `st wt c/go/ls/ll/cleanup/rm/prune/restack` all function — only the shell-level `cd` is missing.
- **tmux integration requires WSL** or a Unix-like environment. The [stax.tmux](https://github.com/cesarferreira/stax.tmux) plugin is Unix-only.
Everything else — stacked branches, PRs, restack, sync, undo/redo, TUI, AI generation — works on Windows without limitation.
## Contributing
Before opening a PR, run:
```bash
make test
```
To cut a release, run:
```bash
make release # default minor bump
make release LEVEL=patch # patch bump
make release LEVEL=major # major bump
```
Release automation now finalizes the next versioned entry in `CHANGELOG.md` from commits since the latest `v*` tag inside `cargo release`'s pre-release hook, refreshes the compare links, and leaves a fresh `Unreleased` header for follow-up work. If there are no commits since the last tag, the release exits early instead of creating an empty changelog entry.
Project docs and architecture: [docs/index.md](docs/index.md). Contributor guidelines: [AGENTS.md](AGENTS.md).
## License
MIT © Cesar Ferreira