Buckets:
| name: github-issues | |
| description: "Create, triage, label, assign GitHub issues via gh or REST." | |
| version: 1.1.0 | |
| author: Hermes Agent | |
| license: MIT | |
| platforms: [linux, macos, windows] | |
| metadata: | |
| hermes: | |
| tags: [GitHub, Issues, Project-Management, Bug-Tracking, Triage] | |
| related_skills: [github-auth, github-pr-workflow] | |
| # GitHub Issues Management | |
| Create, search, triage, and manage GitHub issues. Each section shows `gh` first, then the `curl` fallback. | |
| ## Prerequisites | |
| - Authenticated with GitHub (see `github-auth` skill) | |
| - Inside a git repo with a GitHub remote, or specify the repo explicitly | |
| ### Setup | |
| ```bash | |
| if command -v gh &>/dev/null && gh auth status &>/dev/null; then | |
| AUTH="gh" | |
| else | |
| AUTH="git" | |
| if [ -z "$GITHUB_TOKEN" ]; then | |
| if [ -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 | |
| REMOTE_URL=$(git remote get-url origin) | |
| 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) | |
| ``` | |
| --- | |
| ## 1. Viewing Issues | |
| **With gh:** | |
| ```bash | |
| gh issue list | |
| gh issue list --state open --label "bug" | |
| gh issue list --assignee @me | |
| gh issue list --search "authentication error" --state all | |
| gh issue view 42 | |
| ``` | |
| **With curl:** | |
| ```bash | |
| # List open issues | |
| 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 | |
| for i in json.load(sys.stdin): | |
| if 'pull_request' not in i: # GitHub API returns PRs in /issues too | |
| labels = ', '.join(l['name'] for l in i['labels']) | |
| print(f\"#{i['number']:5} {i['state']:6} {labels:30} {i['title']}\")" | |
| # Filter by label | |
| curl -s \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| "https://api.github.com/repos/$OWNER/$REPO/issues?state=open&labels=bug&per_page=20" \ | |
| | python3 -c " | |
| import sys, json | |
| for i in json.load(sys.stdin): | |
| if 'pull_request' not in i: | |
| print(f\"#{i['number']} {i['title']}\")" | |
| # View a specific issue | |
| 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) | |
| labels = ', '.join(l['name'] for l in i['labels']) | |
| assignees = ', '.join(a['login'] for a in i['assignees']) | |
| print(f\"#{i['number']}: {i['title']}\") | |
| print(f\"State: {i['state']} Labels: {labels} Assignees: {assignees}\") | |
| print(f\"Author: {i['user']['login']} Created: {i['created_at']}\") | |
| print(f\"\n{i['body']}\")" | |
| # Search issues | |
| curl -s \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| "https://api.github.com/search/issues?q=authentication+error+repo:$OWNER/$REPO" \ | |
| | python3 -c " | |
| import sys, json | |
| for i in json.load(sys.stdin)['items']: | |
| print(f\"#{i['number']} {i['state']:6} {i['title']}\")" | |
| ``` | |
| ## 2. Creating Issues | |
| **With gh:** | |
| ```bash | |
| gh issue create \ | |
| --title "Login redirect ignores ?next= parameter" \ | |
| --body "## Description | |
| After logging in, users always land on /dashboard. | |
| ## Steps to Reproduce | |
| 1. Navigate to /settings while logged out | |
| 2. Get redirected to /login?next=/settings | |
| 3. Log in | |
| 4. Actual: redirected to /dashboard (should go to /settings) | |
| ## Expected Behavior | |
| Respect the ?next= query parameter." \ | |
| --label "bug,backend" \ | |
| --assignee "username" | |
| ``` | |
| **With curl:** | |
| ```bash | |
| curl -s -X POST \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues \ | |
| -d '{ | |
| "title": "Login redirect ignores ?next= parameter", | |
| "body": "## Description\nAfter logging in, users always land on /dashboard.\n\n## Steps to Reproduce\n1. Navigate to /settings while logged out\n2. Get redirected to /login?next=/settings\n3. Log in\n4. Actual: redirected to /dashboard\n\n## Expected Behavior\nRespect the ?next= query parameter.", | |
| "labels": ["bug", "backend"], | |
| "assignees": ["username"] | |
| }' | |
| ``` | |
| ### Bug Report Template | |
| ``` | |
| ## Bug Description | |
| <What's happening> | |
| ## Steps to Reproduce | |
| 1. <step> | |
| 2. <step> | |
| ## Expected Behavior | |
| <What should happen> | |
| ## Actual Behavior | |
| <What actually happens> | |
| ## Environment | |
| - OS: <os> | |
| - Version: <version> | |
| ``` | |
| ### Feature Request Template | |
| ``` | |
| ## Feature Description | |
| <What you want> | |
| ## Motivation | |
| <Why this would be useful> | |
| ## Proposed Solution | |
| <How it could work> | |
| ## Alternatives Considered | |
| <Other approaches> | |
| ``` | |
| ## 3. Managing Issues | |
| ### Add/Remove Labels | |
| **With gh:** | |
| ```bash | |
| gh issue edit 42 --add-label "priority:high,bug" | |
| gh issue edit 42 --remove-label "needs-triage" | |
| ``` | |
| **With curl:** | |
| ```bash | |
| # Add labels | |
| curl -s -X POST \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues/42/labels \ | |
| -d '{"labels": ["priority:high", "bug"]}' | |
| # Remove a label | |
| curl -s -X DELETE \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues/42/labels/needs-triage | |
| # List available labels in the repo | |
| curl -s \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/labels \ | |
| | python3 -c " | |
| import sys, json | |
| for l in json.load(sys.stdin): | |
| print(f\" {l['name']:30} {l.get('description', '')}\")" | |
| ``` | |
| ### Assignment | |
| **With gh:** | |
| ```bash | |
| gh issue edit 42 --add-assignee username | |
| gh issue edit 42 --add-assignee @me | |
| ``` | |
| **With curl:** | |
| ```bash | |
| curl -s -X POST \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues/42/assignees \ | |
| -d '{"assignees": ["username"]}' | |
| ``` | |
| ### Commenting | |
| **With gh:** | |
| ```bash | |
| gh issue comment 42 --body "Investigated — root cause is in auth middleware. Working on a fix." | |
| ``` | |
| **With curl:** | |
| ```bash | |
| curl -s -X POST \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues/42/comments \ | |
| -d '{"body": "Investigated — root cause is in auth middleware. Working on a fix."}' | |
| ``` | |
| ### Closing and Reopening | |
| **With gh:** | |
| ```bash | |
| gh issue close 42 | |
| gh issue close 42 --reason "not planned" | |
| gh issue reopen 42 | |
| ``` | |
| **With curl:** | |
| ```bash | |
| # Close | |
| curl -s -X PATCH \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues/42 \ | |
| -d '{"state": "closed", "state_reason": "completed"}' | |
| # Reopen | |
| curl -s -X PATCH \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues/42 \ | |
| -d '{"state": "open"}' | |
| ``` | |
| ### Linking Issues to PRs | |
| Issues are automatically closed when a PR merges with the right keywords in the body: | |
| ``` | |
| Closes #42 | |
| Fixes #42 | |
| Resolves #42 | |
| ``` | |
| To create a branch from an issue: | |
| **With gh:** | |
| ```bash | |
| gh issue develop 42 --checkout | |
| ``` | |
| **With git (manual equivalent):** | |
| ```bash | |
| git checkout main && git pull origin main | |
| git checkout -b fix/issue-42-login-redirect | |
| ``` | |
| ## 4. Issue Triage Workflow | |
| When asked to triage issues: | |
| 1. **List untriaged issues:** | |
| ```bash | |
| # With gh | |
| gh issue list --label "needs-triage" --state open | |
| # With curl | |
| curl -s \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| "https://api.github.com/repos/$OWNER/$REPO/issues?labels=needs-triage&state=open" \ | |
| | python3 -c " | |
| import sys, json | |
| for i in json.load(sys.stdin): | |
| if 'pull_request' not in i: | |
| print(f\"#{i['number']} {i['title']}\")" | |
| ``` | |
| 2. **Read and categorize** each issue (view details, understand the bug/feature) | |
| 3. **Apply labels and priority** (see Managing Issues above) | |
| 4. **Assign** if the owner is clear | |
| 5. **Comment with triage notes** if needed | |
| ## 5. Bulk Operations | |
| For batch operations, combine API calls with shell scripting: | |
| **With gh:** | |
| ```bash | |
| # Close all issues with a specific label | |
| gh issue list --label "wontfix" --json number --jq '.[].number' | \ | |
| xargs -I {} gh issue close {} --reason "not planned" | |
| ``` | |
| **With curl:** | |
| ```bash | |
| # List issue numbers with a label, then close each | |
| curl -s \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| "https://api.github.com/repos/$OWNER/$REPO/issues?labels=wontfix&state=open" \ | |
| | python3 -c "import sys,json; [print(i['number']) for i in json.load(sys.stdin)]" \ | |
| | while read num; do | |
| curl -s -X PATCH \ | |
| -H "Authorization: token $GITHUB_TOKEN" \ | |
| https://api.github.com/repos/$OWNER/$REPO/issues/$num \ | |
| -d '{"state": "closed", "state_reason": "not_planned"}' | |
| echo "Closed #$num" | |
| done | |
| ``` | |
| ## Quick Reference Table | |
| | Action | gh | curl endpoint | | |
| |--------|-----|--------------| | |
| | List issues | `gh issue list` | `GET /repos/{o}/{r}/issues` | | |
| | View issue | `gh issue view N` | `GET /repos/{o}/{r}/issues/N` | | |
| | Create issue | `gh issue create ...` | `POST /repos/{o}/{r}/issues` | | |
| | Add labels | `gh issue edit N --add-label ...` | `POST /repos/{o}/{r}/issues/N/labels` | | |
| | Assign | `gh issue edit N --add-assignee ...` | `POST /repos/{o}/{r}/issues/N/assignees` | | |
| | Comment | `gh issue comment N --body ...` | `POST /repos/{o}/{r}/issues/N/comments` | | |
| | Close | `gh issue close N` | `PATCH /repos/{o}/{r}/issues/N` | | |
| | Search | `gh issue list --search "..."` | `GET /search/issues?q=...` | | |
Xet Storage Details
- Size:
- 9.27 kB
- Xet hash:
- eb51d9326b6b26e4de0d87a0bb73c79c61f5fa8098573a14d781e14159ec9e35
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.