---
name: github-pr-workflow
description: "GitHub end-to-end: auth, repos, issues, PR lifecycle, code review, CI/CD, releases, codebase metrics. Covers gh and git+curl fallbacks."
version: 3.0.0
author: Hermes Agent
license: MIT
platforms: [linux, macos, windows]
metadata:
  hermes:
    tags: [GitHub, Pull-Requests, CI/CD, Issues, Code-Review, Repositories, Authentication, Git, Automation, Merge, Releases]
    related_skills: [software-quality-practices, plan]
    category: github
---

# GitHub End-to-End

Complete workflow for working with GitHub: authenticate, manage repositories, triage issues, open and land PRs, review code, and ship releases. Every section shows the `gh` way first, then the `git` + `curl` fallback for machines without `gh`.

> **Load this skill when the user mentions:** GitHub, PR, pull request, issue, repo, code review, CI/CD, release, fork, clone, or wants to land a change on GitHub.

## Prerequisites

### Setup

```bash
# Determine auth method (gh or curl)
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
  AUTH="gh"
else
  AUTH="git"
  if [ -z "$GITHUB_TOKEN" ]; then
    if _hermes_env="${HERMES_HOME:-$HOME/.hermes}/.env"; [ -f "$_hermes_env" ] && grep -q "^GITHUB_TOKEN=" "$_hermes_env"; then
      GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" "$_hermes_env" | head -1 | cut -d= -f2 | tr -d '\n\r')
    elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
      GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
    fi
  fi
fi

# Extract owner/repo from git remote
REMOTE_URL=$(git remote get-url origin 2>/dev/null || true)
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
GH_USER=""
if [ "$AUTH" = "gh" ]; then GH_USER=$(gh api user --jq '.login' 2>/dev/null); fi
if [ -z "$GH_USER" ] && [ -n "$GITHUB_TOKEN" ]; then
  GH_USER=$(curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user | python3 -c "import sys,json; print(json.load(sys.stdin).get('login',''))")
fi
```

For convenience, source `scripts/github-auth-env.sh` to set all variables automatically. This helper is the consolidated auth detector previously found in `github-auth`.

---

## 1. Authentication

### Method 1: Git-Only Authentication

Use when `gh` is not installed. No root/sudo needed.

#### Option A: HTTPS with Personal Access Token (recommended)

1. **Create a token** at https://github.com/settings/tokens. Scopes needed: `repo`, `workflow`, `read:org`.
2. **Configure git:**

```bash
git config --global credential.helper store
git ls-remote https://github.com/<username>/<any-repo>.git
# Username: <github-username>
# Password: <paste the PAT, NOT the GitHub password>
```

3. **Set git identity:**

```bash
git config --global user.name "Their Name"
git config --global user.email "their-email@example.com"
```

**Alternatives:**

```bash
# Cache credentials in memory for 8 hours (no disk storage)
git config --global credential.helper 'cache --timeout=28800'

# Or embed token directly in a per-repo remote URL
git remote set-url origin https://<username>:<token>@github.com/<owner>/<repo>.git
```

#### Option B: SSH Key Authentication

```bash
# Check for existing keys
ls -la ~/.ssh/id_*.pub 2>/dev/null || echo "No SSH keys found"

# Generate a new key
ssh-keygen -t ed25519 -C "their-email@example.com" -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub
# Add the public key at https://github.com/settings/keys

# Test
ssh -T git@github.com

# Configure git to prefer SSH for GitHub
git config --global url."git@github.com:".insteadOf "https://github.com/"
```

### Method 2: gh CLI Authentication

```bash
# Interactive (desktop)
gh auth login

# Token-based (headless/servers)
echo "<TOKEN>" | gh auth login --with-token
gh auth setup-git

# Verify
gh auth status
```

### Troubleshooting Auth

| Problem | Solution |
|---------|----------|
| `git push` asks for password | GitHub removed password auth. Use a PAT as the password, or switch to SSH |
| `Permission to X denied` | Token lacks `repo` scope; regenerate with correct scopes |
| `fatal: Authentication failed` | Cached creds stale — run `git credential reject` then re-authenticate |
| `ssh: connect to host github.com port 22: Connection refused` | Use SSH over HTTPS port in `~/.ssh/config` (Port 443, Hostname ssh.github.com) |
| Multiple GitHub accounts | Different SSH keys per host alias in `~/.ssh/config`, or per-repo credential URLs |

---

## 2. Repository Management

### Clone

```bash
# Pure git
git clone https://github.com/owner/repo-name.git

# Shallow / specific branch
git clone --depth 1 --branch develop https://github.com/owner/repo-name.git

# Via SSH
git clone git@github.com:owner/repo-name.git

# With gh
gh repo clone owner/repo-name
```

