跳转至

name: skill-inventory-to-wiki description: 扫描服务器上所有 SKILL.md 文件(Hermes active + OpenClaw 遗留),解析 frontmatter 提取 name/description/category,去重后生成按分类组织的 wiki 页面并部署。 category: devops tags: [skill, inventory, wiki, audit, batch, mkdocs]


技能清单 Wiki 生成流程

将服务器上 Hermes Agent 和 OpenClaw 遗留的所有 skill 扫描、去重、翻译(可选)、按分类整理为 wiki 页面。

触发条件

  • 用户要求"统计所有 skill"/"查看服务器上有什么 skill"/"skill 清单"
  • 定期审计服务器技能库

执行流程

Step 1: 扫描 Hermes Active Skills

find /root/.hermes/profiles/friend/skills -name "SKILL.md" | sort | while IFS= read -r f; do
    rel="${f#/root/.hermes/profiles/friend/skills/}"
    cat="$(echo "$rel" | cut -d/ -f1)"
    name="$(basename "$(dirname "$f")")"
    desc="$(sed -n '/^description: /{s/^description: //p;q}' "$f")"
    [ -z "$desc" ] && desc="(无描述)"
    desc="$(echo "$desc" | tr -d '"' | head -c 200)"
    echo "HERMES||$name||$cat||$desc"
done > /tmp/hermes_data.txt

Step 2: 扫描 OpenClaw 遗留 Skills(去重)

# 获取 Hermes 已有 name 列表
awk -F'||' '{print $2}' /tmp/hermes_data.txt | sort > /tmp/hermes_names.txt
h
# 获取 Hermes 已有 name 列表
awk -F'||' '{print $2}' /tmp/hermes_data.txt | sort > /tmp/hermes_names.txt# 扫描 OpenClaw 并排除重复
find /root/data/disk/.hermes/skills -name "SKILL.md" | sort | while IFS= read -r f; do
    rel="${f#/root/data/disk/.hermes/skills/}"
    cat="$(echo "$rel" | cut -d/ -f1)"
    name="$(basename "$(dirname "$f")")"
    if grep -qxF "$name" /tmp/hermes_names.txt 2>/dev/null; then
        continue
    fi
    desc="$(sed -n '/^description: /{s/^description: //p;q}' "$f")"
    [ -z "$desc" ] && desc="(无描述)"
    desc="$(echo "$desc" | tr -d '"' | head -c 200)"
    echo "OPENCLAW||$name||$cat||$desc"
done > /tmp/openclaw_data.txt

Step 3: 按分类组织

from collections import defaultdict

hermes_by_cat = defaultdict(list)
for name, cat, desc in hermes_items:
    hermes_by_cat[cat].append((name, desc))

openclaw_by_cat = defaultdict(list)
for name, cat, desc in openclaw_unique:
    openclaw_by_cat[cat].append((name, desc))

Step 4: 构建 Wiki Markdown

模板格式:

## {分类名}

| 名称 | 描述 |
|------|------|
| `{name}` | {description} |

Step 5: 翻译描述(可选)

建立 name→中文描述 的映射:

# 从翻译文件构建映射
with open("/root/skills_translations.txt") as f:
    for line in f:
        if '||' in line:
            parts = line.split('||', 1)
            orig = parts[0].strip()
            trans = parts[1].strip()
            if '. ' in orig[:5]:
                orig = orig.split('. ', 1)[1]
            trans_map[orig] = trans
 '. ' in orig[:5]:
                orig = orig.split('. ', 1)[1]
            trans_map[orig] = trans# 匹配 hermes_data 中的 description
for name, desc in hermes_items:
    if desc in trans_map:
        cn_desc = trans_map[desc]
    # 截断匹配
    elif desc.replace('"', "'").startswith(orig.replace('"', "'")[:60]):
        cn_desc = trans
    else:
        cn_desc = desc  # 保持原样(中文或无法匹配)

Step 6: 写入 wiki

mkdir -p /root/wiki-docs/docs/openclaw/
# 用 Python 脚本生成完整的 wiki markdown,包括 frontmatter 和所有分类

Step 7: 更新导航和部署

# 在 mkdocs.yml 中添加导航入口
# 构建 + 部署
cd /root/wiki-docs && mkdocs build
rsync -a --delete /root/wiki-docs/site/ /var/www/wiki/

关键陷阱

1. 非 UTF-8 字符导致终端命令失败

某些 SKILL.md 包含非 UTF-8 编码字符,直接用 catgrep 可能被当作二进制文件。解决方案:

# 用 python3 读取,errors='replace' 跳过坏字节
python3 -c "
with open(file, 'rb') as f:
    text = f.read().decode('utf-8', errors='replace')
"

或者在 awk 中先过滤不可打印字符。

2. description 截断问题

frontmatter 中的 description 可能很长(含触发条件等),被 head -c 200 截断后与翻译文件中的完整版本不匹配。

解决方案:使用 name 作为 key 进行映射替换(name 唯一且简短),而不是用 description 原文匹配。

3. 管道符 | 破坏表格

markdown 表格中如果 description 含有 | 字符会破坏表格结构。必须转义:

safe_desc = desc.replace('|', '\\|')

4. 描述中的双引号

YAML frontmatter 中的 description 可能带有未转义的双引号,在构建字符串时引发错误。先清理:

desc = desc.replace('"', "'")

5. 文件缓存导致 read_file 返回旧内容

read_file 工具对大文件有缓存机制,读取后内容不变。不要依赖 read_file 的缓存结果,用 terminal + cat 确保获取最新内容。

统计

来源 典型数量
Hermes Active Skills ~100
OpenClaw 遗留(去重) ~170
总计 ~270

|------|---------| | Hermes Active Skills | ~100 | | OpenClaw 遗留(去重) | ~170 | | 总计 | ~270 |## 输出文件

  • /root/wiki-docs/docs/openclaw/skill-inventory.md — 完整的技能清单 wiki 页面