Split large PRs into stacked PRs using Claude Code
Project description
PullSaw
Split large PRs into stacked PRs using Claude Code.
Overview
pullsaw analyzes a feature branch, generates a plan to split it into smaller, reviewable PRs, and executes each step using Claude Code in headless mode. Each stacked PR is self-contained with passing tests.
Prerequisites
- Python 3.12+
- Claude Code installed and authenticated
- Git repository with a feature branch to split
Installation
# Using uv (recommended)
uv pip install -e .
# Or with pip
pip install -e .
Usage
# Run from a feature branch
git checkout my-feature-branch
pullsaw
# With options
pullsaw --base main --head my-feature --strict --yes
Options
| Option | Description |
|---|---|
--base BRANCH |
Base branch (default: auto-detect main/master) |
--head BRANCH |
Head branch (default: current branch) |
--strict |
Fail if drift is detected vs original |
--yes, -y |
Skip confirmation prompt |
--dry-run |
Generate plan only, don't execute |
How It Works
- Check State: Ensures clean working tree and valid branches
- Analyze Diff: Gets changed files between base and head
- Generate Plan: Uses Claude Code to propose a stacked PR plan
- Validate Plan: Ensures all files are covered and steps are valid
- Execute Steps: For each step:
- Create a new branch
- Claude Code implements the step
- Enforce allowlist (rollback unauthorized changes)
- Run format and tests
- Fix failures (up to N attempts)
- Commit
- Drift Check: Verify final state matches original branch
Configuration
Create .pullsaw/config.yml in your repo:
test_cmd: ["mix", "test", "--max-failures", "1"]
format_cmd: ["mix", "format"]
max_fix_attempts: 5
strict: false
Auto-detection
Without a config file, PullSaw auto-detects based on project files:
| File | Test Command | Format Command |
|---|---|---|
mix.exs |
mix test |
mix format |
package.json |
npm/yarn/pnpm test |
npm run format |
Cargo.toml |
cargo test |
cargo fmt |
pyproject.toml |
pytest |
ruff format . |
go.mod |
go test ./... |
go fmt ./... |
Example Session
$ pullsaw
╭──────────────────────────────────╮
│ PullSaw - Stacked PR Splitter │
╰──────────────────────────────────╯
Checking working tree... clean
Base: main
Head: feature-auth
Changed files: 12
Generating plan via Claude Code...
Stacked PR Plan
┏━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ # ┃ Title ┃ Files ┃
┡━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ 1 │ Introduce auth types │ lib/auth/** │
│ 2 │ Add auth plug + config │ lib/auth/plug.ex │
│ 3 │ Wire up routes │ lib/web/router.ex │
└───┴───────────────────────────────┴───────────────────┘
Proceed with execution? [y/n] y
Executing plan...
[1/3] Introduce auth types
Created branch: feature-auth-step-1
Claude Code: implementing...
Format: OK
Tests: PASS
Committed: step(1): Introduce auth types
[2/3] Add auth plug + config
Created branch: feature-auth-step-2
Claude Code: implementing...
Format: OK
Tests: PASS
Committed: step(2): Add auth plug + config
[3/3] Wire up routes
Created branch: feature-auth-step-3
Claude Code: implementing...
Format: OK
Tests: PASS
Committed: step(3): Wire up routes
No drift - stack matches original!
Done! Created branches:
- feature-auth-step-1
- feature-auth-step-2
- feature-auth-step-3
Architecture
- Python CLI (this tool): Orchestration, validation, git operations, test execution
- Claude Code: LLM work - planning, editing files, fixing failures
Python owns the source of truth for:
- Branch creation and naming
- Allowlist enforcement
- Running tests and format commands
- Committing changes
Claude Code never has permission to commit, push, or checkout branches.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pullsaw-0.1.0.tar.gz.
File metadata
- Download URL: pullsaw-0.1.0.tar.gz
- Upload date:
- Size: 53.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27d0979eb3c9c030453571553166e927e93c350e69ff4eb1f9979dd2435d4a80
|
|
| MD5 |
81de7e9e4995b0acd49fa8f5f1107dbc
|
|
| BLAKE2b-256 |
ed52acbeac50401e88fe5efe730398b5cd578ea08e52af0f240bc49d19aef31c
|
Provenance
The following attestation bundles were made for pullsaw-0.1.0.tar.gz:
Publisher:
publish.yml on simedw/pullsaw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pullsaw-0.1.0.tar.gz -
Subject digest:
27d0979eb3c9c030453571553166e927e93c350e69ff4eb1f9979dd2435d4a80 - Sigstore transparency entry: 771718669
- Sigstore integration time:
-
Permalink:
simedw/pullsaw@98eea765b993c70fc7c2cc62774999a395e7d82f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/simedw
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@98eea765b993c70fc7c2cc62774999a395e7d82f -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file pullsaw-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pullsaw-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eeb1bf2edff9b0a9f380b0a1762325516c148ee647bb12ef38cff2a48de3218e
|
|
| MD5 |
cc9f80c388cf4349b6447d76a0e15bff
|
|
| BLAKE2b-256 |
69aabd8490bbd4871df849755e8d3b4250b5753ae79abb888f141a4d5d583ac5
|
Provenance
The following attestation bundles were made for pullsaw-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on simedw/pullsaw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pullsaw-0.1.0-py3-none-any.whl -
Subject digest:
eeb1bf2edff9b0a9f380b0a1762325516c148ee647bb12ef38cff2a48de3218e - Sigstore transparency entry: 771718673
- Sigstore integration time:
-
Permalink:
simedw/pullsaw@98eea765b993c70fc7c2cc62774999a395e7d82f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/simedw
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@98eea765b993c70fc7c2cc62774999a395e7d82f -
Trigger Event:
workflow_dispatch
-
Statement type: