GitHub Issue Writer
Drafts and updates well-structured GitHub issues: analyzes the description, asks targeted clarifying questions, generates a title under 72 characters, and produces a body from the project template.
Mutation class: writes to external services, and only on the update path. The draft
path writes nothing anywhere — it prints a title and body for the user or for issue-flow
to file. The update path edits an existing issue on GitHub through
scripts/update-issue.sh, after the approval gate in Step 5.
Asking the User
Every question in this skill is written as AskUserQuestion options. Use that tool where
the host offers it, or the host's nearest structured-choice equivalent. Where the host has
neither, ask the same question in normal chat as a numbered list of 2–5 options —
recommended first, one short line of description each — and wait for the user to reply
with a number.
Which Workflow
A request naming an existing issue — "update issue #123", "edit that issue", "reword the body", or a bare "update the issue" pointing at one created earlier in this session — runs the Update Workflow. Everything else runs the drafting Workflow below.
For creating new issues on GitHub, use
issue-flowwhen that skill is available. Otherwise use this skill to prepare the title and body, then create the issue with the available GitHub tooling.
Bundled Scripts
This skill includes helper bash scripts in the scripts/ directory:
- get-issue.sh - Fetches existing issue data (title, body, labels, state)
- update-issue.sh - Updates an existing issue
To use bundled scripts, execute them from the skill directory:
bash scripts/get-issue.sh 123
bash scripts/update-issue.sh --issue 123 --title "New Title" --add-label "bug"
Workflow
Follow these steps in order. Adapt based on the scope and complexity of the issue.
Step 1: Gather Initial Input
Ask the user to describe what they want the issue to cover. If they've already provided a description, proceed to analysis.
Step 1.5: Determine Format
Check if the user's request indicates a preferred format:
Auto-detect Short format if keywords present:
- "short issue", "minimal issue", "quick issue"
Auto-detect Default format if keywords present:
- "simple issue", "draft issue", "basic issue"
If not auto-detected, ask the user which format they want, per Asking the User:
question: "What level of detail should this issue have?"
header: "Format"
options:
- label: "Default (Recommended)"
description: "Simple format with description, rationale, references, and implementation notes"
- label: "Short"
description: "Minimal format with just description and rationale (no section headers)"
- label: "Full"
description: "Comprehensive format with acceptance criteria, testing steps, and detailed implementation plan"
Step 2: Analyze and Categorize
Determine the issue type based on the description:
- Feature: New functionality or enhancement
- Bug: Something is broken or not working as expected
- Task: General work item, refactoring, documentation
- Question: Needs discussion or clarification
Type-to-Label Mapping:
Based on the issue type, determine the recommended label:
| Type | Recommended Label | Notes |
|---|---|---|
| Bug | bug |
Add critical if severe/blocking |
| Feature | feature |
New functionality |
| Task | Context-based | See keywords below |
| Question | None | Usually doesn't need a label |
Task Label Keywords:
- Keywords: refactor, cleanup, reorganize, restructure →
refactoring - Keywords: improve, enhance, update, optimize, better →
improvement - Keywords: research, investigate, explore, spike, prototype →
r&d - Default (no keywords matched):
improvement
Identify which template sections are relevant based on scope:
| Scope | Required Sections |
|---|---|
| Short (minimal, quick) | Brief Description, Rationale (no section headers, bold markers only) |
| Default (simple task, draft) | Description, Rationale; optionally References, Implementation Notes |
| Small (bug fix, typo) | Brief Description, Acceptance Criteria |
| Medium (feature, enhancement) | Brief Description, Rationale, Implementation, Acceptance Criteria |
| Large / Full (architecture, major feature) | All sections as needed |
Step 3: Interactive Questionnaire
Ask clarifying questions to fill in gaps, per Asking the User. Tailor them to the issue type and scope.
For Features:
- What problem does this solve?
- Are there any design references or examples?
- What are the key acceptance criteria?
- Any technical constraints to consider?
For Bugs:
- What is the expected behavior?
- What is the actual behavior?
- Steps to reproduce?
- Any error messages or screenshots?
For Tasks:
- Why is this work needed now?
- Are there dependencies on other work?
- What does "done" look like?
Ask at most three of these in one round, and only the ones the description leaves open — a question whose answer is already in the user's own text costs a turn and returns nothing.
Step 3.5: Confirm Label
Based on the type-to-label mapping from Step 2, confirm the label, per Asking the User:
question: "Which label best describes this issue?"
header: "Label"
options:
- label: "{{RECOMMENDED_LABEL}} (Recommended)"
description: "Based on issue type: {{TYPE}}"
- label: "{{ALTERNATIVE_LABEL_1}}"
description: "{{DESCRIPTION_1}}"
- label: "{{ALTERNATIVE_LABEL_2}}"
description: "{{DESCRIPTION_2}}"
- label: "No label"
description: "Skip label assignment"
Example for Bug type:
options:
- label: "bug (Recommended)"
description: "This appears to be a bug based on the error behavior described"
- label: "critical"
description: "High priority bug, needs immediate attention"
- label: "improvement"
description: "If this is more of an enhancement than a bug"
- label: "No label"
description: "Skip label assignment"
The other types take the same shape with these labels, No label always last:
| Type | Recommended | Alternative 1 | Alternative 2 |
|---|---|---|---|
| Feature | feature — new functionality |
improvement — enhances what exists |
epic — spans multiple issues |
| Task (refactor keywords) | refactoring — code restructuring |
improvement — general enhancement |
r&d — research and exploration |
| Question | No label (recommended) |
— | — |
Step 4: Generate Title
Create a title that:
- Is under 72 characters (hard limit)
- Starts with the component/area if applicable (e.g., "Button: Add loading state")
- Uses imperative mood ("Add", "Fix", "Update", not "Adding", "Fixed")
- Is specific but concise
- Avoids redundant words like "Issue:" or "Task:"
Good examples:
Button: Add disabled state visual feedbackFix tooltip positioning on viewport edgeTreeView: Implement keyboard navigation
Bad examples:
Issue: There's a problem with buttons(vague, has "Issue:")Adding a new feature for users to be able to see loading spinners(too long, wrong mood)
Step 5: Generate Description
Use the appropriate template based on the selected format.
Writing rules (all formats):
- Never start the description with a markdown header — always lead with plain text
- Use present tense for existing problems ("the button does not respond", not "the button did not respond" or "added responsive button handling") — describe the issue as it currently exists
- Preserve the user's original wording where possible — restructure, don't rewrite
The templates live in references/templates.md, one per format — Full, Default, and
Short. Read the one matching the chosen format. ## Template Section Guidelines below
says what belongs in each section.
Media the user supplied. Where Step 3 turned up a screenshot, a recording, or a mockup that exists as a file, reference it in the body by its path at the point it belongs, and give it alt text that states what it shows:

