# Skill Publish Sync

> 将本地开发的 Skills 同步到 ClawHub、腾讯 SkillHub 与联想开放平台。支持智能 .gitignore 过滤、平台独立白名单、增量与单个 skill 同步。本技能应在用户需要将本地 skills 发布到上述平台、批量同步技能或检查发布状态时使用。

- Skill: `cat-xierluo/skill-publish-sync` (Agent Skill, multi-file: 9 files)
- Install (CLI): `npx skillmds@latest add cat-xierluo/skill-publish-sync`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cat-xierluo/skill-publish-sync/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: cat-xierluo (https://skillmd.com/u/cat-xierluo)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/cat-xierluo/skill-publish-sync

---


# Skill 同步工具（ClawHub + 腾讯 SkillHub）

将本地开发的 Skills 批量同步到三个公开平台：

- **ClawHub** — 国际通用 Skills 社区，强制 MIT-0 许可证
- **腾讯 SkillHub** — 专为中国用户优化的 Skills 社区，无许可证限制
- **联想开放平台** — 面向联想 AI 智能体生态，使用 `@lenovo-open/skill-cli` (Node.js)；无强制许可证

支持读取 `.gitignore` 智能忽略敏感文件和临时文件。三个平台各自维护独立的 allowlist 列表文件（`allowlist-clawhub.yaml` / `allowlist-skillhub.yaml` / `allowlist-lenovo.yaml`），同一 skill 可在多份文件中独立维护字段（一对多，不强制不重复分发）。

## 依赖

### 系统依赖

| 依赖 | 用途 | 安装方式 |
|------|------|----------|
| `rsync` | `prepare-publish.sh` 复制过滤文件到临时发布目录 | macOS 自带；Linux: `sudo apt-get install rsync` |
| `git` | `prepare-publish.sh` 用 `git ls-files` 精确匹配追踪文件 | macOS 自带；Linux: `sudo apt-get install git` |
| Python 3 | 运行腾讯 SkillHub CLI（Python 脚本） | macOS 自带；Linux: `sudo apt-get install python3` |
| Node.js（≥ 18）+ `npm` | 运行联想 `lenovoskill` CLI（`npx @lenovo-open/skill-cli` 走 Node.js 运行时） | macOS 自带或 `brew install node`；Linux: `sudo apt-get install -y nodejs npm` |

### CLI 工具（按目标平台按需安装）

| CLI | 用途 | 安装命令 | 必需场景 |
|------|------|----------|----------|
| `clawhub` | 发布到 ClawHub | 见 [ClawHub 官方文档](https://docs.openclaw.ai/clawhub/cli) | 仅发布 ClawHub 时 |
| `skillhub` | 发布到腾讯 SkillHub | 官方 CLI，`skillhub self-upgrade` 升级（须 ≥ 2026.7.29） | 仅发布 SkillHub 时 |
| `lenovoskill`（`npx @lenovo-open/skill-cli`） | 上传到联想开放平台 | `npx @lenovo-open/skill-cli <cmd>`（免装，推荐）或 `npm i -g @lenovo-open/skill-cli` | 仅上传联想平台时 |

> **⚠️ 版本要求（重要）**
>
> 腾讯 `skillhub` CLI **必须 ≥ 2026.7.29** 才支持 `publish`/`login`。早期版本（如 2026.3.18）只有 `search`/`install`，不能发布。
> 运行 `skillhub self-upgrade` 升级到最新版；不确定时先用 `skillhub self-upgrade --check-only` 检查是否有新版。

### 首次安装清单（SkillHub 发布）

```bash
# 1. 安装 CLI（macOS/Linux）
curl -fsSL https://skillhub-1388575217.cos.ap-guangzhou.myqcloud.com/install/install.sh | bash

# 2. 升级到支持发布的版本（≥ 2026.7.29）
skillhub self-upgrade

# 3. 验证（应含 publish/login 命令）
skillhub --help

# 4. 登录（token 从 https://skillhub.cn 个人设置获取，skh_ 前缀）
skillhub login --key skh_xxx

# 5. 确认身份
skillhub auth whoami
```



> **ClawHub 强制使用 MIT-0 许可证**（无需署名，允许商业使用）。
> **腾讯 SkillHub 无许可证限制**，CC-BY-NC / MIT / Apache 等均可发布。
>
> **ClawHub 许可证关键条款**（来自官方 Skill Format 文档，截至 2026-08 核实）：
> - "All skills published on ClawHub are licensed under `MIT-0`." —— 所有发布即等同 MIT-0。
> - "Anyone may use, modify, and redistribute published skills, including commercially." —— 允许任何人商用。
> - "Attribution is not required." —— 不要求署名。
> - "ClawHub does not support per-skill license overrides." —— **不支持按 skill 覆盖许可证**（frontmatter 写 `license: CC-BY-NC` 平台层面不生效）。
> - "Do not add conflicting license terms in `SKILL.md`." —— **禁止在 SKILL.md 内加冲突的许可证条款**。
>
> 因此（**v1.7.1 三份独立列表架构**）：
> - 每个平台独立维护自己的 allowlist 文件：ClawHub / SkillHub / 联想各一份
> - MIT 许可证的通用工具 skill → 默认只发 ClawHub（`allowlist-clawhub.yaml`）
> - CC-BY-NC 等限制性许可证的法律类 skill → 默认只发 SkillHub（`allowlist-skillhub.yaml`，ClawHub MIT-0 冲突不收）
> - 想在 ClawHub / SkillHub 之外额外分发到联想生态的 skill → 在 `allowlist-lenovo.yaml` 里登记（无许可证限制，可与 SkillHub / ClawHub 重复登记，即同一 skill 跨多份文件是完全允许的）
> - ClawHub slug 被占用、发不了的 → 从 `allowlist-clawhub.yaml` 移到 `allowlist-skillhub.yaml` 或 `allowlist-lenovo.yaml`
>
> 即**三份独立 allowlist 文件**，每份独立维护各自平台的 skill 名单与字段；同一 skill 可同时出现在多份文件里（一对多），无需为新平台改动旧 skill 的配置。详见 DECISIONS `D-2026-08-12-02`。
>
> ClawHub 许可证详见 [ClawHub Skill Format 官方文档](https://docs.openclaw.ai/clawhub/skill-format)

---

## 平台对比

| 维度 | ClawHub | 腾讯 SkillHub | 联想开放平台 |
|------|---------|---------------|---------------|
| CLI 工具 | `clawhub` | `skillhub`（官方 CLI，`skillhub self-upgrade` 升级；**须 ≥ 2026.7.29** 才支持 publish/login） | `lenovoskill`（`npx @lenovo-open/skill-cli`，Node.js 运行时） |
| 登录命令 | `clawhub login` | `skillhub login --key skh_xxx` | `lenovoskill login`（OAuth 浏览器授权） |
| 发布命令 | `clawhub publish <path> --slug --name --version --changelog` | `skillhub publish <path> [--version] [--changelog] [--dry-run]` | `lenovoskill package && lenovoskill push`（两步：先打 zip 再传） |
| skill 标识 | `--slug` + `--name`（命令行传入） | `slug` + `displayName`（prepare-publish.sh 从 `allowlist-skillhub.yaml` 注入临时副本；源 SKILL.md 不含） | `slug` + `displayName`（prepare-publish.sh 从 `allowlist-lenovo.yaml` 注入临时副本；源 SKILL.md 不含） + 项目内 `.skill-config.json` |
| 凭证位置 | clawhub CLI 默认 | 本地 skillhub 配置 | `~/.lenovoskill/auth.json`（600，OAuth 双 token：Login Token + Biz Token）+ `~/.lenovoskill/config.json`（默认 API） |
| 可见性 | 无 | —（无此概念） | —（无此概念） |
| namespace | 无 | **有**（绑定账号，发布命令不传，记录用于溯源） | **无**（OAuth 双 token，无 namespace 概念） |
| **许可证** | **强制 MIT-0** | **无限制** | **无限制** |
| 默认 registry/API | 内置 | `https://api.skillhub.cn` | `https://open.lenovomm.com` |
| 官方教程 | — | https://skillhub.cn/tutorials#publish-via-cli | https://open.lenovomm.com |

---

## 前置条件

### SKILL.md frontmatter 必需字段

```yaml
---
name: skill-name
description: 技能描述
version: "1.0.0"  # 推荐但不强制
homepage: https://github.com/cat-xierluo/legal-skills  # 自动设置
---
```

> **slug / displayName 不写进 SKILL.md**。SkillHub 需要的字段从 `config/allowlist-skillhub.yaml` 读取；联想开放平台需要的字段从 `config/allowlist-lenovo.yaml` 读取。**每个平台独立白名单，字段独立维护**（同一 skill 可同时出现在多份文件里，一对多）。发布前由 `prepare-publish.sh`（`--platform skillhub` 或 `--platform lenovo`）自动注入临时副本 frontmatter。源 SKILL.md 只保留 skill 标准字段，平台元数据与 skill 本体解耦。ClawHub 不依赖 frontmatter（用 `--slug`/`--name` 命令行参数）。

### CLI 安装

CLI 工具的完整安装方式、版本要求，详见本文档开头「[依赖](#依赖)」章节。

快速验证已安装：

```bash
clawhub --version      # ClawHub
skillhub --help        # SkillHub（确认含 publish/login 命令即为发布版）
```

---

## 使用方式

### 1. 登录平台（首次使用）

**ClawHub**：

```bash
clawhub login
```

**腾讯 SkillHub**（使用 API Token，格式 `skh_xxx`，从 SkillHub 个人设置获取）：

```bash
# token 从 https://skillhub.cn 个人设置获取（skh_ 前缀）
skillhub login --key skh_xxx

# 如需临时使用其他 token 覆盖登录态（不写入本地）
skillhub login --key skh_yyy
```

> 登录后 token 存在本地，后续发布命令无需重复传 token。也可在 `skillhub publish` 时用 `--token skh_xxx` 临时覆盖。

### 2. 验证登录状态

```bash
# ClawHub
clawhub whoami

# 腾讯 SkillHub
skillhub auth whoami
```

### 3. 同步技能

#### ClawHub

**同步单个技能**：

```bash
clawhub sync skills/<skill-name>
```

**同步所有技能**：

```bash
clawhub sync --all
```

> 注意：`--all` 会受 `skills/skill-publish-sync/config/allowlist-clawhub.yaml` 约束。只有在该文件中列出的 skill 才会同步到 ClawHub。SkillHub / 联想同理（各自独立白名单文件）。

#### 腾讯 SkillHub

SkillHub 推荐逐个 skill 发布（通过 `publish` 命令指定目录，标识来自 SKILL.md frontmatter，见下方「单个 Skill 同步工作流」）。

```bash
# 发布目录（自动打包）
skillhub publish ./my-skill

# 指定版本和 changelog
skillhub publish ./my-skill --version 1.2.0 --changelog "新增xxx"

# 先预检（只校验 + 打包，不实际发请求）
skillhub publish ./my-skill --dry-run
```

> SkillHub 用 `slug` + `displayName`（发布前由 prepare-publish.sh 从 `config/allowlist-skillhub.yaml` 注入临时副本，源 SKILL.md 不含）标识 skill，namespace 绑定在账号上（发布时无需命令行指定）。没有可见性概念。

**交互式选择同步**：用户可指定要同步的技能列表与目标平台，我会逐个执行同步命令。

---

## 单个 Skill 同步工作流

当需要同步指定的 skill（而非全部）时，使用此工作流。流程对两个平台通用，仅在「执行发布」步骤区分命令。

### 前置检查

1. **检查登录状态**
   ```bash
   clawhub whoami           # ClawHub
   skillhub auth whoami     # 腾讯 SkillHub
   ```

2. **检查白名单**
   - 读取 `skills/skill-publish-sync/config/allowlist-${PLATFORM}.yaml`（平台特定白名单）
   - 确认目标 skill 出现在对应平台的 allowlist 文件中（每平台独立文件,无 `platforms` 字段）
   - 三平台独立文件路径：
     - ClawHub → `allowlist-clawhub.yaml`
     - SkillHub → `allowlist-skillhub.yaml`
     - 联想 → `allowlist-lenovo.yaml`

3. **检查许可证**（仅 ClawHub 需要）
   - 读取目标 skill 的 SKILL.md frontmatter 中的 `license` 字段
   - ClawHub：只有 MIT 许可证的 skill 才能同步
   - SkillHub / 联想：无许可证限制，均可同步

### 版本检测

比较两个版本号（按目标平台读取记录）：

| 来源 | 位置 | 格式 |
|------|------|------|
| **新版本** | `skills/<skill-name>/SKILL.md` frontmatter 的 `version` | `"1.2.0"` |
| **已记录版本** | `skills/skill-publish-sync/config/sync-records.yaml` 中 `records.<skill>.platforms.<platform>.version` | `"1.1.0"` |

**版本比较逻辑**（语义化版本）：
```
new_version > recorded_version → 需要同步
new_version == recorded_version → 跳过（无变化）
new_version < recorded_version → 警告（版本回退？）
recorded_version 为 null → 需要同步（首次发布）
```

### 执行同步

#### 步骤 1：准备发布目录

```bash
# ClawHub（默认平台，可省略 --platform）
bash skills/skill-publish-sync/scripts/prepare-publish.sh skills/<skill-name>

# 腾讯 SkillHub
bash skills/skill-publish-sync/scripts/prepare-publish.sh --platform skillhub skills/<skill-name>
```

脚本会创建临时目录：
- ClawHub：`/tmp/clawhub-publish-<skill-name>`
- SkillHub：`/tmp/skillhub-publish-<skill-name>`

#### 步骤 2：执行发布

**ClawHub**（使用 `clawhub publish`，显式指定 slug 和 name）：

```bash
clawhub publish /tmp/clawhub-publish-<skill-name> \
  --slug <skill-name> \
  --name "<Display Name>" \
  --version "<新版本号>" \
  --changelog "<变更说明>"
```

> **⚠️ 必须指定 --slug 和 --name**
> - 临时目录名可能包含前缀（如 `clawhub-publish-`），导致发布时 slug 不正确
> - `--slug <skill-name>` 确保使用正确的 skill 标识符
> - `--name "<Display Name>"` 确保在 ClawHub 上显示正确的名称

**腾讯 SkillHub**（使用 `skillhub publish`，指定版本与 changelog）：

```bash
skillhub publish /tmp/skillhub-publish-<skill-name> \
  --version "<新版本号>" \
  --changelog "<变更说明>"
```

> **腾讯用 slug + displayName 标识 skill**：这两个字段**不写进源 SKILL.md**，而是发布前由 `prepare-publish.sh --platform skillhub` 从 `config/allowlist-skillhub.yaml` 读取并注入临时副本 frontmatter（slug 默认取 `name`，displayName 取配置的 `display_name`）。namespace 绑定在账号上（发布时无需命令行指定），没有可见性概念。
> 版本号与 changelog 通过 `--version` / `--changelog` 显式传入；不带 `--version` 时由 CLI 决定。发布前可加 `--dry-run` 只做预检。

**联想开放平台**（使用 `lenovoskill`，先打 zip 再传两步）：

```bash
# 1. 准备：先在临时目录生成 .skill-config.json（联想 CLI 项目级配置）
cd /tmp/lenovo-publish-<skill-name>
# 源 skill 若已带 .skill-config.json,prepare-publish.sh 已一并复制,跳过 init;
# 否则跑一次 init 生成默认配置再按需编辑:
lenovoskill init

# 2. 打 zip
lenovoskill package
# → 生成 <skill-name>.zip

# 3. 上传 zip
lenovoskill push
```

> **联想同样从 SKILL.md frontmatter 读 slug + displayName**：这两个字段由 `prepare-publish.sh --platform lenovo` 注入临时副本（与 SkillHub 共用同一段 python 逻辑）。同时联想 CLI 也读项目内 `.skill-config.json`——两者并存，`displayName` / `slug` 字段在 SKILL.md frontmatter 与 `.skill-config.json` 中应保持一致。
> `.skill-config.json` 建议**仅在临时目录生成/复制**，不要 commit 进源 skill（它是联想 CLI 的私有适配层，与 skill 标准结构无关）。

#### 步骤 3：更新同步记录

更新 `skills/skill-publish-sync/config/sync-records.yaml`，在对应平台下写入记录：

```yaml
records:
  <skill-name>:
    platforms:
      clawhub:                      # 或 skillhub / lenovo
        version: "<新版本号>"
        last_sync: "<ISO 8601 时间>"
        git_hash: "<当前 commit hash>"
        status: synced
        changelog_summary: "<变更说明>"
        url: "https://clawhub.ai/skills/<skill-name>"      # ClawHub
        # 或 SkillHub：
        # url: "https://skillhub.cn/skills/<slug>"
        # 或联想开放平台:
        # url: "https://open.lenovomm.com/skills/<slug>"
        publish_id: "<从命令输出获取>"
        # SkillHub 还要加一行 namespace: "<your-namespace>";ClawHub / 联想无 namespace 字段
```

### 示例：同步 skill（按分流策略发到对应平台）

**A. MIT 工具 → ClawHub（以 git-batch-commit 为例）**

```bash
# 1. 检查白名单
grep -A1 "^git-batch-commit:" skills/skill-publish-sync/config/allowlist-clawhub.yaml
# 输出：platforms: [clawhub]   # MIT 工具，只发 ClawHub

# 2. 比较版本
# SKILL.md: version: "1.2.0"
# sync-records.yaml: platforms.clawhub.version: "1.1.0" → 需要同步

# 3. 准备并发布到 ClawHub
bash skills/skill-publish-sync/scripts/prepare-publish.sh skills/git-batch-commit
clawhub publish /tmp/clawhub-publish-git-batch-commit \
  --slug git-batch-commit --name "Git Batch Commit" \
  --version "1.2.0" --changelog "本次变更说明"

# 4. 更新 sync-records.yaml 的 platforms.clawhub 字段
```

**B. CC-BY-NC 法律类 → SkillHub（以 legal-qa-extractor 为例）**

```bash
# 1. 检查白名单
grep -A2 "^legal-qa-extractor:" skills/skill-publish-sync/config/allowlist-skillhub.yaml
# 输出：platforms: [skillhub]  +  display_name: "法律问答知识提取"

# 2. 准备（--platform skillhub 会自动从配置读 display_name/slug 注入临时副本 frontmatter）
bash skills/skill-publish-sync/scripts/prepare-publish.sh --platform skillhub skills/legal-qa-extractor

# 3. 发布到 SkillHub
skillhub publish /tmp/skillhub-publish-legal-qa-extractor \
  --version "<新版本号>" --changelog "<变更说明>"

# 4. 更新 sync-records.yaml 的 platforms.skillhub 字段
```

### 失败处理

- 同步失败时记录 `status: failed`
- 不重试，让用户决定后续操作
- 记录失败原因到 `changelog_summary`

---

## SkillHub 专属说明

### Token 获取

1. 登录 [SkillHub](https://skillhub.cn)
2. 在个人设置中生成 API Token（格式 `skh_xxx`，前缀 `skh_`）
3. 通过 `skillhub login --key skh_xxx` 保存到本地

```bash
skillhub login --key skh_xxx
skillhub auth whoami     # 确认身份
```

> **安全提醒**：Token 等同于账号密码，**绝对不要**写入配置文件或提交到 Git。`skillhub login` 已将 token 存到本地，后续发布命令无需重复传 token（CLI 自动读取）；如需临时覆盖，可在 `skillhub publish` 时用 `--token skh_xxx`。

### slug / namespace 与版本号规则

腾讯 SkillHub 用 `slug` + `displayName` + `namespace`（账号绑定）标识 skill。**slug/displayName 不写进源 SKILL.md**，而在 `config/allowlist-skillhub.yaml`（平台特定白名单）配置，发布前由 `prepare-publish.sh` 注入临时副本 frontmatter：

- **slug**：kebab-case，2-128 字符（实测校验正则）。默认 = skill 目录名（即 `name`）；在 `allowlist-skillhub.yaml` 用 `slug:` 字段覆盖（处理重名/被占用）。slug 在同一 namespace 下唯一，跨 namespace 可重名（如 `@cat-xierluo/md2word` 与他人的 `@xxx/md2word` 不冲突）
- **namespace**：命名空间，**绑定在账号上**（服务端 `@<namespace>/<slug>` 格式），发布命令**无需也无法**命令行指定——CLI 用登录账号的身份，服务端自动归到你的 namespace 下。你的 namespace 可通过 `skillhub search <你的skill>` 查看已发布 skill 的 `@xxx/slug` 前缀得知
- **version**：合法 SemVer（`major.minor.patch`），如 `1.0.0`。建议从 `0.1.0` 或 `1.0.0` 开始。已发布的版本不可修改，只能发布新版本

### API host

默认 API host 为 `https://api.skillhub.cn`。如需覆盖，`skillhub publish` 时用 `--host <URL>` 指定。

### SKILL.md frontmatter 要求

腾讯 SkillHub 发布时从（临时副本的）SKILL.md frontmatter 读取 skill 元数据。**源 SKILL.md 不需要写 slug/displayName**——`prepare-publish.sh` 会从 `config/allowlist-skillhub.yaml` 读取并注入临时副本：

| 字段 | 来源 | 说明 |
|------|------|------|
| `slug` | 默认取 `name`；`allowlist-skillhub.yaml` 的 `slug:` 可覆盖 | kebab-case，2-128 字符 |
| `displayName` | `allowlist-skillhub.yaml` 的 `display_name:`（必填） | SkillHub 展示名（中文） |
| `version` | 源 SKILL.md frontmatter（或 `--version` 覆盖） | 合法 SemVer |
| `summary` | 源 SKILL.md（可选） | 简短摘要 |
| `description` | 源 SKILL.md（可选） | 较长描述 |
| `tags` | 源 SKILL.md（可选） | 标签数组 |
| `license` | 源 SKILL.md（可选） | 许可证标识 |
| `homepage` | 源 SKILL.md（可选） | 主页地址 |

> **ClawHub 与腾讯字段差异**：ClawHub 用 `name` 字段 + 命令行 `--slug`/`--name`；腾讯用 `slug` + `displayName`（注入临时副本，源文件不写；namespace 绑定账号）。源 SKILL.md 只保留 `name`/`description`/`version` 等标准字段，两个平台的标识需求都由发布流程（命令行参数 / 配置注入）满足，不污染 skill 本体。

### CLI 版本注意

腾讯 `skillhub` CLI 是 Python 脚本。**老版本（< 2026.7.29）不支持 `publish`/`login`**，运行 `skillhub self-upgrade` 升级到最新版；不确定时先用 `skillhub self-upgrade --check-only` 检查是否有新版。

### 发布频率限制（避免并发限流）

腾讯 SkillHub API 对发布请求有频率限制。**连续多次 `skillhub publish` 会触发限流**（报错 "请求过于频繁,请稍后再试"），单个 publish 失败需重试，且会拖慢整个批量流程。

**实测安全间隔**：每个 `skillhub publish` 之间 `sleep 12-15` 秒。批量发布 N 个预计总耗时 ≈ N ×（单次发布耗时 + 15s）。

**推荐写法**（**串行 + sleep,不要并行**）:

```bash
while IFS='|' read -r s v; do
  bash prepare-publish.sh --platform skillhub "skills/$s" >/dev/null 2>&1
  echo "$s: $(skillhub publish /tmp/skillhub-publish-$s --version "$v" --changelog '...' 2>&1 | tail -1)"
  sleep 15
done <<EOF
skill-a|1.0.0
skill-b|1.1.0
EOF
```

**失败处理**：遇到限流报错，不要立即重跑整批。`sleep 30-60` 秒后单独重试失败的 skill（从 `sync-records.yaml` 看哪些平台仍是 `pending`）。

**避免**：`for s in ...; do (skillhub publish $s &); done`（并行）—— 100% 触发限流。

---

## 联想开放平台（LenovoSkill CLI）上传

联想开放平台（`https://open.lenovomm.com`）是面向联想 AI 智能体生态的 skill 分发渠道。**v1.7.1 三份独立列表架构下，联想与 ClawHub / SkillHub 是平行的第三渠道**——任何 skill 都可在 `allowlist-lenovo.yaml` 登记（无许可证限制），与 ClawHub / SkillHub 平台独立维护字段。一对多：同一 skill 可同时出现在 `allowlist-clawhub.yaml` + `allowlist-skillhub.yaml` + `allowlist-lenovo.yaml` 多份文件里。

### 与前两个平台的差异

| 维度 | ClawHub | 腾讯 SkillHub | 联想开放平台 |
|------|---------|---------------|--------------|
| 平台角色 | **独立列表平台**（`allowlist-clawhub.yaml`） | **独立列表平台**（`allowlist-skillhub.yaml`） | **独立列表平台**（`allowlist-lenovo.yaml`，与 SkillHub / ClawHub 平行） |
| 工作流 | 单条 `publish` 命令 | 单条 `publish` 命令 | **两步**：`package` 打 zip → `push` 上传 zip |
| CLI | `clawhub` | `skillhub`（Python） | `lenovoskill`（`npx @lenovo-open/skill-cli`，Node.js） |
| 凭证 | `clawhub login` | `skillhub login --key skh_xxx` | `lenovoskill login`（OAuth 浏览器授权） |
| 标识机制 | 命令行 `--slug/--name` | 临时副本 frontmatter（slug/displayName） | 临时副本 frontmatter（slug/displayName）+ 项目内 `.skill-config.json` |
| namespace | 无 | **有**（绑定账号，发布命令不传，记录溯源用） | **无**（OAuth 双 token） |
| 许可证 | 强制 MIT-0 | 无限制 | 无限制 |
| 同步记录 | `sync-records.yaml` `platforms.clawhub` | `sync-records.yaml` `platforms.skillhub`（含 namespace） | `sync-records.yaml` `platforms.lenovo`（无 namespace） |

### CLI 安装

```bash
# 方式 1：npx（推荐，免装、始终最新版）
npx @lenovo-open/skill-cli <cmd>

# 方式 2：全局安装（短命令、可离线）
npm install -g @lenovo-open/skill-cli
# 之后用 lenovoskill 代替 npx @lenovo-open/skill-cli
```

下文统一用 `lenovoskill`；用 npx 时替换即可。**前置依赖：Node.js ≥ 18 + npm**（详见本文档「依赖 · 系统依赖」章节）。

### 首次登录

```bash
lenovoskill login          # 浏览器 OAuth 授权
lenovoskill whoami         # 确认身份
lenovoskill logout         # 退出登录
```

凭证与配置（OAuth 安全存储，**禁止写入仓库或公开平台**）：

| 文件 | 模式 | 用途 |
|------|------|------|
| `~/.lenovoskill/auth.json` | 600 | **Login Token** + **Biz Token**（OAuth 双 token）+ 用户信息 |
| `~/.lenovoskill/config.json` | 默认 | API URL（默认 `https://open.lenovomm.com`）+ 默认可见性 |

### 上传工作流（5 步）

```bash
# ① 准备安全过滤目录（复用 prepare-publish.sh，会触发 SkillHub/联想 共用的 frontmatter 注入段）
bash skills/skill-publish-sync/scripts/prepare-publish.sh --platform lenovo skills/<skill-name>
# → 生成 /tmp/lenovo-publish-<skill-name>;临时副本 SKILL.md 已注入 slug + displayName

cd /tmp/lenovo-publish-<skill-name>

# ② 准备 .skill-config.json（联想 CLI 项目级元数据,与 SKILL.md frontmatter 互补）
#    - 源 skill 目录若已带 .skill-config.json,prepare-publish.sh 已一并复制,直接用
#    - 否则从源目录复制,或在此目录跑一次 lenovoskill init 生成默认配置再按需编辑
lenovoskill init            # 生成默认 .skill-config.json(可手编 ignore patterns)

# ③ 打 zip
lenovoskill package
# → 生成 <skill-name>.zip

# ④ 上传 zip
lenovoskill push
```

> **⚠️ 必须先过 prepare-publish.sh**：联想 CLI 没有内置 .gitignore 过滤,直接打源目录会把本地真实 `config/*.yaml`、`scripts/` 中间产物、`.DS_Store` 等一并打进 zip 上传。先过 prepare-publish.sh,临时目录已是 git ls-files 过滤后的"干净内容",再打 zip 才安全。

### `.skill-config.json`

联想 CLI 在每个 skill 项目根目录读取该文件,包含：

- **Skill 元数据**:name / version / description 等(可与 SKILL.md frontmatter 一致)
- **Ignore patterns**:打包时额外排除的文件(配合 prepare-publish.sh 双重保险)

> **建议**:此文件**仅在临时目录里生成/复制**,不写回源 skill 仓库——它只是联想 CLI 的私有适配层,与 skill 标准结构无关。
>
> **与 frontmatter slug/displayName 的关系**:联想 CLI 同时从 SKILL.md frontmatter 读 `slug` + `displayName`（由 prepare-publish.sh 注入临时副本）和项目内 `.skill-config.json` 读元数据。**两者应保持一致**——`displayName` / `slug` 在 SKILL.md frontmatter 与 `.skill-config.json` 中不能冲突。

### 命令速查

| 命令 | 用途 |
|------|------|
| `lenovoskill login` | 登录联想开放平台（OAuth） |
| `lenovoskill logout` | 注销 |
| `lenovoskill whoami` | 显示当前登录用户 |
| `lenovoskill init` | 在当前目录初始化 .skill-config.json |
| `lenovoskill package` | 将当前目录打包成 zip |
| `lenovoskill push` | 上传 zip 到联想开放平台 |

### 失败处理

- **`push` 失败**:先跑 `lenovoskill whoami` 确认登录态;检查 `.skill-config.json` 字段;确认 zip 是 `package` 刚生成的。
- **打包内容有误**:**回到步骤 1**,改源 skill + 重跑 `prepare-publish.sh`,不要在源目录直接重打(会绕过安全过滤)。
- **想撤销已发布的版本**:参考本文档「修复已发布的技能」章节,但联系联想平台支持由用户自行决定,不在本 skill 范围内。

### 与 sync-records.yaml 的关系

联想平台发布结果**必须**写进 `sync-records.yaml` 的 `platforms.lenovo`——与 SkillHub / ClawHub 一样，是三平台独立白名单架构的固定组成部分。`platforms.lenovo` 字段与 `platforms.skillhub` / `platforms.clawhub` 同构（`version` / `last_sync` / `git_hash` / `status` / `changelog_summary` / `url` / `publish_id`），**无 `namespace` 字段**（OAuth 双 token 机制，没有 namespace 概念）。

```yaml
records:
  <skill-name>:
    platforms:
      lenovo:                      # 三平台独立白名单架构(联想是独立第三选项,允许与其他平台字段独立维护)
        version: "<新版本号>"
        last_sync: "<ISO 8601 时间>"
        git_hash: "<当前 commit hash>"
        status: synced
        changelog_summary: "<变更说明>"
        url: "https://open.lenovomm.com/skills/<slug>"
        publish_id: "<从 lenovoskill push 输出获取>"
        # 无 namespace:联想用 OAuth 双 token(Login Token + Biz Token)
```

> **⚠️ NOT_VERIFIED**：以上 LenovoSkill CLI 命令(`login`/`init`/`package`/`push` 行为、`.skill-config.json` 字段、OAuth 流程、平台许可证审核、**是否实际从 SKILL.md frontmatter 读 slug/displayName**)的细节**来自用户提供的材料,未独立实测**。首次实际推送时以 CLI 实际行为为准;若行为有偏差（比如实测发现联想 CLI 不读 frontmatter 而完全依赖 `.skill-config.json`），回头修正本节——inject 段对 lenovo 可能无意义，需重新评估本决策。

---

## 同步策略

### 版本号处理

- 从技能的 `CHANGELOG.md` 第一行提取版本号
- 格式要求：`## [x.y.z] - YYYY-MM-DD`
- 自动处理 `v` 前缀（`v1.0.0` → `1.0.0`）

### 自动字段

| 字段      | 处理方式                                     |
| --------- | -------------------------------------------- |
| `homepage` | 自动设置为 GitHub 仓库地址                   |
| `version`  | 从 CHANGELOG.md 提取（如 SKILL.md 中未指定） |

### 同步范围控制（白名单机制，v1.7.1 三份独立列表）

**配置文件：** 三份独立白名单文件（v1.7.1 架构，详见 DECISIONS `D-2026-08-12-02`）：

- `skills/skill-publish-sync/config/allowlist-clawhub.yaml`（ClawHub 平台白名单）
- `skills/skill-publish-sync/config/allowlist-skillhub.yaml`（SkillHub 平台白名单）
- `skills/skill-publish-sync/config/allowlist-lenovo.yaml`（联想开放平台白名单）

**优先级：白名单 > 默认忽略规则**

- 如果对应平台的 `allowlist-${PLATFORM}.yaml` **存在**：只同步该文件中列出的 skill（每个平台独立读取自己的白名单文件）
- 如果对应平台的 `allowlist-${PLATFORM}.yaml` **不存在**：使用默认忽略规则（忽略 test/、private-skills/、node_modules/）

**配置格式（v1.7.1 三份独立列表，每份文件无 `platforms` 字段）：**

```yaml
# allowlist-clawhub.yaml（17 条 MIT 通用工具,display_name/slug 可选）
md2word:
patent-download:
# ...

# allowlist-skillhub.yaml（12 条 CC-BY-NC 法律类,display_name 必填）
legal-qa-extractor:
  display_name: "法律问答知识提取"
contract-copilot:
  display_name: "合同起草与审查助手"
# ...

# allowlist-lenovo.yaml（0 条,初始空,用户按需手动添加）
# contract-copilot:
#   display_name: "合同起草与审查助手"    # 与 skillhub 分区独立维护,可不同
```

**一对多语义**：同一 skill 可同时出现在多份文件里（如 contract-copilot 可同时在 `allowlist-skillhub.yaml` 和 `allowlist-lenovo.yaml` 登记，各自维护独立的 `display_name`）。无需为新平台改动旧 skill 的配置。

如需启用/禁用某 skill 在某平台，直接在对应平台的 allowlist 文件里添加/注释该 skill 行即可。

### 配置文件与隐私（example vs 本地，v1.7.1 三份独立列表）

配置文件分公开模板和本地真实两份，便于本技能被他人复用：

| 文件 | 角色 | 是否入库 | 含本地真实数据 |
|------|------|----------|----------------|
| `config/allowlist-clawhub.example.yaml` | ClawHub 白名单公开模板 | ✅ 入库 | 无 |
| `config/allowlist-skillhub.example.yaml` | SkillHub 白名单公开模板 | ✅ 入库 | 无 |
| `config/allowlist-lenovo.example.yaml` | 联想白名单公开模板 | ✅ 入库 | 无 |
| `config/sync-records.example.yaml` | 同步记录公开模板 | ✅ 入库 | 用占位符 |
| `config/allowlist-clawhub.yaml` | ClawHub 本地真实白名单 | ❌ gitignore 排除 | — |
| `config/allowlist-skillhub.yaml` | SkillHub 本地真实白名单 | ❌ gitignore 排除 | — |
| `config/allowlist-lenovo.yaml` | 联想本地真实白名单 | ❌ gitignore 排除 | — |
| `config/sync-records.yaml` | 本地真实同步记录 | ❌ gitignore 排除 | 填入实际 publish_id 等 |

> **首次使用**：复制对应 `.example.yaml` 去掉 `.example` 后缀，填入你的实际数据。三份白名单文件**独立维护**——同一 skill 可在不同白名单中独立登记（一对多）。
> SkillHub / 联想的白名单文件中 `display_name` 必填（中文展示名，发布前由 `prepare-publish.sh` 注入临时副本 frontmatter；源 SKILL.md 不含此字段）。`sync-records.yaml` 记录 `publish_id` 等发布结果。
>
> 根目录 `.gitignore` 的 `**/config/*.yaml` + `!**/config/*.example.yaml` 规则**对所有平台（ClawHub / SkillHub / 联想）均生效**（共用 `prepare-publish.sh` 过滤逻辑）。实测发布 `skill-publish-sync` 自身时，三份真实 `allowlist-*.yaml` 与 `sync-records.yaml` 均不进入临时目录，只有 `.example.yaml` 模板会上传。
>
> 根目录 `.gitignore` 已通过 `**/config/*.yaml` + `!**/config/*.example.yaml` 规则，自动排除真实配置、保留模板。

### 文件过滤规则

发布时会自动应用 .gitignore 过滤规则，确保敏感文件和临时文件不会被上传。三个平台共用同一套过滤逻辑。

**双重过滤机制**：

1. **项目根目录 .gitignore** - 自动检测 Git 仓库根目录的 `.gitignore`
2. **技能内部 .gitignore** - 如果技能目录有自己的 `.gitignore`，会额外应用

**默认排除**（始终生效）：

- `.git/` - Git 目录
- `node_modules/` - Node.js 依赖
- `__pycache__/` - Python 缓存
- `.DS_Store` - macOS 系统文件

### 同步流程

每次同步前，会自动：

1. **创建临时目录** - 前缀随平台变化：`/tmp/clawhub-publish-<skill>` 或 `/tmp/skillhub-publish-<skill>`
2. **复制过滤后的文件** - 使用 rsync 遵循 .gitignore 规则复制文件
3. **发布到目标平台** - 从临时目录执行对应平台的发布命令
4. **清理临时目录** - 发布完成后自动清理

### 手动准备发布目录

如需手动检查将要发布的文件：

```bash
# ClawHub（默认）
bash skills/skill-publish-sync/scripts/prepare-publish.sh skills/trademark-assistant
ls -la /tmp/clawhub-publish-trademark-assistant/

# 腾讯 SkillHub
bash skills/skill-publish-sync/scripts/prepare-publish.sh --platform skillhub skills/trademark-assistant
ls -la /tmp/skillhub-publish-trademark-assistant/
```

## 安全最佳实践

### 发布前检查清单

- [ ] 确认 `.gitignore` 包含所有敏感文件模式
- [ ] 使用 `prepare-publish.sh` 检查将要发布的文件
- [ ] 不要在技能中包含 API keys、密码等
- [ ] 使用 `.env.example` 代替 `.env` 文件

### 常见敏感文件

- `.env` - 环境变量（使用 `.env.example` 作为模板）
- `config.yaml` - 配置文件（使用 `config.example.yaml` 作为模板）
- `*.db`, `*.sqlite` - 数据库文件
- `logs/` - 日志目录
- `downloads/`, `output/` - 输出目录

### 修复已发布的技能

如果发现已发布的技能包含敏感信息：

1. **立即更新** - 从技能目录中删除敏感文件
2. **更新 .gitignore** - 确保未来不会再次包含
3. **重新发布** - 用 `clawhub publish` / `skillhub publish` / `lenovoskill push` 更新对应平台
4. **联系平台支持** - 如果需要删除旧版本

**重要提醒**：

- **两个平台都是公开平台**：发布的技能任何人都可以访问
- **不要包含客户信息**：案例文件、沟通记录等应排除
- **不要包含凭证**：API keys、tokens 等应使用环境变量

## 常见问题

### 版本号未更新？

检查 CHANGELOG.md 格式：

```markdown
## [1.0.0] - 2026-03-21

### 新增
- 新功能描述
```

### 同步失败？

1. ClawHub：运行 `clawhub sync --dry-run` 检查配置
2. SkillHub：确认 `skillhub auth whoami` 登录正常、`config/allowlist-skillhub.yaml` 已为该 skill 配 `display_name`（slug 默认取 name；缺 display_name 时 prepare-publish.sh 会 fail-closed 报错）
3. 联想开放平台：确认 `lenovoskill whoami` 登录正常、`config/allowlist-lenovo.yaml` 已配 `display_name`（联想也由 prepare-publish.sh 注入 frontmatter）；Node.js ≥ 18 + `npx` 可用；临时目录已生成 `.skill-config.json`（init 生成或从源复制）
4. 确认 SKILL.md frontmatter 格式正确
5. 检查白名单：目标 skill 是否在对应平台的 `allowlist-${PLATFORM}.yaml` 中（v1.7.1 三份独立列表，每平台独立文件）

### ClawHub 提示许可证冲突？

CC-BY-NC 等 license 与 ClawHub MIT-0 冲突，无法发布到 ClawHub。这类 skill 改发 SkillHub（无许可证限制）。详见 [ClawHub Skill Format 官方文档](https://docs.openclaw.ai/clawhub/skill-format)。

## 输入/输出

### 输入

- 必需：本地开发的 skill 目录
- 可选：指定技能名称列表、目标平台（`clawhub` / `skillhub` / `lenovo`，每平台独立白名单文件 `allowlist-${PLATFORM}.yaml`；详见平台对比表与同步范围控制章节）、白名单配置

### 输出

- 同步结果报告（成功/失败列表，按平台分组）
- 错误信息（如有）

## 同步记录

每次同步后，会更新 `config/sync-records.yaml` 记录文件，便于溯源和增量同步。记录按平台分别存储。

### 记录结构

```yaml
records:
  <skill-name>:
    platforms:
      clawhub:    # ClawHub 平台记录
        <fields>
      skillhub:   # 腾讯 SkillHub 平台记录(含 namespace 字段)
        <fields>
      lenovo:     # 联想开放平台记录(无 namespace 字段)
        <fields>
```

### 记录字段

| 字段 | 说明 | 适用平台 |
|------|------|----------|
| `version` | 同步时的版本号 | 三者 |
| `last_sync` | 最后同步时间 (ISO 8601) | 三者 |
| `git_hash` | 同步时的 commit hash | 三者 |
| `status` | `synced` / `pending` / `failed` / `skipped` / `slug_conflict` / `deleted` | 三者 |
| `changelog_summary` | 变更摘要 | 三者 |
| `url` | 平台发布地址 | 三者 |
| `publish_id` | 平台内部 ID | 三者 |
| `namespace` | 命名空间（账号绑定，如 `cat-xierluo`；发布时不传，记录用于溯源安装命令） | **仅 SkillHub**（ClawHub / 联想无 namespace 概念） |

### 记录示例

```yaml
records:
  md2word:
    platforms:
      clawhub:
        version: "1.1.8"
        last_sync: "2026-03-24T16:42:00+08:00"
        git_hash: "fbb1db4"
        status: synced
        changelog_summary: "全书自动更新目录域"
        url: "https://clawhub.ai/skills/md2word"
        publish_id: "k97dtn63cty6ezwzj9g9ry85818axkdq"
      skillhub:
        namespace: "cat-xierluo"
        version: "1.1.8"
        last_sync: "2026-08-01T10:00:00+08:00"
        git_hash: "fbb1db4"
        status: synced
        changelog_summary: "首次发布 SkillHub"
        url: "https://skillhub.cn/cat-xierluo/md2word"
        publish_id: "sh_xxxxxxxxxxxxxxxx"
      lenovo:
        # 无 namespace:联想用 OAuth 双 token(Login Token + Biz Token)
        version: "1.1.8"
        last_sync: "2026-08-12T10:00:00+08:00"
        git_hash: "fbb1db4"
        status: synced
        changelog_summary: "首次发布联想开放平台"
        url: "https://open.lenovomm.com/skills/md2word"
        publish_id: "len_xxxxxxxxxxxxxxxx"
```

### 用途

1. **增量同步**：只同步 `status: pending` 或版本更新的 skill（按平台独立判断）
2. **溯源**：通过 `git_hash` 追溯发布时的代码状态
3. **快速访问**：通过 `url` 直接访问平台上的 skill 页面