### Create a New Repository

**With gh:**

```bash
gh repo create my-new-project --public --clone
gh repo create my-new-project --private --description "A useful tool" --license MIT --clone
gh repo create my-org/my-new-project --public --clone
```

**With curl:**

```bash
curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/user/repos \
  -d '{"name": "my-new-project", "description": "A useful tool", "private": false, "auto_init": true, "license_template": "mit"}'

git clone https://github.com/$GH_USER/my-new-project.git
cd my-new-project
git add . && git commit -m "Initial commit" && git push -u origin main
```

### Fork & Sync

**With gh:**

```bash
gh repo fork owner/repo-name --clone
gh repo sync $GH_USER/repo-name
```

**With git + curl:**

```bash
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/owner/repo-name/forks
sleep 3 && git clone https://github.com/$GH_USER/repo-name.git
cd repo-name && git remote add upstream https://github.com/owner/repo-name.git

# Sync
git fetch upstream && git checkout main && git merge upstream/main && git push origin main
```

### Repo Info & Settings

**With gh:**

```bash
gh repo view owner/repo-name
gh repo list --limit 20
gh repo edit --description "Updated" --add-topic "machine-learning,python"
```

**With curl:**

```bash
# Info
curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/$OWNER/$REPO \
  | python3 -c "import sys,json; r=json.load(sys.stdin); print(f'Stars: {r[\"stargazers_count\"]} Default: {r[\"default_branch\"]}')"

# Settings (enable wiki, auto-merge, etc.)
curl -s -X PATCH -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/$OWNER/$REPO \
  -d '{"has_wiki": false, "allow_auto_merge": true}'

# Topics
curl -s -X PUT -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/topics \
  -d '{"names": ["ml", "python"]}'
```

### Secrets, Releases, Actions

| Action | gh | curl endpoint |
|--------|-----|--------------|
| Set secret | `gh secret set KEY --body "val"` | `PUT /repos/{o}/{r}/actions/secrets/KEY` (encryption required) |
| Create release | `gh release create v1.0 --generate-notes` | `POST /repos/{o}/{r}/releases` |
| List workflows | `gh workflow list` | `GET /repos/{o}/{r}/actions/workflows` |
| Rerun CI | `gh run rerun ID` | `POST /repos/{o}/{r}/actions/runs/ID/rerun` |
| Branch protection | `gh repo edit --enable-auto-merge` | `PUT /repos/{o}/{r}/branches/NAME/protection` |

For full details on Actions, secrets encryption, and branch protection, see `references/github-api-cheatsheet.md`.

---

## 3. Issues

### List & View

**With gh:**

```bash
gh issue list
gh issue list --state open --label "bug" --assignee @me
gh issue view 42
```

**With curl:**

```bash
# List open issues (exclude PRs from the API)
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/repos/$OWNER/$REPO/issues?state=open&per_page=20" \
  | python3 -c "import sys,json; [print(f\"#{i['number']:5} {i['title']}\") for i in json.load(sys.stdin) if 'pull_request' not in i]"

# View
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/42 \
  | python3 -c "import sys,json; i=json.load(sys.stdin); print(f\"#{i['number']}: {i['title']}\n{i['body']}\")"
```

### Create, Label, Assign, Comment, Close

**With gh:**

```bash
gh issue create --title "Bug: ..." --body "## Description..." --label "bug,backend" --assignee username
gh issue edit 42 --add-label "priority:high" --remove-label "needs-triage"
gh issue comment 42 --body "Root cause found..."
gh issue close 42 --reason "not planned"
gh issue reopen 42
```

**With curl:**

```bash
# Create
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues \
  -d '{"title": "Bug", "body": "Steps...", "labels": ["bug"]}'

# Labels
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/42/labels \
  -d '{"labels": ["priority:high"]}'

# Assign
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/42/assignees \
  -d '{"assignees": ["username"]}'

# Comment
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/42/comments \
  -d '{"body": "Investigating..."}'

# Close / reopen
curl -s -X PATCH -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/42 \
  -d '{"state": "closed", "state_reason": "completed"}'
```

### Templates

Use `templates/bug-report.md` and `templates/feature-request.md` for new issues.

---

## 4. Pull Request Lifecycle

### Branch & Commit

```bash
git fetch origin && git checkout main && git pull origin main
git checkout -b feat/description

# Conventional commit message (see references/conventional-commits.md)
git commit -m "feat: add JWT-based authentication

- Add login/register endpoints
- Add User model with password hashing
- Add auth middleware
- Add unit tests"
```

