Skip to content
Skills Library
Programming · Tool integration

github-attachments

/github-attachments

Upload images and files to GitHub PRs, issues, and comments so GitHub hosts them instead of committing binaries to the repository. Use when adding screenshots to a PR or issue body, replacing committed screenshots or raw.githubusercontent.com links with hosted attachments, or when `gh` appears to offer no way to attach a file.

Repository
ai-config ↗
Licence
GPL-3.0
Last repository change
08.10.2026

Assessment and security

100/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
1
Scripts
0
HTML comments
0
URLs in SKILL.md
5
Deprecation

No signals detected. Repository changed on 08.10.2026, without references to retired models or APIs.

Licence

GPL-3.0, otwarta.

What it does and when to use it

Upload images and files to GitHub PRs, issues, and comments so GitHub hosts them instead of committing binaries to the repository. Use when adding screenshots to a PR or issue body, replacing committed screenshots or raw.githubusercontent.com links with hosted attachments, or when `gh` appears to offer no way to attach a file.

Original author description

Upload images and files to GitHub PRs, issues, and comments so GitHub hosts them instead of committing binaries to the repository. Use when adding screenshots to a PR or issue body, replacing committed screenshots or raw.githubusercontent.com links with hosted attachments, or when `gh` appears to offer no way to attach a file.

How it works

  1. Upload one file
  2. Order matters: upload, reference, then verify
  3. Bulk uploads and migrations
  4. Alternatives
Original SKILL.md headings · 130 lines, 807 words
SKILL.md preview
---
name: github-attachments
description: Upload images and files to GitHub PRs, issues, and comments so GitHub hosts them instead of committing binaries to the repository. Use when adding screenshots to a PR or issue body, replacing committed screenshots or raw.githubusercontent.com links with hosted attachments, or when `gh` appears to offer no way to attach a file.
---

# GitHub Attachments Without Committing Binaries

Screenshots attached to a PR are review ephemera. Committing them puts
binaries in git history forever — bloating every clone, with no value to the
codebase. The historical workaround (commit the image, link it via a
`raw.githubusercontent.com` URL pinned to a SHA) is the anti-pattern this
skill replaces.

`gh` has no attachment support: no flag on `gh pr create`, `gh pr edit`,
`gh issue create`, or `gh issue comment`. The GraphQL API has no upload
mutation. But an undocumented endpoint produces exactly the
`https://github.com/user-attachments/assets/<uuid>` URLs that browser
drag-and-drop produces, and it works from `curl` with a `gh` token.

**This endpoint is undocumented and unofficial. GitHub could change or
remove it without notice.** Verified working against github.com on
2026-08-04.

## Upload one file

`repository_id` must be a repository **you can push to** — it is the upload
quota's owner, not the destination. It does not have to be the repo the issue
or PR lives in, and the resulting URL is not tied to it: an asset uploaded
against your fork renders fine on an upstream issue.

So when contributing to a repo you do not own, do not reach for the upstream's
id. Pushable means `permissions.push` is true — being able to open issues or
PRs against a public repo does not grant it, and for any upstream you
contribute to via a fork it will be false:

```bash
gh api repos/OWNER/REPO --jq .permissions
```

Prefer your own fork (or any repo you own) as the upload target. Using an
upstream you lack push on fails with a misleading `404 Not Found` rather than
a permissions error, which reads like a broken endpoint or a bad asset name.

```bash
FILE=screenshot.png
# A repo you can push to — your fork, not the upstream you are filing against.
REPO_ID=$(gh api repos/YOUR-FORK-OWNER/REPO --jq .id)
TOKEN=$(gh auth token)

curl -sS -X POST \
  "https://uploads.github.com/user-attachments/assets?name=$(basename "$FILE")&content_type=image/png&repository_id=$REPO_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" \
  --data-binary "@$FILE"
```

HTTP 201 with `{"url":"https://github.com/user-attachments/assets/<uuid>"}`.

`content_type` must match the file (`image/png`, `image/jpeg`, ...).

Reference the returned URL as ordinary markdown in the body:

```markdown
![Login screen](https://github.com/user-attachments/assets/<uuid>)
```

