Files
magnus919_agent-skills/opensource-contributions/scripts/check-pr-template-compliance.py
T
Magnus Hedemark c5c4d2434a feat: add opensource-contributions skill
Comprehensive open source contribution guidance with progressive disclosure.
Restructured from a single ~23K-token monolithic SKILL.md into a concise
~1.2K-token orchestrator with 10 focused reference files.

Changes:
- SKILL.md: 1,720 lines → 100 lines (AgentSkills.io compliant frontmatter)
- 10 new reference files covering phases 0a-4, pitfalls, default posture
- Portable PR template compliance checker script (stdlib-only)
- All personal context scrubbed for public export
- AGENTS.md + README.md updated with trigger table entry

Signed-off-by: Magnus Hedemark <magnus919@pm.me>
2026-05-28 21:43:30 -04:00

101 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""
PR Template Compliance Checker
Validates that a PR body matches the structure of the project's PR template.
Exits 0 (compliant) or 1 (non-compliant) with a reason.
Usage:
python3 check-pr-template-compliance.py /tmp/pr-body.md /tmp/pr-template.md
The checker verifies:
- Every section header from the template appears in the body
- Required sections are not empty
- Checkboxes are present when the template includes them
"""
import re
import sys
def extract_section_headers(text: str) -> list[str]:
"""Extract markdown section headers (## or ###) from text."""
headers = re.findall(r'^(#{2,3})\s+(.+)$', text, re.MULTILINE)
return [h[1].strip() for h in headers]
def extract_checkboxes(text: str) -> list[str]:
"""Extract checkbox items from text."""
return re.findall(r'-\s*\[\s*[ x]?\s*\]\s*(.+)', text)
def has_content(text: str, header: str) -> bool:
"""Check if a section has substantive content (not just N/A or empty)."""
pattern = re.compile(rf'^#{{2,3}}\s+{re.escape(header)}\s*$', re.MULTILINE)
match = pattern.search(text)
if not match:
return False
# Get content after this header until the next header or end
start = match.end()
remainder = text[start:].lstrip('\n')
# Find next header
next_header = re.search(r'^#{2,3}\s+', remainder, re.MULTILINE)
section_text = remainder[:next_header.start()] if next_header else remainder
section_text = section_text.strip()
# Empty or only N/A/not applicable
if not section_text:
return False
if section_text.lower() in ('n/a', 'na', 'not applicable', 'none'):
return False
return True
def main():
if len(sys.argv) < 3:
print("Usage: check-pr-template-compliance.py <pr-body.md> <pr-template.md>")
sys.exit(1)
body_path = sys.argv[1]
template_path = sys.argv[2]
try:
with open(body_path) as f:
body = f.read()
except FileNotFoundError:
print(f"FAIL: PR body file not found: {body_path}")
sys.exit(1)
try:
with open(template_path) as f:
template = f.read()
except FileNotFoundError:
print(f"SKIP: No template found at {template_path} — skipping compliance check")
sys.exit(0)
template_headers = extract_section_headers(template)
body_headers = extract_section_headers(body)
missing = [h for h in template_headers if h not in body_headers]
empty_sections = [h for h in template_headers if h in body_headers and not has_content(body, h)]
if missing:
print(f"FAIL: Missing required template sections: {', '.join(missing)}")
sys.exit(1)
if empty_sections:
print(f"WARN: Empty or N/A sections: {', '.join(empty_sections)}")
# Check checkboxes
template_checkboxes = extract_checkboxes(template)
body_checkboxes = extract_checkboxes(body)
missing_boxes = [c for c in template_checkboxes if c not in body_checkboxes]
if missing_boxes:
print(f"WARN: Missing checkboxes: {', '.join(missing_boxes)}")
print("PASS: PR body complies with template")
sys.exit(0)
if __name__ == '__main__':
main()