name: github-issues
description: Create, manage, triage, and close GitHub issues. Search existing issues, add labels, assign people, and link to PRs. Works with gh CLI or falls back to git + GitHub REST API via curl.
version: 1.1.0
author: Hermes Agent
license: MIT
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-authskill) - - Inside a git repo with a GitHub remote, or specify the repo explicitly
- - OS:
- - Version:
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
Steps to Reproduce
1.
2.
Expected Behavior
Actual Behavior
Environment
`
Feature Request Template
`
Feature Description
Motivation
Proposed Solution
Alternatives Considered
`
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=... |