take-issue
Take a GitHub issue onto the branch — export the thread and its attachments, commit them, and hand the number back to whoever called. Invoke as `/take-issue <number|url>`; its callers are `/task`, `/p
SKILL.md.claude/skills/take-issue/SKILL.md
GitHubEnd state of this skill: docs/issue/<n>/ holds the thread and its attachments,
that export is committed on the branch, you have read it, and control is back
with the caller — which is where every decision about the work is made.
This skill decides nothing. It is the one place an issue gets pulled onto a branch, so its contract to its callers is that the issue has been taken: exported, committed, and there for a later session to re-read.
Argument shape
The argument is an issue number or a full issue URL, optionally with prose
around it — fix the sidebar scroll #847 is the same invocation as 847. The
prose is the operator's own summary of the issue and is worth reading as a hint
at what they care about, but the export outranks it: where the two disagree,
the thread is what the work is against.
Step 0 — Mode gate: native plan mode is not supported
If the session is in native plan mode — the launch banner or system prompt
says so, ExitPlanMode is the offered exit, or an edit is refused for that
reason — stop before running anything below. Tell the operator:
- this flow is plan-file-based (
@.claude/skills/plan/SKILL.md), and native plan mode cannot write the two artifacts it depends on: the export underdocs/issue/<n>/and the plan underdocs/plans/. Both must be committed and pushed, because the operator reviews from a different machine than the one you run on; - the plan-mode approval UI is unreliable in web sessions regardless (anthropics/claude-code#72704);
- so switch to accept-edits or auto and send any reply — the run picks up from there; no re-invocation needed.
Do not work around it, and do not fall back to a chat-only plan.
Step 1 — Export the issue locally (then read the export)
Look under docs/issue/<n>/ before running anything: the export is normally
already there. .claude/hooks/prompt-issue-export.sh runs this step's command
ahead of a session's first turn when that prompt ends in #<N>, and names
what it wrote in that turn's context — so where the export exists, this step is
the reading below and nothing else. Run the command when it is absent: a number
that reached you any other way, a hook that reported a failure, or a session
whose first prompt did not end in one.
First command: run the bundled exporter:
python3 scripts/export-github-item.py <issue-number|https://github.com/OWNER/REPO/issues/N> [--repo OWNER/REPO]
The script writes docs/issue/<n>/issue.md (body + comments + timeline) and
downloads any image / file attachments referenced in the thread to
docs/issue/<n>/attachments/.
Consult the issue only through the export — never gh issue view, the GitHub
MCP tools, or WebFetch in its place. gh issue view alone does not fetch
attachments: GitHub's private-user-images.githubusercontent.com URLs require
an authenticated request even when the issue is public, which is why the export
script exists. WebFetch on a github.com issue page often fails outright in
isolated environments. For a quick metadata check unrelated to the task at hand
— labels, assignees, linked PRs —
gh issue view <n> --json title,body,labels,assignees,state,url is still fine.
Then read docs/issue/<n>/issue.md end to end, and open the files under
docs/issue/<n>/attachments/ when you need pixels (screenshots, mockups, design
references).
Auth: the script reads $GH_TOKEN (or $GITHUB_TOKEN) first, then falls
back to gh auth token. One of those must be available. Deps: stdlib Python
3.9+ only — no pip install needed.
Repo: if --repo OWNER/REPO is omitted and the argument isn't a full issue
URL, the script reads origin from the current git checkout. Pass --repo
explicitly when exporting an issue from a different repo than the one you're
working in.
If the export fails, stop and report — do not start solving the task from
the title alone. Tell the user what failed and how to proceed. The script exits
non-zero on every failure — auth, network, repo not found, or an attachment that
wouldn't download — so check the status, not just the last line. A partial
attachment failure still writes the Markdown and prints
Downloaded 2/3 attachment(s) plus each URL it missed: the thread is there, but
you'd be reading it with pixels missing.
Attachments on github.com/user-attachments/… reach that state through two
accommodations in the exporter, worth knowing if one ever regresses. GitHub
redirects those URLs to a pre-signed S3 URL, and S3 rejects a request that also
carries an Authorization header
(400 InvalidArgument: Only one auth mechanism allowed) — so the script drops
that header whenever a redirect changes host. The remote-session egress proxy
separately refuses the path outright, with a 403 whose JSON message names
repository-scoped endpoints (the tell that it is the proxy and not GitHub), so a
download that fails retries on a direct connection — the same accommodation
.claude/hooks/gh-shim.sh makes for gh. A Failed to download line therefore
means both attempts failed: report it rather than planning as though you had
seen the pixels.
Attachments with no extension
The filename stem is the asset id, so an attachment's type is not readable off
its name — file docs/issue/<n>/attachments/* before deciding what to open. An
image you read directly. A video (e.g. ISO Media, Apple QuickTime movie)
you cannot play: load @.claude/skills/take-issue/video-frames.md, which
extracts frames you can read as images.
Step 2 — Commit the export
git add docs/issue/<n> && git commit -m "docs: #<n> export the issue"
Commit it now, before any planning. The export is the source of truth for what the issue says, so an agent resuming this branch — after a context wipe, a handoff, or in a parallel session — re-reads the thread instead of re-exporting it, and committing it up front means that holds even if the session dies mid-plan. Never commit it on the trunk or a promotion branch.
@.claude/skills/finalize/SKILL.md deletes the whole docs/issue/ tree in the
last commit before gh pr ready, so this add and that delete cancel out in the
squash. You must not delete it.
Step 3 — Return to the caller
Hand back the export path (docs/issue/<n>/issue.md) and the number. Whether
it is the number a PR closes is the caller's to decide, and often it isn't
one: an issue taken for context — a related report, the thread a decision was
argued in, the parent of a carve — is read and closes nothing. Where the caller
carves, @.claude/skills/plan/carving.md settles which number the PR closes.
Then stop. Everything downstream belongs to the caller.