soia-meta-publish-market · v1.5.1 · 2026-08-08 · sha256 c80253e226e62b65
soia-meta-publish-market v1.5.1B
Immutable. This exact content is served forever at /api/v1/blob/c80253e226e62b65.
--- name: soia-meta-publish-market description: 把已正式发版的技能上架到外部市场(腾讯 SkillHub、小红书 Red Skill):筛选可独立运行的技能、叠加平台 frontmatter、预检后交由客户提交。触发:「上架 SkillHub」「发到 Red Skill」「上架技能市场」 version: 1.5.1 created_at: 2026-08-04 20:00:00 updated_at: 2026-08-09 00:40:00 created_by: claude fable 5 updated_by: claude fable 5 --- # soia-meta-publish-market 把**已经正式发版**的技能投递到外部市场。与插件市场(`soia` marketplace)不同, 外部市场一次只收一个技能,用户拿到的是**孤立的一份**——所以上架不是复制粘贴, 需要先筛选和改写。 > **执行前置:必须有客户当次的明确授权,且只上架已发版内容。** > 上架是对外动作,且外部平台一旦收录就有审核与展示记录,撤回成本高于插件市场。 > 客户说「改一下这个技能」不等于「上架它」。另外**只投递 `main` 上的正式版**: > dev 快照带 `-SNAPSHOT`,上架等于把开发中状态发给陌生用户。 > `--dry-run` 预检与 `--list-eligible` 盘点无需授权。 ## 客户可读说明 ### 这个技能可以做什么 | 客户想要 | 技能会做 | 客户能看到 | |---|---|---| | 看哪些技能能上架 | 扫全仓,按 hard 依赖筛选 | 逐技能的可否上架与原因 | | 上架某个技能到 SkillHub | 打包 → 叠加平台字段 → `--dry-run` 预检 → 交客户提交 | 暂存路径、预检结果、待执行命令 | | 发到小红书 Red Skill | 打包并给出上传指引 | 暂存路径与上传入口说明 | | 更新已上架的技能 | 保持 slug 不变重新打包,提示填写变更说明 | 版本对比与 changelog 建议 | | 上架前检查技能是否就绪 | 打包并对暂存产物跑 R1-R6 就绪门禁 | 逐项通过/警告/硬缺口报告,硬缺口拒绝打包 | ### 客户如何使用 ```bash # 1. 看这个仓哪些技能可以上架 python3 scripts/stage_for_market.py --repo-dir <域仓路径> --list-eligible # 2. 打包某一个(不会上传;按渠道过滤文件) python3 scripts/stage_for_market.py --repo-dir <域仓路径> \ --skill <技能名> --out <暂存目录> --channel skillhub|redskill \ --display-name "<中文展示名>" # 3. 发版前咨询:对工作树跑一遍就绪门禁,不留产物(见「上架就绪门禁」) python3 scripts/stage_for_market.py --repo-dir <域仓路径> \ --skill <技能名> --out <暂存目录> --allow-unreleased --check-only ``` **打包内容直接从 `origin/main` 导出**,不读工作副本——本地检出在哪个分支都不影响 结果,也就不会因为有人切走分支而误打包未发布内容(多 AI 共用检出时这是常态)。 `main` 上没有该技能、或 main 版本带 `-SNAPSHOT`,一律拒绝打包。 `--channel redskill` 时 **`--display-name` 是必填**,缺省直接拒跑,原因见 [展示名与平台主键](#展示名与平台主键必填)。 打包后由**客户本人**执行投递命令——见下方两个渠道。 ### 依赖与安装 | 依赖 | 类型 | 缺失时怎么处理 | |---|---|---| | Python 3 | 强依赖 | 安装后重试 | | `skillhub` CLI | SkillHub 渠道 | 见下方安装命令;未装则只做打包与预检说明 | | SkillHub 实名认证 + API Token | SkillHub 渠道 | 未认证无法创建 Token,也无法发布;提示客户先完成 | | `@xhs/skillhub-upload` | Red Skill 渠道(路径 A) | 未装则改走网页上传(路径 B),或提示客户先装 | | 浏览器登录态 | Red Skill 渠道(路径 B) | 客户在小红书创作服务平台自行操作 | ## 两条硬规则(决定了本技能怎么筛选和改写) 装整个域(Claude Code 与 Codex 共用同一份域插件): ```bash claude plugin marketplace add soia-team/soia-open-skills claude plugin install soia-meta@soia ``` 只装这一个技能: ```bash npx skills add soia-team/soia-open-skills -g -a '*' -s soia-meta-publish-market -y ``` **WorkBuddy** 的装载单位是角色化专家而不是插件,`npx skills add -a '*'` 覆盖不到它,需要单独安装,见 [docs/install/workbuddy.md](https://github.com/soia-team/soia-open-skills/blob/main/docs/install/workbuddy.md)。 ### 1. 只上架零 hard 依赖的技能 外部市场的用户**不会同时装我们仓里的同伴技能**。声明了 `dependencies.hard` 的技能到了那边是断链的——装了也跑不起来。脚本按此自动筛选,遇到 hard 依赖 直接拒绝打包并说明原因。`optional` 依赖不阻断,但应在简介里写一句「配合 某某技能效果更好」。 ### 2. 只有正式版能上市场(硬门禁) 市场拿到的是最终用户直接使用的东西,必须是已发版内容。脚本**直接从 `origin/main` 导出**,而不是校验工作副本——后者依赖检出在哪个分支,不可靠。 | 情况 | 结果 | |---|---| | main 上有该技能且版本无 `-SNAPSHOT` | 导出并打包 | | main 上没有该技能 | 拒绝:「尚未发版,不能上架」 | | main 版本带 `-SNAPSHOT` | 拒绝:「不是正式版」 | `--allow-unreleased` 仅供本地演练,**不得用于真实上架**。 ### 4. 上架就绪门禁(打包后机器检查) 外部市场会用 AI 评测上架的技能,历史评语点名过的缺口类型在打包阶段就要被机器 查出来——有硬缺口直接拒绝打包,不让它流到市场上去挨评。门禁细节见下方 [上架就绪门禁](#上架就绪门禁)。 ## 上架就绪门禁 外部市场(腾讯 SkillHub 等)会用 AI 评测上架的技能。历史评语点名过几类缺口—— 没有能力边界、没有触发词、没有真实输出样例、没有测试保障、依赖源全境外——这些 **在打包阶段就被机器检查出来**,有硬缺口直接拒绝打包,不让它流到市场上去挨评。 `stage_for_market.py` 打包后对**暂存产物**(不是仓库)跑五道检查: | 编号 | 检查 | 等级 | 判据 | 修复指引 | |---|---|---|---|---| | R1 | 能力边界 | 硬缺口 | SKILL.md 没有含「不负责」/「能力边界」的标题节 | 补一节「不负责什么」,用两三行说清不做什么 | | R2 | 触发词 | 硬缺口 | frontmatter `description` 不含「触发」/「Triggers」 | 在 description 里写明触发场景,例如「触发:…」 | | R3 | 输出样例 | 硬缺口 | 没有含真实数据的「样例/示例」小节;全是 `<占位符>` 的表格不算 | 给出一节真实输入→输出的样例表格 | | R4 | 测试证据 | 硬缺口 | `tests/` 里没有只引用本技能与标准库的专属自包含测试;或有但进包后跑不起来 | 给技能写一个自包含的最小测试:只引用本技能包内文件与标准库;引用其他技能名的共享测试不进包 | | R5 | 境外源提示 | 警告 | 包内只有境外 URL(无 `.cn`/npmmirror 等境内源) | 优先替换为国内可访问的安装源或镜像 | | R6 | 安全预检 | 硬缺口 | 包内含疑似凭据样式串(npm_/ghp_/xox 等前缀+长随机段)、以 `npm_` 开头的小写标识符(安全扫描按密钥前缀误报,云鼎 2026-08-08 实报 ai-cli-upgrade 健康度 47)、或 pipe-to-shell 命令字样(`curl … \| sh`) | 真凭据移除、占位符换明显假值;标识符更名避开前缀;安装建议改「下载→审阅→本地执行」表述 | 门禁行为:逐项打印通过/警告/硬缺口;存在**硬缺口** → 删除暂存目录并拒绝打包 (退出码 1);只有警告 → 照常产出。R4 只收「专属」测试——只引用本技能包内 文件与标准库的自包含测试,会**拷贝进包**作为证据,并在包的布局里实跑;引用 其他技能名的跨技能共享测试不进包(归仓级 CI 管),只在报告里提示跳过。 本门禁不预测评测分数,只消除历史评语点名过的缺口类型。 ### 咨询用法:--check-only ```bash # 对工作树做就绪检查(不导出 main、不留暂存产物,只出报告) python3 scripts/stage_for_market.py --repo-dir <域仓路径> \ --skill <技能名> --out <暂存目录> --allow-unreleased --check-only ``` `--check-only` 走完整个打包+门禁流程后删除暂存产物,只留报告——适合在 PR 阶段 先跑一遍咨询,不必等发版。与 `--allow-unreleased` 组合即「对工作树做咨询检查」。 ### 3. slug 用仓内技能名,展示名用中文 `slug` 必须全网唯一,我们的技能名已带 `soia-` 前缀,天然满足,且与仓内名 一一对应、便于追溯;`displayName` 另给中文可读名,面向普通用户。 ```yaml slug: soia-env-network-diagnose # = 仓内技能名,勿改 displayName: 网络诊断助手 # 中文,面向市场读者 summary: <一句话简介,缺省取 description> license: MIT ``` 平台字段**叠加**在原 frontmatter 之上,不替换——仓内的 `name`/`version`/ `created_by` 等字段保留,实测与平台字段共存不冲突。 ## 渠道一:腾讯 SkillHub(CLI) ### 一次性准备(客户自行完成) 1. 手机号登录 <https://skillhub.cn> → 个人中心完成**实名认证**(未认证不能建 Token) 2. 个人中心 → API keys → 创建 → 复制 `skh_` 开头的 Token(**只显示一次**) 3. 安装 CLI 并登录: ```bash curl -fsSL https://skillhub.cn/install/install.sh | bash -s -- --cli-only ``` ```bash echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc ``` ```bash skillhub login --key <你的Token> --host https://api.skillhub.cn ``` `skillhub auth whoami` 应输出 userId / handle / role 三行。 ### 投递 ```bash skillhub publish <暂存目录>/<技能名> --dry-run ``` 看到 `✓ Dry-run passed: <slug>@<version>` 表示格式合规。确认后由客户执行: ```bash skillhub publish <暂存目录>/<技能名> --changelog "首次发布" ``` 返回 `✓ Published: skillId=xxx status=pending_review` 即进入审核。更新时保持 slug 不变、改 version,`--changelog` 写本次变更。 **注意**:`publish` 是对外发布动作,本技能不代客户执行;预检 `--dry-run` 可代跑。 ### 常见失败 | 现象 | 原因 | |---|---| | `403 请先完成实名认证` | 认证未完成 | | `409 slug 已被占用` | 该 slug 全网已存在,换名 | | `401 invalid api key` | Token 失效,重建 | ## 渠道二:小红书 Red Skill Red Skill 有**两条上传路径**,都由小红书官方工具承担投递,本技能只负责前置打包。 ### 路径 A:官方 uploader 技能(推荐,AI 驱动) 小红书发布了 `@xhs/skillhub-upload` —— 一个 CLI + 配套技能,由 AI 助手驱动完成 授权、打包、上传与提交。**不要自己实现上传逻辑**:接口与字段由平台方维护, 自造一份必然漂移。 一次性准备(客户自行执行): ```bash npm install -g "https://fe-video-qc.xhscdn.com/fe-platform-file/104101b83221qt9bu7k0653u0hejenq0004pf88k9rpr6a.tgz" ``` 安装**不会自动注册技能**,还要把包内的 SKILL.md 复制进 agent 的技能目录: ```bash mkdir -p <agent-skills-dir>/skillhub-upload cp "$(npm root -g)/@xhs/skillhub-upload/skill/SKILL.md" \ <agent-skills-dir>/skillhub-upload/SKILL.md ``` 之后把本技能产出的暂存目录交给它: > 请按照 https://redskill.xiaohongshu.net/uploader.md 中的说明帮我把 > `<暂存目录>/<技能名>` 上传到小红书 SkillHub 官方技能接手后的流程(了解即可,不由我们实现):`whoami` 查授权 → `login --agent` 出授权链接与用户码(手机浏览器打开跳 App)→ 拉实时标签列表 → 问 source(原创/转载)与 tag → `publish --dry-run --agent` 出待提交载荷供审阅 → 用户明确说「提交」后才真实提交。 要点:**Skill ID 是平台主键、跨版本不可改名**,dry-run 阶段若提示无法自动派生, 需慎重确认后用 `--identifier` 指定。 #### 实测要点(2026-08-05 跑通全链得到) **Red Skill 有文件类型白名单,SkillHub 没有。** 同一份技能目录,SkillHub `--dry-run` 直接通过,Red Skill 却报 `目录中包含不支持上传的文件:agents/openai.yaml,请移除后重试`——它只收 `.md/.txt/.html/.css/.js/.py/.json/.xml`,我们的 `agents/openai.yaml`(Codex 界面元数据)不在其中。因此打包必须带 `--channel redskill` 做剔除;剔除后 dry-run 通过,payload 里 `version` 正是 main 的正式版号。 **CLI 输出是 `RESULT_JSON:` 结构化行。** 实测 PATH 上的 `skillhub-upload` shim 在某些环境下吞掉输出(命令静默、退出码 0),直接调 `node "$(npm root -g)/@xhs/skillhub-upload/cli/index.mjs" <子命令>` 才能看到。 排障时先用这条确认,别把静默当成功。 **`--dry-run` 不需要授权**,只有真提交才需要;所以预检可以随时跑。 **CLI 只支持首发,更新版本走网页——新旧两版 CLI 都实测过。** 实测 2026-08-06:技能 1.2.0 审核通过、生效中后,对同一 Skill ID 提交 1.3.1, 旧版 `@xhs/skillhub-upload` 0.1.1 与最新 `redskillhub-upload` 1.0.2 均被拒 `SUBMIT_REJECTED: Skill ID 已被占用`——1.0.2 是包上传到 100% 后才在最终提交 被拒,说明是提交端点不接受既有 ID,不是打包问题。已上架技能的版本更新走 创作平台 Builder hub → 该技能 → **更新版本** → 上传本技能打好的文件夹/zip; 打包仍由本技能完成,上传由客户本人执行。 **两家平台的版本号来源不同——SkillHub 读包、Red Skill 网页自增。** 实测 2026-08-06:同一份 1.3.1 的包,SkillHub 技能页正确显示 v1.3.1(读 frontmatter `version`);Red Skill 网页「更新版本」不读包内版本,直接拿线上 版本 +patch(1.2.0 → 平台标 V1.2.1,内容实为 1.3.1)。CLI 路径倒是读 frontmatter(dry-run 载荷 version=1.3.1),但 CLI 不能更新已生效技能(见上条)。 后果:Red Skill 的展示版本会与仓内版本错位且逐次累积。回执里两个号都要写; 若更新表单有版本输入框,应手填对齐仓内版本。 **uploader 已迁移到 npm 公共包 `redskillhub-upload`(1.0.x 版本线)。** 网页「通过对话上传」的口令会自举安装它(`ensure-cli.mjs` 锁 1.0.x 取最新); CDN tgz 装的 `@xhs/skillhub-upload` 0.1.1 是旧版。新版 `publish` 在进程内 自动复用/刷新登录态并自产二维码(`prompt.qrCodePath`),凭据仍在 `~/.skillhub-upload` 共享。 **`login` 会用 refresh token 自动续期,`publish` 不会。** access token 过期后 `publish` 直接报 `NEED_LOGIN`;此时先跑一次 `login --agent`——有未过期的 refresh token 时它静默续期返回 `loggedIn: true`,无需重新扫码授权。 **标签是实时拉取的**,不要硬编码——实测当前为效率工具 / 内容创作 / 学习成长 / 职场办公 / 编程开发 / 生活决策 / 金融理财 / 其它,但以拉到的为准。 #### 展示名与平台主键(必填) **`name` 不能落到仓内技能名上。** 2026-08-06 首次真提交被拒: `SUBMIT_REJECTED: 名称长度不符合要求`——载荷的 `name` 取自 frontmatter 的 `soia-env-network-diagnose`(25 字符),超了平台限制;改成「网络诊断助手」后通过。 官方 uploader 的取值优先级是 `flags.name || metadata.name || identifier` (`submit.mjs`),所以正确做法是投递时传 `--name`,**不动仓内 frontmatter**—— 改 frontmatter 会连带影响 identifier 派生与仓内技能身份。 平台的长度上限未公开,我们只知道 25 被拒、6 通过。因此不猜阈值,改用一条确定性 约束:**`--channel redskill` 必须给 `--display-name`,缺省直接拒跑**。长英文技能名 对市场读者本来也没有意义。 **`--identifier` 要显式钉住。** Skill ID 是平台主键、跨版本不可改名。不显式指定时 它由 frontmatter 的 `name` 派生,将来一旦改名就会在平台上**另建一个新技能**,而不是 更新原有的。所以投递命令固定带 `--identifier <仓内技能名>`。 **`--yes` 不覆盖最后一道确认门。** `confirmBeforeSubmit` 是无条件的:从 stdin 读一行, 必须是字面量 `submit`,空输入按取消处理。这是平台设计的人工闸门,必须客户明确说提交 后才应答;`edit` 后跟 `key=value` 行可在确认阶段改 `name`/`identifier`/`version`/ `description`/`detail`/`tag`。 ### 路径 B:网页上传 创作服务平台 → Builder hub → Red Skill → 上传 Skill → **上传文件**。两步: ① 上传源码 ② 填写信息。 | 约束 | 值 | |---|---| | 接受形态 | 含 `SKILL.md` 的**文件夹**或 **zip** | | 文件类型 | `.md/.txt/.html/.css/.js/.py/.json/.xml` 等代码与配置 | | 单文件上限 | 10 MB | | 总大小上限 | 30 MB | | 必选项 | 内容来源(原创 / 转载)、勾选《Skill 发布安全规范》 | 我们的技能目录(SKILL.md + scripts + references)天然满足这些约束。 ### 与 media 域的配合 `soia-media-publish-rednote-card` 产的笔记可挂载对应的 Skill 卡片——内容讲场景、 卡片直接转化,是同一平台内的闭环。 ## 不负责什么 - **不代客户执行 `publish` 或点上传**。这两个动作会把内容送到外部平台并进入 审核记录,必须由客户本人执行。本技能只做打包与预检。 - **不上架未发版内容**。只从域仓 `main`(正式版)打包;dev 快照带 `-SNAPSHOT`。 - **不改仓内技能**。打包在暂存目录进行,不回写源仓;要改依赖或描述请走正常 PR 流程,发版后再上架。 - **不自造上传实现**。Red Skill 的投递由官方 `@xhs/skillhub-upload` 承担, SkillHub 由官方 `skillhub` CLI 承担;平台接口与字段由它们维护,我们只做 前置筛选与打包,避免自造一份必然漂移的副本。 - **不管插件市场**。`soia` marketplace 的 pin 刷新属 `soia-meta-skill-release`。 ## 私密信息与中间数据 - **不读取、不回显、不存储 API Token**。登录由客户执行 `skillhub login` 完成, 凭据存在 CLI 自己的配置里;本技能只调用 `skillhub auth whoami` 确认登录态。 - 暂存目录默认放系统临时目录,不进仓库;打包产物随时可重新生成。 ## 日志与完成回执 | 技能 | 渠道 | slug | 版本 | 预检 | 状态 | |---|---|---|---|---|---| | `<skill>` | SkillHub / Red Skill | `<slug>` | `<version>` | passed / failed | 待客户提交 / 已提交待审核 | ## 前向测试 - `--list-eligible` 能正确区分零 hard 依赖与有 hard 依赖的技能 - 对有 hard 依赖的技能执行打包时报错并说明原因,不产出暂存目录 - 打包后 frontmatter 同时含平台字段与原字段,正文一字不改 - `--display-name` 缺省时回落到原 `name`,`--summary` 缺省时回落到 `description` - `--channel redskill` 缺 `--display-name` 时退出码非零,且不产出暂存目录 - redskill 的投递命令同时带 `--name` 与 `--identifier`,且给出的是 `--dry-run` 形态 - 就绪门禁 R1-R6 各项都有**一正一反**的用例:缺边界节/触发词/真实样例/专属测试;R6 覆盖 pipe-to-shell 字样、npm_ 前缀标识符、凭据样式串三反例与大写环境变量/词中 npm_ 不误伤两正例 被拒,补齐后放行;全境外 URL 记警告不阻断,含 npmmirror 等境内源则无警告 - R4 只拷「专属」测试(只引用本技能与标准库)进包并在包布局实跑;布局耦合的 测试会被抓出来拒包;引用其他技能名的跨技能共享测试不进包,只在报告里提示 - `--check-only` 跑完整个流程后不留暂存产物