Create a pull request
Open a pull request for the current branch on whichever platform the repository lives on, with a title and description written from the commits that belong to this branch alone. A finished result is a PR the reviewer can act on without asking what changed or why — never an empty template.
When to use
- The user asks to open, create, raise, submit, or draft a PR or MR.
- The user asks to "put this up for review" or "send this to review".
Do not use this for pushing a branch, writing a commit message, or reviewing an existing PR — do those directly.
Instructions
1. Establish the base branch and how the PR will be created
Find out up front whether a CLI is available, so you learn how this PR will be opened before spending the work of drafting it — not after:
git remote get-url origin # which platform
command -v gh || command -v glab || command -v az
If none is available, or the host is unrecognised, the fallback in step 5 is what will run. Say so now rather than at the end: the user may want to install and authenticate the CLI first, and that is much cheaper to hear before the description is written than after.
The default branch is often but not always main. Detect it rather than
assuming:
git symbolic-ref --quiet --short refs/remotes/origin/HEAD | sed 's@^origin/@@'
# if that prints nothing:
git remote show origin | sed -n 's/.*HEAD branch: //p'
Then check whether this branch is stacked on another unmerged branch. Any branch
that is an ancestor of HEAD but not of the default branch is a candidate base:
BASE=<default branch>
CURRENT=$(git branch --show-current)
for b in $(git for-each-ref --format='%(refname:short)' refs/heads refs/remotes); do
name="$b"
for r in $(git remote); do name="${name#$r/}"; done # origin/feat-a -> feat-a
[ "$name" = "$CURRENT" ] || [ "$name" = "$BASE" ] && continue
git merge-base --is-ancestor "$b" HEAD 2>/dev/null || continue
git merge-base --is-ancestor "$b" "$BASE" 2>/dev/null && continue
echo "$(git rev-list --count "$BASE..$(git merge-base "$b" HEAD)") $name"
done | sort -rn | awk '!seen[$2]++'
Stripping the remote prefix and deduplicating matters: without it a two-branch
stack reports four candidates, because the local branch and its remote-tracking
ref are both ancestors of HEAD.
If that prints nothing, the default branch is the base. If it prints candidates,
the branch is stacked. Each line is <commits ahead of base> <branch>, sorted
so the first line is the likely base — the candidate whose merge-base with
HEAD is furthest from the default branch, which is the nearest parent in the
stack. Deeper stacks print their whole chain, nearest first.
Tell the user what you found and confirm the base before continuing — targeting the wrong base puts someone else's unmerged commits in this PR. Show them the difference concretely, since it is the whole point:
git log --oneline --no-merges "$BASE..HEAD" # what a base-branch PR would contain
git log --oneline --no-merges "<candidate>..HEAD" # what a stacked PR would contain
2. Read only this branch's work
git log --no-merges --reverse "$BASE..HEAD" # the commits to describe
git diff --stat "$BASE...HEAD" # scope of the change
git diff "$BASE...HEAD" # the change itself
Use three dots for diffs: it compares against the merge base, so commits that landed on the base branch after this one was cut are excluded. Ignore merge commits from the base branch — they are not this branch's work.
Read the actual diff before writing the description. The commit messages tell you what the author intended; only the diff tells you what the PR does.
3. Draft the title and description
Follow ~/.claude/skills/docs/pr-template.md (repo path: docs/pr-template.md)
for the title format and every section's guidance. If the repository has its own
template — .github/pull_request_template.md, .gitlab/merge_request_templates/,
or docs/pull_request_template.md — that one wins; fill its sections instead.
- Ticket ID — take it from the branch name (
feature/SEG-7777-retry-refresh) or from the commit messages. If none is present, ask the user for one; if there is genuinely no ticket, drop that segment rather than inventing a placeholder. - Descriptor — infer it from the diff and state your choice to the user.
- Testing instructions — derive them from the tests in the diff and from how the changed code is actually exercised. Ask the user rather than inventing steps you cannot verify, and if the change is untestable, say so and why.
- Risk, splash zone, related links — fill from the diff. Leave a section out
entirely if it does not apply; never ship a heading with
N/Aunder it.
4. Confirm before creating
Show the user the resolved base branch, the final title, and the full description. Wait for approval. Do not create the PR until they approve it, and do not push to a branch that has no upstream without saying so first.
5. Push and create
Push the branch, then create the PR with the platform's CLI. Write the description to a file in the scratchpad and pass it by path — bodies passed inline break on backticks, quotes, and newlines.
git push -u origin HEAD
| Remote host | Command |
|---|---|
| github.com | gh pr create --base "$BASE" --title "$TITLE" --body-file "$BODY" |
| gitlab.com or self-hosted GitLab | glab mr create --target-branch "$BASE" --title "$TITLE" --description "$(cat "$BODY")" |
| dev.azure.com, visualstudio.com | az repos pr create --target-branch "$BASE" --title "$TITLE" --description "$(cat "$BODY")" |
| bitbucket.org | No first-party CLI. Fall back below. |
Identify the host with git remote get-url origin. Add --draft (glab: also
--draft) when the user asks for a draft.
If the CLI is missing, unauthenticated, or the host is unrecognised, do not guess at an API call. Fall back to handing the user everything they need to open it themselves:
- Push the branch anyway (with the same confirmation as above). The compare URL resolves to nothing until the branch exists on the remote, so skipping the push makes the rest of the fallback useless.
- Give them the compare URL for their host — for GitHub,
<repo-url>/compare/<base>...<branch>?expand=1. Derive the web URL from the remote, stripping any trailing.gitand converting an SSH remote (git@host:owner/repo.git) tohttps://host/owner/repo. - Give them the title, and the path to the body file so they can paste it without it being mangled by the terminal. Offer to print it inline instead.
Report the PR URL when the CLI path succeeds.
Guidelines
- Creating a PR is outward-facing and visible to other people. Confirm first, every time, even when the user's request was enthusiastic.
- Never open a PR from the default branch. If
HEADis the base branch, stop and offer to move the commits to a new branch. - Check for uncommitted changes first and tell the user if the working tree is dirty — they may have meant to include that work.
- If
$BASE..HEADis empty, there is nothing to open a PR for. Say so. - Describe what the branch does, not what each commit did. A reviewer reads the PR, not the commit log.
- Do not claim a change was tested unless you ran the tests or the user said so.