mirror of
https://github.com/moeru-ai/airi.git
synced 2026-08-14 00:48:06 +00:00
chore(.agents/skills): upload GitHub attachment skill
This commit is contained in:
@@ -40,7 +40,7 @@ Create a reviewable PR from the exact commits intended for publication.
|
||||
```
|
||||
|
||||
Use `before: absent` for a new state and `after: removed` for a deleted state. A capture failure is blocking; record its reason instead of silently omitting the state.
|
||||
9. Upload every local image as a GitHub user asset while composing the PR. Use the available GitHub/`gh` CLI workflow; do not commit screenshots to the source branch or another repository.
|
||||
9. Upload every local image as a GitHub user asset by invoking `$upload-github-attachment` while composing the PR.
|
||||
10. Put all pairs under `## Visual changes`, with an image row followed by its component or page name row:
|
||||
|
||||
```markdown
|
||||
@@ -50,7 +50,7 @@ Create a reviewable PR from the exact commits intended for publication.
|
||||
| Settings / Connection | Settings / Connection |
|
||||
```
|
||||
|
||||
11. Verify that every user-asset URL renders in the created PR. Remove temporary worktrees only after upload succeeds; clear ignored `.vishot` captures when they are no longer useful locally.
|
||||
11. Verify that every user-asset URL matches the PR intent. Remove temporary worktrees only after upload succeeds; clear ignored `.vishot` captures when they are no longer useful locally.
|
||||
|
||||
## Visual Evidence Contract
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: upload-github-attachment
|
||||
description: Upload a local image or file to GitHub's user-attachments storage and return a URL suitable for issue, pull request, discussion, or comment Markdown. Use when Codex must embed local screenshots, videos, logs, or other evidence in GitHub content without committing the files to a repository.
|
||||
---
|
||||
|
||||
# Upload GitHub Attachment
|
||||
|
||||
Use the bundled script so authentication, repository resolution, MIME detection, and URL extraction remain consistent.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Require the `gh` CLI and `curl`.
|
||||
- Prefer an existing `gh auth login` session.
|
||||
- When interactive `gh` authentication is unavailable, require `GH_TOKEN` or `GITHUB_TOKEN` in the environment.
|
||||
- Stop and ask the user to configure one of those authentication methods if `gh auth token` cannot resolve a token. Never print, persist, or interpolate the token into diagnostic output.
|
||||
|
||||
## Upload
|
||||
|
||||
Run from a checkout of the destination repository:
|
||||
|
||||
```bash
|
||||
.agents/skills/upload-github-attachment/scripts/upload-github-attachment.sh /absolute/path/to/image.png
|
||||
```
|
||||
|
||||
Specify the repository when the current directory cannot resolve it:
|
||||
|
||||
```bash
|
||||
.agents/skills/upload-github-attachment/scripts/upload-github-attachment.sh \
|
||||
--repo moeru-ai/airi \
|
||||
/absolute/path/to/image.png
|
||||
```
|
||||
|
||||
The script prints only the final `https://github.com/user-attachments/assets/...` URL. Embed it with `` for an image or `[label](URL)` for another file.
|
||||
|
||||
## Verification
|
||||
|
||||
1. Request the returned URL and follow redirects.
|
||||
2. Require HTTP 200 and the expected content type.
|
||||
3. After creating or editing the GitHub content, reopen it and verify that the attachment renders.
|
||||
|
||||
The upload endpoint is currently undocumented by GitHub. Treat a rejected request as a blocking capability change instead of falling back to committing PR-only artifacts. Background and the observed Bearer-token flow: <https://island94.org/2026/08/programmatically-upload-attachments-to-github-issues-pull-requests-comments>.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Upload GitHub Attachment"
|
||||
short_description: "Upload files as GitHub user attachments"
|
||||
default_prompt: "Use $upload-github-attachment to upload this file for a GitHub issue or pull request."
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
printf 'Usage: %s [--repo OWNER/REPO] [--content-type MIME] FILE\n' "$(basename "$0")"
|
||||
}
|
||||
|
||||
repository=''
|
||||
content_type=''
|
||||
|
||||
while (($# > 0)); do
|
||||
case "$1" in
|
||||
--repo)
|
||||
repository="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--content-type)
|
||||
content_type="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--*)
|
||||
printf 'Unknown option: %s\n' "$1" >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
;;
|
||||
*)
|
||||
if [[ -n "${attachment_file:-}" ]]; then
|
||||
printf 'Only one file can be uploaded per invocation.\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
attachment_file="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! command -v gh >/dev/null 2>&1; then
|
||||
printf 'GitHub CLI (gh) is required. Install it, then run gh auth login or set GH_TOKEN/GITHUB_TOKEN.\n' >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
printf 'curl is required.\n' >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
if [[ -z "${attachment_file:-}" || ! -f "$attachment_file" ]]; then
|
||||
printf 'Provide one existing local file.\n' >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ -z "$repository" ]]; then
|
||||
repository="$(gh repo view --json nameWithOwner --jq .nameWithOwner 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if [[ -z "$repository" ]]; then
|
||||
printf 'Could not resolve a GitHub repository. Run inside a checkout or pass --repo OWNER/REPO.\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
attachment_token="${GH_TOKEN:-${GITHUB_TOKEN:-}}"
|
||||
if [[ -z "$attachment_token" ]]; then
|
||||
attachment_token="$(gh auth token 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if [[ -z "$attachment_token" ]]; then
|
||||
printf 'GitHub authentication is required. Run gh auth login or set GH_TOKEN/GITHUB_TOKEN.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$content_type" ]]; then
|
||||
if ! command -v file >/dev/null 2>&1; then
|
||||
printf 'The file utility is required for MIME detection; install it or pass --content-type.\n' >&2
|
||||
exit 127
|
||||
fi
|
||||
content_type="$(file --brief --mime-type "$attachment_file")"
|
||||
fi
|
||||
|
||||
repository_id="$(GH_TOKEN="$attachment_token" gh api "repos/$repository" --jq .id)"
|
||||
attachment_name="$(basename "$attachment_file")"
|
||||
|
||||
response="$(curl --silent --show-error --fail-with-body \
|
||||
--request POST \
|
||||
--header "Authorization: Bearer $attachment_token" \
|
||||
--header 'Accept: application/json' \
|
||||
--url-query "name=$attachment_name" \
|
||||
--url-query "content_type=$content_type" \
|
||||
--url-query "repository_id=$repository_id" \
|
||||
--data-binary "@$attachment_file" \
|
||||
'https://uploads.github.com/user-attachments/assets')"
|
||||
|
||||
attachment_url="$(printf '%s' "$response" | sed -n 's/.*"url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
|
||||
|
||||
if [[ "$attachment_url" != https://github.com/user-attachments/* ]]; then
|
||||
printf 'GitHub did not return a user-attachment URL.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$attachment_url"
|
||||
Reference in New Issue
Block a user