### Open a PR

**With gh:**

```bash
gh pr create --title "feat: add auth" --body "## Summary..." --draft --reviewer user1,user2
```

**With curl:**

```bash
BRANCH=$(git branch --show-current)
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls \
  -d "{\"title\": \"feat: add auth\", \"body\": \"...\", \"head\": \"$BRANCH\", \"base\": \"main\", \"draft\": true}"
```

Use `templates/pr-body-bugfix.md` and `templates/pr-body-feature.md` for structured PR descriptions.

### Monitor CI

**With gh:**

```bash
gh pr checks
gh pr checks --watch
```

**With curl:**

```bash
SHA=$(git rev-parse HEAD)
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'Overall: {d[\"state\"]}'); [print(f'  {s[\"context\"]}: {s[\"state\"]}') for s in d.get('statuses',[])]"

# Poll loop
for i in $(seq 1 20); do
  STATUS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
    https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['state'])")
  echo "Check $i: $STATUS"
  [ "$STATUS" = "success" ] || [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ] && break
  sleep 30
done
```

For diagnosing CI failures, see `references/ci-troubleshooting.md`.

### Merge

**With gh:**

```bash
gh pr merge --squash --delete-branch
gh pr merge --auto --squash --delete-branch
```

**With curl:**

```bash
PR_NUMBER=<number>
curl -s -X PUT -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/merge \
  -d '{"merge_method": "squash", "commit_title": "feat: add auth (#'$PR_NUMBER')"}'
git push origin --delete $(git branch --show-current)
git checkout main && git pull origin main && git branch -d $(git branch --show-current)
```

---

## 5. Code Review

### Review Local Changes (Pre-Push)

```bash
git diff main...HEAD --stat
git diff main...HEAD

# Check for common issues in the diff
git diff main...HEAD | grep -n "print(\|console\.log\|TODO\|FIXME\|debugger"
git diff main...HEAD | grep -in "password\|secret\|api_key\|token.*="
git diff main...HEAD | grep -n "<<<<<<<\|=======\|>>>>>>>"
```

Present findings in this structure:

```
## Code Review Summary

### Critical
- **src/auth.py:45** — SQL injection risk. Suggestion: parameterized queries.

### Warnings
- **src/models.py:23** — Password stored in plaintext.

### Suggestions
- **src/utils.py:8** — Duplicates logic in core/utils.py:34. Consolidate.

### Looks Good
- Clean separation of concerns in middleware layer
```

### Review a PR on GitHub

**Step 1 — Gather context:**

```bash
gh pr view 123
gh pr diff 123 --name-only
```

**Step 2 — Check out locally:**

```bash
git fetch origin pull/123/head:pr-123
git checkout pr-123
```

**Step 3 — Review and run checks:**

```bash
git diff main...pr-123
python -m pytest 2>&1 | tail -20
ruff check . 2>&1 | head -30
```

**Step 4 — Post review:**

```bash
# General comment
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/123/comments \
  -d '{"body": "Overall looks good..."}'

# Formal review with inline comments
HEAD_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/123 \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")

curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/123/reviews \
  -d "{
    \"commit_id\": \"$HEAD_SHA\",
    \"event\": \"REQUEST_CHANGES\",
    \"body\": \"See inline comments.\",
    \"comments\": [
      {\"path\": \"src/auth.py\", \"line\": 45, \"body\": \"🔴 Use parameterized queries.\"},
      {\"path\": \"src/models.py\", \"line\": 23, \"body\": \"⚠️ Hash passwords with bcrypt.\"}
    ]
  }"
```

For the review output template, see `references/review-output-template.md`.

### Review Checklist

- **Correctness** — edge cases, nulls, concurrency, error paths
- **Security** — no hardcoded secrets, input validation, parameterized queries, auth checks
- **Quality** — clear naming, DRY, focused functions
- **Testing** — new paths tested, happy path + errors covered
- **Performance** — N+1 queries, blocking operations, caching
- **Documentation** — public APIs documented, non-obvious logic commented

---

## 6. Codebase Metrics & Inspection

Quick repository analysis for size, language composition, and code-to-comment ratios using `pygount`.

### Install pygount

```bash
pip install --break-system-packages pygount 2>/dev/null || pip install pygount
```

### Full Language Summary

```bash
cd /path/to/repo
pygount --format=summary \
  --folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,.eggs,*.egg-info" \
  .
```

**Always use `--folders-to-skip`** — without it, pygount crawls dependency trees and may hang.

### Project-Type Exclusions

