| name | create-pr |
| description | Create a pull request with a Conventional Commits title, auto-generated description based on branch changes, and the repository PR template. Use when the user asks to create, open, or submit a pull request. |
create-pr
Mission
Create a GitHub pull request for the current branch with a valid Conventional Commits title and an auto-generated description that summarizes the actual changes.
Prerequisites
git and gh CLI installed and authenticated (gh auth status).
- The
gh token must include the project scope for project assignment.
If missing, run: gh auth refresh -s project
- The current branch is not
main.
- Changes have been committed and pushed to the remote.
PR title rules
The title must pass the repository PR title checker. The authoritative pattern is defined in
.github/pr-title-checker-config.json — read that file at runtime to get the current CHECKS.regexp
value rather than relying on a hardcoded pattern.
Additional prose rules (not fully enforced by the checker):
- The
! breaking-change indicator must appear after the optional scope and before the :,
never between the type and the scope (for example feat!(scope): is invalid; feat(scope)!: is correct).
- The
: must be followed by a single space and a non-empty description.
Allowed types: feat, fix, docs, test, ci, chore, refactor.
Format:
<type>[optional scope][!]: <description>
Examples:
feat(cmdb): add bulk object creation endpoint
fix(auth): handle expired JWT gracefully
docs: update README with new env vars
refactor(services)!: replace requests with httpx
PR description template
The body must use the structure from .github/pull_request_template.md:
# Pull request description
## Describe your changes
<auto-generated summary — see below>
## Issue ticket number and link
<leave blank or fill if the user provides one>
## Checklist before requesting a review
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] Any dependent changes have been merged and published in downstream modules
- [ ] New or updated tests follow the AAA pattern and `test_<feature>_should_<expected behavior>_when_<state under test>` naming convention
Auto-generating the description
Fill the Describe your changes section by analysing the branch diff against main:
- Run
git log --oneline main..HEAD to list commits on this branch.
- Run
git diff main...HEAD --stat to get a file-level change summary.
- Run
git diff main...HEAD (or read key hunks) to understand the actual changes.
- Write a concise summary (3-8 bullet points) covering:
- What changed (new files, modified modules, deleted code).
- Why it changed (purpose, motivation, context from commit messages).
- Notable details (new dependencies, config changes, migration steps, breaking changes).
Keep the summary factual and concise. Do not pad with filler.
Workflow
-
Validate branch
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
- If
BRANCH_NAME is main, stop with a warning: cannot create a PR from main.
-
Check for unpushed work
-
Verify there are no uncommitted changes: git status --porcelain.
-
Verify the branch is pushed:
git fetch origin "$BRANCH_NAME"
if git rev-parse --verify --quiet "origin/$BRANCH_NAME" > /dev/null; then
git log "origin/$BRANCH_NAME"..HEAD --oneline
else
echo "Branch '$BRANCH_NAME' has not been pushed to origin yet."
exit 1
fi
-
If there are uncommitted changes or unpushed commits, warn the user and stop.
-
Check for existing PR
PR_NUMBER=$(gh pr list --head "$BRANCH_NAME" --state open --json number -q '.[0].number')
- If a PR already exists, inform the user and provide the PR URL. Stop unless the user wants to update it.
-
Determine PR title
- Examine the branch commits to infer the primary change type and scope.
- Propose a Conventional Commits title to the user.
- Ask the user to confirm or edit the title before proceeding.
- Validate the final title against the
CHECKS.regexp pattern read from .github/pr-title-checker-config.json,
and enforce that : is followed by a single space and a non-empty description.
-
Generate PR description
- Follow the "Auto-generating the description" section above.
- Build the full body using the PR template structure.
- Present the generated description to the user for review.
-
Create the PR
-
Write the final body to a temporary file.
-
Create the PR and capture the number and URL:
PR_URL=$(gh pr create --title "$PR_TITLE" --body-file "$TEMP_FILE")
PR_NUMBER=$(gh pr view "$PR_URL" --json number -q '.number')
-
Clean up the temporary file.
-
Print the new PR URL.
-
Add labels
-
Fetch the current repository labels: gh label list --json name --jq '.[].name'
-
Analyse the PR title type, changed files (git diff main...HEAD --name-only), and branch name.
-
Select the most relevant labels from the fetched list that match the PR content.
-
Only apply labels that exist in the repository — never create new labels.
-
Use the REST API to apply labels (avoids classic-projects errors with gh pr edit):
gh api "repos/{owner}/{repo}/issues/$PR_NUMBER/labels" \
--method POST -f 'labels[]=label1' -f 'labels[]=label2'
-
Assign to project and set status
-
Use gh project item-add (not gh pr edit --add-project, which only works with classic projects).
Capture the item ID from the output:
ITEM_ID=$(gh project item-add 3 --owner vestas \
--url "$PR_URL" --format json | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
-
Set the project status to In Progress using gh project item-edit.
Fetch the field ID and option ID dynamically:
FIELDS_JSON=$(gh project field-list 3 --owner vestas --format json)
STATUS_FIELD_ID=$(echo "$FIELDS_JSON" | python3 -c "
import sys, json
for f in json.load(sys.stdin)['fields']:
if f['name'] == 'Status':
print(f['id']); break")
IN_PROGRESS_ID=$(echo "$FIELDS_JSON" | python3 -c "
import sys, json
for f in json.load(sys.stdin)['fields']:
if f['name'] == 'Status':
for o in f['options']:
if o['name'] == 'In Progress':
print(o['id']); break
break")
PROJECT_ID=$(gh project list --owner vestas --format json | python3 -c "
import sys, json
for p in json.load(sys.stdin)['projects']:
if p['number'] == 3:
print(p['id']); break")
gh project item-edit --project-id "$PROJECT_ID" --id "$ITEM_ID" \
--field-id "$STATUS_FIELD_ID" --single-select-option-id "$IN_PROGRESS_ID"
-
If any command fails with a missing-scope error, ask the user to run
gh auth refresh -s project and retry.
PR Lifecycle & Safety
- Never merge or close a PR: Your role ends at creating the PR. You MUST NOT use
gh pr merge, gh pr close, or any other tool to merge or close the Pull Request yourself.
- Never push directly to main: All changes must go through a Pull Request. You MUST NOT commit or push directly to the
main branch, even for small fixes or "fixing the build".
- Wait for CI and User Approval: A Pull Request is the final verification step. Even if you believe the code is perfect, you must wait for the project's automated CI checks (GitHub Actions) to pass and for the user to manually review and approve the changes.
- Release Process: The project's release process is triggered by merging PRs into
main. By merging prematurely, you bypass safety checks and risk breaking the automated release pipeline.
Guardrails
- Never create a PR without user confirmation of the title.
- Never skip the PR template structure.
- Never use
--no-verify or force push.
- NEVER merge a PR.
- NEVER close a PR.
- NEVER push directly to the
main branch.
- If
gh is unavailable or unauthenticated, ask the user for guidance.
- If the title does not match the required pattern, reject it and ask again.
0