Create a GitHub PR with structured description from Jira ticket and code changes
Scanned 6/5/2026
Install via CLI
openskills install event4u-app/agent-config---
model_tier: medium
name: git-pr-create
pack: git
intent: "Open a pull request with a generated description and stripped attribution footers"
routes_to: [git-workflow]
replaces: [create-pr]
tier: 1
cluster: git-pr-create
skills: [git-workflow]
description: Create a GitHub PR with structured description from Jira ticket and code changes
suggestion:
eligible: true
trigger_description: "open a PR, create a pull request, make a PR for this branch"
trigger_context: "branch is ahead of base and not yet on a PR"
workspaces:
- agent-config-maintainer
packs:
- git
---
# /create-pr
Top-level entry point for the `/create-pr` family. Bare `/create-pr`
runs the full create-PR flow described below. The `:description-only`
sub-command produces a copyable PR description without creating the PR.
## Sub-commands
| Sub-command | Routes to | Purpose |
|---|---|---|
| `/create-pr` (bare) | this file (`## Default flow`) | Full flow — generate description, push, create PR |
| `/create-pr:description-only` | `commands/pr/create/description-only.md` | Generate the PR description as a copyable markdown block, no PR creation |
## Dispatch
1. Parse the user's argument: `/create-pr[:<sub>] [args]`.
2. Bare `/create-pr` → run the `## Default flow` below verbatim.
3. `/create-pr:description-only` → load
`commands/pr/create/description-only.md` and follow its
`## Instructions` section verbatim.
4. Unknown sub-command → print the table above and ask which one.
## Default flow
Uses `/create-pr:description-only` to generate the PR content, then creates the PR via GitHub API.
### 1. Check prerequisites
- Verify the current branch is NOT the default branch (`main` / `master`).
- Run `git status` — warn if there are uncommitted changes.
- Run `git log origin/{default}..HEAD --oneline` to verify there are commits to push.
- If the branch has not been pushed yet, ask the user (in their language) whether to push.
### 1b. Freshness gate — MANDATORY before opening any PR
The branch may have diverged from its target base while you were
working. A PR opened against a stale base creates merge conflicts the
moment another PR lands first — the exact failure that motivates this
gate.
Run, in order:
1. `git fetch origin {target-base} --quiet` (default: `main`).
2. `git rev-list --count HEAD..origin/{target-base}` — number of
commits HEAD is **behind** the base.
3. `gh pr list --state open --base {target-base} --limit 20 --json number,headRefName,files`
— open PRs targeting the same base.
**Decision matrix:**
| Behind | Overlapping open PR touches same files? | Action |
|---|---|---|
| `0` | — | Proceed to Step 2 |
| `1–N` | No | Surface the count, ask: rebase / merge-main / proceed-anyway / cancel |
| `1–N` | **Yes** | STOP — surface the overlapping PR number, ask: stack on top of it / wait for it to land / proceed-anyway-and-accept-conflicts / cancel |
Never improvise the base or silently proceed when the branch is behind
and overlap exists. The 10-second fetch beats hours of rebase
reconciliation after the parent PR lands.
### 2. Generate PR content
Run `/create-pr:description-only` Steps 1–4 to generate the PR title and body.
This handles: Jira ticket extraction, diff analysis, commit messages, **PR template filling**.
**CRITICAL**: The PR body MUST use the project's PR template (`.github/pull_request_template.md`).
Read the template file and fill in its sections. If the template does not exist, use the
fallback structure defined in `/create-pr:description-only`. NEVER invent a custom body structure.
**Preview gate** — read `commands.create_pr.preview_description` from
`.agent-settings.yml` (default `false` when unset):
- `false` (default): skip Steps 5–6 of `/create-pr:description-only` (the
copyable preview block + adjust loop). Use the generated title and body
directly in Step 3 below. This saves agent tokens by avoiding a full
re-render of the description in chat. The user can still edit the PR
body in the GitHub UI after creation.
- `true`: run Steps 5–6 of `/create-pr:description-only` — present the
title and body as copyable blocks and ask for adjustments before
proceeding. The user reviews and adjusts the content in that step.
### 2b. Council review — explicitly excluded
`/create-pr` does **not** prompt for council review, even when
`ai_council.enabled: true`. Invoking the PR command is an explicit
delivery action; interrupting it with a billable opt-in question is
out of scope.
Users who want a council pass on the diff run `/council diff:<base>..<head>`
**before** `/create-pr`. Do not re-add the prompt here without an explicit
user request.
### 3. Create the PR (draft-vs-ready, verbosity-gated)
- **Head branch**: EXACT output of `git branch --show-current` from step 1.
- **Base branch**: default branch (`main` / `master`).
**Behavior change vs. legacy:** with `verbosity.routine_confirmations:
false` (default), `/create-pr` creates the PR as draft silently. Override
per-invocation with `:ready` / `:final` / `:draft`. Restore the prompt
by flipping `routine_confirmations: true`. See
[`docs/customization.md` § Verbosity](../../docs/customization.md#verbosity).
Resolve `"draft"` (first match wins):
1. `:ready` / `:final` arg → `false`.
2. `:draft` arg → `true`.
3. `routine_confirmations: true` → ask `1. draft / 2. ready`.
4. Default → `true` (silent draft).
#### 3a. Tool selection — single-call mandate
```
POST THE PR WITH ONE github-api CALL.
NEVER COMPOSE THE BODY THROUGH SHELL, PYTHON, OR TEMP FILES.
```
Use the `github-api` tool **directly** with the markdown body in the
JSON `data` field. The body is a regular JSON string — escaping is the
tool's job, not yours.
```
github-api
method: POST
path: /repos/{owner}/{repo}/pulls
data: { "title": "...", "body": "<markdown>", "head": "<branch>",
"base": "main", "draft": <bool> }
```
**Hard prohibitions** (each one cost 3+ extra tool calls in past runs):
- ❌ `python3 -c "import urllib..."` / `python3 - <<PY ... PY` heredocs
to serialize the body or POST it.
- ❌ `save-file PR_BODY.md` → read back → `curl -d @PR_BODY.md`.
- ❌ `gh pr create --body-file …` shelling out when `github-api` is
available in this surface.
- ❌ Splitting `title` and `body` into two API calls (create + PATCH).
The body may contain any Markdown — code fences, tables, multi-line
HTML, emoji. Do **not** preprocess, escape, or strip newlines before
handing it to `github-api`.
If the surface genuinely lacks `github-api` (rare), fall back to
`gh pr create --title "..." --body-file <(echo -n "<body>")` as a
**single** shell call, never the python-urllib path.
#### 3b. Submit
Create the PR with the approved title/body and the resolved `draft`.
**Verify after `draft: false`** — the GitHub REST API sometimes ignores
the flag:
```bash
gh pr view {number} --json isDraft --jq '.isDraft'
# returns true → gh pr ready {number}
```
**Silent-draft postscript** (rule 4) → see step 4b.
### 4. After creation
#### 4a. Strip attribution footers (mandatory)
Some `github-api` tool surfaces append attribution server-side after
the agent has sent a clean body. Per
[`no-attribution-footers`](../rules/no-attribution-footers.md), every
PR body must be re-checked and stripped after every write.
Run this strip-pass **after PR creation and after every body PATCH**:
1. Re-fetch the PR body:
```
GET /repos/{owner}/{repo}/pulls/{number}
```
2. Search the body (case-insensitive) for any of:
- `Generated with [Augment Code]` / `🤖 Generated with`
- `Pull Request opened by [Augment Code]`
- `Co-authored by Augment Code`
- Any `augmentcode.com` link the user did not ask for
3. **No match → done.** Skip steps 4–5; the body is already clean.
This is the common path and **must not** spend a verify-GET.
4. Match found → remove the footer(s) together with surrounding
`---` separators and trailing whitespace, then:
```
PATCH /repos/{owner}/{repo}/pulls/{number}
{ "body": "<cleaned body>" }
```
Re-fetch the body **once** to verify the strip stuck. If a
pattern reappears (server re-injection), repeat the PATCH once;
if it still reappears, surface the issue to the user and stop
(do not enter a strip/PATCH loop).
5. Briefly note in the reply how many footers were removed (omit
the line entirely when nothing was stripped — silence is the
expected path, not "no footers found").
#### 4b. Show the PR URL (verbosity-gated)
Per `verbosity.post_action_reports` (default `minimal`):
- `off` → nothing.
- `minimal` → `→ #N opened: <url>`. Append `→ created as draft — run
\`gh pr ready N\` to flip` when silent-draft (rule 4); omit on ready.
- `full` → multi-line: PR number, URL, draft state, base/head, ready-reminder.
#### 4c. Jira transition (only when transitioned)
Linked ticket + `routine_confirmations: true` → ask `1. Yes / 2. No`.
Default (`false`) → skip silently. **Only emit a transition line when
an actual Jira API call succeeded** — never announce "skipped".
#### 4d. Settings short-circuit — single read per run
`verbosity.routine_confirmations`, `verbosity.post_action_reports`, and
`commands.create_pr.preview_description` are read **once** at the top
of the run and cached for the whole `/create-pr` invocation. Do **not**
re-read `.agent-settings.yml` in 4b / 4c — both branches resolve from
the cached values from step 1.
When all three resolve to their silent defaults (`false` / `minimal` /
`false`), steps 4b–4c collapse to the single `→ #N opened: <url>` line
from 4b and a silent 4c. No extra file reads, no "checking settings…"
narration, no confirmation prompts.
### Rules
- **Always use the PR template** from `.github/pull_request_template.md`.
- **Preview is opt-in** — `commands.create_pr.preview_description` (default `false`). `/create-pr:description-only` always previews.
- **Push the branch first** if needed (with permission).
- **No attribution footers** — see [`no-attribution-footers`](../rules/no-attribution-footers.md); strip-pass at 4a defends against tool injection.
- Only create the PR — never merge.
- Only commit or push with explicit permission.
No comments yet. Be the first to comment!