Install
openclaw skills install @songhonglei/skill-hub-queryQuery, install, update, and edit AI agent skills on any compatible Skill Hub (self-hosted, or any Hub implementing the documented API contract). Dual-channel: with a token it uses the authenticated API (full features including private skills); without a token it falls back to the unauthenticated channel (public skills only). Covers: list newly published skills, search by keyword / author / time / source, inspect version history, install or upgrade a specific version, and edit a skill's card metadata (display name, summary, tags, visibility, applicable position, etc.) via a safety-first GET -> diff -> backup -> PUT -> dual-channel verify -> auto-rollback flow. Trigger phrases include "what's new on the hub", "search for X skill", "install X", "update Y skill", "edit hub card info", "show skill version history", "skill-hub-query".
openclaw skills install @songhonglei/skill-hub-querybuild-better-skills suite — see Stages for the lifecycle map.Drive any compatible Skill Hub from the command line with a single, predictable interface. Works with self-hosted Hubs that implement the documented API contract.
Heads-up: this tool talks to Hubs that implement the API contract below. It does NOT target clawhub.ai — that Hub has its own API surface and an official CLI named
clawhub. Use this tool when you have a private or compatible Hub to drive.
When the user asks a Hub-related question ("what's on the hub", "install X",
"search for Y"), run sync.sh / query.sh / install.sh directly — the
scripts pick the right channel:
| User state | Channel | Capability |
|---|---|---|
| Token set (env or credentials file) | OpenAPI (<HUB_API_PREFIX>/* with auth header) | Full features incl. private skills |
| No token | Legacy fallback (<HUB_LEGACY_API_PREFIX>/* without auth) | Search & install public skills |
When falling back the first time, the script emits a one-shot notice suggesting the user request a token for long-term stability. After that it stays silent.
Only when the user explicitly needs to:
doctor.sh explicitly recommends it# Option A: env var (CI / temporary)
export SKILL_HUB_URL="https://hub.your-company.com"
export SKILL_HUB_TOKEN="<your-token>"
# Option B: credentials file (recommended, cross-skill reusable)
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/skill-hub-query"
cat > "${XDG_CONFIG_HOME:-$HOME/.config}/skill-hub-query/credentials.json" <<EOF
{
"endpoint": "https://hub.your-company.com",
"token": "<your-token>",
"authHeader": "Authorization",
"authScheme": "Bearer "
}
EOF
chmod 600 "${XDG_CONFIG_HOME:-$HOME/.config}/skill-hub-query/credentials.json"
Then verify with bash scripts/doctor.sh.
Never echo a full token back to the user, write it outside the credentials directory, or commit it to git.
bash sync.sh (incremental; zero HTTP if nothing changed)bash query.sh keyword Xbash sync.sh then bash query.sh slug X to get the latest version and summarybash install.sh X --yes--yes, install.sh blocks on stdin for already-installed skills; in non-interactive mode it refuses outright — preventing silent overwriteinstall.sh <slug> --yes (avoid rate limiting); report failures separately⚠️ You MUST configure
SKILL_HUB_URL(or.endpointin the credentials file) before any network call. There is no baked-in default, because no single public Hub is guaranteed to implement this contract. Runbash scripts/doctor.shfirst — it will tell you exactly what's missing.
| Env variable | Default | Purpose |
|---|---|---|
SKILL_HUB_PROVIDER | (unset) | Select a built-in adapter for a known public Hub instead of the generic contract. Currently supports skillhub_cn (see Built-in provider: skillhub.cn). When unset, the generic-contract behavior below applies. |
SKILL_HUB_URL | (must be set) | Hub base URL, e.g. https://hub.your-company.com (ignored when SKILL_HUB_PROVIDER is set) |
SKILL_HUB_API_PREFIX | /api/v1/skill | Primary API path |
SKILL_HUB_LEGACY_API_PREFIX | /api/skill | Fallback API path |
SKILL_HUB_AUTH_HEADER | Authorization | HTTP header name for auth |
SKILL_HUB_AUTH_SCHEME | Bearer | Header value prefix (note trailing space; set to "" for API-key auth) |
SKILL_HUB_TOKEN | (unset) | API token (takes precedence over credentials file) |
SKILL_HUB_TOKEN_FILE | ${XDG_CONFIG_HOME:-~/.config}/skill-hub-query/credentials.json | Credentials file |
SKILL_HUB_SKILLS_DIR | (auto-detect) | Where installed skills land |
SKILL_HUB_CACHE_DIR | ${XDG_CACHE_HOME:-~/.cache}/skill-hub-query | Where the local cache lives |
SKILL_HUB_EDIT_PREFIX | same as SKILL_HUB_LEGACY_API_PREFIX | Path prefix for /edit and /detail (edit.sh only) |
SKILL_HUB_DISABLE_EDIT | 0 | Set to 1 to disable edit.sh when your Hub does not implement /edit |
SKILL_HUB_BACKUP_RETENTION | 20 | How many recent edit.sh backups to keep per slug (older are pruned) |
SKILL_HUB_DOWNLOAD_TIMEOUT | 120 | curl --max-time (seconds) for skill ZIP download in install.sh |
SKILL_HUB_OWNER_EMAIL | (auto: git config user.email) | Email used by edit.sh owner pre-check; override if your git identity differs from your Hub identity |
Resolved in order: SKILL_HUB_SKILLS_DIR -> ~/.claude/skills/ ->
~/.openclaw/workspace/skills/ -> ~/.config/skills/ (first existing wins).
echo 'export SKILL_HUB_URL="https://hub.your-company.com"' >> ~/.bashrc
echo 'export SKILL_HUB_TOKEN="<your-token>"' >> ~/.bashrc
source ~/.bashrc
Or — for richer per-Hub profiles — use the credentials file:
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/skill-hub-query"
cat > "${XDG_CONFIG_HOME:-$HOME/.config}/skill-hub-query/credentials.json" <<EOF
{
"endpoint": "https://hub.your-company.com",
"token": "<your-token>",
"authHeader": "Authorization",
"authScheme": "Bearer "
}
EOF
chmod 600 "${XDG_CONFIG_HOME:-$HOME/.config}/skill-hub-query/credentials.json"
Never commit tokens to git, never hardcode them in scripts, and never echo the full token to the user.
skillhub.cn is a China-optimized public skills hub. Because its API shape differs from the generic contract above, this skill ships a built-in adapter for it. Activate it with:
export SKILL_HUB_PROVIDER=skillhub_cn # one-off
# or persist it:
echo 'export SKILL_HUB_PROVIDER=skillhub_cn' >> ~/.bashrc
When the provider is active, SKILL_HUB_URL / token / API-prefix env vars are
ignored — the adapter targets https://api.skillhub.cn directly. No token
is needed (all supported operations are public, read-only).
| Operation | Command | Supported? |
|---|---|---|
| Search / browse | query.sh keyword <kw> · query.sh today · query.sh combo --keyword= --category= --source= | ✅ live (no local cache) |
| Skill detail | query.sh slug <slug> | ✅ |
| Version history | query.sh versions <slug> | ✅ |
| Install | install.sh <slug> [--yes] | ✅ |
sync.sh | — | ⚪ no-op (live search needs no cache; informational, exit 0) |
query.sh author <handle> | — | ❌ no author-filter param on skillhub.cn (use keyword) |
edit.sh (edit card metadata) | — | ❌ not available — see below |
skillhub.cn card metadata (displayName / summary / tags / category) is a
one-way mirror synced from the upstream source (clawhub / GitHub). The only
write endpoints the platform exposes are publish / unlist / relist / delete /
claim — none of which edit card fields. To change a card, update the
upstream source and re-publish/let it re-sync. (This differs from a
generic Hub that implements a PUT /edit contract, which edit.sh drives.)
export SKILL_HUB_PROVIDER=skillhub_cn
bash scripts/query.sh keyword code # search
bash scripts/query.sh today # browse newest
bash scripts/query.sh slug skill-creator # detail
bash scripts/query.sh versions skill-creator # versions
bash scripts/install.sh skill-creator --yes # install
bash scripts/doctor.sh # shows the capability matrix
bash scripts/doctor.sh
doctor.sh probes both channels (OpenAPI with token, legacy without) plus
optionally the /edit endpoint. If at least one channel works, the skill is
usable.
All list queries hit a local JSON cache (${SKILL_HUB_CACHE_DIR}/skill-cache.json),
parsed with jq (sub-ms).
| Trigger | Sync action |
|---|---|
| Cache missing or corrupt | Full sync |
| User asks "refresh cache" | Forced full sync |
| Routine query | Incremental (records with updatedAt newer than cursor) |
| Incremental page 1 entirely new (= 100 records) | Upgrade to full sync |
Incremental sync does NOT prune removed/withdrawn skills (it only unions new records). If you hit a "found in cache but install 404s" case, run
bash sync.sh --full. The full sync log will report "pruned N removed".
The legacy channel (no token) identifies callers by source IP. On shared
hosts (containers, CI runners), all agents/users share one identity.
scope=mine returns the list for that shared identity.
For per-user isolation on shared hosts, configure a per-user SKILL_HUB_TOKEN.
| User request | Action |
|---|---|
| "Search for calendar-related skills" | bash sync.sh && bash query.sh keyword calendar |
| "What was published this week?" | bash sync.sh && bash query.sh time this_week |
| "Anything on 2026-05-20?" | bash query.sh time 2026-05-20 (single day: 00:00 - 23:59) |
| "Between 5/18 and 5/22?" | bash query.sh time 2026-05-18:2026-05-22 |
| "Last week by user X?" | bash sync.sh && bash query.sh combo --since=last_week --author=X |
| "Only this account (avoid prefix collisions)" | bash query.sh author alice@example.com --exact |
| "Official skills today?" | bash sync.sh && bash query.sh combo --since=today --source=official |
| "Show details of skill XX" | bash query.sh slug XX |
| User request | Action |
|---|---|
| "Install calendar" | 1) Check cached version 2) Confirm with user 3) After approval: bash install.sh calendar --yes |
| "Update html-go-live to latest" | First query.sh slug html-go-live for new version + confirm with user -> after approval bash install.sh html-go-live --yes (--yes must be user-authorized) |
| "Install html-go-live 2.4.0" | Same confirmation flow -> after approval bash install.sh html-go-live 2.4.0 --yes |
| "Search skillhub.cn for X" | SKILL_HUB_PROVIDER=skillhub_cn bash query.sh keyword X (no token; see Built-in provider: skillhub.cn) |
| "Install X from skillhub.cn" | SKILL_HUB_PROVIDER=skillhub_cn bash install.sh X --yes |
--yesis a user-authorization flag; an LLM/agent caller must not add it on its own. The user must explicitly say "yes" / "go ahead" / "install" first; SKILL.md examples in automated paths intentionally omit--yes.
| User request | Action |
|---|---|
| "Version history of html-go-live" | source scripts/_lib.sh && api_get "${HUB_API_PREFIX}/versions/html-go-live?limit=20" | jq |
| "What changed in 2.4.0?" | api_get "${HUB_API_PREFIX}/versions/html-go-live/2.4.0" | jq .data.version.changelog |
| User request | Action |
|---|---|
| "skill-hub-query is broken / I can't find anything" | bash doctor.sh (probes all channels and reports the precise issue) |
| "Refresh cache / rebuild cache / prune removed skills" | bash sync.sh --full |
Use edit.sh to modify the Hub card for a skill you own. You can only edit
skills you own; non-owners get 403 from the Hub.
Hub compatibility: This requires your Hub to implement
PUT /edit/<slug>andGET /detail/<slug>. If your Hub does not implement them, run:export SKILL_HUB_DISABLE_EDIT=1and edit.sh will refuse to run with an informative message instead of producing confusing errors.
| User request | Action |
|---|---|
| "Show current card info for my-skill" | bash edit.sh <slug> --show |
| "Change summary of my-skill to xxx" | First --show or --dry-run to confirm -> after approval: bash edit.sh <slug> --summary "xxx" --yes |
| "Add tags Demo / Test to my-skill" | First --show to grab current tags, build the merged list -> after approval bash edit.sh <slug> --tags "old1,old2,Demo,Test" --yes |
| "Change visibility of my-skill to public" | High-risk: surface the prompt to the user explicitly (including any Hub-side policy that may silently downgrade) -> after explicit approval add --yes |
Agent caller rule (same as install.sh):
--yesis a user-authorization flag. An agent must NOT add it on its own; the human user must explicitly approve. Without--yes,edit.shblocks on stdin in a TTY and refuses to proceed in non-interactive mode.
Array fields (tags / scene / business / ...) are full overwrites, not additive. To append, first
--showto read the current list, then submit the merged list.
Supported fields: see
bash edit.sh --helpfor the full list of CLI flags.
Safety guarantees (five-stage flow):
- GET the authoritative current value
- Owner pre-check (server-side 403 is the ultimate safety net)
- Build patch + show diff (skip PUT if no actual diff)
- Backup to disk + user confirm (backups in
${SKILL_HUB_CACHE_DIR}/edit-backups/, latest 20 per slug)- PUT + dual-channel verify with retry + auto-rollback on mismatch (visibility silent-downgrade is a documented partial-success edge case)
Your Hub must implement these endpoints (response envelope
{code: 200, message: "", data: {...}}):
GET <endpoint><SKILL_HUB_API_PREFIX>/search?page=N&size=N&orderBy=updatedAt&order=desc
-> data: {records: [<skill>...], total: <number>}
GET <endpoint><SKILL_HUB_API_PREFIX>/versions/<slug>?limit=N
-> data: {items: [{version, changelog, ...}, ...]}
GET <endpoint><SKILL_HUB_API_PREFIX>/versions/<slug>/<version>
-> data: {version: {...}}
GET <endpoint><SKILL_HUB_LEGACY_API_PREFIX>/download/<slug>?version=<v>&track=true
-> body: zip bytes (Content-Type may be application/octet-stream)
Each <skill> record minimally contains:
{
"slug": "string",
"displayName": "string",
"summary": "string",
"owner": {"displayName": "...", "handle": "...", "email": "..."},
"source": "official|personal|external",
"updatedAt": <epoch_ms>,
"latestVersion": {"version": "x.y.z"}
}
For edit.sh (optional), additionally:
PUT <endpoint><SKILL_HUB_EDIT_PREFIX>/edit/<slug>
body = JSON patch (any subset of editable fields)
empty body returns the current snapshot
-> data: <full skill metadata>
GET <endpoint><SKILL_HUB_EDIT_PREFIX>/detail/<slug>
-> data: {skill: <full metadata>}
If your Hub uses a different auth scheme, override SKILL_HUB_AUTH_HEADER and
SKILL_HUB_AUTH_SCHEME (e.g. SKILL_HUB_AUTH_HEADER=X-API-Key
SKILL_HUB_AUTH_SCHEME="").
📖 Need the full request/response field reference? When implementing or debugging a self-hosted Hub, read
references/api.md— it documents every endpoint's exact parameters, response envelope, and field semantics in detail.
skill-hub-query/
├── SKILL.md # this file
├── credentials.example.json # template for the credentials file
├── references/
│ └── api.md # detailed API contract reference
└── scripts/
├── _lib.sh # path discovery + token + curl wrapper (sourced)
├── _edit_lib.sh # edit.sh internals (sourced)
├── doctor.sh # one-shot self-check
├── sync.sh # full / incremental cache sync
├── query.sh # query the local cache
├── install.sh # download + safe extract + atomic dir replace
└── edit.sh # five-stage safe metadata editor (optional Hub features)
Cache & credentials (NOT in skill directory, NOT git-tracked):
${SKILL_HUB_CACHE_DIR:-~/.cache/skill-hub-query}/
├── skill-cache.json # full skill index (key = slug)
├── skill-cache-meta.json # sync cursor + metadata
├── skill-versions.json # locally installed skill versions
└── edit-backups/ # snapshots before each edit (latest 20 per slug)
${XDG_CONFIG_HOME:-~/.config}/skill-hub-query/credentials.json (chmod 600)
# 1) source the lib (loads token / paths / curl wrapper)
source ./scripts/_lib.sh
# 2) call any API path (api_get auto-routes: token -> OpenAPI; none -> legacy)
api_get "${HUB_API_PREFIX}/search?keyword=calendar&size=20"
api_get "${HUB_API_PREFIX}/versions/html-go-live?limit=10"
api_get "${HUB_API_PREFIX}/versions/html-go-live/2.4.0"
# 3) download a zip (public skills don't need auth; private skills do)
api_download "html-go-live" "2.4.0" "/tmp/x.zip"
For direct curl calls, pass the auth header when you have a token:
-H "$(load_auth_header): $(load_auth_scheme)$SKILL_HUB_TOKEN".
| Error | What to do |
|---|---|
[info] No SKILL_HUB_TOKEN configured, falling back to legacy channel | Normal; consider setting a token for long-term stability |
[error] No SKILL_HUB_TOKEN found, and the legacy fallback channel is unavailable | Both channels are down; run doctor.sh to pinpoint |
HTTP 401 / token invalid | Token expired or revoked; refresh it or remove it to fall back to legacy |
[error] Skill 'X' access denied (HTTP 403, private skill) | Private skill; configure a token with permission, or ask the owner |
[error] Skill 'X' is private (response code=403) | Same as above (some Hubs return 200 + JSON error) |
[error] Skill 'X' not found (HTTP 404) | Wrong slug / removed; try query.sh slug X and possibly sync.sh --full |
[error] Download failed: X (network error) | curl-layer failure (network/DNS); check connectivity |
zip contains unsafe paths | Defense against directory traversal; report to Hub maintainer |
Extracted content is missing SKILL.md | Malformed zip; aborted without touching existing install |
install failed; rolling back | Atomic dir replace failed (rare; usually disk full) |
X exists and --yes not given | Agent didn't get user authorization; have the user say "yes" first |
| Cache corruption (jq parse failure) | sync.sh rebuilds automatically |
edit.sh is disabled because SKILL_HUB_DISABLE_EDIT=1 | Your Hub does not expose /edit; unset the var only if it does |
install.sh uses the legacy /download/<slug> path (public skills: no auth needed; private: token required).starredAt, starred, litToday, etc.) are deprecated and may be missing depending on Hub version.edit.sh requires optional /edit and /detail endpoints; not all Hubs expose them.require_hub_url exit-in-cmdsub trap: endpoint="$(require_hub_url)" silently dropped the exit because exit only kills the subshell. Refactored to require_hub_url || exit $?; endpoint="$(load_endpoint)" (api_get / api_download / edit.sh). Adds an explicit doc-comment so callers don't regress.--max-time: 30s for JSON API (api_get / _edit_lib PUT/GET) and 120s for ZIP downloads (overridable via SKILL_HUB_DOWNLOAD_TIMEOUT). Previously a hung Hub would hang the entire skill indefinitely.sync.sh now validates the first-page response is non-empty JSON before computing total / pages, instead of silently reporting "Hub has 0 skill(s)" on failure.edit.sh now calls require_hub_url before printing any step output (previously: empty [edit] Hub URL : and [1/5] fetching... were printed before the actual error).load_auth_scheme now auto-appends a trailing space if non-empty and missing one (so SKILL_HUB_AUTH_SCHEME="Bearer" works, not just "Bearer "); explicit empty (X-API-Key style) still keeps no space.SKILL_HUB_BACKUP_RETENTION (edit.sh backups) and the new SKILL_HUB_DOWNLOAD_TIMEOUT.sync.sh and doctor.sh now register trap EXIT handlers that prune their mktemp temp files on any exit path (success / error / Ctrl-C). install.sh already had one.skill-hub-query.
Generalized fork of an internal Hub query tool:
SKILL_HUB_URL / SKILL_HUB_AUTH_HEADER / SKILL_HUB_API_PREFIX / SKILL_HUB_LEGACY_API_PREFIX env-driven configuration (no baked-in default; see Hub compatibility note)git config user.email (no internal-network-specific paths)edit.sh made truly optional via SKILL_HUB_DISABLE_EDIT=1 for Hubs without /edit endpoint