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

GitHub

End 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 under docs/issue/<n>/ and the plan under docs/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.