For a bug that is next to the actual-behaviour sentence, not in a trailing section — the
image is the symptom, so it reads with the sentence it evidences. For a feature or a
mockup it is the Full template's ### Examples. One image per point; a body opening with
four screenshots is read as none.
On this path the draft is the deliverable, so the reference is written and nothing is
uploaded — issue-flow files the issue and uploads with it. List every referenced path
under the draft in Step 6, because a path nobody uploads renders as a broken image. The
Update Workflow is the exception: it edits an issue that already exists, and its Step 6
uploads through --attach itself.
A file that is not on disk cannot be referenced this way — an image pasted into the conversation is content in the transcript, not a path. Describe what it shows in prose, or ask the user to save it and give you the path.
Step 6: Present to User
Show the user:
- Title: The generated title
- Description: The formatted description
- Type suggestion: bug, feature, enhancement, documentation, etc.
- Label suggestions: Based on the content
- Attachments: every local path the body references, one per line, or nothing where the body references none
A finished Default-format draft:
**Title:** `Tooltip: Fix clipping at the viewport edge`
**Description:**
The tooltip is cut off when its anchor sits near the bottom of the window. It renders
below the anchor regardless of the space available, so the last two lines fall outside
the viewport and cannot be scrolled into view.

It reproduces on any page where an anchor is within roughly 80px of the bottom edge, in
every browser tested. Users lose the end of the text, which on the form fields is where
the validation rule is stated.
#### Rationale
The tooltip carries validation copy nobody else states, so a clipped tooltip means a rule
the user cannot read at all.
#### Implementation Notes
Placement is resolved once on mount from an already-clamped anchor rect, so the overflow
check always passes. Resolve placement after measuring instead, and flip above the anchor
when the rect overflows.
Type: Bug · Label: bug · Attachments: .tmp/screenshots/tooltip-clipped.png
Then ask whether they want changes, per Asking the User. Once they are satisfied,
stop. Do not create the issue on GitHub, and do not offer to — issue-flow files it, and
the user decides when. This step's deliverable is the text above and nothing else.
Update Workflow
Use this workflow when the user wants to update an existing issue.
Step 1: Identify the Issue
Determine which issue to update:
- Issue specified: User provides issue number or URL
#123,123,https://github.com/owner/repo/issues/123
- Recent issue in session: If an issue was just created in this conversation, offer to use that
- Ask: If neither, ask the user for the issue number or URL
Step 2: Fetch Current Issue Data
bash scripts/get-issue.sh <issue-number-or-url>
Show the user:
- Current title
- Current description (summarized if long)
- Current labels
Step 3: Determine Changes
Ask the user what they want to change:
- Title only
- Description only
- Both title and description
- Labels (add or remove)
- Media to add — a screenshot or recording that exists as a file, referenced in the new description per Step 5 of the create workflow. Record each path and its alt text; Step 6 uploads them.
Step 4: Apply Changes
For description updates:
- If minor edit: Apply the specific change
- If rewrite: Use the same template logic as creating (Steps 2-5 of create workflow)
For title updates:
- Follow the same title guidelines (under 72 chars, imperative mood)
Step 5: Present Changes for Approval
An issue body is public and replaces what was there — an overwritten description cannot be recovered from the issue itself. Show the changes and wait for approval before updating. Skip the preview only when the user asked for that in so many words ("update without showing", "skip preview").
Show the user:
- Before: Current title/description
- After: New title/description
- Label changes: Labels being added/removed
- Attachments: every file Step 6 would upload, one path per line, or nothing where there are none
Then ask for confirmation, per Asking the User. An upload is public and permanent, so a file that was not on that list does not go out.
Step 6: Update on GitHub
bash scripts/update-issue.sh \
--issue {{ISSUE_NUMBER}} \
--title "{{NEW_TITLE}}" \
--body "{{NEW_DESCRIPTION}}" \
--add-label "{{LABEL}}" \
--remove-label "{{LABEL}}" \
--attach "{{PATH}}#{{ALT_TEXT}}"
--attach uploads a local file and rewrites the matching local path in the new body,
keeping the alt text already written there. Repeat it once per file, and pass only paths
the body references — an unreferenced one is appended to the end of the issue instead. The
script refuses a path that is not on disk before it edits anything. Needs gh 2.99.0 or
newer and does not work on GitHub Enterprise Server; where either is missing, update the
text and say the image could not be uploaded.
Uploading is not idempotent, and which way it bites depends on --body. Passed together
with a body, gh rewrites the paths that body references, so a retry replaces rather than
duplicates. Passed alone, gh keeps the existing body and appends — and it cannot tell a
file is already attached, so an attach-only retry leaves two copies of every image. After
a partial failure, retry with the body, or pass only the paths that did not upload.
Report one line — Updated #<N>: <what changed> — <url> — and stop. Do not re-fetch the
issue to confirm the write; the script fails loudly if it did not land.
Template Section Guidelines
Description (Required)
The opening paragraph before any sections, written to the Step 5 writing rules. Length by format: Short 4-6 sentences, Default 4-8 sentences, Full 1-2 sentences (the detailed sections follow).
Rationale (Optional)
Explain the "why" when it's not obvious:
- Business/technical value
- Why this approach vs alternatives
- Architectural decisions
References (Optional)
Links that provide context:
- Design files (Figma, screenshots)
- Similar implementations
- Documentation or RFCs
- Related issues/PRs
Things to Consider (Optional)
Important aspects affecting implementation:
- Edge cases
- Performance considerations
- Integration points
- Future extensibility
Implementation (Medium/Large scope)
Functional checklist, not micromanagement:
- Clear about WHAT needs to be done
- Context for WHY
- Code snippets for complex patterns
- Logical order
UI Behavior (If applicable)
Expected user interactions:
- Focus management
- Keyboard navigation
- Animations/transitions
- Responsive behavior
Acceptance Criteria (Required)
Testable yes/no checkboxes defining completion.
Testing Steps (Optional)
How to find and test in the application:
- Navigation steps
- Actions to trigger
- Expected outcomes
Examples (Optional)
Screenshots, mockups, or code examples showing expected result. A bug's repro image is not an example — it belongs beside the actual-behaviour sentence, per Step 5.
Out of Scope (Optional)
What is explicitly NOT part of this task. Prevents scope creep.
Technical Constraints (Optional)
Specific requirements:
- Performance targets
- Browser support
- Accessibility standards
Prerequisites
ghCLI installed and authenticated (gh auth login) — required for updating issues- Must be run from within a GitHub repository for update operations
See references/helper-scripts.md for detailed script documentation, usage examples, and exit codes.
Error Handling
- If
ghCLI is not available, skip update operations and just provide the formatted output - If not in a git repository, skip GitHub integration and just provide the formatted output