
Gh Pr Metadata
- 41 installs
- 2.6k repo stars
- Updated August 4, 2026
- module-federation/core
Helps with ai & agent building tasks.
About
gh-pr-metadata is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- gh-pr-metadata
- AI & Agent Building
- AI-coding skill
Gh Pr Metadata by the numbers
- 41 all-time installs (skills.sh)
- Ranked #8,104 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/module-federation/core --skill gh-pr-metadataAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 41 |
|---|---|
| repo stars | ★ 2.6k |
| Last updated | August 4, 2026 |
| Repository | module-federation/core ↗ |
What it does
Helps with ai & agent building tasks.
Files
GH PR Metadata
Overview
Normalize the current branch's GitHub PR metadata to this repo's expectations. Keep the title in conventional-commit style, keep the body aligned to .github/pull_request_template.md, and validate before handoff.
Ground the PR metadata in the live branch state, not stale branch names or old commit subjects. Always inspect the current branch diff versus its base before rewriting the PR title/body.
Read references/repo-pr-format.md when you need the exact section order, checklist items, or a title example.
Workflow
1. Resolve the current branch PR.
gh pr view --json number,title,body,url,headRefName,baseRefName2. Inspect the current branch state against the PR base branch.
At minimum, check:
git diff --name-status origin/<base>...HEAD
git diff --stat origin/<base>...HEAD
git log --oneline --decorate --no-merges origin/<base>..HEADUse these to answer:
- what files actually differ from the base right now
- which changes are functional versus cleanup/tooling/docs
- whether the existing PR title/body still matches the current branch after rebases, reverts, or scope narrowing
3. Validate the current title and body.
python3 .codex/skills/gh-pr-metadata/scripts/validate_pr_metadata.py4. If the PR body needs a clean template scaffold, print one:
python3 .codex/skills/gh-pr-metadata/scripts/validate_pr_metadata.py --print-template5. Rewrite the PR title in conventional-commit style.
Rules:
- Prefer
type(scope): summary - Keep the title short and direct
- Use repo-typical types such as
fix,feat,docs,refactor,chore,test,ci,build,perf,revert - Keep the scope tight to the affected package or subsystem when useful
- Do not add prefixes like
[codex] - Make sure the title describes the current branch diff, not the original branch intent if the branch was later narrowed or partially reverted
6. Rewrite the PR body to preserve the repo template structure:
## Description## Related Issue## Types of changes## Checklist
7. Update the PR with gh.
Prefer writing the body to a temporary file first, then:
gh pr edit --title "<new-title>" --body-file /tmp/pr-body.md8. Re-run validation and report whether the PR metadata is now compliant.
python3 .codex/skills/gh-pr-metadata/scripts/validate_pr_metadata.pyBody Guidance
- Keep
Descriptionprose-first and specific to the branch. - Reflect the branch as it exists now, especially after rebases, cleanups, or partial reverts.
- Summarize the real file-level themes from the live diff instead of copying commit messages mechanically.
- Put issue references in
Related Issue; if there is no issue, say so plainly instead of deleting the section. - In
Types of changes, check only the boxes that actually apply. - In
Checklist, preserve all repo checklist items and mark only the items that are true. - Do not remove required sections just because the PR is small.
- Keep the body concise; do not turn it into a changelog dump.
Title Guidance
Good examples:
fix(node): normalize remote chunk parsingchore(manifest): drop extra compat cleanupdocs(agents): prefer normalized webpack path requires
Bad examples:
update prfix stuff[codex] cleanup
Validation
Use the helper script to detect:
- non-conventional PR titles
- missing or reordered template sections
- missing repo checklist items
The script validates either the current PR from gh or explicit --title / --body-file input.
interface:
display_name: 'GH PR Metadata'
short_description: 'Normalize PR title and body'
default_prompt: "Use $gh-pr-metadata to update this repo's PR title and body to match the template and conventional-commit style."
Repo PR Format
Source files:
.github/pull_request_template.mdAGENTS.md
Required PR Body Sections
Keep these sections in this order:
1. ## Description 2. ## Related Issue 3. ## Types of changes 4. ## Checklist
Required Checklist Items
Types of changes:
- [ ] Docs change / refactoring / dependency upgrade- [ ] Bug fix (non-breaking change which fixes an issue)- [ ] New feature (non-breaking change which adds functionality)
Checklist:
- [ ] I have added tests to cover my changes.- [ ] All new and existing tests passed.- [ ] I have updated the documentation.
Title Convention
Prefer conventional-commit style:
type(scope): short summaryExamples:
fix(node): normalize remote chunk parsingdocs(agents): prefer normalized webpack path requireschore(manifest): drop extra compat cleanup
Avoid:
- bracketed prefixes like
[codex] - vague summaries like
update pr - titles that do not describe the branch's actual change
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from pathlib import Path
TITLE_RE = (
r"^(build|chore|ci|docs|feat|fix|perf|refactor|revert|test)"
r"(\([^)]+\))?!?: .+"
)
DEFAULT_TEMPLATE = """## Description
<!--- Provide a general summary of your changes in the Title above -->
<!--- Describe your changes in detail -->
## Related Issue
<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->
## Types of changes
- [ ] Docs change / refactoring / dependency upgrade
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
## Checklist
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
- [ ] I have updated the documentation.
"""
def run(cmd: list[str], cwd: Path) -> str:
proc = subprocess.run(
cmd,
cwd=str(cwd),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False,
)
if proc.returncode != 0:
raise RuntimeError(proc.stderr.strip() or proc.stdout.strip())
return proc.stdout
def read_current_pr(cwd: Path) -> tuple[str, str]:
raw = run(
["gh", "pr", "view", "--json", "title,body", "--jq", "{title: .title, body: .body}"],
cwd,
)
data = json.loads(raw)
return data["title"], data["body"] or ""
def load_body(args: argparse.Namespace, cwd: Path) -> tuple[str, str]:
if args.title or args.body or args.body_file:
title = args.title or ""
if args.body_file:
body = Path(args.body_file).read_text()
else:
body = args.body or ""
return title, body
return read_current_pr(cwd)
def load_repo_template(cwd: Path) -> str:
template_path = cwd / ".github" / "pull_request_template.md"
if template_path.exists():
return template_path.read_text()
return DEFAULT_TEMPLATE
def parse_template_requirements(template: str) -> tuple[list[str], list[str], list[str]]:
sections: list[str] = []
type_lines: list[str] = []
checklist_lines: list[str] = []
current_section = ""
for raw_line in template.splitlines():
line = raw_line.strip()
if line.startswith("## "):
sections.append(line)
current_section = line
continue
if not line.startswith("- [ ] "):
continue
if current_section == "## Types of changes":
type_lines.append(line)
elif current_section == "## Checklist":
checklist_lines.append(line)
return sections, type_lines, checklist_lines
def validate_title(title: str) -> list[str]:
import re
errors: list[str] = []
if not title:
errors.append("title is empty")
return errors
if not re.match(TITLE_RE, title):
errors.append(
"title does not match conventional format "
"(expected `type(scope): summary` or `type: summary`)"
)
if title.startswith("["):
errors.append("title should not start with a bracketed prefix")
return errors
def validate_body(
body: str,
required_sections: list[str],
required_type_lines: list[str],
required_checklist_lines: list[str],
) -> list[str]:
errors: list[str] = []
positions: list[int] = []
for section in required_sections:
idx = body.find(section)
if idx == -1:
errors.append(f"missing section: {section}")
positions.append(idx)
valid_positions = [p for p in positions if p != -1]
if valid_positions and valid_positions != sorted(valid_positions):
errors.append("required sections are out of order")
for line in required_type_lines:
if line not in body:
errors.append(f"missing type checkbox: {line}")
for line in required_checklist_lines:
if line not in body:
errors.append(f"missing checklist item: {line}")
return errors
def print_template(cwd: Path) -> None:
sys.stdout.write(load_repo_template(cwd))
def main() -> int:
parser = argparse.ArgumentParser(
description="Validate PR title/body against repo conventions."
)
parser.add_argument("--title", help="Explicit PR title to validate.")
parser.add_argument("--body", help="Explicit PR body to validate.")
parser.add_argument("--body-file", help="Path to a PR body file to validate.")
parser.add_argument(
"--repo-root",
default=".",
help="Repo root. Defaults to current working directory.",
)
parser.add_argument(
"--print-template",
action="store_true",
help="Print a repo-compliant PR body scaffold and exit.",
)
parser.add_argument(
"--format",
choices=["text", "json"],
default="text",
help="Output format.",
)
args = parser.parse_args()
cwd = Path(args.repo_root).resolve()
if args.print_template:
print_template(cwd)
return 0
required_sections, required_type_lines, required_checklist_lines = (
parse_template_requirements(load_repo_template(cwd))
)
title, body = load_body(args, cwd)
errors = validate_title(title) + validate_body(
body,
required_sections,
required_type_lines,
required_checklist_lines,
)
payload = {
"ok": not errors,
"title": title,
"errors": errors,
}
if args.format == "json":
sys.stdout.write(json.dumps(payload, indent=2) + "\n")
else:
if errors:
sys.stdout.write("PR metadata validation failed:\n")
for error in errors:
sys.stdout.write(f"- {error}\n")
else:
sys.stdout.write("PR metadata validation passed.\n")
return 0 if not errors else 1
if __name__ == "__main__":
raise SystemExit(main())