
Get Pr Comments
Fetch and flatten all GitHub PR comments—issue, review, and inline—so your agent can triage feedback without clicking through the UI.
Install
npx skills add https://github.com/gupsammy/claudest --skill get-pr-commentsWhat is this skill?
- Python CLI: fetch_pr_comments.py with PR number and optional --repo OWNER/REPO
- Output modes: json or text for agent consumption
- Covers issue-level comments, review bodies, and inline review comments via gh api
- Paginates GitHub API responses with --slurp flattening for complete threads
- Detects bot authors via login suffix heuristics for filtering noise
Adoption & trust: 1 installs on skills.sh; 253 GitHub stars; 2/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
Recommended Skills
Triagemattpocock/skills
Caveman Commitjuliusbrussee/caveman
Using Git Worktreesobra/superpowers
Finishing A Development Branchobra/superpowers
Git Commitgithub/awesome-copilot
Git Guardrails Claude Codemattpocock/skills
Journey fit
Common Questions / FAQ
Is Get Pr Comments safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Get Pr Comments
#!/usr/bin/env python3 """ Fetch and organize all PR comments (issue-level, review bodies, inline review comments). Usage: fetch_pr_comments.py <pr-number> [--repo OWNER/REPO] [--output json|text] Examples: fetch_pr_comments.py 31 fetch_pr_comments.py 33 --repo gupsammy/Claudest --output json """ from __future__ import annotations import argparse import json import re import subprocess import sys BOT_SUFFIXES = ("[bot]",) def run_gh(args: list[str]) -> str: result = subprocess.run( ["gh"] + args, capture_output=True, text=True, timeout=30 ) if result.returncode != 0: print(f"gh command failed: {result.stderr.strip()}", file=sys.stderr) sys.exit(2) return result.stdout.strip() def _parse_slurped(raw: str) -> list: """Parse --slurp output which wraps each page's array in an outer array.""" if not raw: return [] pages = json.loads(raw) # --slurp produces [[page1...], [page2...], ...] — flatten to single list return [item for page in pages for item in page] def is_bot(login: str) -> bool: return any(login.endswith(s) for s in BOT_SUFFIXES) def _api_prefix(repo: str | None) -> str: """Return the repos/OWNER/REPO or repos/{owner}/{repo} prefix for gh api.""" if repo: return f"repos/{repo}" return "repos/{owner}/{repo}" def fetch_issue_comments(pr_number: int, repo: str | None) -> list[dict]: prefix = _api_prefix(repo) raw = run_gh(["api", f"{prefix}/issues/{pr_number}/comments", "--paginate", "--slurp"]) items = _parse_slurped(raw) return [ { "type": "issue_comment", "id": c["id"], "user": c["user"]["login"], "is_bot": is_bot(c["user"]["login"]), "body": c["body"], "created_at": c["created_at"], "url": c.get("html_url", ""), } for c in items ] def fetch_reviews(pr_number: int, repo: str | None) -> list[dict]: prefix = _api_prefix(repo) raw = run_gh(["api", f"{prefix}/pulls/{pr_number}/reviews", "--paginate", "--slurp"]) items = _parse_slurped(raw) return [ { "type": "review", "id": r["id"], "user": r["user"]["login"], "is_bot": is_bot(r["user"]["login"]), "state": r["state"], "body": r["body"], "submitted_at": r.get("submitted_at", ""), "url": r.get("html_url", ""), } for r in items if r["body"].strip() # skip empty review bodies ] def fetch_inline_comments(pr_number: int, repo: str | None) -> list[dict]: prefix = _api_prefix(repo) raw = run_gh(["api", f"{prefix}/pulls/{pr_number}/comments", "--paginate", "--slurp"]) items = _parse_slurped(raw) return [ { "type": "inline_comment", "id": c["id"], "user": c["user"]["login"], "is_bot": is_bot(c["user"]["login"]), "body": c["body"], "path": c.get("path", ""), "line": c.get("line") or c.get("original_line"), "side": c.get("side", ""), "diff_hunk": c.get("diff_hunk", ""), "created_at": c["created_at"], "url": c.get("html_url", ""), "in_reply_to_id": c.get("in_reply_to_id"), "commit_id": c.get("commit_id", ""), } for c in items ] # --- Actionable item extraction --- MUST_FIX_PATTERNS = [ re.compile(r"^###?\s*must.fix", re.IGNORECASE | re.MULTILINE), re.compile(r"\*\*must.fix\*\*", re.IGNORECASE), re.compile(r"^###?\s*(?:required|blocking|critical)", re.IGNORECASE | re.MULTILINE), ] OPTIONAL_PATTERNS = [ re.compile(r"^###?\s*optional", re.IGNORECASE | re.MULTILINE), re.compile(r"\*\*optional\*\*", re.IGNORECASE), re.compile(r"^###?\s*(?:suggestions?|nit|minor|non-blocking)", re.IGNORECASE | re.MULTILINE), ] INLINE_SE