stax 0.96.1

Fast stacked Git branches and PRs
Documentation

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.
  • GitHub-native stacks, automatically. When your repo has GitHub's native Stacked PRs enabled and github/gh-stack is installed, st ss registers the stack with GitHub under the hood — zero config, zero extra commands. See Native GitHub Stacked PRs.
  • 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:

brew install cesarferreira/tap/stax

cargo-binstall

cargo binstall stax

Prebuilt binaries

Download the latest binary from GitHub Releases:

# 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, extract stax.exe and st.exe, and place them on your PATH. See 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:

cargo install --path . --locked
# or
make install

No system OpenSSL? Use the vendored feature:

cargo install --path . --locked --features vendored-openssl

Verify the install:

st --version

Native macOS GUI

Download the architecture-specific app from the same GitHub Release as the CLI. For Apple Silicon:

curl -fLO https://github.com/cesarferreira/stax/releases/latest/download/Stax-aarch64-apple-darwin.zip
ditto -x -k Stax-aarch64-apple-darwin.zip .
mv Stax.app /Applications/

On an Intel Mac, use Stax-x86_64-apple-darwin.zip instead. The app is a separate release artifact, not a new crates.io package, and installing it does not enlarge the stax or st CLI binaries.

Unsigned releases are supported. Download only from the project Releases page, move the app to /Applications, then Control-click Stax.app, choose Open, and choose Open again. If macOS still blocks the first launch, use System Settings → Privacy & Security → Open Anyway for Stax. Do not disable Gatekeeper globally. Releases open normally when the optional signing and notarization credentials are configured.

Contributors can also build or install the app locally:

make gui-app           # Build target/gui-app/Stax.app
make install-gui-app   # Build and install $HOME/Applications/Stax.app
st gui [path]          # Launch a fresh Stax.app instance for one repository

The bundle id is com.cesarferreira.stax. st gui [path] is macOS-only; it canonicalizes the supplied path, defaults to the current directory when omitted, and launches LaunchServices as open -n -b com.cesarferreira.stax --args <canonical-path>. The -n flag is intentional: every invocation starts a fresh app process/window for exactly one repository.

Quickstart

st setup handles shell integration, AI agent skills, and GitHub auth in a single step:

st setup --yes
# 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:

# 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 <branch> to reconfigure.

Next: Quick Start guide · Merge & cascade workflow

Highlights

Parallel AI lanes

Spin up multiple AI agents on isolated branches, all tracked as normal stax branches:

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.

st wt         # open the worktree dashboard
st wt rs      # restack every lane at once when trunk moves
st wt promote # move the current lane branch back to the main worktree
st ss         # submit PRs for the ones that are ready

Lanes start warm: instead of deleting a removed worktree, stax parks it as a reusable warm slot (resetting it to trunk and running git clean -fd, which keeps gitignored dependency directories like node_modules or .venv). The next lane adopts that slot instead of a cold checkout, so agents keep their built deps and don't re-install from scratch. Set worktree.reconcile to re-sync deps on adopt, or worktree.reuse_slots = false to opt out.

Agent worktrees · Multi-worktree workflow

Native GitHub Stacked PRs

When a GitHub repo has native Stacked PRs enabled, stax can register your submitted PRs as a native GitHub Stack automatically. This requires GitHub's github/gh-stack CLI extension — install it once:

gh extension install github/gh-stack
# or let stax install it for you:
st doctor --fix

That's it — no config needed. From then on, st ss/st bs auto-link multi-PR stacks under the hood, no extra command required. Existing stax PR body/comment stack links keep working; the native GitHub stack map is added on top.

st ss                    # auto-links native stack when available
st stack link            # manually re-link the current stack
st stack unlink          # unstack a locally tracked native GitHub Stack

Repos without the feature, users without the extension, and non-GitHub remotes keep the existing stax behavior — this is purely additive and never blocks a submit. stax also strips ambient GH_TOKEN/GITHUB_TOKEN before talking to gh stack, since GitHub's private-preview native-stack API rejects Personal Access Tokens and only accepts an OAuth-authenticated gh login (gh auth login). st doctor recommends gh-stack v0.0.6+ for reliable auth-error diagnostics, and reports whether the extension is missing, outdated, or up to date. st stack unlink delegates to gh stack unstack, so stacks that stax registered with gh stack link may need gh stack checkout <pr> first to create gh-stack's local tracking; otherwise remove the native stack in the GitHub UI.

Native GitHub Stacks guide

Cascade stack merge

Merge from the bottom of the stack up to your current branch, with CI and readiness checks:

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 --stack          # validate current PR once; let GitHub mark lower PRs merged when possible
st merge --stack --full   # stack-merge the full stack even from the middle
st merge --remote         # merge remotely on GitHub while you keep working
st merge --all            # merge the whole stack regardless of position

Merge and cascade

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.

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.

st restack
st undo
st redo

Undo/redo safety

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. Stack/Summary/Patch pane visibility is remembered per repo.

TUI guide

Native macOS GUI

