rafa pages
- rafa
- rafa pr
rafa pr
The pull request of a branch: show it, open, retarget, close, list, wait on, merge and triage it, all through the GitHub CLI.
rafa pr works on pull requests through the GitHub CLI. It shows the one on your branch, waits on its checks, explains a red one, and merges and cleans up after it.
Spends: 🪙 only triage --resolve, which runs a small fixed plan through the loop; plain triage spends nothing. Changes: open, retarget, close and merge write to GitHub; merge also switches your checkout to the base, pulls, and deletes both branches. Needs: gh signed in, and merge rights on the repository for merge; see Prerequisites.
Things to know before you use it:
mergeasks y/N first;--yesanswers for a script. A repository with no CI merges with--skip-checks.- While a loop of the project runs,
mergedoes not offerrafa self-update: the loop starts its sessions from the installed rafa. triage --resolverefuses a branch carrying a config module the base does not hold byte for byte, unless--allow-local-config.
Used by the workflows The cycle, rafa next (wait, triage, merge) and Stretches (stretch item --wait merges into the stretch branch).
In the target layout this code lives in the private package rafa-pr, bundled into @open-tomato/rafa (Repository layout). In 0.47.0 it is already a package, @open-tomato/rafa-pr in packages/rafa-pr, but only as a facade: it re-exports this code from the root package's src/, where the files still live until their move. You install and type the same either way. [verify: the release the move lands in]
Actions
| Action | Does |
|---|---|
current | show the open pull request of the current branch on one line |
show | show one pull request in detail, with its checks and its last triage |
view | open a pull request in the browser |
open | open a pull request from a pushed branch, or print the one already open on it |
retarget | move a pull request onto another base branch |
close | close a pull request without merging it |
list | list the open pull requests with their checks, mergeability and release forecast |
wait | wait for one pull request's checks to settle, and answer what they came to |
merge | merge a pull request and clean up both branches |
triage | assess a pull request: its class, the evidence, and a follow-up prompt 🪙 with --resolve |
rafa pr current
Show the open pull request of the current branch on one line.
rafa pr current
Reads the open pull request whose head is the branch checked out at the project root, and prints one line carrying its number, title, state, the verdict over its checks, and its URL. A part the provider answered nothing for is left out, so a pull request that only answers a URL prints as that URL alone. When the checks cannot be read at all the verdict is left out and what the GitHub CLI said is warned about, where a pull request with no checks prints checks none. With --output=json the branch, the line, the parts it was built from and any checks problem are the data of the terminal result event. Refuses with exit code 2 where pr.provider is not gh.
Examples:
rafa pr current: Prints one line for the open pull request of the branch checked out at the project root.rafa pr current --output=json: Writes a start event, then a result event holding the branch, the line and each part of it.
Output formats: text, json.
rafa pr show
Show one pull request in detail, with its checks and its last triage.
rafa pr show [n]
Reads one pull request in full and prints its number and title, its state, author, head branch, base branch and mergeability, its URL, the issues it closes, every check with its state and its link, and the last rafa triage comment made on it. The closes line marks an issue the provider links that the body names by no closing keyword, and an issue the body names by keyword that the provider does not link. Without a number it reads the open pull request whose head is the branch checked out at the project root. The checks and the triage comment are read separately, and one that cannot be read says so in its own section rather than refusing the command, where a pull request with no checks reads checks none. With --output=json the data of the terminal result event holds the fields detail, bodyCloses, checks, checksProblem, triage, triageProblem, text: the pull request, the issues its body closes by keyword, the checks, the triage and the rendered text, each read that failed carrying its problem. Refuses with exit code 2 where pr.provider is not gh.
| Argument | Type | What it is |
|---|---|---|
n | number | The pull request number. The open pull request of the current branch when it is left out. |
Examples:
rafa pr show: Shows the open pull request of the branch checked out at the project root.rafa pr show 41: Shows pull request 41, whatever branch is checked out.rafa pr show 41 --output=json: Writes a start event, then a result event holding the pull request, its checks and its last triage.
Output formats: text, json.
rafa pr view
Open a pull request in the browser.
rafa pr view [n]
Hands one pull request to the GitHub CLI to open in the browser, and writes one line naming what was opened. Without a number it opens the open pull request whose head is the branch checked out at the project root, and the line carries that pull request's URL; with a number it opens that pull request and names it alone, reading nothing else about it. A GitHub CLI that could not open it — absent, unauthenticated, or holding no such pull request — refuses with exit code 1 carrying what it said. With --output=json the number, where it came from, the branch, the URL and the line are the data of the terminal result event. Refuses with exit code 2 where pr.provider is not gh.
| Argument | Type | What it is |
|---|---|---|
n | number | The pull request number. The open pull request of the current branch when it is left out. |
Examples:
rafa pr view: Opens the open pull request of the branch checked out at the project root.rafa pr view 41: Opens pull request 41, whatever branch is checked out.rafa pr view 41 --output=json: Writes a start event, then a result event naming the pull request that was opened.
Output formats: text, json.
rafa pr open
Open a pull request from a pushed branch, or print the one already open on it.
rafa pr open [--head=<string>] [--base=<string>] [--title=<string>]
[--body-file=<string>]
Opens a pull request from the branch --head into the branch --base, titled --title, its body what the file --body-file holds, and prints its number, title, branches and URL. The head is a branch already pushed: nothing is pushed for it. When the head already has an open pull request, that pull request is printed and none is opened, with a warning when it is into another base. Exit code 1, with nothing opened, for a flag left out or blank, a head equal to the base, and a body file that cannot be read or holds nothing but whitespace. With --output=json whether it opened one, the two branches and the pull request are the data of the terminal result event. Refuses with exit code 2 where pr.provider is not gh. Starts no session.
| Flag | Type and default | What it does |
|---|---|---|
--head | string | The branch to open the pull request from, already pushed. |
--base | string | The branch to open the pull request into. |
--title | string | The title of the pull request. |
--body-file | string | The file holding the body, as markdown; read from the directory rafa runs in. |
Examples:
rafa pr open --head=feat/rafa-9 --base=stretch/9 --title="rafa-9: one item" --body-file=body.md: Opens feat/rafa-9 into stretch/9, or prints the pull request already open on feat/rafa-9.rafa pr open --head=feat/rafa-9 --base=main --title="rafa-9" --body-file=body.md --output=json: Gives whether one was opened, the two branches and the pull request as the terminal result.
Output formats: text, json.
rafa pr retarget
Move a pull request onto another base branch.
rafa pr retarget <n> [--base=<string>]
Moves the pull request <n> onto the branch --base and prints one line naming it and both bases. A pull request already on that base is left alone: nothing is sent, a line says so, and the exit code is 0. Exit code 1, with nothing sent, for a line naming no number or more than one, a --base left out or blank, and a pull request the repository does not hold, and 1 for an edit the provider refused. With --output=json the number, whether it was retargeted and the two bases are the data of the terminal result event. Refuses with exit code 2 where pr.provider is not gh. Starts no session.
| Argument | Type | What it is |
|---|---|---|
n | number, required | The number of the pull request to retarget. |
| Flag | Type and default | What it does |
|---|---|---|
--base | string | The branch to move the pull request onto. |
Examples:
rafa pr retarget 41 --base=stretch/9: Moves #41 onto stretch/9, or says it is already there.rafa pr retarget 41 --base=main --output=json: Gives the number, whether it was retargeted and both bases as the terminal result.
Output formats: text, json.
rafa pr close
Close a pull request without merging it.
rafa pr close <n> [--reason=<string>] [--delete-branch] [--yes]
Closes the open pull request <n> without merging it, leaving --reason as its closing comment. It prints the title, the head branch and the issues a merge would have closed, which stay open, then asks Close #<n> without merging? [y/N]; --yes skips the question, and without a terminal and without --yes it refuses. An answer that is not yes closes nothing and the exit code is 0. The head branch stays on the remote unless --delete-branch is on the line. Exit code 1, with nothing closed, for a line naming no number or more than one, a --reason left out or blank, a pull request the repository does not hold, one already merged and one already closed, and 1 for a close the provider refused. With --output=json the number, the title, the head branch, those issues, whether it was closed and whether the branch was deleted are the data of the terminal result event. Refuses with exit code 2 where pr.provider is not gh. Starts no session.
| Argument | Type | What it is |
|---|---|---|
n | number, required | The number of the pull request to close. |
| Flag | Type and default | What it does |
|---|---|---|
--reason | string | Why the pull request is closed unmerged. Posted as its closing comment. |
--delete-branch | boolean | Delete the head branch with the close, on the remote and in this checkout. The branch stays when it is left out. |
--yes | boolean | Close without asking. Needed where standard input is no terminal. |
Examples:
rafa pr close 41 --reason="Replaced by #52 on the new base.": Shows #41 and the issues that stay open, asks, then closes it with that comment.rafa pr close 41 --reason="Replaced by #52." --delete-branch --yes: Closes #41 without asking and deletes its head branch.rafa pr close 41 --reason="Replaced by #52." --yes --output=json: Gives the number, the title, the head, the issues left open and both outcomes as the terminal result.
Output formats: text, json.
rafa pr list
List the open pull requests with their checks, mergeability and release forecast.
rafa pr list
Lists every open pull request of the repository at the project root, one row each carrying its number, its title, the branch it is from, how long ago it last moved, the verdict over its checks and whether it merges. The checks and the mergeability are read per pull request, so the command sends one GitHub CLI command for the list and two more for each row; a read that failed leaves its cell as ? and says what the CLI reported on a line under the table rather than refusing the whole list. A pull request GitHub ran nothing for reads checks none. Where the release is on, a last column shows the release forecast the pull request body carries, marked (base moved) when the base branch, as last fetched, holds another version or other waiting fragments than the forecast was computed against, with a line under the table naming both; it fetches nothing and sends no further command. With --output=json every row and the rendered table are the data of the terminal result event. Refuses with exit code 2 where pr.provider is not gh.
Examples:
rafa pr list: Lists the open pull requests of the repository at the project root.rafa pr list --output=json: Writes a start event, then a result event holding every row and the rendered table.
Output formats: text, json.
rafa pr wait
Wait for one pull request's checks to settle, and answer what they came to.
rafa pr wait [n] [--timeout=<string>] [--no-hint]
Polls the checks of one pull request until they settle or the deadline passes, printing a line per poll, and then prints the verdict with every check under it. Without a number it waits on the open pull request whose head is the branch checked out at the project root. It writes nothing: no comment, no label, no merge and no repair session. A pull request with no check at all is polled again while its head commit is less than two minutes old, since a push's checks take some seconds to register, and is answered at once when the head is older. Green exits 0; a red pull request and one with no check registered exit 1, and the report says the pull request does not merge cleanly only when it reads as conflicting; a deadline that passes with checks still running exits 3. The report is printed once: with --output=json a green wait gives the verdict, the checks, the polls and the rendered text as the data of the terminal result event, and every other ending carries the report as the message of that event instead. Refuses with exit code 2 where pr.provider is not gh.
| Argument | Type | What it is |
|---|---|---|
n | number | The pull request number. The open pull request of the current branch when it is left out. |
| Flag | Type and default | What it does |
|---|---|---|
--timeout | string | How many minutes to wait before giving up. 20 when it is left out. |
--hint | boolean, default true | Ends by naming the one step that follows, asking it where there is a terminal to answer on; --no-hint reads nothing and prints nothing. |
Examples:
rafa pr wait: Waits on the open pull request of the branch checked out at the project root.rafa pr wait 41 --timeout=5: Waits up to five minutes on pull request 41, then exits 3 if its checks are still running.rafa pr wait 41 --output=json: Writes a start event, a line per poll, then a result event holding the verdict and the checks when they are green, and the report as its error message when they are not.
Output formats: text, json.
rafa pr merge
Merge a pull request and clean up both branches.
rafa pr merge [n] [--yes] [--skip-checks] [--method=<string>] [--no-hint]
Merges one pull request through the GitHub CLI and then, in code, switches to the base branch, pulls it fast-forward only, deletes the head branch locally and on the remote where it is still there, and prunes, reporting each step; a head branch this checkout has no local branch for is reported as a skipped local delete, and the rest still run. Refuses before it asks anything on a tracked change in the working tree or an untracked path the pull request's head or the base's remote-tracking branch holds, on a pull request that is not green or does not merge, and on a head branch checked out in another worktree, except a clean, ended loop worktree under loop.worktreeDir, which it removes after copying its two plan files out. It shows the pull request, its branches and the method and asks Merge? [y/N]; --yes skips the question, and without a terminal and without --yes it refuses. A pull request that reports no checks at all is refused unless --skip-checks is given, which is refused on any pull request that does report checks; with it the command reads how many workflows the repository defines, prints a warning for that case, asks Merge #<n> with no checks? [y/N] (which --yes answers only where the repository defines no workflow, or where no workflow file on the base has a pull_request trigger naming it), and after the merge posts one comment on the pull request saying so. Where the release runs, the release guard reads the branch before the question: a collision is refused unless dangerous.acceptVersionCollision is true, and pr.versionCollision sets whether a missing or stale branch merges silently, with a warning, after its own question, or not at all. A step that fails never undoes the merge: it prints what is left as commands to paste and exits 1. After the merge it ticks the Closes #<n> line of every issue the pull request closes, first on the checklist of the open epic that issue's epic: label names, printing one line per epic, then on every open board whose checklist lists it, or on the roadmap issue while no issue carries type:roadmap, warning rather than failing when one of those writes does not land. It ends by reading every open issue whose "Blocked by:" line names an issue this pull request closes, asking whether to remove spec:blocked from each one whose blockers have all closed; --yes does not answer that question, and every failure of that reading is a warning. With board.relationships set to native it ticks no epic checklist and, in place of that reading, prints the open issues the merge freed, whose blockers GitHub clears by itself, asking nothing and writing nothing. With --output=json the pull request, the method, the steps that ran, the follow-ups, the roadmap tick and the unblock reading, or the freed issues in native mode, are the data of the terminal result event; the tick of each closed issue's epic checklist is one line per epic and no key of that data, and under native there is none. Refuses with exit code 2 where pr.provider is not gh.
| Argument | Type | What it is |
|---|---|---|
n | number | The pull request number. The open pull request of the current branch when it is left out. |
| Flag | Type and default | What it does |
|---|---|---|
--yes | boolean | Merge without asking. Needed where standard input is no terminal. |
--skip-checks | boolean | Merge a pull request that reports no checks at all, after a warning and its own question. Refused where the pull request reports any check; --yes beside it is refused where the repository defines a workflow that may run on pull requests into the base, or its workflow count could not be read. |
--method | string | How to merge: squash, merge or rebase. pr.mergeMethod when it is left out. |
--hint | boolean, default true | Ends by naming the one step that follows, asking it where there is a terminal to answer on; --no-hint reads nothing and prints nothing. |
Examples:
rafa pr merge: Asks, then merges the open pull request of the branch checked out at the project root.rafa pr merge 41 --yes: Merges pull request 41 without asking, then runs the clean-up.rafa pr merge 41 --skip-checks: Warns that nothing on GitHub tested pull request 41, asks, merges it and comments that it had no checks.rafa pr merge 41 --method=rebase --output=json: Merges by rebase and writes a result event holding each clean-up step and the follow-ups.
Output formats: text, json.
rafa pr triage
Assess a pull request: its class, the evidence, and a follow-up prompt.
rafa pr triage [n] [--no-comment] [--resolve] [--allow-local-config]
[--max-attempts=<number>] [--no-hint]
Assesses one pull request in code — no session is started and no model is asked. It reads the pull request, its checks, the failing job logs and, for a head that does not merge, the conflicting files from git merge-tree, and classifies it as one of green, pending, conflict-lockfile, conflict-manifest, conflict-other, conflict-version (the release guard read a stamped version), ci-install, ci-lint, ci-types, ci-test or ci-other. It prints the class, the evidence it was read from with the failing log capped at 40 lines, and a follow-up prompt carrying all of it, so a session handed that prompt assesses nothing again. It leaves one triage comment per pull request and edits that comment on every later assessment; a pull request whose head has not moved since it was assessed is shown its stored triage and assessed again by nothing. Without a number it assesses the open pull request of the branch checked out at the project root; with no pull request there it takes the red ones, assessing one, or the two or three of them that moved in the last 72 hours, and listing more than three with the command for each. With --resolve a pull request whose class is simple is handed to the ordinary loop over the pinned plan for that class, in a worktree under ~/.rafa/worktrees/pr-<n> removed on success, each session capped at pr.resolveBudget; it waits on the checks after every attempt and, at --max-attempts or on an attempt ending as the one before it, updates the comment, removes the worktree, prints the follow-up prompt and exits 3. A conflict-version pull request is not handed to the loop: --resolve converts it in code, with no session and no attempt spent, turning the stamped changelog section into a release fragment and setting the version file and the changelog back to the merge base's in one commit it pushes to the branch, and exits 3 when the conversion or the push is refused. It reads the triage comment only from an author holding write access to the repository or listed in board.trustedAuthors, and ignores and reports one written by anybody else. A cross-repository pull request, more than one candidate, and under --resolve a pull request whose author is neither trusted nor a known dependency-bump bot, are refused with exit code 2; under --resolve a head carrying a rafa.config.ts or rafa.config.mjs that origin/<base> does not hold as the same file is refused with exit code 1 unless --allow-local-config is passed. With --output=json the selection, every reading, what --resolve came to and the rendered text are the data of the terminal result event. Refuses with exit code 2 where pr.provider is not gh.
Spends: 🪙 with --resolve: runs a small fixed plan through the loop
| Argument | Type | What it is |
|---|---|---|
n | number | The pull request number. The open pull request of the current branch when it is left out. |
| Flag | Type and default | What it does |
|---|---|---|
--comment | boolean, default true | Leave the triage as a comment on the pull request; --no-comment reads one and writes none. |
--resolve | boolean | Run the pinned plan for a simple class in a worktree of its own, waiting on CI after each attempt, or convert a conflict-version stamp into a release fragment with no session. Exits 3 when the attempt guard gives up or the conversion is refused. |
--allow-local-config | boolean | Under --resolve, run a head whose rafa.config.ts or rafa.config.mjs origin/<base> does not hold as the same file, printing one warning line; without it such a head is refused with exit 1. |
--max-attempts | number, default 2 | How many resolve runs a pull request gets. An assessment spends none and reports the count. |
--hint | boolean, default true | Ends by naming the one step that follows, asking it where there is a terminal to answer on; --no-hint reads nothing and prints nothing. |
Examples:
rafa pr triage: Assesses the open pull request of the branch checked out at the project root, or the red ones.rafa pr triage 41 --no-comment: Assesses pull request 41 and writes nothing to it, reading the triage comment already there.rafa pr triage 41 --output=json: Writes a result event holding the class, the evidence and the follow-up prompt.rafa pr triage 41 --resolve --max-attempts=3: Runs the pinned plan for a simple class in ~/.rafa/worktrees/pr-41, up to three attempts.
Output formats: text, json.