Then apply it: `gh pr edit N --body-file body.md` (or `gh issue edit`,
`gh pr comment --body-file`, ...).

## Order matters: upload, reference, *then* verify

**An uploaded asset returns HTTP 404 with a 9-byte "Not Found" body until
some PR or issue body actually references it.** The upload returns 201 and a
valid URL immediately, but the URL does not resolve until it is referenced.

This is not propagation delay. Waiting does not help, and re-uploading only
creates orphaned assets. Do not diagnose a 404 at this stage as a failed
upload.

Two different 404s, distinguished by when they happen: on the **POST** it
means the token cannot push to `repository_id` (use a repo you own); on
**fetching an asset URL** it means nothing references it yet.

So:

1. Upload every file, recording the name→URL mapping as you go.
2. Rewrite the PR/issue body to reference the new URLs.
3. Apply the body with `gh pr edit` / `gh issue edit`.
4. **Only then** verify the assets resolve.

Verify by byte count against the source, not by HTTP status —
`curl -o /dev/null -w '%{http_code}' -L` can report a misleading status:

```bash
exp=$(wc -c < "$FILE")
got=$(curl -sS -L --max-time 30 "$URL" | wc -c)
[ "$exp" = "$got" ] || echo "MISMATCH: expected $exp, got $got"
```

A 9-byte response is the literal string `Not Found` — the asset is not
referenced anywhere yet.

## Bulk uploads and migrations

For several files, append each `name<TAB>url` pair to a TSV as you upload,
then do the body rewriting with a small Python regex substitution over that
mapping. That keeps the fragile per-file step separate from the fragile
body-rewriting step, so a failure in one does not force redoing the other.

Under `zsh` with `noclobber` set, `>` and `>>` silently fail when
creating or overwriting a file — which can silently lose the whole mapping.
Use `: > mapping.tsv` to create it, or pipe through `tee -a`.

To migrate screenshots already committed to a repository:

1. Extract the blobs with `git show <sha>:<path> > out.png`. They may live
   on commits no longer reachable from any branch, so find the SHA from the
   PR's commit list or the reflog rather than assuming `HEAD` has them.
2. Upload them and rewrite the PR bodies to the new URLs.
3. Verify (order above), then `git rm` the files.

`git rm` removes the files from the tree but not the blobs from history;
purging those requires rewriting history, which is rarely worth it for a few
small images.

## Alternatives

`gh` CLI extensions such as `gh-image` wrap this same endpoint, and
cli/cli#12960 requests native support. The raw `curl` above needs no
dependencies.

Endpoint credit:
<https://island94.org/2026/08/programmatically-upload-attachments-to-github-issues-pull-requests-comments>

Versions

The database has no other skills named github-attachments.

Database snapshot: 13.11.2026 · package version 8c6d477e6dc0 (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100github-image-upload
    Upload local images and other files (PDF, zip, log, …) to GitHub and embed them in a pull request description, an issue, or a comment — producing canonical github.com/user-attachments URLs (private-repo uploads stay private). Use when asked to "attach a screenshot to the PR",…
  • 100gh-attach
    Uploads a local file (screenshot, image, PDF, zip, video) to GitHub user-attachments, downloads GitHub user-attachments, and embeds local files in a PR, issue, or comment. Use when asked to "attach a screenshot to the PR", "add an image to the issue", "embed before/after…
  • 100gh-attach
    Upload and download GitHub user-attachments (screenshots, PDFs, zips, videos) from the terminal; use when asked to attach or embed a file in a PR, issue, or comment, or download an attachment URL.
  • 100gh-attach
    Upload and download GitHub user-attachments (screenshots, PDFs, zips, videos) from the terminal; use when asked to attach or embed a file in a PR, issue, or comment, or download an attachment URL.
  • 87github-screenshots
    Embed screenshots, images, diagrams, GIFs, and screen recordings in GitHub PRs and issues — or stage them ahead of a PR, collect them into one attachments comment, or get a durable public link to share a visual with a person. Use this whenever a visual needs to end up in a PR…
  • 100gh-image
    Upload local images to GitHub and get canonical user-attachments embed URLs; use when asked to attach a screenshot to a PR, issue, or comment, or to embed before/after images in a README.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.