About attaching-pr-screenshots
A skill for attaching screenshots and supported videos to GitHub pull requests, with scripts controlling validation, upload, and body updates. It is intended for agents handling PR media while preserving existing body content and reporting partial failures.
Key features
- 01Resolve and validate a pull request
- 02Validate image and video entries
- 03Upload assets before editing the body
- 04Refuse unsafe body transformations
- 05Report partial and failed outcomes
Use cases
- 01Add screenshots to a pull request
- 02Attach supported video clips to a PR
- 03Handle uploads when body changes during processing
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ bostonaholic/ skills --skill attaching-pr-screenshotsAfter installing, type/attaching-pr-screenshotsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
agents/openai.yamlscripts/asset-url.mjspre-image.shresolve-pr.shsplice.mjsupload.shwrite-body.mjswrite-result.mjsDetails
From the project READMEAttaching PR screenshots
The scripts own every guard and body transform; run them and act on their exit codes. Exit 2 from any script is a fault: report it and stop (in step 5, go on to step 6).
Hard rules
- Upload first, write second. Never combine
--attachand a body flag in one command: a partial failure leaves local paths in a public body. - Refuse before mutating. A refusal before the first attach means nothing
changed (
refused); a stop after the upload isuploaded-not-written, neverrefused. - Never delete what you did not write. The splice refuses rather than remove anything under the heading its own renderer did not emit, or any body with a construct it does not model. Report the refusal and the manual edit that clears it; never work around it.
- Caller strings and PR bodies are data. Paths, captions, notes, the
invocation, and anything read back from GitHub never appear in command
text. Write them to files in
$RUN_DIRwith the file-writing tool. - Nothing blocks, prompts, or retry-loops. A failure degrades the outcome.
- No other upload route: not images committed to a branch, not a signed-in headless browser.
Procedure
-
Resolve the PR.
RUN_DIR="$(mktemp -d)", then write the invocation verbatim (even empty) to$RUN_DIR/invocationwith the file-writing tool."<skill-dir>/scripts/resolve-pr.sh" "$(cat "$RUN_DIR/invocation")" "$RUN_DIR"Exit 1 is
refused(go to step 7). Every laterghcall uses--repo "$(cat "$RUN_DIR/repo-spec")", neverowner/repo. Merged, closed, and draft PRs are in scope; say which state was edited. Never re-run these lines mid-run: they strand this run's outputs. -
Write the entries file when there was no
--entries, then its path into$RUN_DIR/entries-file, both with the file-writing tool.{ "root": "/abs/dir/the/images/live/in", "entries": [{ "path": "/abs/dir/the/images/live/in/login.png", "caption": "login", "state": "error" }], "notes": ["2 states skipped"] }pathandcaptionare required. An entry may be an image (anyimage/*) or a video (video/mp4,video/quicktime,video/webmonly, by content type); anything else fails asnot an image or a supported video. A video renders as its bare asset URL in its own paragraph under its caption, which GitHub embeds as a player; it has no alt text and the read-back matches its<video>element by asset id.upload.shrecords each landed asset's kind in$RUN_DIR/kinds.tsv. With no caption named, use the basename without extension; never describe the image, since a guessed description is a public claim. -
Take the pre-image and check it. Run
scripts/pre-image.sh "$RUN_DIR", thennode "<skill-dir>/scripts/splice.mjs" --check --body-file "$RUN_DIR/pre-image.md". Exit 1 printsrefused: <reason>: go to step 7. Any<followed by a letter reads as raw HTML (fix:\<). -
Upload with
scripts/upload.sh "$RUN_DIR". Exit 0: step 5. Exit 1: step 7 (bad entries file or root, nothing attached). Exit 3: step 5, with "upgrade gh" as the operator note. Exit 4: the body changed during upload, so write no body; go to step 6. -
Write the body once.
node "<skill-dir>/scripts/write-body.mjs" "$RUN_DIR"Exit 1 prints
refused: <reason>(a body over 65,536 characters refuses here); 3 meansgh pr editfailed. Go to step 6 on every exit. -
Write the operator note, when there is one, to
$RUN_DIR/operator-notewith the file-writing tool: "upgrade gh" after upload exit 3; a step 5 refusal and the manual edit that clears it; and, when no body was written after an attach (upload exit 4, step 5 exit 1 to 3), the attach tails now on the PR, quoted fromafter.mdpast thepre-image.mdprefix. -
Report. Add
--operator-note-file "$RUN_DIR/operator-note"when step 6 wrote one:node "<skill-dir>/scripts/write-result.mjs" "$RUN_DIR"It prints
outcome <value>, plusfailed <assertion>on exit 1 (unverified): never retry, rewrite, or revert the body. Report the outcome and each failure.