下载地址
https://www.zhengdianxue.com/wp-content/uploads/skills/org-pjg8fqlr--skillhub-publish-toolkit.zip将提示词发送给你的 AI 安装该 skills
请根据 https://skillhub.cn/install/skillhub.md,安装 @org-pjg8fqlr/skillhub-publish-toolkit。- 把本地 Skill 发布到 SkillHub(skillhub.cn)的完整流程与已踩坑记录。当用户提到"
- 发布skill"
- "
- 上架SkillHub"
- "
- skillhub publish"
- …(完整说明见下方「阅读全文」)
阅读全文 (展开查看完整说明)
概述
SkillHub 发布流程
把本地 Skill 目录发布到 skillhub.cn。已实测可用(环境 Windows + Git Bash)。
关键结论(先看这几条)
- Windows 不需要 WSL。平台文档写"Windows 用户请使用 WSL",但 CLI 本身是纯 Python 脚本
(skills_store_cli.py),Git Bash 直接能跑。
- 官方安装器生成的 wrapper 在 Windows 有路径 bug,必须替换(见第 2 步)。
- frontmatter 缺
slug/version/displayName会被阻断发布,发之前先跑--dry-run。 slug冲突报409,改名重发即可。
但注意:平台文档说的"slug 全网唯一"与实测不符 —— 实测搜到 7 个不同作者发布的技能共用同一个 slug(命名空间隔离)。所以遇到重名不一定要放弃:直接试发,报 409 再改名。反过来,选 slug 前先查 https://api.skillhub.cn/api/v1/search?q=<slug>确认没有同名者,能避免撞车。
- 上架成功 ≠ 可被搜到。实测已上架技能在业务关键词下百条内零召回,只有 slug 精确查询能排第 1(见文末量化章节)。
第 1 步:本地预检(不需要登录)
skillhub publish "<skill目录绝对路径>" --dry-run预期:✓ Dry-run passed: <slug>@<version>
这一步不发任何数据,纯本地格式校验。发布前必做——能把 frontmatter 格式问题提前挡掉。
第 2 步:安装 CLI(Windows/Git Bash)
官方安装命令(Git Bash 里跑):
curl -fsSL https://skillhub.cn/install/install.sh | bash -s -- --cli-only--cli-only 表示只装 CLI,不碰 openclaw 插件配置、不装 find-skills 工作区技能。
如果 curl 管道失败(如被沙箱拦截 curl: (23) client returned ERROR on write),改为手动:
# 下载并解包
mkdir -p /tmp/skillhub_kit && cd /tmp/skillhub_kit
curl -fsSL "https://skillhub-1388575217.cos.ap-guangzhou.myqcloud.com/install/latest.tar.gz" -o kit.tar.gz
tar -xzf kit.tar.gz
# 本地安装
bash cli/install.sh --cli-only然后必须替换 wrapper 修路径 bug(官方 wrapper 传 /c/Users/... 给 Windows 版 python,会被误解析成 d:cUsers...,报 can't open file):
cat > "$HOME/.local/bin/skillhub" <<'WRAPPER'
#!/usr/bin/env bash
set -euo pipefail
# Windows/Git Bash:必须把 HOME 转成正斜杠的 Windows 形式再交给 python
CLI="$(cygpath -m "$HOME" 2>/dev/null || echo "$HOME")/.skillhub/skills_store_cli.py"
if [[ ! -f "$HOME/.skillhub/skills_store_cli.py" ]]; then
echo "Error: CLI not found at ~/.skillhub/skills_store_cli.py" >&2
exit 1
fi
exec python3 "${CLI}" "$@"
WRAPPER
chmod +x "$HOME/.local/bin/skillhub"
"$HOME/.local/bin/skillhub" --version # 应输出 skillhub <版本号>用
cygpath -m自动转换,不要写死用户名(写死后换机器就废)。若 Python 不在 PATH,wrapper 里把
python3换成 python 绝对路径。
第 3 步:登录(需要用户本人提供 API Key)
"$HOME/.local/bin/skillhub" login --key skh_xxxxxxxx --host https://api.skillhub.cn- API Key 在 skillhub.cn → 个人中心 → API keys(直链
/dashboard/keys)创建 - 创建弹窗只显示一次完整 Token,必须当场复制
- 前置条件:必须完成实名认证(人脸核身),否则创建 Token / 发布都会失败
- 验证登录:
skillhub auth whoami应输出userId / handle / role
⚠️ Token 等同账号密码。若用户把 Token 交给助手执行,发布完成后建议提醒其到控制台删除该 Token。
🚨 关键坑:两种密钥不能混用(实战踩坑)
平台上有两套完全不同的密钥,外观和用途都不同:
| 个人 API Token | 企业密钥 | |
|---|---|---|
| 前缀 | skh_ | sk-ent- |
| 页面标题 | 「API keys」(可创建多个,命名区分) | 「我的密钥」 |
| 页面说明 | — | "用于 CLI 工具、CI/CD 集成等编程方式访问 API";"每个账号仅有一个密钥" |
| 调用方式 | 传给 skillhub login --key | Authorization: Bearer sk-ent-xxx |
| 用途 | 发布/搜索/安装社区技能 | 仅用于下载企业技能(install <slug> --secret sk-ent-xxx) |
CLI 源码硬性校验(resolve_user_token):
if not token.startswith("skh_"):
die("token 必须以 skh_ 开头(早期 sh_pat_ 体系已下线)")即 publish 命令只认 skh_ 开头的人 Token,企业密钥一定失败。
判断依据:如果密钥页面出现"每个账号仅有一个密钥""重置后旧密钥立即失效",说明那是企业密钥页,走错了地方;个人 Token 页允许创建多个并命名。拿 sk-ent- 去 login 会报 Login failed: invalid or expired API key(cmd_login 会把非 skh_ 前缀路由到企业 verify 接口)。
正确做法:让用户打开 https://skillhub.cn/dashboard/keys 创建 skh_ Token。
附带教训:不要从截图抄密钥。64 位随机字符肉眼转录极易出错,
且错误结果与"密钥无效"无法区分,会浪费一轮排查。
直接要求用户以文本形式粘贴。
第 4 步:发布
"$HOME/.local/bin/skillhub" publish "<skill目录绝对路径>" --changelog "首次发布"预期:✓ Published: skillId=xxxxx status=pending_review
pending_review= 提交成功,进入平台审核(审核期间详情页显示"未找到"是正常的)- 更新版本:保持
slug不变,改version与内容,再 publish 并填 changelog publish同时支持目录或 .zip 路径
frontmatter 规范(平台原文)
必填(缺失即阻断发布):
| 字段 | 要求 |
|---|---|
slug | kebab-case,长度 2–128,全网唯一 |
version | 合法 SemVer,如 1.0.0 |
displayName | 对外展示名称 |
建议字段(不阻断):summary、description、tags、license、homepage
注意:平台不强制 name(WorkBuddy/Claude 技能格式用),但建议同时保留 name + description以便同一目录在本地也能被识别为技能。实测两者并存可通过 dry-run。
图标:CLI 发布不会自动上传头像;无 iconUrl 时用平台默认占位图。
示例:
---
slug: my-skill-name
displayName: 我的技能
version: 1.0.0
summary: 一句话简介
description: 触发描述(写清什么场景该用这个技能,Agent 靠它判断是否加载)
tags:
- 标签1
- 标签2
license: MIT
name: my-skill-name
---报错对照表
| 报错 | 原因 | 处理 |
|---|---|---|
401 invalid api key | Token 失效/被撤销 | 重新创建 Token 后 login |
Login failed: invalid or expired API key | ① 用了 sk-ent- 企业密钥(发布只认 skh_)② 从截图抄错字符 | 去 /dashboard/keys 创建 skh_ Token;要求用户以文本形式粘贴 |
token 必须以 skh_ 开头 | 同上,用了企业密钥 | 同上 |
403 请先完成实名认证 | 未实名 | 回浏览器完成人脸核身 |
409 slug 已被其他用户占用 | slug 冲突 | 改 SKILL.md 的 slug,重跑 dry-run 后发布。但先确认是否真的冲突:实测平台上存在多个同名 slug(见"关键结论"第 4 条),文档宣称的"全网唯一"并不成立 |
429 请求过于频繁 | 限频 | 等 1 分钟重试 |
command not found: skillhub | PATH 未含 ~/.local/bin | 用完整路径 "$HOME/.local/bin/skillhub" |
can't open file 'd:cUsers...' | Windows 路径转换 bug | 按第 2 步替换 wrapper |
Dry-run 报 SKILL.md 缺少 ... | frontmatter 缺必填字段 | 补齐 slug/version/displayName |
| 详情页"未找到" | 审核进行中 | 正常,个人中心 → 我的 Skill 看状态 |
有用的附加命令
# 搜平台上同类技能(发布前做竞品定位,非常有用)
skillhub search 关键词
# 本机已安装技能列表
skillhub list
# 评论区(发布后互动)
skillhub comment list <slug>
# 在线调用技能
skillhub run <slug>核验"是否真的上架"的正确姿势
四个坑,别踩:
| 做法 | 结果 |
|---|---|
curl …/skills.json(旧 COS 索引) | ❌ 已失效,返回 {"total":0,"skills":[]} |
curl …/index/skills.json | ❌ 404 |
skillhub skill info <slug> | ❌ 没这个子命令,skillhub skill 只有 rankings / reports / evaluation |
https://skillhub.cn/skills/search?q=xxx | ❌ 无效路由,会落到"Skill Not Found"页,不能据此判断未上架 |
✅ 权威核验:搜索 API
curl -fsSL "https://api.skillhub.cn/api/v1/search?q=<关键词>"返回 results[],每条含 slug / name / displayName / namespace.handle / downloads / stars(收藏)/ installs / version / updated_at / publisher。
判读要点:
- 用 slug 精确查——已上架技能的 slug 查询会排在第 1 位。
- 无命中时返回的是"热门兜底列表"(tencent-docs / summarize / find-skills 这一组),而不是空结果。
CLI skillhub search 同样如此 —— 结果里有热门技能 ≠ 搜索命中了,务必核对 slug。
downloads/stars只在搜索 API 和 rankings JSON 里有,CLI 的search文本输出不显示这两个数。- 审核中(
pending_review)的技能不在公开索引里,与"从未发布"从公网无法区分;要区分必须登录看个人中心。
观测脚本模板(一次查多个关键词、自动识别兜底):
python -c "
import json, urllib.request, urllib.parse
def rec(q):
u = 'https://api.skillhub.cn/api/v1/search?q=' + urllib.parse.quote(q)
return json.loads(urllib.request.urlopen(u, timeout=30).read().decode('utf-8')).get('results', [])
for s in rec('我的技能slug')[:5]:
print(s.get('slug'), s.get('name'), 'dl=', s.get('downloads'), 'stars=', s.get('stars'))
"Windows 下若
python不在 PATH,把上面命令里的python换成自己的 Python 绝对路径。注意
--search-limit可调(默认 20);要判断"排在第几"必须把它拉到 100,否则看不到窗口外的结果。
两条发布路线、分类字段与可发现性
两条发布路线
| 路线 | 需要 | 实测 |
|---|---|---|
| 网页发布 | 只需浏览器登录,不需要 Token | ✅ 已验证可行(表单提交 → 审核 → 上架) |
| CLI 发布 | skh_ 个人 Token(skillhub login --key skh_xxx) | --dry-run 可本地预检,正式发布需 Token |
网页路线适合单次发布、不想生成 Token 的场景;CLI 适合批量发布与版本迭代(可脚本化、可预检)。
CLI
login --help写着也接受sk-ent-企业密钥,但那只走企业源;社区发布路径校验 Token 必须以skh_开头(源码resolve_user_token硬性判断)。
🚨 网页发布的"自动抓取描述"陷阱
网页表单会读取上传 zip 内的 SKILL.md,自动填出技能描述。传错包 → 表单里显示的是别的技能的描述(实测踩过:误传了另一个技能的 zip,表单抓出的是那个技能的文案,肉眼极易漏看直接提交)。
上传前必做:核验 zip 里的 SKILL.md 属于本次要发布的技能。
python -c "
import zipfile
z = zipfile.ZipFile(r'路径/包.zip')
print([n for n in z.namelist()])
print([l for l in z.read('SKILL.md').decode('utf-8').splitlines()
if l.startswith('slug:') or l.startswith('description:')][:2])
"Git Bash 没有
zip命令,打包用 Pythonzipfile(递归时排除__pycache__)。
分类字段:两套分类体系(容易混)
平台上存在两套彼此独立的分类,实测确认互不映射:
| 团队空间「分类管理」页 | 平台 category 字段 | |
|---|---|---|
| 内容 | 通用办公 / 研发工具 / 需求设计 / 安全合规…(可自建) | 12 个英文枚举 |
| 谁能改 | 团队自己,可新增分类 | 平台枚举,个人新增不了 |
| 在哪看 | 发布表单的分类下拉 | 搜索 API results[].category |
实测反例:某技能发布时已选中团队分类「需求设计」,但 API 里 category 仍为 ""(空);另一技能团队分类并非同名项,category 却有值。→ 团队分类选择不会自动写入平台 category 字段。
平台分类枚举(12 个,实测采集):
design-media(设计媒体)、content-creation、office-efficiency、data-analysis、dev-programming、ai-agent、business-ops、education、life-service、knowledge-management、professional、it-ops-security
发布后必须核验分类是否真的写进去了 —— 实测出现过分类为空的情况,后果是没有类目浏览入口:
curl -fsSL "https://api.skillhub.cn/api/v1/search?q=<slug>" | python -c "
import json, sys
for r in json.load(sys.stdin).get('results', []):
print(r['slug'], '| category=', repr(r.get('category')), '| dl=', r.get('downloads'))
"若为空的处理顺序:① 等 72 小时排除索引延迟;② 找技能编辑页是否有独立的平台类目字段;
③ 都没有则提工单让平台补。在团队「分类管理」页新增分类并不能解决这个问题。
⚠️ 上架成功 ≠ 可被搜到(量化结论)
实测已上架技能(发布后 1 天内):
| 检索方式 | 结果 |
|---|---|
| slug 精确查询 | rank 1 ✅ |
| 业务关键词(如「改色」「换底色」「位图」) | rank 69/100 或百条内零命中 ❌ |
score 机制:无语义匹配时,平台用全局热门技能兜底填窗,这些技能 score 一律 0.050(地板值,如下载量百万级的通用技能);真语义命中时约 0.10。→ 分数有区分度,但新技能在业务关键词下拿不到分,于是被高下载量技能以地板分挤满窗口。
另有一个反直觉现象:搜中文显示名时 score 可能只有 0.0507(几乎等于地板值)却排第 1,而 slug 精确查询时 score 为 0.1079。→ 中文显示名的语义权重看起来接近于零,高排名可能来自"名称精确匹配"的后置规则而非文本相似度。机制未确证。
判断:新技能 0 下载时,描述关键词调优对排名的边际作用有限(描述里同一关键词已出现 4 次、精确词仍零召回)。未确证:无法排除索引延迟 —— 发布后 72 小时复测同一批关键词可区分「索引未完成」与「权重压制」。
对"观察期指标"的影响:若把考核指标设为"下载量 > N",实际测的是平台是否分发曝光,而不是技能质量。建议拆成两层:
- 可达性 = 外部渠道(内容/社群)带来的技能页访问 —— 测渠道能否导流
- 转化率 = 访问 → 安装 → 咨询 —— 才测产品本身
另一条必须记住的口径:downloads 里可能包含自己或团队的验证性安装,公开数据无法区分自流量与外部流量。所以单看下载数会高估真实需求量,报告时务必说明。
🚨 合规红线:代码里不要硬编码联系方式(实测被拦)
实测经过:技能包提交安全扫描,整体判「可疑」,唯一被标记的文件是脚本,归因标签 「疑似推广引流」,理由:"报告末尾固定附带外部引流联系方式"。同包的 SKILL.md、README.md、_meta.json 均判安全。
结论:判定是按文件粒度的,且只针对可执行代码里的硬编码联系信息。平台审核含"内容合规过滤"环节,代码内嵌联系方式是标准风控触发点。
正确做法:双轨设计
| 公开发布版 | 直发客户版 | |
|---|---|---|
| 脚本默认 | 不输出任何联系方式 | 环境变量开启 |
| 联系方式位置 | README / SKILL.md 的"作者与服务"章节 | 报告末尾 |
import os
# 默认空字符串,公开发布版干净合规
CONTACT_FOOTER = os.environ.get("MY_SKILL_CONTACT", "").strip()
# 只有配置了才输出
if CONTACT_FOOTER:
LINES.append(CONTACT_FOOTER)风险不对称:联系方式被拦 = 整个技能拿不到线索;收益只是"多一行字"。永远优先保证能上架。
注意:示例文本也要处理干净——注释里写 ` 联系方式:xxxxx 这类示例,仍可能被模式匹配命中。注释里改用 <在此填入你的补充说明>` 这样的中性占位。
发布前检查清单
- [ ] SKILL.md frontmatter 含
slug/version/displayName - [ ]
slug全网唯一(先skillhub search看看有没有重名) - [ ]
--dry-run通过 - [ ] 可执行代码内无联系方式 / 推广话术(含注释里的示例文本)← 见上方合规红线
- [ ] 报告/输出产物里无固定附带的外部联系方式
- [ ] 技能目录内无敏感信息(无本地绝对路径写死、无客户数据、无真实姓名、无自己的其他技能 slug 交叉引用)
- [ ]
__pycache__/ 测试产物已清理(打包时排除) - [ ] 脚本依赖已确认(如声明"零依赖",实际只 import 标准库)
- [ ] 技能能被独立运行(不依赖本机特定路径或其他私有技能)
- [ ] README 里写清不做什么(边界声明能大幅降低售后咨询量)
- [ ] 版本号已递增(合规/内容变更必须走新版本;同版本无法覆盖已发布内容)
- [ ] 发布后核验:搜索 API 里
category是否有值、downloads是否在动
附录:为什么要做这套流程
一条被反复验证的经验:上架是一次性的动作,可被搜到是持续的问题。把每次上架的失败原因(驳回原文、报错码、分类为空、关键词零召回)记下来,下次发布就能少走一轮。本技能的价值基本都在"报错对照表"和"合规红线"这两节里。