Skip to content
Development
Skill

/create-pr

Create or update GitHub PR with gh CLI. Auto-extracts ticket ID from branch name, generates title/summary from commits. Auto-detects existing PR and switches to update mode. Supports --stack for stacked PR chains (per-layer PRs with chained bases; never executes push/rebase).

From plugin
sd0x-dev-flow
18899 skills16 agents5 hooks
Install
$ npx -y skills add sd0xdev/sd0x-dev-flow --skill create-pr --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/create-pr

Context preview

The summary Claude sees to decide when to auto-load this skill.

Create or update GitHub PR with gh CLI. Auto-extracts ticket ID from branch name, generates title/summary from commits. Auto-detects existing PR and switches to update mode. Supports --stack for stacked PR chains (per-layer PRs with chained bases; never executes push/rebase).

SKILL.md

create-pr.SKILL.md
name: create-pr
description: "Create or update GitHub PR with gh CLI. Auto-extracts ticket ID from branch name, generates title/summary from commits. Auto-detects existing PR and switches to update mode. Supports --stack for stacked PR chains (per-layer PRs with chained bases; never executes push/rebase). Default: --dry-run (show command, don't execute). Use when: user asks to open/create/update a PR, says /create-pr, wants a stacked PR chain, wants to refresh PR description after new commits, or says 'update pr', 'update PR title', 'refresh PR body'."
allowed-tools: Bash(git:*), Bash(gh:*), Bash(mktemp:*), Bash(rm:*), Bash(bash:*), Read, Write, Grep, Glob, AskUserQuestion

Create PR

Input

`/create-pr [--head <branch>] [--base <branch>] [--title <title>] [--stack <branch...>] [--update] [--execute] [--dry-run]`

  • `--head`: Source branch (default: current branch)
  • `--base`: Target branch (default: `{TARGET_BRANCH}` or `main`)
  • `--title`: Override auto-generated title (**rejected** with `--stack`)
  • `--stack`: Stacked PR chain mode, bottom layer first (mutually exclusive with `--head`; the bottom layer's base follows the same `--base` / `{TARGET_BRANCH}` / `main` resolution as normal mode) — see [Stacked PR Mode](#stacked-pr-mode)
  • `--update`: Force update mode (re-generate title/body for existing PR)
  • `--dry-run`: Show command without executing (default). Scopes to mutating `gh` calls — the Step 1 `git fetch --prune origin` still runs, so the preview reflects the server's real refs
  • `--execute`: Actually create/update the PR (requires user confirmation)
  • No args: use current branch → default target, dry-run mode. Auto-detects existing PR → update mode

Workflow

0. Mode Dispatch (first, before anything else)

**When `--stack` is present**: read `references/stack-mode.md` and run Phases A–D from there. Skip generic Steps 1, 5, 6 and 7 entirely — Phase A must run `git fetch --prune origin` and classify sync state *before* any PR planning, so the generic `ls-remote` / local `base..head` path must not run first. What is reused, per layer, exactly as Phase C directs:

| Reused | Skipped | |--------|---------| | Steps 2–4 content generation (ticket ID, title, body) | Step 1 gather — Phase A replaces it | | Step 4b sanitization, Step 7b post-creation verify | Step 5 pre-flight + mode detection — Phase A/B replace it | | **Step 5a's smart-diff update logic** — the routing decision is Phase B's, the diff-and-update mechanics are Step 5a's | Steps 6–7 single-PR output/execute — Phase C emits per-layer commands instead |

Otherwise continue with Step 1 below.

1. Gather Info

The fetch runs **first and alone** — the ref-range reads below it (`git log` and `git diff` over `refs/remotes/origin/*`) depend on the remote-tracking refs it refreshes, so it cannot join the parallel batch. It runs even in `--dry-run` (the dry-run promise at the top scopes to mutating `gh` calls; refreshing and pruning local remote-tracking refs is how the preview describes the commits that would actually ship). `ls-remote` in the next fence only LISTS the server's refs — it never updates `refs/remotes/*` — so without this fetch they can be missing or stale (another clone pushed, or this one never fetched) and the PR body would describe old commits. Same exact form and same discipline as stack mode's Phase A: the explicit exit keeps a failed fetch from being followed by reads of stale refs.

**Emit this line in the turn that runs the fetch**, before the fence — `rules/git-workflow.md` lists `status | diff | log | branch | rev-parse` as allowed and `git fetch` is in neither that list nor the forbidden one, which makes it a Default-tier deviation, and a deviation is declared per run, not once during development (same shape as [stack-mode.md § Phase A](references/stack-mode.md); the stated reason differs because the fetch serves PR-body range generation here, sync classification there):

[DEVIATION] rule=rules/git-workflow.md § allowed ops default=fetch is not in the allowed list chosen=git fetch --prune origin
reason=the PR body is generated from origin/<base>..origin/<head>; without the fetch every range is computed from stale remote-tracking refs signal=fetch is absent from the forbidden closed set (add|commit|push|stash|reset --hard|rebase) and writes only remote-tracking refs — no working tree, no history
git fetch --prune origin || exit "$?"

The rest are independent — run them in parallel:

# Current branch
git rev-parse --abbrev-ref HEAD

# Remote repo (owner/repo)
gh repo view --json nameWithOwner --jq '.nameWithOwner'

# Check if head branch is pushed
git ls-remote --heads origin -- 'feat/PROJ-42-add-widget'

# Check existing PR
gh pr list --head 'feat/PROJ-42-add-widget' --base 'main' --json number,title,state

# Commits between base..head
git log --oneline 'refs/remotes/origin/main..refs/remotes/origin/feat/PROJ-42-add-widget'

# Full diff for summary
git diff 'refs/remotes/origin/main...refs/remotes/origin/feat/PROJ-42-add-widget' --stat

2. Extract Ticket ID

From branch name, extract ticket ID using `{TICKET_PATTERN}` (default: `[A-Z]+-\d+`):

| Branch Pattern | Ticket ID | |----------------|-----------| | `fix/PROJ-520` | `PROJ-520` | | `fix/PROJ-520-2` | `PROJ-520` | | `feat/PROJ-123-some-desc` | `PROJ-123` | | `refactor/PROJ-999` | `PROJ-999` |

Regex: first match of `{TICKET_PATTERN}` — take first match. Strip trailing `-N` suffixes.

3. Generate Title

Format: `<type>: [<TICKET>] <concise summary>`

  • `<type>`: from branch prefix (`fix/` → `fix`, `feat/` → `feat`, `docs/` → `docs`, `refactor/` → `refactor`)
  • `<TICKET>`: extracted ticket ID (omit if none found)
  • `<concise summary>`: summarize commits in <60 chars, focus on main changes

4. Generate Body

## Summary

<3-5 bullet points summarizing changes from commits>

## Ticket

[<TICKET>]({ISSUE_TRACKER_URL}<TICKET>)

## Test plan

- [ ] <test items based on what chan
Read more
Ships withsd0x-dev-flow

Language: English | 繁體中文 | 简体中文 | 日本語 | 한국어 | Español The harness layer for Claude Code. Let the model choose the path. Keep "done" verifiable. Full control plane on Claude Code. Skills-only distribution for Codex CLI and other compatible agents.

Get the whole plugin

Other skills on sd0x-dev-flow.