SkillHub 发布助手(skillhub-cli-publish)
把本地 skill 发布 / 更新到 SkillHub 社区源的可复用工作流。核心是封装三个最容易踩的坑,
让"发 skill"变成一条命令的事。
> 📌 上手先看
> - 一键发布:python scripts/publish_skill.py "<skill目录>" --bump minor --changelog "本次更新说明" --publish
> - 只预检不发布:python scripts/publish_skill.py "<skill目录>" --dry-run-only
> - 发布后核对(不打包不发布,只查一次):python scripts/publish_skill.py "<skill目录>" --verify-only
> - 排错看 TROUBLESHOOTING.md;发布前清单看 references/release-checklist.md
✨ 装一个 skill 即可发布(零 CLI 依赖)
本 skill 的发布脚本 scripts/publish_skill.py 用 Python 标准库直接调用 SkillHub 官方 API
(POST /api/v1/community/skills/publish,Bearer 鉴权),不依赖你单独安装 skillhub CLI 二进制。
你只需要:
1. 安装本 skill;
2. 在 SkillHub 网页完成 注册 + 实名认证 + 创建 API Token(平台合规硬约束,必须人工一次);
3. 说"发布更新" → 脚本自动:检查登录态 → 打包干净副本 → dry-run 预检 → 发布 →
核对线上是否生效 → 记录历史 → 打印四态结论(✅ 已生效 / ⏳ 审核中 / ℹ️ 线上领先本地 / ❓ 未知)。
不需要你去刷网页确认,也不会出现"不确定 → 重发 → 409"的循环。
> 🔎 发布后「到底生效没有」——用 --verify-only 一次核对,别刷网页、更别重发(2026-09-08 实锤)
> 发布接口返回 2xx 只代表平台已收下;install/搜索真正拉到的是平台 latestVersion,它可能滞后:
> 本 skill 某次上传 11 天后 latestVersion 仍是上一版,站点页面也显示旧版(同批其它技能
> 都是发布当天即生效,所以这是异常个例,不是"审核本来就慢")。
> --verify-only 只查一次并给出确定结论(不轮询、不重试):
> | 结论 | 含义 | 该做什么 |
> |---|---|---|
> | ✅ 已生效 | latestVersion = 本次版本 | 收工 |
> | ⏳ 审核中 | 版本已在平台列表,但 latestVersion 还是旧版 | 等;🚫 不要重发(同 slug 同版本必 409) |
> | ℹ️ 线上领先本地 | 线上版本比本地还新 | 本地副本旧了,先拉最新版再改,别覆盖别人改动 |
> | ❓ 未知 | 查询失败,或版本还没进平台列表 | 不代表失败。刚发布就查很常见:平台入库有延迟(实测发布成功 20 秒后仍可能查不到),过几分钟复查即可 |
> 💡 发布前体积预检(自动):打包后会按「体积」扫描每个顶层目录(不靠目录名),发现未排除的大目录(>50MB,如 weights/、assets/ckpt/ 等非 models 命名的大模型/资源)会告警点名,避免上传触发 SkillHub 413 超限。若确属需随包发布的资源且平台上限允许,可忽略;若误带,把它加入发布器的 EXCLUDE_DIRS 即可。全局已排除:models/venv/.venv/__pycache__/node_modules 等。
🚦 发布红线(做不到会被拒或泄密)
- 路径必须用 Windows 绝对路径:skillhub 是 Windows Python 脚本,
/c/Users/...
(Git Bash 写法)会被当成相对路径拼到 CWD 下,报"路径不存在"。
要用C:/Users/你的用户名/.workbuddy/skills/xxx这种形式(助手脚本已自动转换,直接传目录即可)。 - 不要打包密钥/运行态文件:国网 logo(state_grid.png)、
.env、*.bak、tmp/、
__pycache__、node_modules、.git一律不能进发布包。助手默认还会排除钩子/守护进程
的运行态文件(.log/.lock/.jsonl/.pid/.session/.swp等);若某运行态文件
扩展名本身是合法内容(如_session_inject.json),在技能根目录加.skillhubignore声明即可。
助手自动排除,但手写 zip 时要注意。 - 先 dry-run 再发布:正式发布前务必
--dry-run-only预检(校验 metadata + 打包),通过再--publish。
⚠️ 参数名是--dry-run-only(不是--dry-run)。 - slug 决定技能身份:更新必须保持同一 slug(平台形如
@你的handle/slug,升版本后覆盖更新)。
已实锤:同 slug 同版本不可重发(409)。跨账号是否全局唯一未证实——取新 slug 前建议先到站点搜一下。 - SKILL.md 前置字段齐全:
name/slug/displayName/description/summary/
version/license/agent_created不能少(description字段对加载准确性很重要)。 - 同 slug 同版本不可重发:平台不允许覆盖同一版本号。若只改了文档/CHANGELOG 想同步,
必须--bump升版本(如 patch)再发;直接重发相同版本会报409 slug 冲突:版本 X 已存在。 - 🔴 技能包只能发「文本资产」,二进制媒体一律拒收(2026-08-28 实锤):
平台会 400不允许的文件类型: assets/xxx.png——且 dry-run 阶段检测不到,只有真发布才报,
白跑一轮。发布器已默认排除.png/.jpg/.gif/.webp/.svg/.ttf/.otf/.woff/.pdf/.zip/.mp3/.mp4等
(见EXCLUDE_SUFFIX),并在 dry-run 时点名告警被排除的媒体文件。
正确做法:图片/字体类资产不进包,改由脚本运行时现场生成——
参考satirical-art-generator/scripts/stamp_seal.py::_ensure_asset():
打开资产前先判断存在性,缺失就 subprocess 调make_sign.py生成;同时放一个
assets/README.md说明生成方式。这样装完即用、不缺件、也不触发平台拒收。 - 🔴 版本号不能凭感觉挑(2026-09-13 实锤):本地版本 ≠ 线上版本,本地 CHANGELOG 也
不等于「平台已收下什么」。发版前必须两步问证:①--verify-only读线上latestVersion;
② 读_skillhub_history.jsonl里已用过的号——平台收下过就会 409,哪怕线上
latestVersion比它更低(实测:4.9.17 于前一天发布过,线上 latestVersion 却是 4.9.16)。
正确姿势:发本地最高号,一次把积累的待发版全部送达;不发中间号、不做 squash
(重编号会同时撞「本地已用号」与「历史已发号」,且丢失变更粒度)。
📄 .skillhubignore(技能级排除声明,可选但推荐)
发布器自动读取技能根目录的 .skillhubignore(gitignore 风格),把里面声明的文件/目录
从发布包排除——声明随技能走、可审计、可移植,比全局硬编码更稳。
默认已被发布器排除(无需声明):
- 目录:__pycache__ / tmp / outputs / node_modules / models / venv / .venv /
.git / .workbuddy / .idea / .vscode / __MACOSX / .pytest_cache
- 后缀(运行态家族,默认生效,对所有技能):.pyc .bak .tmp
.log .lock .pid .session .swp .swo .jsonl .DS_Store Thumbs.db
- 命名:.gitignore .skillhubignore daemon.log(平台禁止上传的元文件 + 发布器内部账本)
- 敏感:state_grid / secret / credential / password / .env 家族
什么该写进 .skillhubignore? 钩子 / 守护进程在本地生成的运行态文件,且其
扩展名本身是合法内容、不能靠全局后缀排除的。例如 keeper 的 hooks/_session_inject.json、
keeper_daemon/.daemon.json:.json 是技能内容,全局排除会误杀,只能逐个声明。
> 示例(keeper 的真实 .skillhubignore):
> > # 钩子 / 守护进程运行态产物(绝非技能源码)
> hooks/hook_audit.log
> hooks/_session_inject.json
> hooks/_session_inject.json.lock
> hooks/inject_log.jsonl
> keeper_daemon/daemon.log
> keeper_daemon/.daemon.json
>
🔧 标准发布流程(照做即可)
0. 前置检查(只需人工一次)
- 本 skill 自带发布能力,不需要安装 skillhub CLI。装好这个 skill 就能发。
- 平台硬约束(须人工一次):到 https://skillhub.cn 完成 注册 → 实名认证 → 创建 API Token
(手机号验证码 + 人脸核身 + 一次性 Token)。Token 形如skh_xxx。 - 凭证自动复用
~/.skillhub/credentials.json(若你之前装过官方 CLI 并已登录,零配置);
否则首次发布用--token skh_xxx或设环境变量SKILLHUB_TOKEN,脚本会自动落盘凭证,下次免带。 - 本地 skill 目录含
SKILL.md,且 frontmatter 字段齐全。
1. 升级版本号(可选)
改 SKILL.md 的 version: 字段,或用助手 --bump minor(自动 +0.1.0)。
首次发布可用 0.1.0 / 1.0.0。
2. dry-run 预检(必做)
python scripts/publish_skill.py "C:/Users/你的用户名/.workbuddy/skills/你的skill" --dry-run-only
输出 ✓ Dry-run passed: <slug>@<version> 即通过。
> 若被发布的技能自带 scripts/publish_gate.py(例如 keeper),发布器会在 dry-run 前
> 自动执行它,且跑的是即将上传的 clean 副本而非源目录——校验对象必须是真要发出去的东西。
> 门禁亮红灯即拦截发布(不会把带病包传上去);门禁脚本自身异常或超时(1800s)同样拦截。
> 逃生用 --no-gate(会打印留痕)。对没有自带门禁的技能零影响,不改变既有流程。
3. 正式发布
python scripts/publish_skill.py "C:/Users/你的用户名/.workbuddy/skills/你的skill" \
--bump minor --changelog "修复路径坑、新增干净副本打包" --publish
- 同 slug、更高版本 → 覆盖更新(升版本后发);同 slug 同版本不允许重发,
想同步文档/CHANGELOG 必须升版本再发。 - 发布成功会返回
skillId(本 skill 为104845),并自动追加一行到_skillhub_history.jsonl
(记录版本/时间/changelog/成败),更新时同 slug 覆盖即可,复盘发布历史直接看该文件。
4. 验证(一条命令,不要反复刷网页)
python scripts/publish_skill.py "C:/Users/你的用户名/.workbuddy/skills/你的skill" --verify-only
- 发布时若带
--publish,脚本会在成功后自动核对一次并打印上表四种结论之一。 --verify-only可随时单独复查(不打包、不发布、不联网上传,只做 2 个 GET)。- 铁律:⏳ 审核中/❓ 未知 都不要重发——同 slug 同版本会被 409 拒绝(历史里就因
误判 pending 为"没发上去"而重发失败一次)。真要"再发一次",必须先--bump patch升版本。
🤔 常见问题速查
- 报"路径不存在: D:\c\Users..." → 用了 Git Bash 的
/c/...路径。改用C:/Users/...
(助手已自动转,直接传目录即可)。 - dry-run 报缺字段 → SKILL.md frontmatter 少了 name/slug/version 之一,补上。
- 想撤回发布 → SkillHub 没有 CLI 删除;去站点后台或联系平台处理。
- 报"slug 冲突:版本 X 已存在" → 平台不允许同 slug 同版本覆盖重发。若仅改了文档/CHANGELOG,
用--bump patch升一个小版本再发即可(见发布红线第 6 条)。 - 发布成功但站点/install 还是旧版 → 跑
--verify-only看四态结论;⏳ 审核中就等,
不要重发(同版本必 409)。详见TROUBLESHOOTING.mdQ10。 - 换电脑/环境 → 助手脚本用
safe_home()多候选(expanduser/USERPROFILE/HOME/Path.home)自动纠正
HOME 错位,无需硬编码;
只需目标机有可用 token(设过SKILLHUB_TOKEN或~/.skillhub/credentials.json含user.token),无需安装 skillhub CLI。
更多见 TROUBLESHOOTING.md。