Upload local images to any GitHub repository (image hosting), generating CDN links ready to paste directly (Markdown / plain URL), with support for deletion and list management. No frontend dependency; works in any environment (including when organizing assets).
Overview
Image hosting = any GitHub repository + an image directory (default blog/). This skill calls the GitHub Contents API via
gh api (the same interface as the web-based image host), uploading images to the target repository
and returning CDN-accelerated links. Supports multiple CDNs, multiple files, subdirectories, and duplicate-name protection.
Zero-configuration experience: On first use, run scripts/setup.sh for one-click initialization — it automatically guides login,
detects/creates the repository,reads the repository configuration and links with the img.shenzjd.com project web frontend, detects the branch,
and generates a local cache. After that, users only need to tell the AI "upload xxx.png to image hosting"; no manual configuration of anything is required.
Configuration path: Remote configuration is always read from the repository .img.shenzjd.com/config.json(consistent with the img.shenzjd.com
web frontend); the old repository's .imgx-config/config.json is still readable for compatibility, but all new writes use the new path.
Visual backend: If you don't want to use the command line, just open img.shenzjd.com
web frontend — the same image hosting repository, drag-and-drop upload (compression/watermark/WebP), image management (preview/delete/link copy),
visual settings (branch/directory/CDN). After changing the configuration on the web frontend, rerun setup.sh to sync it to the AI side.
When to Use
- User wants to upload local images to a GitHub-hosted figure bed and get CDN links
- User wants a markdown image link like
for blog/docs - User wants to delete an image from the figure bed repo
- User wants to list/search images already in the figure bed repo
- User mentions image hosting / upload image / CDN link / jsdelivr / jsdmirror / image hosting
- User asks to organize assets and host them (AI cannot use the web UI, this skill covers that gap)
When NOT to Use:
- User has a figure bed web app with its own upload flow and can use it directly
- Target repo is not a GitHub repo (e.g. local-only or other platforms)
ghCLI is not installed or not authenticated (install andgh auth loginfirst)
Core Pattern
Step 0: First use — one-click initialization (setup.sh)
For new users on first use (or when unsure whether the configuration is ready), first run:
scripts/setup.sh
It will automatically complete,no manual configuration required from the user throughout:
- Login guidance: When not logged in, run
gh auth login, show the output authorization link and one-time code to the user, and guide them to complete authorization in the browser (only once) - Automatic Owner retrieval: Get the currently logged-in GitHub username; no manual entry needed
- Repository readiness: Detect the default repository
img.shenzjd.com; if it doesn't exist, automatically create it (public) and write a promotional/usage README - Configuration linkage (core): Read the repository's
.img.shenzjd.com/config.json(shared with the img.shenzjd.com project web frontendthe same configuration), automatically inherit branch/directory/cdn; if the repository has no configuration, initialize a project-format configuration and write it back to the repository (immediately usable on the web frontend) - Branch detection: Automatically find the branch where the image directory actually resides (avoiding the pitfall of default_branch being inconsistent with the image branch)
- Configuration persistence: Write to the local cache
~/.config/github-figure-bed/config.env, which the three operation scripts read automatically, after whichuse directly with zero parameters - Self-test: Upload a 1x1 test image and delete it to verify the full chain
Optional parameters:--repo <仓库名> to change the repository name;--cdn <cdn> to change the default CDN;
--no-test to skip the self-test;--force-readme to force rewriting the promotional README.
Step 1: Determine the target repository and configuration
Priority (high→low):Command-line parameters > environment variables IMGX_* > Local cache ~/.config/github-figure-bed/config.env(setup.sh syncs from remote) > In-script DEFAULT_* default section。
Configuration linkage: The remote config source is the repository .img.shenzjd.com/config.json(shared with the img.shenzjd.com
project web frontend); setup.sh automatically reads and syncs it to the local cache; after modifying the config on the project web frontend,
rerun setup.sh to refresh the local copy.
- Repository: setup.sh has already configured it automatically (default
img.shenzjd.com); when uploading to another repository, pass--owner/--repo - Branch: if not specified, automatically detects the repository
default_branch(usuallymain, zero configuration) - CDN: default
jsdelivr; recommended for mainland China usersjsdmirror; for local debugging you can useraw - Base directory: default
blog/, can be changed with--diror the environment variableIMGX_DIRchange
If the user has not provided a repository and has never run setup, first run scripts/setup.sh(or have the user set
IMGX_OWNER / IMGX_REPO)。
Step 2: Upload images
scripts/upload.sh <文件1> [文件2 ...] \
[--owner O] [--repo R] [--branch B] [--cdn jsdelivr|jsdmirror|jsd-onmicrosoft|statically|raw] \
[--dir 子目录] [--force] [--silent]
- Supports uploading multiple files at once;
--dirappend a subdirectory (e.g.--dir 2026/08→blog/2026/08/) - Duplicate name protection (default): when the target file already exists, automatically rename to
<时间戳>_<原文件名>, without overwriting existing files — in an image hosting scenario, overwriting would break already-published online references;--forceonly then overwrite - Outputs a JSON array, each item containing
{file, path, cdn_url, markdown, html_url, action}:cdn_url: CDN link (with the correct branch)markdown: directly pasteableaction:created/renamed(auto-renamed) /replaced
After uploading, show the user: Markdown format + plain URL, for example:

