Reads a .split-plan.md file (from split-plan), clones the repository at the source branch, then iteratively executes each split: checkout the target branch (existing PR branch or a new branch from base), cherry-pick the listed commits, push, and create a new pull request when the plan says "New PR". Processes one split at a time so you can fix conflicts or stop between splits.
- Automate the plan: split-plan only writes the plan; split-exec performs the git operations and opens PRs so you don't have to cherry-pick and push by hand.
- Iterative/slow: One split at a time. If a cherry-pick conflicts, the tool stops and tells you where; you resolve in the workdir and can re-run or edit the plan. WHY: Batch execution would leave the repo in a hard-to-recover state on the first conflict.
- New PRs only: When the plan says "New PR:
branch-name", split-exec creates that branch from the target base, cherry-picks, pushes, and opens a new PR via the GitHub API. - Route-to is disabled: "Route to: PR #42" splits are rejected at runtime. Modifying an existing PR's branch (cherry-pick + push) is too risky — if anything goes wrong the PR is corrupted. Use "New PR" splits instead and close the old PR after merge.
- Target branch must exist on the remote. split-exec checks this before cloning (fail-fast). If the plan's
target_branch(e.g.staging) does not exist on the repo, create it first or change the plan. You can run split-exec from any directory; it clones the plan's repo (fromsource_pr) into the workdir.
- Plan file: Path to a
.split-plan.mdproduced by split-plan (or edited by hand). The file must have YAML frontmatter withsource_pr,source_branch,target_branch, and a ## Split section with### N. Title, New PR:branch-name, optional PR title: or Title: (used for commit message and GitHub PR title — use repo-style e.g.feat: add PRR workflow), Files: (recommended) or Commits: for each split. - Rewrite plan (optional): When present (e.g.
.split-rewrite-plan.yamlbeside the group plan, or--rewrite-plan <path>), split-exec runs ordered ops per split (cherry-pick or commit-from-sha) on a rebuild branch (e.g.feature/logging-rebuild). When absent, split-exec does the bare minimum: one commit per split from Files: (or cherry-picks from Commits:).
- Group plan — Run split-plan (or edit by hand) to produce
.split-plan.mdwith splits and Files: per split. - Rewrite plan — Run split-rewrite-plan to analyze the repo and produce
.split-rewrite-plan.yamlwith ordered ops per split (so each split branch gets a linear history that only touches that split’s files). Requires a clone; use-w .split-exec-workdirto reuse the same workdir as split-exec if desired. - Execution — Run split-exec; if a rewrite plan is present it executes those ops and pushes to rebuild branches (e.g.
newBranch-rebuild). When satisfied, run with--promoteto force-push each rebuild branch over the original branch.
WHY rebuild branches: Building clean history on a new branch (e.g. feature/logging-rebuild) keeps the original branch untouched until you verify the result. Only when you run with --promote does split-exec force-push rebuild to original.
Staleness: If the rewrite plan’s source_tip_sha does not match the current source branch tip, split-exec fails and tells you to re-run split-rewrite-plan. WHY: Applying an outdated plan to a different source state would replay the wrong commits; failing is safer than silently producing wrong branches.
Empty splits: Splits that have no ops in the rewrite plan are skipped with a one-line warning. WHY: The generator still emits them (empty ops array) so the rewrite plan's split list matches the group plan; the executor skips so we don't push an empty branch.
No matching rewrite entry: If the rewrite plan has no split whose branchName matches a group-plan split's New PR: (e.g. typo or generator omitted it), split-exec skips that split with a warning instead of falling back to file-based. WHY: When a rewrite plan is loaded we only run rewrite ops and only push to rebuild branches; falling back for one split would push to the original branch name and mix strategies.
# Execute the plan (clone, cherry-pick, push, create PRs)
split-exec .split-plan.md
# Use a custom workdir (default: .split-exec-workdir in cwd)
split-exec .split-plan.md --workdir /tmp/split-work
# Dry run: parse plan and print what would be done; no clone/push
split-exec .split-plan.md --dry-run
# Force-push when remote has diverged (overwrite remote branches)
split-exec .split-plan.md --force-push
# With rewrite plan (uses .split-rewrite-plan.yaml beside plan if present; pushes to branch-rebuild)
split-exec .split-plan.md --rewrite-plan .split-rewrite-plan.yaml
# After verifying rebuild branches, promote them over the original branches (force-push)
split-exec .split-plan.md --rewrite-plan .split-rewrite-plan.yaml --promote
# Run pill analysis on the output log when the run finishes
split-exec .split-plan.md --pill
# Verbose
split-exec .split-plan.md -v| Option | Default | Description |
|---|---|---|
-w, --workdir <dir> |
.split-exec-workdir |
Directory used for the git clone and cherry-picks. WHY: So you can inspect or fix the repo between runs; default is in cwd so it's easy to find. |
-n, --dry-run |
off | Parse the plan and print each split (commits, route-to or new-PR); do not clone, cherry-pick, or push. |
-y, --yes |
off | Reserved for future per-split confirmation; currently unused. |
-v, --verbose |
off | Verbose logging. |
--force-push |
off | On push rejection (remote has newer commits), force-push to overwrite the remote branch. WHY: Use when re-running a plan and your local split branches are the source of truth (e.g. after fixing commits). |
--rewrite-plan <path> |
(beside plan) | Path to rewrite plan (.split-rewrite-plan.md / .yaml / .json). If unset, looked up beside the group plan file. When present, split-exec runs ordered ops and pushes to rebuild branches. WHY optional: Without a rewrite plan, split-exec still works (one commit per split from Files:); the rewrite plan adds phased history when you run split-rewrite-plan first. |
--rebuild-suffix <suffix> |
-rebuild |
Suffix for rebuild branch when using a rewrite plan (e.g. feature/logging → feature/logging-rebuild). WHY: So the original branch name is only overwritten when you run --promote; you can inspect the rebuild PR first. |
--promote |
off | When using a rewrite plan, after building rebuild branches, force-push each rebuild branch over the original branch. WHY: Use only when satisfied with the rebuild; this overwrites the original branch so the new history becomes the canonical one. |
--pill |
off | When the run finishes, run pill analysis on the output log and append to pill-output.md / pill-summary.md. WHY: Split-exec has no LLM calls; passing --pill lets you still get operational improvement suggestions from the log. |
| Code | Meaning |
|---|---|
0 |
Success: one or more splits were pushed (or dry-run completed). |
2 |
All executed splits were already up-to-date (remote had the commits; nothing pushed). Use this to distinguish "no work done" from "changes pushed". |
| non-zero | One or more splits failed (e.g. cherry-pick conflict, push failed). |
When a rewrite plan is present, split-exec runs only the rewrite plan’s ops (cherry-pick or commit-from-sha) per split and pushes to rebuild branches (e.g. newBranch-rebuild). When no rewrite plan is present:
- New PR (file-based, recommended): If the split lists Files:, the tool copies only those files from the source branch into the new branch and creates one new commit (split title). No cherry-pick — so each PR contains only the intended changes (e.g. workflow-only or ticker-only).
- New PR (cherry-pick): If the split lists only Commits:, the tool cherry-picks those commits in order. Note: cherry-pick is all-or-nothing per commit; a commit that touches multiple areas will bring all changes into that PR.
- Route to PR #N: Disabled. Use "New PR" and close the old PR after merge.
PR titles: The GitHub PR title (and the file-based commit message) use PR title: or Title: from the plan when present; otherwise the split heading (e.g. ### 1. PRR Workflow Infrastructure). To match the repo's style (e.g. conventional commits like chore: add PRR workflow), add a line such as - **PR title:** \chore: add PRR workflow`` to each split.
If any cherry-pick fails (e.g. conflict), the tool aborts the cherry-pick, prints the workdir path, and exits. You can resolve conflicts in the workdir, then re-run (the tool will re-clone or you can skip that split by editing the plan).
Requires GITHUB_TOKEN with repo scope (clone, push, create PR). Same as prr; see root README and .env.example. No LLM is used.
Token check: Before cloning, split-exec verifies the token by calling the GitHub API (getDefaultBranch). If the token is invalid or the account has no access to the repo, you get a clear error and no splits run. WHY: Failing early avoids clone + first-push failure with a raw "Authentication failed" from git.
Push auth: Push uses the same URL format as the initial git ls-remote check (https://${token}@...) so clone and push behave consistently. If push still fails with "Invalid username or token", create a new token at https://github.com/settings/tokens with repo scope and set GITHUB_TOKEN in .env.
- Each split prints a one-line status (
[n] Pushedor[n] Already up-to-date) and, when a PR exists, the PR URL on the next line (in cyan). - The final summary repeats all PR URLs in one line: Open PRs: (when all splits were already up-to-date) or PRs: (when some were pushed). WHY: So you can copy links from one place even when skimming the log.
- split-exec-output.log — Console output tee (same directory as run, or
PRR_LOG_DIR). WHY prefix: So split-exec and split-plan/prr don't overwrite each other's logs.
The target branch is checked once upfront (before cloning). If it's missing, you get one error and no splits run. The plan's target_branch (e.g. staging) is not present on the remote. Either:
- Create the branch on the remote: e.g. push an initial commit to
stagingfrom another clone, or create it in the GitHub UI. - Change the plan: Edit
.split-plan.mdfrontmatter and settarget_branchto an existing branch (e.g.main). - Manual fetch (if the branch exists but ls-remote failed): From the workdir printed in the error, run:
cd <workdir> && git fetch origin <target-branch>.
The rewrite plan’s source_tip_sha does not match the current source branch tip (e.g. new commits were pushed). Re-run split-rewrite-plan to generate a new rewrite plan from the current source, then run split-exec again.
This happens when the remote branch was modified after the plan was generated (e.g. someone pushed to the same branch). The tool fetches and rebases; if there are conflicts it tries to resolve them (e.g. .github/workflows/ files via checkout --theirs). If resolution fails or conflicts are in other files:
- Re-run with
--force-pushto overwrite the remote branch (use when your split is the source of truth). - Resolve manually: The error message includes the workdir and a
git add ... && git rebase --continuecommand. Resolve the conflicted files in that workdir, then run the command and push (or re-run split-exec). - Re-generate the plan if the remote branch has diverged enough that merging no longer makes sense.