The native GUI opens a repository-scoped workspace with searchable Stack, Changes, and Inspector panes. It can check out, create, rename, delete, move, and reorder local branches; restack the selected branch or all tracked branches; submit the current stack as Draft; open the selected PR; and safely undo or redo fully local recorded operations. Destructive and history-rewriting actions show exact previews and confirmations, including explicit auto-stash follow-ups for dirty move/reorder/restack flows.

When the app starts without an explicit path, it reopens the most recently used project. Use the project dropdown in the toolbar to switch between recent projects or choose Add Project… to open another repository. An explicit st gui <path> launch still opens that repository.

Use / to search, 1/2/3 to toggle panes, and drag dividers to resize them; visibility and widths persist per canonical repository. Native menus and keyboard shortcuts dispatch the same typed actions as the buttons. Submit is always confirmed first, pushes the current stack as Draft, and does not show CLI prompts or auto-open PR pages. st gui [path] only launches the app.

GUI guide

AI branch names, PR details, and standups

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:

st config --set-ai

PR templates & AI · Reporting

Commands

Command What it does
st Launch interactive TUI
st gui [path] Launch the installed native macOS GUI for a repository
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 <name> / st add <name> Create a branch stacked on current
st create --ai -a --yes Generate branch name + first commit message
st create <name> --below Insert a new branch below current, carrying tracked/untracked prepared changes with it
`st get [branch PR]`
st ss Submit the full stack, open/update linked PRs; temporary-publishes branches that need restack
st submit --plan [--json] Preview fetch, push, PR, retarget, metadata, and stack-link actions without changing local or remote state
st branch submit Submit only the current branch; can publish a temporary rebased head when needed
st upstack submit Submit current branch and descendants, chaining temporary publish heads when needed
st reviews --stack [--json] Stack-wide review/comment inbox, including inline file/line locations on GitHub (st comments remains the current-PR view)
st next Move to the next unmerged branch upstack; fork choices are deterministic
st merge Cascade-merge from bottom to current (--when-ready, --downstack-only/--ds, --stack, --stack --full, --remote, --all)
st ready Interactive PR readiness dashboard for all tracked PRs, newest changed PR first: merge, ping, fix, wait, or draft (--current/--stack for current stack, --plain for table output)
st ci / st ci --oneline CI status — full per-check table, or one compact line per branch across the stack (multi-branch defaults to the roll-up)
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 sweep Classify all local branches (merged/gone/stale/active); --delete removes merged branches (including tracked merged PRs) and upstream-gone branches with no unique work
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 <name> "<task>" Spawn an AI agent on a new lane
st wt Open the worktree dashboard
st wt promote Retire the current lane and check its branch out in the main worktree
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 <cmd> Run a command on each branch in the stack
st run --parallel --jobs 4 <cmd> Run checks concurrently in isolated temporary worktrees without switching the main worktree; each command receives STAX_RUN_BRANCH
st freeze / st unfreeze Protect/unprotect a tracked branch from restacks, imported-branch refreshes, and squash-merge cleanup rebases
st completions <shell> Generate completions for Bash, Zsh, Fish, PowerShell, or Elvish
st doctor --fix Check repo/config health and apply safe local repairs after one confirmation
st draft [branch] / st undraft [branch] Toggle a PR between draft and ready-for-review
st pr / st pr body / st pr list / st pr list --ready / st issue list Open current PR · view/edit PR body · list PRs · PR readiness · list issues

Full reference: docs/commands/core.md · docs/commands/reference.md

Performance

Benchmarked with hyperfine on this repo. Absolute times vary by repo and machine; the ratios do not.

Run make benchmark-status for deterministic cold 10/50/100-branch scaling fixtures, or add global --trace to a command to see instrumented Git subprocess and wall-clock timings.

Benchmark stax vs Freephite vs Graphite
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 · Compatibility notes

Configuration

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. When STAX_CONFIG_DIR is unset, a repo-root stax.toml overlays only the values it sets:

[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

Integrations

tmux

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:

set -g @plugin 'cesarferreira/stax.tmux'

See the stax.tmux README for full setup and configuration options.


AI and editor integration guides:

Shared skill/instruction file used across agents: skills.md

st changelog can generate notes between refs, and st changelog find [query] or st changelog --find [query] fuzzy-finds commits in the selected range. Use --path to scope either mode to a subdirectory.

stax runs on Windows (x86_64) with prebuilt binaries on 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. st wt promote performs the handoff but likewise requires the printed cd command.
    • The sw quick alias is not available.
    • st wt rm (bare) cannot relocate the shell. Specify: st wt rm <name>.
  • Worktree commands still work. st wt c/go/ls/ll/promote/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 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:

make test

On macOS this uses Docker when available. make test-native is the guarded fallback: it checks the file-descriptor limit, sanitizes the environment, and runs nextest with the optimized test profile. Native macOS timings can still vary substantially with endpoint-security tooling, so Docker remains the recommended full-suite path.

To cut a release, run:

make release                  # default minor bump
make release LEVEL=patch      # patch bump
make release LEVEL=major      # major bump

Release automation regenerates CHANGELOG.md with git-cliff inside cargo release's pre-release hook, grouping the commits since the latest v* tag under the new version (config in cliff.toml). See docs/workflows/releasing.md.

Project docs and architecture: docs/index.md. Contributor guidelines: AGENTS.md.

License

MIT © Cesar Ferreira