Plain URL:https://cdn.jsdelivr.net/gh/alice/my-figure-bed@main/blog/photo.png
Step 3: Delete images
scripts/delete.sh <文件名1> [文件名2 ...] [--owner O] [--repo R] [--branch B] [--dir 子目录] [--silent]
- The filename refers to the filename under the base directory (excluding the
blog/prefix) - When unsure whether it exists, first
scripts/list.sh <关键词>to confirm
Step 4: List images
scripts/list.sh [关键词] [--owner O] [--repo R] [--branch B] [--dir 子目录] [--limit N] [--full]
- With no arguments, lists files (default 50 items); pass a keyword for fuzzy filename filtering (e.g.
list.sh 响应性) --fulloutputs complete JSON (including size); by default only outputs a filename list
Quick Reference
| Operation | Command | Description |
|---|---|---|
| First-time initialization | setup.sh |
Login guidance + automatic configuration + repository ready (zero manual configuration) |
| Upload | upload.sh img.png |
Returns CDN + Markdown links (no arguments needed after setup) |
| Upload (multiple) | upload.sh a.png b.png c.png --dir 2026/08 |
Batch + subdirectory |
| Delete | delete.sh a.png |
Delete by filename |
| List | list.sh / list.sh 关键词 --full |
List/search image hosting files |
| Overwrite | upload.sh img.png --force |
Overwrite and update if it already exists |
| Environment variables | IMGX_OWNER IMGX_REPO IMGX_BRANCH IMGX_CDN IMGX_DIR |
Avoid repeatedly passing arguments |
Common Mistakes
- 404 caused by wrong branch: jsDelivr-based ones look for
mainby default. The script will automatically detect the default branch, but if the image is actually on a non-default branch (e.g.master), you must--branch masterspecify it explicitly. - Add to jsdmirror/jsdelivr links
?format=webp: these two CDNs have no dynamic transformation; adding parameters will definitely cause a 404. - Overwrite existing files: by default, duplicate names are automatically renamed for protection; only use this when you confirm you want to update the content at the same link
--force。 - Forgot to specify the repository:
owner/repoautomatically written to the config file by setup.sh, no need to fill in manually; when setup has not been run, you need to pass parameters or setIMGX_OWNER/IMGX_REPO, the script cannot infer the target repository out of thin air. - Uploading without ever running setup: will error because owner/repo is empty, first run
setup.sh。 - Changed config on the web but not synced locally: after the remote config (
.img.shenzjd.com/config.json) changes, rerunsetup.shto refresh the local cache. - Large image failure: the script already uses a temporary file to assemble the JSON payload to avoid command-line length limits; if it still fails, check the GitHub repository 100MB single-file limit and the network.
- 404 immediately after upload: the CDN has caching, usually takes a few seconds to a few minutes to take effect, retry later.
- Filename contains spaces/special characters: will break the link, rename before uploading (Chinese is fine).
References
references/cdn-links.md: CDN link format, full list of pitfalls, repository API reference- Inside the script:
scripts/setup.sh(one-click initialization) /scripts/upload.sh/scripts/delete.sh/scripts/list.sh