open-pr — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited open-pr (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Create a GitHub PR with inferred defaults, a detailed generated body, and mandatory user confirmation before creation. Project-agnostic — works with any repo. Supports PR templates, CODEOWNERS, conventional commits, monorepos, and multiple ticket systems out of the box.
Parse arguments from the skill invocation string. All are optional, --flag value format:
| Arg | Alias | Default | Description |
|---|---|---|---|
--base | -b | Inferred (see Phase 2) | Target branch |
--assignee | -a | Current gh user | PR assignee |
--reviewer | -r | Ask user | Reviewer(s), comma-separated |
--label | -l | Inferred from paths | Label(s), comma-separated |
--images | -i | None | Image paths for screenshots |
--draft | -d | Ask user | Create as draft |
--no-draft | Create as ready for review | ||
--title | -t | Auto-generated | Override title |
--ticket | -j | Extracted from branch | Override ticket ID |
You MUST execute all 5 phases in order. Do NOT skip Phase 4 (confirmation).
Run these checks in parallel where possible:
gh auth status. If not authenticated, stop and tell the user to run gh auth login.git status --porcelain. If output is non-empty, warn the user about uncommitted changes and ask whether to proceed or abort.gh pr view --json url,state 2>/dev/null. If a PR already exists for this branch:gh pr edit instead?"gh pr edit)git log {base}..HEAD --oneline (after base is determined in Phase 2). If empty, stop — nothing to open a PR for.#### Optional Config File
Check for configuration files (strictly optional — skill works perfectly without them):
.github/open-pr.yml (repo-level config)~/.config/open-pr.yml (user-level config)Supported config keys:
base: develop # default base branch
draft: true # default draft status
labels:
path_mappings: # custom path→label mappings
"packages/payments/": "payments"
"libs/auth/": "auth"
reviewers:
default: ["alice"] # default reviewer suggestions
use_codeowners: true # enable CODEOWNERS parsing (default: true)
template: ".github/PULL_REQUEST_TEMPLATE/feature.md" # force a specific template
body_footer: "QA: https://staging.example.com" # append to PR bodyIf config files are not found, proceed silently with built-in defaults. Never error on missing config.
#### CI Status Warning
After confirming the branch has been pushed (or will be pushed), run:
gh run list --branch {branch} --limit 3 --json status,conclusion,name 2>/dev/nullIf any recent runs have conclusion: "failure", warn: "Recent CI runs have failures: {names}. This won't block PR creation, but you may want to investigate." This is advisory only — never block on CI status.
#### Ticket ID
--ticket provided, use it directly.([A-Z][A-Z0-9]+-\d+) — e.g., DP-1067, ENG-42(sc-\d+) (case-insensitive) — e.g., sc-12345(AB#\d+) — e.g., AB#7890#(\d+) in branch name, or bare number prefix like 123-fix-bug → #123(HOTFIX) (case-insensitive)Also scan commit messages for issue references (#\d+) to collect related issues for the body.
#### Base Branch
--base provided, use it.base, use that (unless --base was given).gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'main, master, develop, plus any release/* branchesgit merge-base distanceStacked PR detection: If the resolved base branch is not main, master, develop, or release/*, note: "This appears to be a stacked PR targeting {base}." Include this note in the Phase 4 preview.
#### Assignee
--assignee provided, use it.gh api user --jq '.login'#### Reviewer
--reviewer provided, use it..github/CODEOWNERS, CODEOWNERS, or docs/CODEOWNERS. If found, parse it and match changed file paths against patterns. Extract usernames/teams. CODEOWNERS format: each line is pattern @owner1 @owner2. Match using glob patterns (last matching pattern wins per file, same as GitHub). If config has reviewers.use_codeowners: false, skip this step.git log --format='%aN' -5 -- {file}, deduplicate, exclude self.gh pr list --limit 15 --state merged --json author,reviews --jq '.[].reviews[].author.login', deduplicate, exclude self.reviewers.default, include those with label "(default from config)".#### Labels
--label provided, use those directly.gh label list --json name --jq '.[].name'git diff --name-only {base}..HEADConfig-based mappings (if config has labels.path_mappings):
Dynamic directory-to-label matching:
packages/payments/src/foo.ts → packages, payments, src)payments matches paths under packages/payments/; label auth matches paths under libs/auth/Universal fallback heuristics (always applied):
*.md files or paths under docs/ → labels containing "doc"test/, tests/, __tests__/, or files matching *test*, *spec* → labels containing "test".github/, .circleci/, ci/, .gitlab-ci*, Jenkinsfile, Dockerfile, docker-compose*, terraform/, infra/ → labels containing "ci", "infra", or "devops"Size labels: Calculate total lines changed from git diff --stat. If the repo has labels matching size/XS, size/S, size/M, size/L, size/XL (case-insensitive):
Monorepo package matching: If monorepo detected (see Phase 3), match package/workspace names against available labels.
#### Draft Status
--draft provided → draft = true--no-draft provided → draft = falsedraft, use that as default.#### Collect Change Data (run in parallel)
git log {base}..HEAD --pretty=format:'%h %s' — commit listgit diff --stat {base}..HEAD — file change statsgit diff {base}..HEAD — full diff (only if <5000 lines; check with git diff {base}..HEAD | wc -l first)#### Size Classification Calculate total lines changed (additions + deletions from git diff --stat). Classify:
Include this in the Phase 4 preview. If XL, add advisory: "This is a very large PR. Consider splitting into smaller, focused PRs for easier review."
#### Detect Monorepo Structure Check for monorepo indicators (in parallel with diff collection):
pnpm-workspace.yaml, lerna.json, nx.json, rush.json, turbo.jsonpackages/ or apps/ directories at repo rootIf detected, identify which packages/workspaces have changes by mapping changed file paths to package directories. This information is used for:
#### Detect Conventional Commits Parse the commit list. Check if >50% of commits follow the pattern type(scope): description or type: description (where type is one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert).
If conventional commits are detected:
feat: → feature PR, fix: → bugfix PR)BREAKING CHANGE: in its body/footer or uses ! after the type (e.g., feat!:), add a "Breaking Changes" section to the body[TICKET-ID] feat: descriptionIf conventional commits are NOT detected (<=50% match), fall back to the current diff analysis behavior.
#### Detect PR Template Before generating the body, check for repository PR templates in this order:
.github/PULL_REQUEST_TEMPLATE.md.github/pull_request_template.mdPULL_REQUEST_TEMPLATE.md (repo root)pull_request_template.md (repo root).github/PULL_REQUEST_TEMPLATE/*.md — if found, list them and ask the user to pick one via AskUserQuestionIf config specifies template, use that path directly instead of searching.
If a template is found:
<!-- ... -->) and replace them with actual content generated from the diff/commits*Generated with Claude Code* footerIf no template is found, use the built-in template below.
#### Generate Title
--title provided, use it.[TICKET-ID] Imperative description if ticket exists, or just Imperative description.[HOTFIX] Imperative description.#### Generate PR Body (built-in template — used only when no repo template is found)
## Summary
{2-5 bullets: what changed and why, written from analyzing the actual diff and commits}
## Changes
{If monorepo: group by package with package name as sub-heading}
{If conventional commits: group by type — Features, Bug Fixes, Refactoring, etc.}
{Otherwise: group by area}
### {Area/Package/Type 1}
- {specific change}
### {Area/Package/Type 2}
- {specific change}
(use a flat bullet list instead of grouped headings if changes are simple/few)
{If breaking changes detected:}
## Breaking Changes
- {description of breaking change and migration path}
## Related Issues
{List any GitHub issue references found in commits or branch name}
{Use "Closes #123" for issues this PR fixes}
{Use "Relates to #123" for referenced but not fixed issues}
(OMIT this section if no issue references found)
## Screenshots
{embedded images if URLs were provided}
{TODO: drag-and-drop images in browser — if local file paths were provided}
(OMIT this entire section if no images)
## Testing
- [ ] {test item inferred from the changes}
- [ ] {build/compile verification relevant to the project}
- [ ] {manual verification step if applicable}
---
*Generated with [Claude Code](https://claude.com/claude-code)*If config specifies body_footer, append it before the "Generated with Claude Code" line.
Fill in each section by actually reading and understanding the diff/commits — do not use generic placeholders. The summary and changes sections should reflect what actually changed.
Present a full preview to the user. Format it clearly:
=== PR Preview ===
Title: [DP-1067] Add safe area support
Base: main
Assignee: mosamaasif
Reviewer: alice (CODEOWNERS), bob (recent author)
Labels: client, mobile, size/M
Draft: Yes
Size: M (142 lines changed)
Template: .github/PULL_REQUEST_TEMPLATE.md
{If stacked PR: "Note: Stacked PR targeting `feature/base-branch`"}
{If monorepo: "Packages: @app/payments, @app/auth"}
--- Body ---
(full PR body from Phase 3)
--- End Body ---Then ask via AskUserQuestion:
If user picks "Edit details", ask what they want to change, apply the change, and show the preview again. Loop until they approve or abort.
git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/nullgit push -u origin HEADgit pushgh pr create: gh pr create \
--base "{base}" \
--title "{title}" \
--assignee "{assignee}" \
--reviewer "{reviewer1},{reviewer2}" \
--label "{label1},{label2}" \
{--draft if draft} \
--body "$(cat <<'PRBODYEOF'
{generated body}
PRBODYEOF
)"--reviewer if none selected--label if none selected--draft if not draftgh pr create--images, remind: "Local images can't be uploaded via CLI. Open the PR in your browser and drag-and-drop the images into the Screenshots section."gh pr checks."[TASK-ID] prefix from title and omit ticket-related content from body.[HOTFIX] prefix. No ticket link.gh pr edit flow.--stat for body generation.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.