name: draft-pr description: Open a pull request in the browser with prefilled title, description, and labels
Open Pull Request
Analyze the current branch, generate a PR title/description/labels, push to the user's fork, and open the GitHub PR creation page in the browser with everything prefilled.
Tool Strategy
This skill uses mcp__content-agent__* tools when available (preferred — deterministic, structured results). When the MCP server is not configured, fall back to filesystem-based alternatives noted as Fallback in each step. See .claude/skills/shared/mcp_fallbacks.md for detailed fallback procedures. The skill must complete successfully either way.
Phase 1: Gather Branch Data
Derive upstream repo identifier:
UPSTREAM_REPO=$(git remote get-url origin | sed -n 's|.*[:/]\([^/]*/[^/]*\)\.git$|\1|p; s|.*[:/]\([^/]*/[^/]*\)$|\1|p')Get current branch and branching point:
git branch --show-current git merge-base HEAD masterAbort if not on a feature branch:
- If on
master, or ifmerge-baseequalsHEAD(no diverging commits), inform the user: "No diverging commits found. Please switch to your feature branch first." - Stop execution.
- If on
Collect commit history (all commits since branching point):
MERGE_BASE=$(git merge-base HEAD master) git log --no-merges --format="%H %s%n%b---" ${MERGE_BASE}..HEADCollect diff information:
MERGE_BASE=$(git merge-base HEAD master) git diff --stat ${MERGE_BASE}..HEAD git diff ${MERGE_BASE}..HEAD git diff --name-only ${MERGE_BASE}..HEAD
Phase 2: Analyze Changes
2.1 Categorize Change Type
Determine which categories apply based on changed files and diff content:
- New rule (new
rule.ymladded) - Modified rule (existing
rule.ymlchanged) - New or modified template (
shared/templates/) - Remediation changes (Bash/Ansible files)
- Profile changes (
.profilefiles) - Control file changes (
controls/) - Test scenario changes (
tests/) - Build system or infrastructure changes
- Documentation changes
2.2 Identify Affected Products
Look for product indicators:
- CCE identifiers in
rule.ymlfiles (cce@rhel8,cce@rhel9,cce@rhel10, etc.) - Product-specific paths (
products/<product>/) - Profile or control file names containing product identifiers
- Platform applicability in rule definitions
2.3 Read Key Changed Files
For significant changed files, use MCP functions to get structured metadata:
- Rules: Use
mcp__content-agent__get_rule_detailswith the rule ID to get title, description, rationale, template, severity, references, and platform info - Profiles: Use
mcp__content-agent__get_profile_detailswith product and profile ID to get profile structure and rule selections - Controls: Use
mcp__content-agent__get_control_detailsto understand control framework structure - Templates: Use
mcp__content-agent__get_template_schemato get template parameter info
Fallback: Read the files directly — rule.yml, .profile, control YAML, and template files in shared/templates/<name>/.
2.4 Check Test Coverage
- Look for added/modified test scenarios in
tests/subdirectories - Check if rules use templates (inheriting template tests)
2.5 Detect Issue References
Scan commit messages for patterns like Fixes #N, Resolves #N, Closes #N, or bare #N references.
Phase 3: Generate PR Content
3.1 PR Title
Generate a PR title following project conventions:
- Under 70 characters
- Imperative mood ("Add", "Fix", "Update")
- Useful for changelog
3.2 PR Body
Read the PR template for reference:
cat .github/pull_request_template.md
Draft all three sections following the template format exactly:
Description Section
Auto-generate from analysis:
- New rules: "Add new rule
<rule_id>that. The rule has <severity>severity and targets. It uses the <template>template / includes custom OVAL and <Bash/Ansible/both> remediation." - Rule modifications: "Modify rule
<rule_id>to." - Profile/control changes: "Update <profile/control> for
to ." - Template changes: "Modify template
<template_name>to." - Multi-change PRs: List each major change as a separate bullet point.
Rationale Section
Attempt to infer from:
- Commit message bodies (look for explanations of "why")
- Rule
rationalefield inrule.yml - Security references indicating compliance requirements
- Profile additions indicating framework alignment
If rationale cannot be fully inferred, include what was found and mark gaps for user input.
Include issue reference if detected in commit messages, otherwise leave a placeholder.
Review Hints Section
Auto-generate:
- Affected products with build commands:
./build_product --datastream-only <product> - Test commands if test scenarios exist:
Note: Adjust./tests/automatus.py rule --libvirt qemu:///session <product_vm_name> --datastream build/ssg-<product>-ds.xml <rule_id>qemu:///sessiontoqemu:///systemand<product_vm_name>to the actual VM name based on reviewer's local setup. - Key files to review: highlight the most important changed files
- Review approach: suggest reviewing all commits together or in sequence, based on how related the commits are
- Related issues/PRs: include any references found in commit messages
Write the body to $TMPDIR/pr_body.md. Do NOT write PR_DESCRIPTION.md to the repo root.
3.3 Infer Labels
Fetch available labels from the repository:
gh label list -R ${UPSTREAM_REPO} --limit 200 --json name --jq '.[].name'If
gh label listfails (e.g., auth issue), skip label inference and tell the user why. Continue to Phase 4 without labels.Match available labels against the PR content using these heuristics:
- Product labels: If changed files or CCE refs mention a product (rhel8, rhel9, rhel10, fedora, ocp4, etc.), look for a matching label
- New rule: If new
rule.ymlfiles were added, look for a "new rule" or similar label - Bug fix: If commit messages indicate a fix (commit message starts with "fix", "Fix", contains "bug", etc.), look for a "bug fix" or "bug" label
- Profile changes: If
.profilefiles changed, look for a "profiles" label - Control changes: If
controls/files changed, look for a "controls" label - Template changes: If
shared/templates/files changed, look for a "templates" label - Test changes: If test scenarios were added/modified, look for a "tests" label
- Documentation: If docs were changed, look for a "documentation" label
Only select labels that actually exist in the fetched label list. Be conservative — pick labels that clearly apply rather than guessing.
3.4 Resolve Milestone
Fetch open milestones that look like version numbers:
gh api repos/${UPSTREAM_REPO}/milestones --jq '[.[] | select(.title | test("^[0-9]+\\."))] | sort_by(.title) | reverse'Apply selection logic:
- Exactly one match: use it automatically. Note the milestone title for Phase 4.
- Zero matches: warn the user ("No release milestone found — creating PR without one") and continue without a milestone.
- Multiple matches: list the milestone titles and ask the user to pick one, or skip.
Store the resolved milestone title (or none) for use in Phase 4.
Phase 4: Push & Open in Browser
4.1 Push to Fork
Verify the
forkremote exists:git remote get-url forkIf this fails, tell the user: "No
forkremote found. Add it with:git remote add fork <your-fork-url>" and stop.Push the branch:
BRANCH=$(git branch --show-current) git push fork "${BRANCH}"If the push fails with a non-fast-forward error (e.g., branch was rebased), retry with
--force-with-lease:git push --force-with-lease fork "${BRANCH}"--force-with-leaseis safe — it refuses to overwrite remote commits that you haven't seen locally, preventing accidental data loss while still allowing rebased branches to be pushed.
4.2 Open PR Creation Page
Construct and run the gh pr create --web command with all prefilled metadata:
BRANCH=$(git branch --show-current)
FORK_OWNER=$(git remote get-url fork | sed -n 's|.*[:/]\([^/]*\)/[^/]*\(\.git\)\{0,1\}$|\1|p')
gh pr create --web \
--repo ${UPSTREAM_REPO} \
--head "${FORK_OWNER}:${BRANCH}" \
--base master \
--title "<generated title>" \
--body-file "$TMPDIR/pr_body.md" \
--label "<label1>" --label "<label2>" \
--milestone "<resolved milestone title>"
Determine FORK_OWNER by parsing the fork remote URL (extract the GitHub username from the URL).
- If no labels were inferred, omit the
--labelflags. - If no milestone was resolved, omit the
--milestoneflag. - If
--webcombined with other flags fails, fall back to:- Create the PR directly (remove
--web). - Open the resulting PR URL in the browser:
xdg-open <pr_url>.
- Create the PR directly (remove
4.3 Clean Up
Remove the temporary body file:
rm -f "$TMPDIR/pr_body.md"
4.4 Report
Tell the user:
- The PR title that was used
- The labels that were applied (or note if labels were skipped)
- The milestone that was set (or note if no milestone was found)
- That the browser should have opened to the PR creation page
- If fallback was used, mention the PR was created directly and the URL
Important Notes
- Run all
ghandgit pushcommands withdangerouslyDisableSandbox: true—ghcredentials may be stored in the system keyring, which the sandbox blocks, causing silent 401 auth failures. - Analyze ALL commits in the branch, not just the latest one
- Use
--no-mergeswhen reading commit log to skip merge commits - Be specific about products — use actual product names (rhel8, rhel9, rhel10)
- Handle large diffs gracefully — for branches with many changes, summarize by category rather than listing every file
- Match the PR template format exactly — the body must follow
.github/pull_request_template.md - The fork remote is
fork, upstream isorigin— determine the upstream repo fromgit remote get-url origin - Derive the fork owner from the
forkremote URL — never hardcode a GitHub username