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
模板格式:
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
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 编码字符,直接用 cat 或 grep 可能被当作二进制文件。解决方案:
# 用 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 含有 | 字符会破坏表格结构。必须转义:
4. 描述中的双引号
YAML frontmatter 中的 description 可能带有未转义的双引号,在构建字符串时引发错误。先清理:
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 页面