Create well-formatted GitHub issues with intelligent AI-powered label suggestions and content type detection. Use whenever the user wants to create a bug report, feature request, question, or documentation issue on GitHub, or says 'file an issue', 'create an issue', or 'gh issue create'. Do NOT use for managing existing issues, organizing issue hierarchies (use issues-workflow instead), or for PR creation.
From gh-toolsnpx claudepluginhub terrylica/cc-skills --plugin gh-toolsThis skill is limited to using the following tools:
references/ai-prompts.mdreferences/content-types.mdreferences/evolution-log.mdreferences/label-strategy.mdExecutes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
Guides idea refinement into designs: explores context, asks questions one-by-one, proposes approaches, presents sections for approval, writes/review specs before coding.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Create well-formatted GitHub issues with intelligent automation including AI-powered label suggestions, content type detection, template formatting, and related issue linking.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
Use this skill when:
Slash command: /gh-tools:issue-create
Natural language triggers:
--repo owner/repo flagGitHub issue bodies support 65,536 characters (not bytes — UTF-8 multibyte characters count as 1). Always aim to fill a single post rather than splitting across multiple issues or comments.
Principle: One comprehensive post is more valuable than many fragmented ones. Pack as much analysis, context, history, and multi-perspective reasoning as possible into a single issue body or comment.
When composing long-form issue content:
echo "$BODY" | wc -m (characters, not bytes)<details><summary>) for dense reference material — they don't reduce the char budget but improve readabilityPre-post size check pattern:
# Build body, then verify it fits
BODY=$(cat <<'EOF'
... your content ...
EOF
)
CHARS=$(echo "$BODY" | wc -m | tr -d ' ')
echo "Body size: ${CHARS}/65536 chars"
if [ "$CHARS" -gt 65536 ]; then
echo "WARNING: Exceeds limit by $((CHARS - 65536)) chars — trim or split"
fi
# From within a git repository
bun ~/eon/cc-skills/plugins/gh-tools/scripts/issue-create.ts \
--body "Login page crashes when using special characters in password"
bun ~/eon/cc-skills/plugins/gh-tools/scripts/issue-create.ts \
--repo owner/repo \
--body "Feature: Add dark mode support for better accessibility"
bun ~/eon/cc-skills/plugins/gh-tools/scripts/issue-create.ts \
--repo owner/repo \
--body "Bug: API returns 500 error" \
--dry-run
bun ~/eon/cc-skills/plugins/gh-tools/scripts/issue-create.ts \
--repo owner/repo \
--title "Bug: Login fails with OAuth" \
--body "Detailed description..." \
--labels "bug,authentication"
bun ~/eon/cc-skills/plugins/gh-tools/scripts/issue-create.ts \
--body "Question: How to configure..." \
--no-ai
| Option | Short | Description |
|---|---|---|
--repo | -r | Repository in owner/repo format |
--body | -b | Issue body content (required) |
--title | -t | Issue title (optional) |
--labels | -l | Comma-separated labels |
--dry-run | Preview without creating | |
--no-ai | Disable AI features | |
--verbose | -v | Enable verbose output |
--help | -h | Show help |
gh CLI (required) - GitHub CLI toolgh-models extension (optional) - Enables AI featuresgh extension install github/gh-models
| Level | Behavior |
|---|---|
| WRITE/ADMIN | Full functionality |
| TRIAGE | Can apply labels |
| READ | Shows formatted content for manual copy |
| NONE | Suggests fork workflow |
Logs to: ~/.claude/logs/gh-issue-create.jsonl
Events logged:
preflight - Initial checkstype_detected - Content type detectionlabels_suggested - Label suggestionsrelated_found - Related issues searchissue_created - Successful creationdry_run - Dry run completionGitHub Issues have no API for programmatic image upload. The web UI's drag-and-drop uses an internal S3 policy flow that is intentionally not exposed to API clients (cli/cli#1895).
The ?raw=true URL resolves via github.com — if the image doesn't exist at that path on the remote, it silently 404s (broken image, no error). Run this preflight before creating the issue:
# 1. Detect repo context
OWNER_REPO=$(gh repo view --json nameWithOwner -q '.nameWithOwner')
BRANCH=$(git rev-parse --abbrev-ref HEAD)
VISIBILITY=$(gh repo view --json visibility -q '.visibility')
# 2. Verify images are git-tracked (not gitignored)
IMG_DIR="path/to/images"
for f in ${IMG_DIR}/*.png; do
git ls-files --error-unmatch "$f" >/dev/null 2>&1 \
|| echo "WARNING: $f is NOT tracked by git (check .gitignore)"
done
# 3. Verify images are committed (not just staged or untracked)
UNCOMMITTED=$(git diff --name-only HEAD -- "${IMG_DIR}/" 2>/dev/null)
UNTRACKED=$(git ls-files --others --exclude-standard -- "${IMG_DIR}/" 2>/dev/null)
if [[ -n "$UNCOMMITTED" || -n "$UNTRACKED" ]]; then
echo "FAIL: Images not committed — commit and push first"
echo " Uncommitted: ${UNCOMMITTED}"
echo " Untracked: ${UNTRACKED}"
exit 1
fi
# 4. Verify commit is pushed to remote (local commits invisible to github.com)
LOCAL_SHA=$(git rev-parse HEAD)
REMOTE_SHA=$(git rev-parse "origin/${BRANCH}" 2>/dev/null)
if [[ "$LOCAL_SHA" != "$REMOTE_SHA" ]]; then
echo "FAIL: Local commits not pushed — run: git push origin ${BRANCH}"
exit 1
fi
# 5. Build image base URL
IMG_BASE="https://github.com/${OWNER_REPO}/blob/${BRANCH}/${IMG_DIR}"
echo "Image base URL: ${IMG_BASE}/<filename>.png?raw=true"
echo "Repo visibility: ${VISIBILITY}"
if [[ "$VISIBILITY" == "PRIVATE" ]]; then
echo "NOTE: Images only visible to authenticated collaborators"
fi
Preflight checklist (what each step catches):
| Step | Check | Failure Mode |
|---|---|---|
| 1 | Repo context exists | No OWNER_REPO to build URLs |
| 2 | Images are git-tracked | .gitignore silently excludes them |
| 3 | Images are committed | Staged/untracked files don't exist on remote |
| 4 | Commit is pushed | Local-only commits are invisible to github.com |
| 5 | URL construction | Wrong branch name → 404 |
?raw=true vs raw.githubusercontent.comFor images already committed and pushed, use github.com/blob/...?raw=true URLs — not raw.githubusercontent.com:
<!-- BROKEN for private repos (no browser cookies on raw.githubusercontent.com) -->

<!-- WORKING for all repos (browser has cookies on github.com, gets signed redirect) -->

Scripting pattern (batch images → issue body):
IMG_BASE="https://github.com/${OWNER_REPO}/blob/${BRANCH}/${IMG_DIR}"
gh issue create --title "Feedback with screenshots" --body "$(cat <<EOF
## Item 1

## Item 2

EOF
)"
See AP-07 in GFM Anti-Patterns for the full technical explanation.
For images only on disk (not committed), four options:
| Method | How | Permanent? | Preflight? |
|---|---|---|---|
| Commit + push first | git add images, push, run preflight, then use ?raw=true URLs | Yes (repo-hosted) | Yes (5-step) |
| Web UI paste | Open issue in browser, Ctrl/Cmd+V images into comment box | Yes (user-attachments CDN) | None |
| Web UI drag-and-drop | Drag image files into the comment box | Yes (user-attachments CDN) | None |
| Playwright automation | Script automates the browser file-attachment flow | Yes (user-attachments CDN) | None |
GitHub has no API for image uploads, but the browser's file-attachment flow can be automated via Playwright to get permanent user-attachments CDN URLs without any commit/push preflight.
How it works:
~/.claude/tools/pw-github-profile/)<img> tag with a user-attachments CDN URL into the comment textareaKey implementation details (GitHub's 2026 React comment composer):
textarea[placeholder="Use Markdown to format your comment"] (dynamic React IDs — do NOT match by id)page.waitForEvent("filechooser")<img width="W" height="H" alt="Image" src="https://github.com/user-attachments/assets/UUID" /> (HTML <img> tag, not  markdown)textarea#new_comment_field and file-attachment input[type='file'] selectors no longer existtextarea.fill("")Chrome CDP note: chromium.connectOverCDP() fails with Chrome 136+ (WebSocket timeout). Use chromium.launchPersistentContext() with Playwright's bundled Chromium instead. Chrome 136+ also requires --user-data-dir for CDP (DevTools remote debugging requires a non-default data directory), making CDP impractical for reusing existing browser sessions.
Run from a git directory or use --repo owner/repo flag.
gh extension listgh label list --repo owner/repols ~/.cache/gh-issue-skill/labels/Install gh-models extension:
gh extension install github/gh-models
After this skill completes, check before closing:
Only update if the issue is real and reproducible — not speculative.