Skill 发布与注册
skill publish 到底做了什么——地址如何生成、两个真实踩过的坑、以及唯一可信的成功信号。
Skill 发布与注册
核心认知:publish 不搬运任何文件,它只记录"这个 skill 住在哪"
skill publish 不会把 skill 的文件拷贝进 --registry 指向的仓库,也不会
帮你 git add / commit / push 任何东西。它做的唯一一件事,是往
<registry-checkout>/skills.json 里写(或更新)一条 manifest 记录。
这条记录里最关键的两个字段——source 和 path——来自 skill 自己所在的那个 git
仓库(它的 origin remote、skill 在仓库里的相对路径、以及发布那一刻所在的
分支),不是来自 --registry 参数指向的仓库。这两个仓库可以是完全不同的
两个仓库。
换句话说:skill 物理上放在哪,注册表记录就指向哪。 --registry 只是"往哪个
manifest 里写一条记录"的落笔位置,不是"skill 内容搬到哪里"。
推荐流程
cd <your registry content repo> # 例如 thefool-skills
git switch main && git pull # 分支很重要——见下面的"两个坑"
# 1. 新增 skill
mkdir -p skills/my-skill && vim skills/my-skill/SKILL.md
# frontmatter 必须包含 metadata.version,且必须是合法 semver
# 2. 先 push——这一步让地址变得真实可达
git add skills/my-skill && git commit -m "feat(skills): add my-skill" && git push
# 3. 登录(过期了就重新登录)
npx @cogito.ai/cli@latest auth status
# 4. 校验
npx @cogito.ai/cli@latest skill validate skills/my-skill
# 5. 发布——在仓库根目录执行;--registry 指向这里
npx @cogito.ai/cli@latest skill publish skills/my-skill --registry .
# 6. manifest 变更也要提交(skills.json 是 git 侧的事实源)
git add skills.json && git commit -m "chore(registry): index my-skill" && git push逐步说明:
- 第 1 步:skill 目录必须有合法的
SKILL.md,且metadata.version(缺失可以, 但格式错误会在第 5 步被直接拒绝——见下方"字段速查"里的versionMissing)。 - 第 2 步:这一步决定了
entry.source指向的地址是否真实可达。顺序错了会 产生下面"坑一"描述的后果。 - 第 3 步:
auth status只读本地凭证文件,不会向服务端验证 token 是否已过期 ——凭证有效期 24 小时,过期后auth status依然显示已登录,但索引会静默降级失败。 不确定就直接重新auth login。 - 第 4 步:
skill validate不发布,只做 Agent Skills 规范校验,可以放心多跑。 - 第 5 步:必须在仓库根目录执行,因为
entry.source/entry.path是从 skill 自己所在仓库推导出来的(见上面的核心认知);--registry .指向当前这个 registry checkout。 - 第 6 步:
publish从不git add/commit/push——skills.json的变更 永远是一次未提交的工作区改动,提交它是你自己的责任。
两个容易踩的坑(CLI 作者本人真实踩过)
坑一:顺序很重要——先 push skill,再 publish
publish 记录的地址形如 .../tree/<branch>/skills/my-skill——它不会校验
这个地址当下是否可达。如果在 push 之前就 publish,记录会指向一个远端还不存在的
东西;indexed: true 依然会返回(因为 indexing 只检查你本机有没有登录凭证,
不检查内容是否真实可 clone),但除你之外没有人能真正安装它。
坑二:在你想永久记录的那条分支上发布
真实事故:在 cogito-lib 仓库的 feat/sliced-dispatch 分支上执行了 publish,
导致注册表记录变成了 .../tree/feat/sliced-dispatch/.claude/skills/...——把一条
短命分支引用(merge 后就被删除)烤进了一条永久记录里。请在 main 分支上
发布。
从另一个仓库发布(skill 仓库 ≠ registry 仓库)
允许,但需要同时满足两个条件:
- skill 所在的源仓库必须公开可达(陌生人能真的
git clone它); - 你发布时所在的必须是长期存在的分支(不是随时会被删除的短命分支——见坑二)。
从 0.16.0 开始,CLI 会在输出中明确打印 source/path,并且当"skill 自己的
仓库"和"--registry checkout 的仓库"不一致时,会大声告警。这个告警不会阻断
发布——用它做个人/自托管场景(比如你不打算让别人安装的私有 skill)是合法用法,
告警的目的只是让这件事保持可见,而不是禁止它。
唯一可信的成功信号:indexed: true
只信任 --json 输出里的 indexed: true。网页返回 200 不能作为证据——
那个路由是客户端渲染的,即使是一个根本不存在的 skill id,页面照样会返回 200。
字段速查
以下定义与 CLI 仓库根目录 README.md("Verifying a publish"一节)保持一致:
indexed—— 这条记录是否也被 POST 进了托管注册表,即回答"这个 skill 是 否真的进了共享注册表,而不只是我本地的--registrycheckout"。indexed: false从不意味着 manifest 写入失败——entry/manifestPath永远先被写入,且 不会因为 indexing 失败而回滚。anonymous——indexed: false+anonymous: true表示你当时未登录, indexing 请求根本没有发出——这是设计内的"仅本地"路径,不是错误。versionMissing—— 仅当SKILL.md完全没有可解析的版本号 (metadata.version,回退到metadata['thefool.version'])时为true。 这不会阻断发布,只是一条警告——真正非法(非 semver)的版本号是另一种更严重的 失败:skill publish会在写入任何东西之前直接拒绝。registrySource——--registrycheckout 自己的originremote(归一化 方式与entry.source相同)。只有能被解析出来时才会出现——即--registry本身指向一个有可 clone 的origin的 git 仓库;对于一个不是 git 仓库的临时 registry 目录(本地实验时常见),这个字段缺席并不代表出错。sourceRepoDiffersFromRegistry—— 当entry.source(skill 自己的仓库) 与registrySource(--registrycheckout 自己的仓库)不一致时为true—— 人类可读输出里的⚠⚠告警正是由这个字段驱动的。只有在registrySource存在时才会出现——无法解析registrySource时不会做比较,所以这个字段会被 省略,而不是默认给false(那样会误导性地读成"已确认一致")。
每一步实际产出什么
| 步骤 | 产出 |
|---|---|
| 第 2 步 push skill | 地址变得真实可达 |
| 第 5 步 publish | (a) 本地 skills.json 新增一条记录 (b) 若已登录,同时被索引进托管注册表 |
| 第 6 步 push manifest | git 侧的事实源与托管注册表对齐 |