| name | github |
| version | 4.0.0 |
| description | Execute GitHub operations (PRs, issues, milestones, labels, comments, merges) using Python scripts with structured output and error handling. Use when working with pull requests, issues, review comments, CI checks, or milestones instead of raw gh. Use when you say "create a PR", "add label to issue", or "check CI status". Do NOT use to read a pasted github.com URL (use github-url-intercept). |
| license | MIT |
| metadata | {"domains":["github","pr","issue","labels","milestones","comments","reactions"],"type":"integration","complexity":"intermediate","generator":{"keep_headings":["Decision Tree","Offline Invocation","Script Reference","Output Format","See Also"]}} |
GitHub Skill
Use these scripts instead of raw gh commands for consistent error handling and structured output.
Triggers
| Phrase | Operation |
|---|
create a PR | new_pr.py |
respond to review comments | post_pr_comment_reply.py |
check CI status | get_pr_checks.py / get_pr_check_logs.py |
close issue | close_issue.py |
add label to issue | set_issue_labels.py |
Decision Tree
Need GitHub data?
├─ List PRs (filtered) → get_pull_requests.py
├─ PR info/diff → get_pr_context.py
├─ CI check status → get_pr_checks.py
├─ CI failure logs → get_pr_check_logs.py
├─ Review comments → get_pr_review_comments.py
├─ Review threads → get_pr_review_threads.py
├─ Unique reviewers → get_pr_reviewers.py
├─ Unaddressed bot comments → get_unaddressed_comments.py
├─ PR merged check → test_pr_merged.py
├─ Copilot follow-up PRs → detect_copilot_followup_pr.py
├─ Validate PR description → validate_pr_description.py
├─ Issue info → get_issue_context.py
├─ Merge readiness check → test_pr_merge_ready.py
├─ Latest milestone → get_latest_semantic_milestone.py
├─ Actionable backlog → get_actionable_items.py
└─ Need to take action?
├─ Create issue → new_issue.py
├─ Create PR → new_pr.py
├─ Reply to review → post_pr_comment_reply.py
├─ Reply to thread (GraphQL) → add_pr_review_thread_reply.py
├─ Comment on issue → post_issue_comment.py
├─ Add reaction → add_comment_reaction.py
├─ Apply labels → set_issue_labels.py
├─ Set issue milestone → set_issue_milestone.py
├─ Set PR/issue milestone (auto-detect) → set_item_milestone.py
├─ Assign issue → set_issue_assignee.py
├─ Resolve threads → resolve_pr_review_thread.py
├─ Unresolve threads → unresolve_pr_review_thread.py
├─ Process AI triage → invoke_pr_comment_processing.py
├─ Assign Copilot → invoke_copilot_assignment.py
├─ Enable/disable auto-merge → set_pr_auto_merge.py
├─ Close PR → close_pr.py
└─ Merge PR → merge_pr.py
Offline Invocation
In a network-restricted sandbox (triage, PR status checks), run read-only PR
scripts under scripts/pr/ with bare python3, not uv run.
uv run <script> resolves the whole project environment first, which downloads
anthropic==0.116.0 (a core dependency in pyproject.toml) from PyPI and times
out with no network. The read-only PR scripts never import the anthropic SDK.
They import only github_core, which parses YAML with a vendored fallback when
PyYAML is absent (issue #1844), so no third-party import is required.
SCRIPTS_DIR="${COPILOT_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-.claude}}/skills/github/scripts/pr"
python3 "$SCRIPTS_DIR/get_pull_requests.py" --state open
python3 "$SCRIPTS_DIR/get_pr_context.py" --pr <N>
python3 "$SCRIPTS_DIR/get_pr_checks.py" --pr <N>
Any python3 works: github_core falls back to a vendored YAML parser when
PyYAML is absent (issue #1844), confirmed by running these scripts with both
PyYAML and the anthropic SDK blocked. The interpreter from
scripts/bootstrap-vm.sh and a project .venv/bin/python3 both ship PyYAML for
the faster path. Do not use uv run for read-only triage offline; it forces the
resolve that fetches anthropic.
The regression guard tests/test_pr_scripts_offline.py asserts the read-only PR
scripts and github_core import with the anthropic SDK blocked.
Scripts
PR Operations (scripts/pr/)
| Script | Purpose | Key Parameters |
|---|
get_pull_requests.py | List PRs with filters | --state, --label, --author, --base, --head, --search, --limit |
get_pr_context.py | PR metadata, diff, files | --pull-request, --include-changed-files, --include-diff |
get_pr_checks.py | CI check status, polling | --pull-request, --wait, --timeout-seconds, --required-only, --output-format {json,human,auto} |
get_pr_check_logs.py | Fetch logs from failing CI checks | --pull-request, --max-lines, --context-lines |
get_pr_review_comments.py | Paginated review comments with stale detection | --pull-request, --include-issue-comments, --detect-stale, --exclude-stale, --only-stale |
get_pr_review_threads.py | Thread-level review data | --pull-request, --unresolved-only |
get_pr_reviewers.py | Enumerate unique reviewers | --pull-request, --exclude-bots |
get_unaddressed_comments.py | Bot comments needing attention | --pull-request |
get_unresolved_review_threads.py | Unresolved thread IDs | --pull-request |
wait_for_unresolved_zero.py | Settling gate: poll until unresolved count holds at zero across bot scans | --pull-request, , , |
Issue Operations (scripts/issue/)
| Script | Purpose | Key Parameters |
|---|
get_issue_context.py | Issue metadata (no comments) | --issue |
get_issue_comments.py | Issue comment thread (discourse) | --issue, --limit |
new_issue.py | Create new issue | --title, --body, --labels |
close_issue.py | Close with optional comment (--verify-claims aborts when a cited commit/PR does not resolve on the remote) | --issue, --reason, --comment, --verify-claims |
reopen_issue.py | Reopen with optional comment | --issue, --comment |
set_issue_labels.py | Apply labels (auto-create) | --issue, --labels, --priority |
set_issue_milestone.py | Assign milestone | --issue, --milestone |
post_issue_comment.py | Comments with idempotency | --issue, --body, --marker |
invoke_copilot_assignment.py | Synthesize context for Copilot | --issue-number, --what-if |
set_issue_assignee.py | Assign users to issues | --issue, --assignees |
Milestone Operations (scripts/milestone/)
| Script | Purpose | Key Parameters |
|---|
get_latest_semantic_milestone.py | Detect latest semantic version milestone | --owner, --repo |
set_item_milestone.py | Assign milestone to PR/issue (auto-detect) | --item-type, --item-number, --milestone-title |
Reactions (scripts/reactions/)
| Script | Purpose | Key Parameters |
|---|
add_comment_reaction.py | Add emoji reactions (batch support) | --comment-id, --reaction, --comment-type, --pull-request |
Notifications (scripts/notifications/)
| Script | Purpose | Key Parameters |
|---|
get_actionable_items.py | List actionable backlog (reviews, authored PRs, assigned issues) | --owner, --repo, --limit |
Utilities (scripts/utils/)
| Script | Purpose | Key Parameters |
|---|
extract_github_context.py | Extract issue/PR references from text | --text, --require-pr, --require-issue |
Workflow Testing (scripts/)
| Script | Purpose | Key Parameters |
|---|
test_workflow_locally.py | Test GitHub Actions locally with act | --workflow, --event, --job, --dry-run |
Output Format
All scripts output structured JSON wrapped in a standard envelope per ADR-051.
Success envelope:
{
"Success": true,
"Data": { "Number": 42, "Title": "..." },
"Error": null,
"Metadata": { "Script": "get_pr_checks.py", "Version": "1.0.0", "Timestamp": "..." }
}
Error envelope:
{
"Success": false,
"Data": null,
"Error": { "Message": "PR not found", "Code": 2, "Type": "NotFound" },
"Metadata": { "Script": "get_pr_checks.py", "Version": "1.0.0", "Timestamp": "..." }
}
Usage:
SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT:-.claude}/skills/github/scripts"
result=$(python3 "$SCRIPTS_DIR/pr/get_pr_context.py" --pull-request 50)
echo "$result" | jq '.Data'
Exit codes follow ADR-035: 0=success, 1=logic error, 2=config error, 3=external failure, 4=auth error.
Process
This skill provides a toolkit of Python scripts for GitHub operations. Use scripts directly or compose them into workflows.
Basic Usage:
- Identify the operation needed using the Decision Tree
- Find the corresponding script in the Script Reference
- Call the script with required parameters
- Parse the JSON output
Example Flow:
SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT:-.claude}/skills/github/scripts"
python3 "$SCRIPTS_DIR/pr/get_pr_context.py" --pull-request 123
python3 "$SCRIPTS_DIR/pr/get_pr_checks.py" --pull-request 123
python3 "$SCRIPTS_DIR/pr/post_pr_comment_reply.py" --pull-request 123 --comment-id 456 --body "CI failures detected"
GitHub Keywords for Issue Linking
GitHub automatically links and closes issues when PRs use specific keywords in PR descriptions, commit messages, or PR comments.
Supported Keywords
| Keyword | Variations | Example |
|---|
| Closes | close, closed | Closes #123 |
| Fixes | fix, fixed | Fixes #456 |
| Resolves | resolve, resolved | Resolves #789 |
Usage Patterns
In PR Descriptions:
## Summary
This PR adds feature X.
Closes #123
Fixes #456
In Commit Messages:
feat: Add feature X
Implements the new feature as specified.
Closes #123
Best Practices:
- Use keywords in PR description for primary issue
- Use keywords in commit bodies for related issues
- One keyword per line for clarity
- Place keywords in dedicated section or at end of description
Anti-Patterns
| Avoid | Why | Instead |
|---|
Raw gh pr view commands | No structured output | Use get_pr_context.py |
Raw gh api for comments | Doesn't preserve threading | Use post_pr_comment_reply.py |
| Replying to thread expecting auto-resolve | Replies DON'T auto-resolve threads | Use resolve_pr_review_thread.py after reply |
| Inline issue creation | Missing validation | Use new_issue.py |
| Multiple individual reactions | 88% slower | Use batch mode in add_comment_reaction.py |
| Hardcoding owner/repo | Breaks in forks | Let scripts infer from git remote |
| Ignoring exit codes | Missing error handling | Check exit codes per ADR-035 |
| Skipping idempotency markers | Duplicate comments | Use --marker parameter |
Raw gh notify or notifications API | 403 with app tokens | Use get_actionable_items.py |
See Also
Verification
Before completing a GitHub operation: