{“content”:”---\nname: github-issues\ndescription: 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.\nversion: 1.1.0\nauthor: Hermes Agent\nlicense: MIT\nmetadata:\n hermes:\n tags: [GitHub, Issues, Project-Management, Bug-Tracking, Triage]\n related_skills: [github-auth, github-pr-workflow]\n---\n\n# GitHub Issues Management\n\nCreate, search, triage, and manage GitHub issues. Each section shows gh first, then the curl fallback.\n\n## Prerequisites\n\n- Authenticated with GitHub (see github-auth skill)\n- Inside a git repo with a GitHub remote, or specify the repo explicitly\n\n### Setup\n\nbash\nif command -v gh &>/dev/null && gh auth status &>/dev/null; then\n AUTH=\"gh\"\nelse\n AUTH=\"git\"\n if [ -z \"$GITHUB_TOKEN\" ]; then\n if [ -f ~/.hermes/.env ] && grep -q \"^GITHUB_TOKEN=\" ~/.hermes/.env; then\n GITHUB_TOKEN=$(grep \"^GITHUB_TOKEN=\" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\\n\\r')\n elif grep -q \"github.com\" ~/.git-credentials 2>/dev/null; then\n GITHUB_TOKEN=$(grep \"github.com\" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\\([^@]*\\)@.*|\\1|')\n fi\n fi\nfi\n\nREMOTE_URL=$(git remote get-url origin)\nOWNER_REPO=$(echo \"$REMOTE_URL\" | sed -E 's|.*github\\.com[:/]||; s|\\.git$||')\nOWNER=$(echo \"$OWNER_REPO\" | cut -d/ -f1)\nREPO=$(echo \"$OWNER_REPO\" | cut -d/ -f2)\n\n\n---\n\n## 1. Viewing Issues\n\nWith gh:\n\nbash\ngh issue list\ngh issue list --state open --label \"bug\"\ngh issue list --assignee @me\ngh issue list --search \"authentication error\" --state all\ngh issue view 42\n\n\nWith curl:\n\nbash\n# List open issues\ncurl -s \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n \"https://api.github.com/repos/$OWNER/$REPO/issues?state=open&per_page=20\" \\\n | python3 -c \"\nimport sys, json\nfor i in json.load(sys.stdin):\n if 'pull_request' not in i: # GitHub API returns PRs in /issues too\n labels = ', '.join(l['name'] for l in i['labels'])\n print(f\\\"#{i['number']:5} {i['state']:6} {labels:30} {i['title']}\\\")\"\n\n# Filter by label\ncurl -s \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n \"https://api.github.com/repos/$OWNER/$REPO/issues?state=open&labels=bug&per_page=20\" \\\n | python3 -c \"\nimport sys, json\nfor i in json.load(sys.stdin):\n if 'pull_request' not in i:\n print(f\\\"#{i['number']} {i['title']}\\\")\"\n\n# View a specific issue\ncurl -s \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/42 \\\n | python3 -c \"\nimport sys, json\ni = json.load(sys.stdin)\nlabels = ', '.join(l['name'] for l in i['labels'])\nassignees = ', '.join(a['login'] for a in i['assignees'])\nprint(f\\\"#{i['number']}: {i['title']}\\\")\nprint(f\\\"State: {i['state']} Labels: {labels} Assignees: {assignees}\\\")\nprint(f\\\"Author: {i['user']['login']} Created: {i['created_at']}\\\")\nprint(f\\\"\\n{i['body']}\\\")\"\n\n# Search issues\ncurl -s \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n \"https://api.github.com/search/issues?q=authentication+error+repo:$OWNER/$REPO\" \\\n | python3 -c \"\nimport sys, json\nfor i in json.load(sys.stdin)['items']:\n print(f\\\"#{i['number']} {i['state']:6} {i['title']}\\\")\"\n\n\n## 2. Creating Issues\n\nWith gh:\n\nbash\ngh issue create \\\n --title \"Login redirect ignores ?next= parameter\" \\\n --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 (should go to /settings)\n\n## Expected Behavior\nRespect the ?next= query parameter.\" \\\n --label \"bug,backend\" \\\n --assignee \"username\"\n\n\nWith curl:\n\nbash\ncurl -s -X POST \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues \\\n -d '{\n \"title\": \"Login redirect ignores ?next= parameter\",\n \"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.\",\n \"labels\": [\"bug\", \"backend\"],\n \"assignees\": [\"username\"]\n }'\n\n\n### Bug Report Template\n\n\n## Bug Description\n<What's happening>\n\n## Steps to Reproduce\n1. <step>\n2. <step>\n\n## Expected Behavior\n<What should happen>\n\n## Actual Behavior\n<What actually happens>\n\n## Environment\n- OS: <os>\n- Version: <version>\n\n\n### Feature Request Template\n\n\n## Feature Description\n<What you want>\n\n## Motivation\n<Why this would be useful>\n\n## Proposed Solution\n<How it could work>\n\n## Alternatives Considered\n<Other approaches>\n\n\n## 3. Managing Issues\n\n### Add/Remove Labels\n\nWith gh:\n\nbash\ngh issue edit 42 --add-label \"priority:high,bug\"\ngh issue edit 42 --remove-label \"needs-triage\"\n\n\nWith curl:\n\nbash\n# Add labels\ncurl -s -X POST \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/42/labels \\\n -d '{\"labels\": [\"priority:high\", \"bug\"]}'\n\n# Remove a label\ncurl -s -X DELETE \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/42/labels/needs-triage\n\n# List available labels in the repo\ncurl -s \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/labels \\\n | python3 -c \"\nimport sys, json\nfor l in json.load(sys.stdin):\n print(f\\\" {l['name']:30} {l.get('description', '')}\\\")\"\n\n\n### Assignment\n\nWith gh:\n\nbash\ngh issue edit 42 --add-assignee username\ngh issue edit 42 --add-assignee @me\n\n\nWith curl:\n\nbash\ncurl -s -X POST \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/42/assignees \\\n -d '{\"assignees\": [\"username\"]}'\n\n\n### Commenting\n\nWith gh:\n\nbash\ngh issue comment 42 --body \"Investigated — root cause is in auth middleware. Working on a fix.\"\n\n\nWith curl:\n\nbash\ncurl -s -X POST \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/42/comments \\\n -d '{\"body\": \"Investigated — root cause is in auth middleware. Working on a fix.\"}'\n\n\n### Closing and Reopening\n\nWith gh:\n\nbash\ngh issue close 42\ngh issue close 42 --reason \"not planned\"\ngh issue reopen 42\n\n\nWith curl:\n\nbash\n# Close\ncurl -s -X PATCH \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/42 \\\n -d '{\"state\": \"closed\", \"state_reason\": \"completed\"}'\n\n# Reopen\ncurl -s -X PATCH \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/42 \\\n -d '{\"state\": \"open\"}'\n\n\n### Linking Issues to PRs\n\nIssues are automatically closed when a PR merges with the right keywords in the body:\n\n\nCloses #42\nFixes #42\nResolves #42\n\n\nTo create a branch from an issue:\n\nWith gh:\n\nbash\ngh issue develop 42 --checkout\n\n\nWith git (manual equivalent):\n\nbash\ngit checkout main && git pull origin main\ngit checkout -b fix/issue-42-login-redirect\n\n\n## 4. Issue Triage Workflow\n\nWhen asked to triage issues:\n\n1. List untriaged issues:\n\nbash\n# With gh\ngh issue list --label \"needs-triage\" --state open\n\n# With curl\ncurl -s \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n \"https://api.github.com/repos/$OWNER/$REPO/issues?labels=needs-triage&state=open\" \\\n | python3 -c \"\nimport sys, json\nfor i in json.load(sys.stdin):\n if 'pull_request' not in i:\n print(f\\\"#{i['number']} {i['title']}\\\")\"\n\n\n2. Read and categorize each issue (view details, understand the bug/feature)\n\n3. Apply labels and priority (see Managing Issues above)\n\n4. Assign if the owner is clear\n\n5. Comment with triage notes if needed\n\n## 5. Bulk Operations\n\nFor batch operations, combine API calls with shell scripting:\n\nWith gh:\n\nbash\n# Close all issues with a specific label\ngh issue list --label \"wontfix\" --json number --jq '.[].number' | \\\n xargs -I {} gh issue close {} --reason \"not planned\"\n\n\nWith curl:\n\nbash\n# List issue numbers with a label, then close each\ncurl -s \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n \"https://api.github.com/repos/$OWNER/$REPO/issues?labels=wontfix&state=open\" \\\n | python3 -c \"import sys,json; [print(i['number']) for i in json.load(sys.stdin)]\" \\\n | while read num; do\n curl -s -X PATCH \\\n -H \"Authorization: token $GITHUB_TOKEN\" \\\n https://api.github.com/repos/$OWNER/$REPO/issues/$num \\\n -d '{\"state\": \"closed\", \"state_reason\": \"not_planned\"}'\n echo \"Closed #$num\"\n done\n\n\n## Quick Reference Table\n\n| Action | gh | curl endpoint |\n|--------|-----|--------------|\n| List issues | gh issue list | GET /repos/{o}/{r}/issues |\n| View issue | gh issue view N | GET /repos/{o}/{r}/issues/N |\n| Create issue | gh issue create ... | POST /repos/{o}/{r}/issues |\n| Add labels | gh issue edit N --add-label ... | POST /repos/{o}/{r}/issues/N/labels |\n| Assign | gh issue edit N --add-assignee ... | POST /repos/{o}/{r}/issues/N/assignees |\n| Comment | gh issue comment N --body ... | POST /repos/{o}/{r}/issues/N/comments |\n| Close | gh issue close N | PATCH /repos/{o}/{r}/issues/N |\n| Search | gh issue list --search \"...\" | GET /search/issues?q=... |\n”}