```bash
# Python projects
--folders-to-skip=".git,venv,.venv,__pycache__,.cache,dist,build,.tox,.eggs,.mypy_cache"

# JavaScript/TypeScript projects
--folders-to-skip=".git,node_modules,dist,build,.next,.cache,.turbo,coverage"

# General catch-all
--folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,vendor,third_party"
```

### Filter by Language

```bash
pygount --suffix=py --format=summary .
pygount --suffix=py,yaml,yml --format=summary .
```

### Detailed File-by-File Output

```bash
pygount --folders-to-skip=".git,node_modules,venv" .
pygount --folders-to-skip=".git,node_modules,venv" . | sort -t$'\t' -k1 -nr | head -20
```

### Output Formats

```bash
pygount --format=summary .      # human-readable table
pygount --format=json .         # structured JSON for scripting
```

### Interpreting Results

| Column | Meaning |
|--------|---------|
| Language | Detected programming language |
| Files | Number of files of that language |
| Code | Lines of actual code |
| Comment | Lines of comments/documentation |
| % | Percentage of total |

Special pseudo-languages: `__empty__`, `__binary__`, `__generated__`, `__duplicate__`, `__unknown__`.

### Pitfalls

1. **Markdown shows 0 code lines** — pygount classifies all Markdown as comments. Expected behavior.
2. **JSON files show low code counts** — use `wc -l` for accurate JSON line counts.
3. **Large monorepos** — use `--suffix` to target specific languages rather than scanning everything.

---

## 7. Code Review

### Review Local Changes (Pre-Push)

```bash
git diff main...HEAD --stat
git diff main...HEAD

# Check for common issues in the diff
git diff main...HEAD | grep -n "print(\|console\.log\|TODO\|FIXME\|debugger"
git diff main...HEAD | grep -in "password\|secret\|api_key\|token.*="
git diff main...HEAD | grep -n "<<<<<<<\|=======\|>>>>>>>"
```

Present findings in this structure:

```
## Code Review Summary

### Critical
- **src/auth.py:45** — SQL injection risk. Suggestion: parameterized queries.

### Warnings
- **src/models.py:23** — Password stored in plaintext.

### Suggestions
- **src/utils.py:8** — Duplicates logic in core/utils.py:34. Consolidate.

### Looks Good
- Clean separation of concerns in middleware layer
```

### Review a PR on GitHub

**Step 1 — Gather context:**

```bash
gh pr view 123
gh pr diff 123 --name-only
```

**Step 2 — Check out locally:**

```bash
git fetch origin pull/123/head:pr-123
git checkout pr-123
```

**Step 3 — Review and run checks:**

```bash
git diff main...pr-123
python -m pytest 2>&1 | tail -20
ruff check . 2>&1 | head -30
```

**Step 4 — Post review:**

```bash
# General comment
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/123/comments \
  -d '{"body": "Overall looks good..."}'

# Formal review with inline comments
HEAD_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/123 \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")

curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/123/reviews \
  -d "{
    \"commit_id\": \"$HEAD_SHA\",
    \"event\": \"REQUEST_CHANGES\",
    \"body\": \"See inline comments.\",
    \"comments\": [
      {\"path\": \"src/auth.py\", \"line\": 45, \"body\": \"🔴 Use parameterized queries.\"},
      {\"path\": \"src/models.py\", \"line\": 23, \"body\": \"⚠️ Hash passwords with bcrypt.\"}
    ]
  }"
```

For the review output template, see `references/review-output-template.md`.

### Review Checklist

- **Correctness** — edge cases, nulls, concurrency, error paths
- **Security** — no hardcoded secrets, input validation, parameterized queries, auth checks
- **Quality** — clear naming, DRY, focused functions
- **Testing** — new paths tested, happy path + errors covered
- **Performance** — N+1 queries, blocking operations, caching
- **Documentation** — public APIs documented, non-obvious logic commented

## 8. Support Files

- `scripts/github-auth-env.sh` — Bash helper that detects auth method and sets environment variables (consolidated from `github-auth`)
- `references/conventional-commits.md` — Commit format guide with types and examples
- `references/ci-troubleshooting.md` — Diagnose test, lint, build, permission, and timeout failures
- `references/github-api-cheatsheet.md` — Full curl-based GitHub API reference for repos, actions, secrets, releases, and branch protection
- `references/review-output-template.md` — Structured review summary with severity guide
- `references/review-output-template-from-github-code-review.md` — Alternative review-output template inherited from the archived `github-code-review` skill
- `templates/bug-report.md` — Markdown bug report template for new issues
- `templates/feature-request.md` — Markdown feature request template for new issues
- `templates/pr-body-bugfix.md` — PR description template for bug fixes
- `templates/pr-body-feature.md` — PR description template for new features
