원클릭으로
create-pr
Create or update a GitHub PR with automatic template detection and filling
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Create or update a GitHub PR with automatic template detection and filling
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | create-pr |
| description | Create or update a GitHub PR with automatic template detection and filling |
| argument-hint | [--ready] [--force] [<title>] |
| model | sonnet |
Create (or update) a GitHub pull request using gh, auto-detecting and filling any PR template.
Write the PR body the way a staff engineer writes a Slack message to a colleague who reviews the code. One direct sentence per idea. No ceremony.
Three things immediately signal AI authorship. Never produce any of them:
**Test plan:**). Never bold error messages, key terms, or warnings mid-paragraph.**Root cause:**, **Caveat:**, **Note:**. Make it a real heading or fold the content into a sentence without a label.For everything else: plain verbs ("is" not "serves as"), Sentence case headings, short paragraphs. No inflation words (critical, pivotal, robust, seamless, leverage, utilize, ensure, facilitate). No hedging meta-commentary ("I'm an agent", "I have not verified"). No closers. No emoji.
How it sounds:
The workflow calls
getMembershipForUserInOrg()with the defaultGITHUB_TOKEN, which only has repo scope and can't read org-private team memberships. The API returns404(not403) when it can't see the membership, producing the misleading error.If the app doesn't have Organization > Members > Read permission, the same error will recur.
Notice two things about that example: the cause and effect are in one sentence connected by a comma, and the caveat is a plain declarative sentence with no label.
--ready: create the PR ready for review instead of as a draft (PRs are drafts by default)--force: skip preview and confirmation; create or update immediately<title>: optional title hint; if omitted, derive from commitsExample invocations:
/create-pr/create-pr --ready/create-pr --force/create-pr Add support for webhook retries/create-pr --ready Fix race condition in job queue/create-pr --force --ready Fix race condition in job queueExtract from user input:
draft = true unless --ready is present (PRs are drafts by default)force = true if --force is presenttitle_hint = remaining text after stripping --ready and --force, or empty stringDetermine the base branch and gather context. The base is normally the repo's default branch, but for stacked PRs (e.g. created with gt) it's the parent branch in the stack.
head=$(git rev-parse --abbrev-ref HEAD) # current branch
default_branch=$(bash "$HOME/.dotfiles/bin/lib/git-default-branch.sh") # default branch (bare name)
If the helper is not available or default_branch is empty, tell the user and stop.
Pick the base in this precedence:
# 1. If a PR already exists for this head, honor its base. Never silently
# retarget it. Use `gh pr list --head` (queries the API by branch name)
# rather than `gh pr view`, which depends on local upstream tracking and
# can miss PRs in worktrees or after re-clones.
existing_pr=$(gh pr list --head "$head" --state open --json number,baseRefName --jq '.[0]' 2>/dev/null || true)
existing_base=$(printf '%s' "$existing_pr" | jq -r '.baseRefName // empty' 2>/dev/null || true)
# 2. Ask gt for the parent (current gt stores stack metadata in
# .git/.graphite_cache_persist, not in git config). Gate on `command -v gt`
# so the skill works for users without gt installed.
gt_parent=""
if command -v gt >/dev/null 2>&1; then
gt_parent=$(gt parent 2>/dev/null || true)
fi
if [ -n "$existing_base" ]; then
base="$existing_base"
stacked=$([ "$base" != "$default_branch" ] && echo true || echo false)
elif [ -n "$gt_parent" ] && [ "$gt_parent" != "$default_branch" ]; then
base="$gt_parent"
stacked=true
else
base="$default_branch"
stacked=false
fi
Then, using $base, run in parallel:
git log origin/$base..HEAD --oneline # commits on this branch
git diff origin/$base...HEAD --stat # changed-file summary
gh pr list --head "$head" --state open --json number,title,isDraft,url --jq '.[0]' # existing PR, if any
If a PR already exists, note its number and URL: you will update it rather than create a new one (see Step 9). Use gh pr list --head rather than gh pr view: it queries by branch name (reliable in worktrees and after re-clones) instead of relying on local upstream tracking.
When composing the PR body in Step 5, use the --stat output to decide how much diff to read:
git diff origin/$base...HEAD.git diff origin/$base...HEAD -- <path>. Skip generated files, lock files, and files whose names make their changes obvious (e.g. version bumps in package.json).Check these locations in order and stop at the first match:
.github/pull_request_template.md.github/PULL_REQUEST_TEMPLATE.md.github/PULL_REQUEST_TEMPLATE/: use default.md if it exists, otherwise the only file present; if multiple files with no clear default, ask the user which to usedocs/pull_request_template.mdpull_request_template.mdCheck for a saved manual test plan (produced by /test-plan):
test_plan_path=".context/test-plan.md"
[ -f "$test_plan_path" ] && test_plan_content=$(cat "$test_plan_path")
If the file exists, hold its full contents for use in Step 5. If not, skip; the body composition step writes its own short test plan instead.
Title:
title_hint is non-empty, use it as the title (trim and keep under 70 characters)Body:
If a template was found, fill each section using the commits and diff:
<<'EOF'), which is literal; write `foo` not \`foo\`, and $var not \$varVoice. Apply the writing voice from the ## Writing voice section at the top of this skill throughout the body. The three hard rules (no em dashes, no bold inside prose, no bolded pseudo-labels) apply to every sentence you write here.
If no template was found, write:
Embedding the saved test plan (if test_plan_content is set):
If a saved test plan was detected in Step 4, embed it inside the PR's testing section. Locate the testing section by matching the first heading whose title contains any of: Test plan, Testing, How did you test, QA. If no such heading exists in the template, append a ## Test plan section at the end.
Insert this block as the section's content (replacing any auto-generated test plan you would otherwise have written):
<details>
<summary>Manual test plan</summary>
<contents of .context/test-plan.md verbatim, with any leading `## Test plan` heading stripped>
</details>
Notes:
## Test plan (or equivalent) heading from the file contents before insertion (the surrounding section heading already provides that context)- [ ] checkbox format exactly; do not re-wrap or reformat<details> and </details> so GitHub renders the collapsible block correctly<details> block rather than stacking themRead the composed body once. If any sentence contains an em dash, bold text outside a line-start label, or a bolded pseudo-label, rewrite that sentence now. Then ask yourself: would a staff engineer write this sentence verbatim in a Slack message to a teammate? If not, simplify it.
If force is true, skip to Step 8 immediately, do not show a preview or ask for confirmation.
Otherwise, display the proposed PR to the user. When stacked=true, include the base in the header so the non-default target is obvious:
Title: <title>
Base: <base> # only show this line when stacked=true; append " (stacked)"
Draft: yes/no
<body>
Ask: "Create this PR? Reply yes to confirm, or describe any changes to make."
Wait for confirmation. If the user requests edits, apply them and show the updated preview before proceeding.
When stacked=true, the parent branch must already exist on origin (GitHub can't open a PR against a base it doesn't have). Check first:
if [ "$stacked" = "true" ] && [ -z "$(git ls-remote --heads origin "$base")" ]; then
echo "Parent branch '$base' is not on origin yet. Push it first (e.g. 'gt submit --stack' or 'git push origin $base') and re-run."
exit 1
fi
Don't push the parent automatically; that's a stack-wide action and belongs to gt.
Then push HEAD:
git push --set-upstream origin HEAD
If the push fails, report the error and stop.
New PR:
The leading : __create-pr-skill__ ; is a shell no-op that flags this invocation as coming from the skill, so the enforce-create-pr-skill PreToolUse hook lets it through. Keep it on bare gh pr create calls; without it, the hook denies the command.
: __create-pr-skill__ ; gh pr create \
--base <base> \
--title "<title>" \
--body "$(cat <<'EOF'
<body>
EOF
)" \
[--draft if draft=true]
Existing PR (found in Step 2; the initial check did not fetch the body to save tokens, so fetch it now if you need to inspect it before writing the update):
gh pr view <number> --json body --jq '.body'
Note the existing body may already contain a <details><summary>Manual test plan</summary>…</details> block; replace it with the new one rather than appending.
gh pr edit <number> \
--title "<title>" \
--body "$(cat <<'EOF'
<body>
EOF
)"
If draft=true and the existing PR is not already a draft:
gh pr ready --undo <number>
If draft=false and the existing PR is a draft:
gh pr ready <number>
On success, display the PR URL. On failure, show the full error output and stop. Do not retry silently.
Identify unlabeled GitHub issues and external PRs that may belong to a specific team, and normalize conventional title scopes to the team's canonical short form
Generate standup notes from GitHub PR activity
Monitor CI checks after pushing, detect flaky vs legit failures, and auto-fix
Reference for GitHub PR review endpoints and resolving review threads via gh CLI. Use when replying to a PR review comment, posting a new review comment, or resolving review threads.
PostHog repo-specific workflow, database access rules, production architecture notes, and SDK repository locations. Use when working in posthog/posthog or any PostHog SDK repo.
Plan, implement, and iteratively review a task end-to-end using Claude + Copilot reviewers in a linear flow.