Create & Maintain GitHub Issues
Turn a user report (text/screenshot) into a well-formed GitHub issue and maintain it, fast, with no full code investigation. The fix is decided in triage, not here.
Mode: act immediately. Do not draft-and-confirm. For a new report: capture, badge, label, gh issue create, return the URL. For an update: make the change and report what changed.
Project configuration
This skill is project-agnostic. Read the consuming project's ## GitHub Issues block (in its AGENTS.md / CLAUDE.md) for:
- Repo:
<owner>/<repo> to file against.
- Issue type: the shape as a first-class GitHub field:
Bug/Enhancement (plus any project types like Task), set with --type. Not a label.
- Labels: the AI-filed label (e.g.
genai) and the area/* vocabulary.
- Attachment upload (optional): a command (named in the config block) that takes a file and prints its public URL. If the project names none, transcribe media instead (see "Attaching files").
If a project has no such block, ask the user for the repo, set the issue type to Bug/Enhancement, and transcribe rather than upload.
The contract
Follow workflow/github-issues.md (the GitHub issue authoring standard): symptom-first, template fields only, ≤120 words of body prose (150 hard max), name the affected service or surface while filenames and symbols wait for the comment, one audience per issue, never prescribe the fix, provenance-tagged repro, badge row on top.
The register is the part that slips. Write each sentence positively, about a thing the reader can observe, in words the product already uses. A negation earns its place only where the missing thing is on the failing path. The body carries no self-correction and no commentary on itself: when a finding changes, edit the body to what is true now.
The word budget is on the body, not the issue. When the evidence outgrows it, post a comment straight after filing: full verification data, filenames and line numbers pinned to a SHA, adjacent findings that aren't this bug. Compressing the body until a verified finding drops out is the wrong trade. A second unrelated problem is still a second issue.
How much to investigate
The goal is a reproducible, well-scoped report, not a diagnosis.
Do (light triage):
- Read the screenshot to ground the symptom in what's on screen.
- Work out steps to reproduce where possible: from the report, the screenshot, and obvious knowledge of the app's flows. A quick look at the relevant route/UI to confirm the repro path is fine.
- Identify the affected surface/area to pick the
area/* label.
- If a root cause is genuinely obvious at a glance, note it as a single provisional
Likely: line, and never chase it.
- Note any factual gap you can see: no logging on the failing path, a missing validation, an unconsumed queue. Absence is a symptom (evidence), not a fix; don't omit it for fear it reads as prescriptive.
Don't (the token waste to avoid):
- No deep code spelunking, no tracing across files, no investigation sub-agents.
- No root-cause hunt, no diffs, no fix, no resolution menu, no acceptance criteria.
- Don't block on reproducing it. If steps can't be derived, say so in one line and file anyway.
Steps
Read the inputs. The user's words + any screenshot (use Read on the image; it renders). Establish the single user-visible symptom.
Steps to reproduce, tagging provenance (required). Start the field with one of Reported by user: / Inferred (unconfirmed): / Not reproduced: <what's missing>. Number the steps, minimal path (≤6). Never present inferred steps as reported.
Write the body. Start with the badge row (see the standard's Badges section), a blank line, then the template fields (bug shape, since most reports are bugs):
- What happened: the symptom. ≤40 words. No function/file name in the first sentence.
- Steps to reproduce: from step 2, provenance tag first.
- Expected behaviour: one sentence.
- Evidence: embed any screenshot/file (see "Attaching files"), plus a one-line description and the affected service or surface if known. Optional trailing
Likely: … line only if the cause is obvious.
For a capability gap use the enhancement shape: Problem / motivation, Desired outcome, Alternatives (only if a real trade-off exists).
Set type + labels from project config: the issue type Bug/Enhancement (via --type), plus the AI-filed label + best-guess area/*. Don't investigate just to be sure of the area. If a configured label doesn't exist in the repo, create it (gh label create) or drop the unknown area/* and file anyway; never let a missing label or type block the create.
Create it (substitute the configured repo, type, and labels):
gh issue create --repo "$REPO" \
--title "<area>: <symptom-first, specific>" \
--body "<body from step 3>" \
--type Bug --label "$AI_LABEL" --label area/<surface>
Title is symptom-first and specific. Never restate the title verbatim in the body's first line: the title is the terse label; the body opens with the fuller observed symptom.
Post the depth as a comment, if there is any. Only when the investigation produced more than the body can hold: full verification data, line numbers (pinned to a SHA, with the symbol named), or adjacent findings that aren't this bug. Skip it for a routine report; most issues need no comment.
gh issue comment <n> --repo "$REPO" --body "<comment>" # <n>: the issue gh issue create printed
Return the issue URL. Any screenshot is already embedded, so nothing is left for the user to do.
Updating an existing issue
Same contract applies. Always fetch current state first:
gh issue view <n> --repo "$REPO" --json number,title,body,labels,issueType,state
Comment vs edit: add a comment for genuinely new information (a follow-up, extra evidence, a repro confirmation). Edit the body only to correct/upgrade what's there or to insert media. Editing must never delete a human's words.
Attaching files (screenshots, screen recordings, logs, PDFs)
GitHub has no attachment API, so gh can't upload files. The upload is the project's responsibility: its config block names an upload command that takes a local file, uploads it under an unguessable key, and prints a public URL. The skill runs that command and turns the URL into markdown; if none is configured, transcribe the media into Evidence instead.
url="$(<the project's configured upload command> "$path")" # prints the public URL
Embed the URL by file type:
- image →
 (inline)
- video →
<video controls src="<url>"></video> only when the URL is a GitHub upload (user-attachments); GitHub strips <video> with an external src, so embed object-store video as a plain [name](url) link
- anything else →
[<name>](<url>) (download link)
Paste the result into the Evidence field (or a comment). (Camo caching and the unique-key rationale live in the standard's Attachments section; don't restate them here.)
Output
Brief. On create: the issue URL, its title, and the labels applied. On update: the issue URL and one line on what changed. Don't recap the whole body.
1---2name: github-issue3description: Create and maintain GitHub issues from user reports and screenshots, fast and without over-investigating. CREATE: use when the user says 'log a GitHub issue', 'file a bug', 'raise an issue', 'open an issue', 'log this on GitHub', or pastes/attaches a screenshot of something broken and asks to report it: captures the symptom, works out steps to reproduce where possible, uploads any screenshot/video and embeds it, applies the GitHub issue authoring standard, and files immediately. UPDATE: use when the user says 'update the issue', 'add a comment to #N', 'attach this to the issue', 'edit the issue body', 'relabel', 'close/reopen the issue': adds comments with media, edits the body, upgrades the repro provenance badge, relabels, and closes/reopens. It does NOT root-cause the code or prescribe a fix; that's for triage.4---56# Create & Maintain GitHub Issues78Turn a user report (text/screenshot) into a well-formed GitHub issue and maintain it, fast, with no full code investigation. The fix is decided in triage, not here.910**Mode: act immediately.** Do not draft-and-confirm. For a new report: capture, badge, label, `gh issue create`, return the URL. For an update: make the change and report what changed.1112## Project configuration1314This skill is project-agnostic. Read the consuming project's `## GitHub Issues` block (in its `AGENTS.md` / `CLAUDE.md`) for:1516- **Repo**: `<owner>/<repo>` to file against.17- **Issue type**: the shape as a first-class GitHub field: `Bug`/`Enhancement` (plus any project types like `Task`), set with `--type`. **Not** a label.18- **Labels**: the AI-filed label (e.g. `genai`) and the `area/*` vocabulary.19- **Attachment upload** (optional): a command (named in the config block) that takes a file and prints its public URL. If the project names none, transcribe media instead (see "Attaching files").2021If a project has no such block, ask the user for the repo, set the issue type to `Bug`/`Enhancement`, and transcribe rather than upload.2223## The contract2425Follow **`workflow/github-issues.md`** (the GitHub issue authoring standard): symptom-first, template fields only, ≤120 words of body prose (150 hard max), name the affected service or surface while filenames and symbols wait for the comment, one audience per issue, **never prescribe the fix**, provenance-tagged repro, badge row on top.2627The register is the part that slips. Write each sentence positively, about a thing the reader can observe, in words the product already uses. A negation earns its place only where the missing thing is on the failing path. The body carries no self-correction and no commentary on itself: when a finding changes, edit the body to what is true now.2829The word budget is on the **body**, not the issue. When the evidence outgrows it, post a comment straight after filing: full verification data, filenames and line numbers pinned to a SHA, adjacent findings that aren't this bug. Compressing the body until a verified finding drops out is the wrong trade. A second unrelated problem is still a second issue.3031## How much to investigate3233The goal is a _reproducible, well-scoped_ report, not a diagnosis.3435**Do (light triage):**3637- Read the screenshot to ground the symptom in what's on screen.38- Work out **steps to reproduce** where possible: from the report, the screenshot, and obvious knowledge of the app's flows. A quick look at the relevant route/UI to confirm the repro path is fine.39- Identify the affected **surface/area** to pick the `area/*` label.40- If a root cause is genuinely obvious at a glance, note it as a single provisional `Likely:` line, and never chase it.41- Note any factual **gap** you can see: no logging on the failing path, a missing validation, an unconsumed queue. Absence is a symptom (evidence), not a fix; don't omit it for fear it reads as prescriptive.4243**Don't (the token waste to avoid):**4445- No deep code spelunking, no tracing across files, no investigation sub-agents.46- No root-cause hunt, no diffs, no fix, no resolution menu, no acceptance criteria.47- Don't block on reproducing it. If steps can't be derived, say so in one line and file anyway.4849## Steps50511. **Read the inputs.** The user's words + any screenshot (use Read on the image; it renders). Establish the single user-visible symptom.52532. **Steps to reproduce, tagging provenance (required).** Start the field with one of `Reported by user:` / `Inferred (unconfirmed):` / `Not reproduced: <what's missing>`. Number the steps, minimal path (≤6). Never present inferred steps as reported.54553. **Write the body.** Start with the **badge row** (see the standard's Badges section), a blank line, then the template fields (bug shape, since most reports are bugs):56 - **What happened**: the symptom. ≤40 words. No function/file name in the first sentence.57 - **Steps to reproduce**: from step 2, provenance tag first.58 - **Expected behaviour**: one sentence.59 - **Evidence**: embed any screenshot/file (see "Attaching files"), plus a one-line description and the affected service or surface if known. Optional trailing `Likely: …` line only if the cause is obvious.6061 For a capability gap use the enhancement shape: **Problem / motivation**, **Desired outcome**, **Alternatives** (only if a real trade-off exists).62634. **Set type + labels** from project config: the issue type `Bug`/`Enhancement` (via `--type`), plus the AI-filed label + best-guess `area/*`. Don't investigate just to be sure of the area. If a configured label doesn't exist in the repo, create it (`gh label create`) or drop the unknown `area/*` and file anyway; never let a missing label or type block the create.64655. **Create it** (substitute the configured repo, type, and labels):6667 ```sh68 gh issue create --repo "$REPO" \69 --title "<area>: <symptom-first, specific>" \70 --body "<body from step 3>" \71 --type Bug --label "$AI_LABEL" --label area/<surface>72 ```7374 Title is symptom-first and specific. Never restate the title verbatim in the body's first line: the title is the terse label; the body opens with the fuller observed symptom.75766. **Post the depth as a comment, if there is any.** Only when the investigation produced more than the body can hold: full verification data, line numbers (pinned to a SHA, with the symbol named), or adjacent findings that aren't this bug. Skip it for a routine report; most issues need no comment.7778 ```sh79 gh issue comment <n> --repo "$REPO" --body "<comment>" # <n>: the issue gh issue create printed80 ```81827. **Return the issue URL.** Any screenshot is already embedded, so nothing is left for the user to do.8384## Updating an existing issue8586Same contract applies. **Always fetch current state first:**8788```sh89gh issue view <n> --repo "$REPO" --json number,title,body,labels,issueType,state90```9192**Comment vs edit:** add a **comment** for genuinely new information (a follow-up, extra evidence, a repro confirmation). **Edit the body** only to correct/upgrade what's there or to insert media. Editing must never delete a human's words.9394- **Add a comment (+ media)**: upload any new screenshot/video and embed it.95 ```sh96 gh issue comment <n> --repo "$REPO" --body "<comment + embedded media>"97 ```98- **Edit the body (+ media)**: `gh issue edit --body` **replaces the entire body**, so fetch it first, modify in place, and pass the full new text.99 ```sh100 gh issue edit <n> --repo "$REPO" --body "<full modified body>"101 ```102- **Upgrade provenance**: when an inferred repro is confirmed: edit the body to swap the orange `repro: inferred` badge for green `repro: user-reported`, and change the Steps tag to `Reported by user:`. The main reason to edit rather than comment.103- **Relabel / retype / triage**: `gh issue edit <n> --repo "$REPO" --add-label area/<x> --remove-label area/<y>`; change the type with `--type Enhancement`.104- **Close / reopen**: an issue a commit resolves should auto-close via `Closes #N` (see `workflow/git.md`); close by hand only when no such commit exists (duplicate, won't-fix, fixed elsewhere): `gh issue close <n> --repo "$REPO" --reason completed` (or `not planned`); `gh issue reopen <n> --repo "$REPO"`.105106## Attaching files (screenshots, screen recordings, logs, PDFs)107108GitHub has no attachment API, so `gh` can't upload files. The upload is the **project's** responsibility: its config block names an upload command that takes a local file, uploads it under an unguessable key, and prints a public URL. The skill runs that command and turns the URL into markdown; if none is configured, transcribe the media into Evidence instead.109110```sh111url="$(<the project's configured upload command> "$path")" # prints the public URL112```113114Embed the URL by file type:115116- **image** → `` (inline)117- **video** → `<video controls src="<url>"></video>` only when the URL is a GitHub upload (`user-attachments`); GitHub strips `<video>` with an external src, so embed object-store video as a plain `[name](url)` link118- **anything else** → `[<name>](<url>)` (download link)119120Paste the result into the Evidence field (or a comment). (Camo caching and the unique-key rationale live in the standard's Attachments section; don't restate them here.)121122## Output123124Brief. On create: the issue URL, its title, and the labels applied. On update: the issue URL and one line on what changed. Don't recap the whole body.