hermes-agent-skill-authoring

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Authoring Hermes-Agent Skills (in-repo)

编写 Hermes-Agent Skill(仓库内)

Overview

概览

There are two places a SKILL.md can live:
  1. User-local, repo-backed: authored skills belong in a git repo — on this machine
    ~/Developer/kernel
    (remote
    ponti-studios/kernel
    ) under
    skills/<name>/
    , the same library described in 3. Any older
    ~/Developer/skills
    checkout is obsolete; do not write there.
    ~/.agents/skills/<name>
    is the install store every agent reads, and
    ~/.hermes/skills/<name>
    holds the loader symlink the installer creates. Whether an entry there is a symlink to its source (an edit is live) or a copy (an edit is invisible until reinstall) depends on how it was installed —
    ls -l
    the entry before you assume an edit landed. A skill whose source of truth is another project repo stays a symlink there and is
    .gitignore
    d — the validator reports it as external and skips it, because CI has no checkout of the other project. When the user names a skills directory, use that one. Layout, validator design and cross-framework portability:
    references/skill-library-repo.md
    .
  2. In-repo (this skill is about this case):
    skills/<category>/<name>/SKILL.md
    or
    optional-skills/<category>/<name>/SKILL.md
    inside the hermes-agent repo — committed, shipped with the package. Use
    write_file
    +
    git add
    .
    skill_manage(action='create')
    does NOT target this tree.
  3. Kernel library:
    ~/Developer/kernel
    (remote
    ponti-studios/kernel
    ), under
    skills/<name>/
    , catalogued in
    skills.sh.json
    . Validated by
    python3 scripts/validate_skills.py
    (catalog parity, links, name/directory agreement),
    muse skills validate <path>
    (format), and
    python3 -m unittest discover -s tests
    . Install and update with
    npx skills
    :
    npx skills add ponti-studios/kernel --skill <name> -g --yes
    writes
    ~/.agents/skills/<name>
    and creates the
    ~/.hermes/skills/<name>
    symlink itself,
    npx skills update -g --yes
    refreshes, and
    npx skills list -g
    prints each entry's
    Source:
    .
    muse skills validate
    owns format only —
    muse skills install
    copies to
    ~/.config/muse/skills/<name>
    and wires no loader, so it is not the installer. Read the repo's own README/AGENTS install section before choosing an installer. Mechanics and the wiring:
    references/skill-library-repo.md
    .
In-repo skills must meet the repo's hardline authoring standards (see AGENTS.md, "Skill authoring standards (HARDLINE)" — that section is the source of truth; this skill is the operational walkthrough). Reviewers reject PRs that violate them, so meeting them up front is cheaper than a salvage pass later.
SKILL.md 有两个存放位置:
  1. 用户本地、由 repo 支撑: 编写的 Skill 应归属于一个 git repo——在这台机器上,是
    ~/Developer/kernel
    (远程仓库
    ponti-studios/kernel
    )下的
    skills/<name>/
    ,也就是第 3 项所述的同一个库。任何较旧的
    ~/Developer/skills
    checkout 都已废弃;不要在那里写入。
    ~/.agents/skills/<name>
    是每个 Agent 都会读取的安装存储位置,而
    ~/.hermes/skills/<name>
    保存安装程序创建的 loader symlink。该条目是指向其源文件的 symlink(编辑会立即生效)还是副本(重新安装前编辑不可见),取决于安装方式;在假定编辑已经落盘前,先对该条目运行
    ls -l
    。如果某个 Skill 的事实源是另一个项目 repo,它会在那里保持为 symlink,并被
    .gitignore
    忽略——validator 会将其报告为 external 并跳过,因为 CI 中没有另一个项目的 checkout。当用户指定某个 skills 目录时,使用该目录。目录布局、validator 设计和跨框架可移植性:
    references/skill-library-repo.md
    。
  2. Repo 内(本 Skill 讨论的就是这种情况): hermes-agent repo 内的
    skills/<category>/<name>/SKILL.md
    或
    optional-skills/<category>/<name>/SKILL.md
    ——应提交到版本控制,并随 package 一同交付。使用
    write_file
    +
    git add
    。
    skill_manage(action='create')
    不针对这棵树。
  3. Kernel 库:
    ~/Developer/kernel
    (远程
    ponti-studios/kernel
    ),位于
    skills/<name>/
    下,并收录在
    skills.sh.json
    中。通过
    python3 scripts/validate_skills.py
    验证(catalog 一致性、链接、名称/目录一致性)、通过
    muse skills validate <path>
    验证格式,并通过
    python3 -m unittest discover -s tests
    运行测试。使用
    npx skills
    安装和更新:
    npx skills add ponti-studios/kernel --skill <name> -g --yes
    会写入
    ~/.agents/skills/<name>
    ,并自行创建
    ~/.hermes/skills/<name>
    symlink;
    npx skills update -g --yes
    会刷新安装;
    npx skills list -g
    会打印每个条目的
    Source:
    。
    muse skills validate
    只负责格式——
    muse skills install
    会将文件复制到
    ~/.config/muse/skills/<name>
    ,且不会连接 loader,因此它不是安装程序。选择安装程序前,先阅读 repo 自身的 README/AGENTS 安装章节。机制与接线方式:
    references/skill-library-repo.md
    。
Repo 内 Skill 必须满足 repo 的 HARDLINE 编写标准(见 AGENTS.md 中的 “Skill authoring standards (HARDLINE)”——该章节是事实源;本 Skill 是操作流程指南)。Reviewer 会拒绝违反这些标准的 PR,因此提前满足要求比之后返工更省成本。

When to Use

何时使用

  • User asks you to add a skill "in this branch / repo / commit"
  • You're committing a reusable workflow that should ship with hermes-agent
  • You're editing an existing skill under
    skills/
    or
    optional-skills/
    (use
    patch
    for small edits,
    write_file
    for rewrites;
    skill_manage
    still works for patch on in-repo skills, but not for
    create
    )
  • Don't use for: personal skills — on this machine those live in
    ~/.agents/skills/
    with a symlink into
    ~/.hermes/skills/
    (see Overview), where
    skill_manage
    cannot edit them in place
  • 用户要求“在这个 branch / repo / commit 中添加 Skill”
  • 你要提交一个可复用 workflow,并且它应随 hermes-agent 一同交付
  • 你正在编辑
    skills/
    或
    optional-skills/
    下的现有 Skill(小改动使用
    patch
    ,重写使用
    write_file
    ;
    skill_manage
    仍可对 repo 内 Skill 执行 patch,但不能用于
    create
    )
  • 不适用于:个人 Skill——在这台机器上,个人 Skill 位于
    ~/.agents/skills/
    ,并通过 symlink 指向
    ~/.hermes/skills/
    (见概览);
    skill_manage
    无法原地编辑它们

Decide the Tier First: Bundled vs Optional

先决定层级:Bundled 与 Optional

  • Bundled (
    skills/<category>/
    )
    — daily-driver behavior, broadly useful across many user types, low footprint. Hard bar: you can say "a user will load this in 5+ sessions per month" with a straight face.
  • Optional (
    optional-skills/<category>/
    )
    — niche, vertical-specific (blockchain, gaming, finance, one app), recurring-job/task skills, or anything heavy. Installed via
    hermes skills install official/<category>/<skill>
    .
When in doubt, optional. Promoting later is easy; demoting is churn. "Would be useful to anyone who ever needs this" is an optional-tier argument, not a bundled one.
Pick the category by what the tool IS, not what it feels like (an AI-agent CLI goes in
autonomous-ai-agents/
even if it "feels productivity"). Confirm existing categories with
search_files(pattern='*', target='files', path='skills')
and don't invent new top-level categories casually.
No router / index / hub skills. A skill whose core content is a routing table pointing at sibling skills adds an indirection hop and duplicates the siblings' own
When to Use
triggers. If the skill would be empty without "load skill X instead" pointers, don't write it — the catalog and each sibling's triggers already do that job.
Split by output shape, not by pipeline stage. A skill that emits nine artifacts is several skills when each output has its own shape, validation and consumer: an agent loading it pays for eight prompts it will not use, and the trigger ("write a LinkedIn post from this script") is buried behind a
--stage
flag. Group stages that share a shape — long-form and short-form video are both spoken prose — and keep production artifacts whose fields are a contract with another program (shot lists, manifests, render specs) in their own skill, because those are read by a renderer rather than by a person. When splitting, slice prompts and schemas out of the original by export name and assert byte equality per constant before deleting the source; retyping a prompt is a silent product change nobody catches in a diff.
  • Bundled(
    skills/<category>/
    )
    ——日常主力行为,对许多用户类型都有广泛用途,且占用空间少。硬性标准:你必须能坦然地说“一个用户每月会加载它 5 次以上”。
  • Optional(
    optional-skills/<category>/
    )
    ——小众、垂直领域专用(区块链、游戏、金融、单一应用)、周期性 Job/任务 Skill,或任何较重的 Skill。通过
    hermes skills install official/<category>/<skill>
    安装。
拿不准时,选择 Optional。 后续升级为 Bundled 很容易,降级则会制造 churn。“任何可能需要它的人都会觉得有用”是支持 Optional 层级的理由,而不是支持 Bundled 的理由。
根据工具本质上是什么来选择 category,而不是根据它给人的感觉(即使一个 AI-agent CLI 让人感觉属于生产力工具,也应放入
autonomous-ai-agents/
)。使用
search_files(pattern='*', target='files', path='skills')
确认现有 category,不要随意创建新的顶层 category。
不允许使用 router / index / hub Skill。 如果一个 Skill 的核心内容只是指向同级 Skill 的路由表,它就会增加一层间接跳转,并重复同级 Skill 自身的
When to Use
触发条件。如果去掉“改为加载 Skill X”之类的指针后,这个 Skill 就所剩无几,那么不要编写它——catalog 和每个同级 Skill 的触发条件已经承担了这项工作。
按输出形态拆分,而不是按 pipeline 阶段拆分。 如果一个 Skill 会生成九种产物,那么当每种输出都有自己的形态、验证方式和消费方时,它应当拆成多个 Skill:Agent 加载它时,会为八个根本用不到的 prompt 付出成本,而真正的触发条件(“根据这个脚本撰写一篇 LinkedIn 帖子”)也会被埋在
--stage
参数之后。将形态相同的阶段归为一组——长视频和短视频都属于口语化 prose——而字段构成与其他程序之间契约的生产产物(shot list、manifest、render spec)应保留在独立的 Skill 中,因为这些文件由 renderer 而不是人来读取。拆分时,应按 export 名称从原文件中切出 prompts 和 schemas,并在删除源内容前针对每个常量断言字节完全相等;重新手动输入 prompt 是一种无人能从 diff 中发现的静默产品变更。

Required Frontmatter

必需的 Frontmatter

Validator source of truth:
tools/skill_manager_tool.py::_validate_frontmatter
. Validator hard requirements:
  • Starts with
    ---
    as the first bytes (no leading blank line).
  • Closes with
    \n---\n
    before the body.
  • Parses as a YAML mapping.
  • name
    field present.
  • description
    field present (validator ceiling 1024 chars — but see the repo hardline below, which is much stricter).
  • Non-empty body after the closing
    ---
    .
Repo-standard shape (all fields expected, even where the validator doesn't enforce them):
yaml
---
name: my-skill-name               # lowercase, hyphens, ≤64 chars (MAX_NAME_LENGTH)
description: Concise capability statement, under sixty chars.
version: 0.1.0                    # semver; new skills start at 0.1.0
author: Real Name (github-handle), Hermes Agent
license: MIT
platforms: [linux, macos, windows]   # audit, don't guess — see Platform Gating
metadata:
  hermes:
    tags: [Short, Descriptive, Tags]
    related_skills: [other-in-repo-skill]
---
Validator 的事实源:
tools/skill_manager_tool.py::_validate_frontmatter
。Validator 的硬性要求:
  • 前几个字节必须以
    ---
    开始(前面不能有空行)。
  • 在正文前必须以
    \n---\n
    结束 frontmatter。
  • 必须能解析为 YAML mapping。
  • 必须包含
    name
    字段。
  • 必须包含
    description
    字段(validator 上限为 1024 个字符——但 repo 下文的 hardline 标准严格得多)。
  • 结束
    ---
    之后必须有非空正文。
Repo 标准格式(即使 validator 没有强制检查,所有字段也应齐全):
yaml
---
name: my-skill-name               # lowercase, hyphens, ≤64 chars (MAX_NAME_LENGTH)
description: Concise capability statement, under sixty chars.
version: 0.1.0                    # semver; new skills start at 0.1.0
author: Real Name (github-handle), Hermes Agent
license: MIT
platforms: [linux, macos, windows]   # audit, don't guess — see Platform Gating
metadata:
  hermes:
    tags: [Short, Descriptive, Tags]
    related_skills: [other-in-repo-skill]
---

description
rules (HARDLINE — the validator's 1024 is NOT the standard)

description
规则(HARDLINE——validator 的 1024 不是标准)

  • ≤ 60 characters. One sentence. Ends with a period.
  • State the capability, not the implementation, and don't repeat the skill name.
  • No marketing words ("powerful", "comprehensive", "seamless", "advanced").
  • The system prompt skill index truncates at 57 chars + "..." — the trigger/capability must be self-contained in that window.
  • If the description contains a
    :
    , wrap it in double quotes or YAML parses it as a mapping and the docs generator crashes. Quotes don't count toward the 60.
Good:
Track named companies for material news with cited digests.
Bad:
Use when a user asks to monitor named competitors or companies for product launches, pricing changes, funding, ...
(240 chars — rejected in review)
  • 不超过 60 个字符。 一句话。以句号结尾。
  • 说明能力,而不是实现方式,也不要重复 Skill 名称。
  • 不使用营销词(“powerful”、“comprehensive”、“seamless”、“advanced”)。
  • System prompt 的 Skill index 会在 57 个字符加
    ...
    处截断——触发条件/能力说明必须在这个窗口内自洽完整。
  • 如果
    description
    中包含
    :
    ,请用双引号包裹,否则 YAML 会将其解析为 mapping,并导致 docs generator 崩溃。引号不计入 60 个字符。
好:
Track named companies for material news with cited digests.
坏:
Use when a user asks to monitor named competitors or companies for product launches, pricing changes, funding, ...
(240 个字符——review 会拒绝)

author
rules

author
规则

  • Credit the human first, then "Hermes Agent" as secondary collaborator:
    Ben Barclay (benbarclay), Hermes Agent
    .
  • Never
    author: Hermes Agent
    alone for contributed skills — credit the human, not the tool, even (especially) when an agent drafted the text.
  • Maintainer-authored skills:
    Teknium (teknium1), Hermes Agent
    .
  • 先署名贡献者本人,再将 “Hermes Agent” 作为次要协作者:
    Ben Barclay (benbarclay), Hermes Agent
    。
  • 对贡献的 Skill,绝不能只写
    author: Hermes Agent
    ——应署名贡献者,而不是工具,即使(尤其是)文本由 Agent 起草。
  • Maintainer 编写的 Skill:
    Teknium (teknium1), Hermes Agent
    。

related_skills
rules

related_skills
规则

  • Every entry must resolve to an existing in-repo skill in the same tree state as your PR. Do not reference skills that were only planned, live in another PR, or exist only in
    ~/.hermes/skills/
    .
  • Verify each entry:
    search_files(pattern='<name>', target='files', path='skills')
    (and
    optional-skills/
    ).
  • 每个条目都必须解析到 PR 所在同一 tree state 中的现有 repo 内 Skill。不要引用仅处于计划阶段、存在于另一个 PR,或仅存在于
    ~/.hermes/skills/
    中的 Skill。
  • 逐项验证:
    search_files(pattern='<name>', target='files', path='skills')
    (以及
    optional-skills/
    )。

Platform Gating: audit, don't trust

Platform Gating:进行审计,不要臆测

platforms:
gates loading by host OS. Set it from what the skill's prose and scripts actually invoke:
Skill uses only…
platforms:
Hermes tools + stdlib Python + cross-platform CLIs
[linux, macos, windows]
bash pipelines,
grep
/
awk
/
sed
chains, heredocs
[linux, macos]
osascript
,
defaults
,
pmset
[macos]
apt
/
systemctl
/
/proc
[linux]
<!-- no-tmp: ok — names the anti-pattern skill authors must avoid -->
POSIX-only signals to search for in
scripts/
:
fcntl
,
termios
,
pty
,
os.fork
,
os.killpg
,
signal.SIGKILL
,
os.kill(pid, 0)
liveness checks, hardcoded
/tmp
/proc
/etc
. Default posture: fix cross-platform first (
tempfile.gettempdir()
,
pathlib.Path
,
psutil.pid_exists
); gate narrower only when the dependency is genuinely platform-bound, and say why in
## Pitfalls
.
platforms:
根据宿主 OS 控制 Skill 是否可加载。应根据 Skill 正文和脚本实际调用的内容进行设置:
Skill 仅使用……
platforms:
Hermes 工具 + stdlib Python + 跨平台 CLI
[linux, macos, windows]
bash pipeline、
grep
/
awk
/
sed
链、heredoc
[linux, macos]
osascript
、
defaults
、
pmset
[macos]
apt
/
systemctl
/
/proc
[linux]
<!-- no-tmp: ok — names the anti-pattern skill authors must avoid -->
在
scripts/
中搜索 POSIX-only 信号:
fcntl
、
termios
、
pty
、
os.fork
、
os.killpg
、
signal.SIGKILL
、
os.kill(pid, 0)
存活检查,以及硬编码的
/tmp
、
/proc
、
/etc
。默认策略是优先修复跨平台兼容性(
tempfile.gettempdir()
、
pathlib.Path
、
psutil.pid_exists
);只有在依赖确实受平台约束时,才使用更窄的门控,并在
## Pitfalls
中说明原因。

Size Limits

大小限制

  • Full SKILL.md: ≤ 100,000 chars enforced (
    MAX_SKILL_CONTENT_CHARS
    ), but target ~100 lines for a simple skill, ~200 for a complex one. Peer skills sit at 8-14k chars.
  • Bulky or branch-specific material goes in
    references/*.md
    ,
    templates/
    , or
    scripts/
    — pointed to from SKILL.md, not inlined.
  • Don't expect the model to inline-write parsers or non-trivial logic every call — ship a helper script in
    scripts/
    and reference it by path.
  • 完整 SKILL.md 不得超过 100,000 个字符(由
    MAX_SKILL_CONTENT_CHARS
    强制执行),但目标应是:简单 Skill 约 100 行,复杂 Skill 约 200 行。同类 Skill 通常为 8-14k 字符。
  • 冗长或与特定分支有关的内容应放入
    references/*.md
    、
    templates/
    或
    scripts/
    ,并从 SKILL.md 指向它们,而不是内联。
  • 不要指望模型在每次调用时都临时编写 parser 或非平凡逻辑——应在
    scripts/
    中交付 helper script,并通过路径引用。

Body Structure (modern section order)

正文结构(现代章节顺序)

undefined
undefined

<Skill> Skill

<Skill> Skill

2-3 sentence intro: what it does, what it doesn't do, dependency stance.
2-3 sentence intro: what it does, what it doesn't do, dependency stance.

When to Use — bulleted triggers (+ "Don't use for:" counter-triggers)

When to Use — bulleted triggers (+ "Don't use for:" counter-triggers)

Prerequisites — exact env vars, installs, API key sourcing

Prerequisites — exact env vars, installs, API key sourcing

How to Run — canonical invocation through the
terminal
tool

How to Run — canonical invocation through the
terminal
tool

Quick Reference — flat command list, no narration

Quick Reference — flat command list, no narration

Procedure — numbered steps, each with a checkable completion criterion

Procedure — numbered steps, each with a checkable completion criterion

Pitfalls — known limits, things that look broken but aren't

Pitfalls — known limits, things that look broken but aren't

Verification — how to prove the skill worked

Verification — how to prove the skill worked


Not every section applies to every skill (a pure-procedure task skill may have no Quick Reference), but When to Use + actionable body + Pitfalls + Verification are the minimum. Cut marketing intros, "Setup Check" no-ops, and re-explanations of env vars already in Prerequisites.

并非每个 Skill 都需要所有章节(纯流程型任务 Skill 可以没有 Quick Reference),但 When to Use + 可执行正文 + Pitfalls + Verification 是最低要求。删掉营销式简介、没有实际作用的 “Setup Check”,以及对 Prerequisites 中环境变量的重复说明。

Reference Hermes tools, not raw shell

引用 Hermes 工具,而不是原始 shell

When the skill needs a capability, name the proper Hermes tool in backticks:
terminal
,
read_file
,
write_file
,
patch
,
search_files
,
web_search
,
web_extract
,
browser_navigate
,
vision_analyze
,
delegate_task
,
cronjob
. Do NOT name shell utilities the agent already has wrapped (
grep
→
search_files
,
cat
→
read_file
,
sed
/
awk
→
patch
,
find
/
ls
→
search_files target='files'
). A CLI-wrapper skill should frame invocations as
terminal(command="{tool} ...", timeout=...)
— bare shell prose ("run
foo --version
") is a review-blocking non-conformance. If the skill depends on an MCP server, name it and document setup in Prerequisites.
当 Skill 需要某项能力时,应在反引号中写出正确的 Hermes 工具:
terminal
、
read_file
、
write_file
、
patch
、
search_files
、
web_search
、
web_extract
、
browser_navigate
、
vision_analyze
、
delegate_task
、
cronjob
。不要写 Agent 已经封装的 shell 工具(
grep
→
search_files
,
cat
→
read_file
,
sed
/
awk
→
patch
,
find
/
ls
→
search_files target='files'
)。CLI-wrapper Skill 应将调用表述为
terminal(command="{tool} ...", timeout=...)
——裸 shell 叙述(“运行
foo --version
”)属于 review 阻断级不合规。如果 Skill 依赖 MCP server,应写出其名称,并在 Prerequisites 中记录安装方式。

Never use machine-local paths

绝不要使用机器本地路径

Write repo-relative paths (
skills/...
,
tools/skill_manager_tool.py
). A
/home/<you>/...
path baked into a committed skill breaks for every other user and is an instant review flag.
应编写 repo 相对路径(
skills/...
、
tools/skill_manager_tool.py
)。将
/home/<you>/...
之类的路径写进已提交的 Skill,会使其在其他所有用户机器上失效,并立即触发 review 标记。

Scripts, wrappers, and what
linked_files
actually indexes

Scripts、wrapper,以及
linked_files
实际索引的内容

tools/skills_tool.py::_LINKED_FILE_SPECS
decides which support files show up in
skill_view
's
linked_files
. It is a fixed set of globs, so a helper that doesn't match is invisible to future sessions even though it runs fine when invoked by path:
DirectoryGlobs indexedRecursive
references/
*.md
no
templates/
*.md *.py *.yaml *.yml *.json *.tex *.sh
yes
assets/
*
(files only)
yes
scripts/
*.py *.sh *.bash *.js *.ts *.rb
no
.mjs
,
.cjs
,
.tsx
,
.zsh
and
.jsx
are not indexed. ESM (
.mjs
, top-level
await
,
import
) is the natural choice for a Node helper, so ship the implementation and wrap it:
scripts/
  my-command.sh        # indexed — the public entry point, named in SKILL.md
  lib/my-command.mjs   # the implementation, private
bash
#!/usr/bin/env bash
set -euo pipefail
exec node "$(dirname "${BASH_SOURCE[0]}")/lib/my-command.mjs" "$@"
Document the wrapper as the command, not the
.mjs
, so the next session finds the capability in the index and runs the right thing. Wrap standalone commands (a CLI); don't wrap a module that other scripts import.
Templates follow the same table: a config template named
vitest.config.ts
, any
*.toml
or
*.jsonc
is invisible in
linked_files
even though SKILL.md can link to it by path. Give a template an indexed extension (
.yml
,
.json
,
.sh
) when a future session must discover it; otherwise accept that only the path in SKILL.md leads there, and say so.
Bundling scripts that live elsewhere. Symlinking (
ln -sfn
) an external script into the skill dir keeps one canonical file, and it is indexed and runnable through the skill path — but the skill stops being portable if copied to another machine. Real copies are portable but drift. Choose deliberately, and record the choice in SKILL.md so the tradeoff is visible.
Pointing a skill at external code. When a skill wraps another project's source that may later be ported into the skill, put the location in exactly one
lib/resolve.mjs
: env-var override first, then a default path, raising a message that names both when neither resolves. Every script imports from it, and no other file in the skill names a repo path — the port then becomes a one-line change instead of a rewrite.
Output directories are configuration too. The same rule governs everywhere a skill writes, not just the code it reads: a render inbox, a per-call usage log dir, a drafts dir, a publishing root are personal-data locations. Each gets its own env var with the old path as its default, resolved through the same
resolve.mjs
, and none of them is spelled as a literal
~/…
inside a script. A skill that hardcodes six of these runs on exactly one machine, and the failure is invisible because the defaults happen to exist there. Then list them in the skill's own
SKILL.md
as a
## Configuration
table (
| Variable | Default | Used for |
) — a future session needs to know what it can override without reading the source.
Secrets follow the same order as paths: environment first, then one user-level config file, and nothing else — no repo
.env
, no
~/.localrc
, no per-tool search path. Every extra location is a question the next debugger has to answer, and the answer changes per machine.
One parser, imported not copied. Put the parse rule in the project's own config package and have each skill
await import()
it from the build (
packages/config/dist/index.js
), the same way it imports the rest of the codebase. A copied parser starts identical and diverges on the first fix, and the copies live in skills — the last place anyone looks. Two consequences: an unbuilt package must fail loudly and name the build command, and the config module's own path cannot come from the file it parses, so resolve that one entry from an env var plus a single default and read everything else from the file.
Keep the file flat (
key: value
,
#
comments, optional quotes,
~
expanded), map each key to its env var, and ignore unknown keys so a newer file cannot break an older tool. Precedence is flag → environment variable → file → built-in default, and the skill's
## Configuration
section carries the key/env/default table.
Prove precedence, don't assume it. Run the same dry run three ways — file only, env var overriding the file, and no config at all — and confirm the resolved output moves each time. A single run with defaults proves nothing about portability: the defaults are exactly what hides a broken lookup on the machine you are testing on.
Port behaviour-identical first, then change. When you do move that code into the skill, port it so it reproduces the original's output exactly and prove that against the original's own golden artifacts — same inputs, byte-identical results, checked against both the recorded expected files and a live run of the upstream build. Only then layer your change on top: with parity established, every later difference is a deliberate decision rather than a translation bug, and you can say which of the two a diff is. Keep the harness testing both directions from then on — parity in the original mode (the port must still equal upstream) and the new mode's invariants (deterministic output, and whatever pattern the rewrite exists to remove actually absent). When the change is meant to alter output, re-record the golden files deliberately and say so; never edit an expected file to make a check pass. Two guards here caught real bugs during a port that a parity check alone would have missed: an invariant assertion on the new mode's output, and a refusal to let the new mode silently fall back to the old code path.
Ship a free preflight. Any skill that spends money, writes into a user's data, or depends on config that can be stale gets a
scripts/check-*.sh
that resolves every dependency, prints what it actually resolved (which binary, which config path, which model, which voice), spends nothing, and exits non-zero only on a real failure. Warn — don't fail — on optional gaps, or the check cries wolf and gets ignored. "Which ffmpeg / which vault did this run actually use" is the question a later session cannot answer from the code.
Make the paid step dry-run by default, requiring an explicit go-ahead flag (
--yes
) to spend: print per-item and total cost from the provider's own published rate (not an estimate you derived), and offer a single-item path (
--only <id>
) so iterating on one prompt costs one item rather than a batch. An expensive stage behind a default-on script is a user-facing hazard, not a convenience.
Prove the join, not the stages. In a chain — script → shot list → narration → clips → cut — every stage can pass its own check while the handoff between two of them is broken: one stage writes a file per item, the next demands a single file, and nothing reconciles them. Run the chain once end to end on the smallest real set that still exercises the join (two items, not twenty), keep the per-item outputs so one item can be re-recorded, and reconcile both sides explicitly — measured footage against measured narration, rows against declared counts. Then price the run: providers often bill a minimum unit, so a four-second request can be charged as five seconds and short items silently carry a penalty. A chain you have only exercised stage-by-stage is a hypothesis, not a pipeline.
tools/skills_tool.py::_LINKED_FILE_SPECS
决定哪些支持文件会出现在
skill_view
的
linked_files
中。它是一组固定的 glob,因此不匹配其中的 helper 即使能按路径正常运行,也不会出现在后续 session 中:
目录被索引的 Glob是否递归
references/
*.md
否
templates/
*.md *.py *.yaml *.yml *.json *.tex *.sh
是
assets/
*
(仅文件)
是
scripts/
*.py *.sh *.bash *.js *.ts *.rb
否
.mjs
、
.cjs
、
.tsx
、
.zsh
和
.jsx
不会被索引。ESM(
.mjs
、顶层
await
、
import
)是 Node helper 的自然选择,因此应交付实现并进行包装:
bash
#!/usr/bin/env bash
set -euo pipefail
exec node "$(dirname "${BASH_SOURCE[0]}")/lib/my-command.mjs" "$@"
应将 wrapper 作为命令写入文档,而不是
.mjs
,这样后续 session 才能在 index 中找到该能力并运行正确的命令。包装独立命令(CLI);不要包装供其他 scripts 导入的 module。
模板遵循同一张表:名为
vitest.config.ts
的 config template,以及任何
*.toml
或
*.jsonc
,在
linked_files
中都不可见,即使 SKILL.md 可以按路径链接它。如果未来 session 必须发现某个 template,请为其使用被索引的扩展名(
.yml
、
.json
、
.sh
);否则只能接受只有 SKILL.md 中的路径能够指向它,并明确说明这一点。
打包位于其他位置的 scripts。 将外部 script symlink(
ln -sfn
)到 Skill 目录,可以保留唯一的 canonical file,并且它会通过 Skill 路径被索引和运行;但复制到另一台机器后,Skill 将不再可移植。真实副本可移植,却会发生 drift。请有意识地选择,并记录这一取舍,以便在 SKILL.md 中体现。
让 Skill 指向外部代码。 当 Skill 包装另一个项目的源代码,而该源代码以后可能被移植进 Skill 时,应将位置只放在一个
lib/resolve.mjs
中:先检查环境变量覆盖值,再使用默认路径;两者都无法解析时,抛出的消息应同时列出两个候选位置。每个 script 都应从它导入,Skill 中不得有其他文件写出 repo 路径——这样移植只需修改一行,而不是重写整个实现。
输出目录同样属于配置。 同一规则适用于 Skill 写入的所有位置,而不只是它读取的代码:render inbox、每次调用的 usage log 目录、草稿目录、发布根目录都是个人数据位置。每个位置都应拥有自己的环境变量,以旧路径作为默认值,通过同一个
resolve.mjs
解析,并且脚本内部不得出现字面量
~/…
。如果一个 Skill 硬编码了六个这样的路径,它就只能在一台机器上运行;而失败并不明显,因为默认值恰好在那台机器上存在。随后,应在 Skill 自己的 SKILL.md 中以
## Configuration
表格(
| Variable | Default | Used for |
)列出这些变量——未来 session 必须无需阅读源码就能知道可以覆盖哪些配置。
Secrets 遵循与路径相同的顺序:首先是环境变量,然后是一个用户级 config file,除此之外没有其他位置——不要使用 repo
.env
、
~/.localrc
或 per-tool 搜索路径。每增加一个位置,下一位调试者就多一个问题,而答案还会因机器而异。
只保留一个 parser,并导入它,不要复制它。 将解析规则放在项目自身的 config package 中,让每个 Skill 从构建产物(
packages/config/dist/index.js
)执行
await import()
,与代码库其他部分的导入方式保持一致。复制的 parser 起初完全相同,但第一次修复后就会分叉,而副本位于 skills 中——这是人们最后才会检查的地方。这会带来两个结果:未构建的 package 必须明确失败并指出构建命令;config module 自身的路径不能来自它所解析的文件,因此应通过一个环境变量加单一默认值解析该条目,其余内容全部从文件读取。
保持文件结构扁平(
key: value
、
#
注释、可选引号、展开后的
~
),将每个 key 映射到对应环境变量,并忽略未知 key,避免较新的文件破坏较旧的工具。优先级为 flag → 环境变量 → 文件 → 内置默认值,Skill 的
## Configuration
章节应包含 key/env/default 对照表。
验证优先级,不要想当然。 用三种方式运行同一次 dry run——仅文件、环境变量覆盖文件、没有配置——并确认每次解析结果都随之变化。只使用默认值运行一次不能证明任何可移植性:默认值恰恰会掩盖测试机器上的查找错误。
先移植行为一致的版本,再进行修改。 将代码移入 Skill 时,应使其精确复现原实现输出,并用原实现自己的 golden artifacts 证明一致性——相同输入、字节完全一致的结果,同时对照已记录的 expected files 和上游构建的实时运行结果。只有在建立一致性后,才能叠加修改;一旦一致性成立,后续每个差异都是有意决策,而不是翻译错误,并且可以指出哪个差异来自哪一侧。从那时起,应让测试 harness 同时覆盖两个方向:原模式下的 parity(移植后仍与上游一致),以及新模式下的不变量(输出确定性,且重写要消除的特定模式确实不再存在)。如果某项修改本来就是要改变输出,应有意重新录制 golden files 并明确说明;绝不能为了让检查通过而修改 expected file。这里有两项保护措施曾捕获仅靠 parity 检查会漏掉的真实 bug:对新模式输出执行不变量断言,以及拒绝让新模式静默回退到旧代码路径。
交付免费的 preflight。 任何会花钱、会写入用户数据,或依赖可能过期的配置的 Skill,都应提供
scripts/check-*.sh
:解析每项依赖、打印实际解析结果(使用哪个 binary、哪个 config path、哪个 model、哪个 voice)、不产生费用,并且只在发生真实故障时以非零状态退出。可选依赖缺失时应发出警告而不是失败,否则这个检查会因“狼来了”而被忽略。“这次运行究竟用了哪个 ffmpeg / 哪个 vault?”是后续 session 无法从代码中回答的问题。
默认将付费步骤设为 dry-run,必须显式提供 go-ahead flag(
--yes
)才能消费资源:按照 provider 自己公布的费率(而不是自行推导的估算值)打印单项目和总成本,并提供单项目路径(
--only <id>
),这样迭代一个 prompt 时只产生一个项目的成本,而不是整批成本。默认启用的脚本背后若隐藏着昂贵阶段,对用户而言是隐患,而不是便利。
验证连接点,而不只是各个阶段。 在一条链路中——script → shot list → narration → clips → cut——每个阶段都可能通过各自的检查,但两个阶段之间的交接仍然已经损坏:一个阶段为每个项目写一个文件,下一个阶段却要求单个文件,且没有任何机制负责协调。使用仍能覆盖连接点的最小真实数据集端到端运行整条链路(两个项目,而不是二十个),保留每个项目的输出,以便单独重新录制某个项目,并显式协调链路两端——实际素材与实际旁白的对应关系、条目数量与声明数量的对应关系。然后为运行定价:provider 通常按最低计费单位收费,因此四秒的请求可能按五秒计费,较短的项目会悄悄承担额外费用。只逐阶段测试过的链路仍然只是一个假设,而不是一条可靠的 pipeline。

Writing Quality Principles

写作质量原则

A skill exists to make the agent's process more predictable — the agent reliably follows the same useful discipline.
  1. Optimize for process predictability. If a line does not change behavior, cut it.
  2. Choose the right context load. The description is paid for every turn; details go in the body or linked references.
  3. End steps with completion criteria. Checkable and, when it matters, exhaustive: "every modified file accounted for" beats "summarize changes."
  4. Co-locate rules with the concept they govern.
  5. Use strong leading words ("tight loop," "root cause," "regression test") over long repeated explanations.
  6. Prune duplication and no-ops. "Be careful" and "use best practices" don't change model behavior — replace with a checkable criterion or delete.
Skill 的作用是让 Agent 的流程更可预测——让 Agent 始终遵循同一套有用的纪律。
  1. 优化流程可预测性。 如果某一行不会改变行为,就删掉它。
  2. 选择正确的 context load。
    description
    每轮对话都会产生成本;细节应放入正文或关联引用。
  3. 以完成标准结束步骤。 标准应当可核验,必要时还应穷尽:“所有修改过的文件均已交代清楚”优于“总结一下变更”。
  4. 将规则与其所约束的概念放在一起。
  5. 使用有力的引导词(“tight loop”、“root cause”、“regression test”),而不是反复进行长篇解释。
  6. 删减重复内容和无效操作。 “谨慎处理”和“使用最佳实践”不会改变模型行为——应将其替换为可核验标准,或者直接删除。

Tests and Docs (required for repo skills)

Repo Skill 必需的测试与文档

  1. Tests live at
    tests/skills/test_<skill>_skill.py
    — stdlib + pytest +
    unittest.mock
    only, no live network. Run via
    scripts/run_tests.sh tests/skills/test_<skill>_skill.py -q
    . (The generic
    tests/tools/test_skill_manager_tool.py
    passing proves nothing about YOUR skill.)
  2. Docs regen: run
    python website/scripts/generate-skill-docs.py
    , then apply scope discipline — the generator rewrites EVERY auto-gen page.
    git checkout --
    everything that isn't yours; the final diff must show only your SKILL.md, your one per-skill docs page, a one-line catalog row, and a one-line
    website/sidebars.ts
    insertion (verify with
    search_files(pattern='<your-slug>', path='website/sidebars.ts')
    — exactly one hit, or the page is an orphan).
  3. .env.example
    (only if the skill needs new env vars): one clearly delimited commented block; touch nothing else in the file.
  1. 测试位于
    tests/skills/test_<skill>_skill.py
    ——仅使用 stdlib + pytest +
    unittest.mock
    ,不访问实时网络。通过
    scripts/run_tests.sh tests/skills/test_<skill>_skill.py -q
    运行。(通用的
    tests/tools/test_skill_manager_tool.py
    通过,并不能证明你的 Skill 是正确的。)
  2. 重新生成文档: 运行
    python website/scripts/generate-skill-docs.py
    ,然后执行范围控制——生成器会重写每一个自动生成页面。对不属于你的内容执行
    git checkout --
    ;最终 diff 只能显示你的 SKILL.md、对应的一个 Skill 文档页面、一行 catalog 条目,以及
    website/sidebars.ts
    中的一行插入项(使用
    search_files(pattern='<your-slug>', path='website/sidebars.ts')
    验证——必须恰好命中一次,否则页面会成为孤立页面)。
  3. .env.example
    (仅当 Skill 需要新增环境变量时):添加一个边界清晰、带注释的代码块;不要修改该文件中的其他任何内容。

Workflow

工作流

  1. Survey peers in the target category with
    search_files(target='files')
    and read 2-3 peer SKILL.md files to match tone and structure. Prefer extending an existing skill over creating a narrow sibling.
  2. Decide tier and category (see above). When in doubt, optional — and ask before pushing rather than defaulting.
  3. Draft with
    write_file
    to
    skills/<category>/<name>/SKILL.md
    (or
    optional-skills/...
    ).
  4. Validate locally:
    python
    import re, pathlib
    from ruamel.yaml import YAML
    content = pathlib.Path("skills/<category>/<name>/SKILL.md").read_text()
    assert content.startswith("---")
    m = re.search(r'\n---\s*\n', content[3:])
    fm = YAML(typ="safe").load(content[3:m.start()+3])
    assert "name" in fm and "description" in fm
    assert len(fm["description"]) <= 60, f"description {len(fm['description'])} chars — hardline is 60"
    assert fm["description"].endswith(".")
    assert "platforms" in fm
    assert len(content) <= 100_000
    Also verify every
    related_skills
    entry exists in-repo.
  5. Add tests + regen docs (previous section).
  6. Git add + commit on the active branch; open a PR.
  7. Note: the CURRENT session's skill loader is cached —
    skill_view
    /
    skills_list
    will not see the new skill until a new session. This is expected, not a bug.
  1. 使用
    search_files(target='files')
    调研目标 category 中的同类 Skill,并阅读 2-3 个同类 SKILL.md 文件,以匹配语气和结构。优先扩展现有 Skill,而不是创建一个狭窄的同类 Skill。
  2. 决定层级和 category(见上文)。拿不准时选择 Optional,并先询问再推进,而不是擅自决定。
  3. 使用
    write_file
    编写
    skills/<category>/<name>/SKILL.md
    (或
    optional-skills/...
    )。
  4. 在本地进行验证:
    python
    import re, pathlib
    from ruamel.yaml import YAML
    content = pathlib.Path("skills/<category>/<name>/SKILL.md").read_text()
    assert content.startswith("---")
    m = re.search(r'\n---\s*\n', content[3:])
    fm = YAML(typ="safe").load(content[3:m.start()+3])
    assert "name" in fm and "description" in fm
    assert len(fm["description"]) <= 60, f"description {len(fm['description'])} chars — hardline is 60"
    assert fm["description"].endswith(".")
    assert "platforms" in fm
    assert len(content) <= 100_000
    还应验证每个
    related_skills
    条目都存在于 repo 内。
  5. 添加测试并重新生成文档(见上一节)。
  6. 在当前 branch 上执行
    git add
    + commit;创建 PR。
  7. 注意: 当前 session 的 Skill loader 使用的是缓存——在新的 session 启动前,
    skill_view
    /
    skills_list
    不会看到新 Skill。这是预期行为,不是 bug。

Editing Existing In-Repo Skills

编辑现有的 Repo 内 Skill

  • Small fix:
    skill_manage(action='patch', ...)
    works on in-repo skills, as does
    patch
    .
  • Major rewrite:
    write_file
    the whole SKILL.md.
  • Supporting files:
    write_file
    to
    references/
    ,
    templates/
    , or
    scripts/
    under the skill dir.
  • Always commit — in-repo skills are source, not runtime state. Re-run the docs generator when frontmatter changed.
  • 小修复:
    skill_manage(action='patch', ...)
    可用于 repo 内 Skill,
    patch
    也可以。
  • 大改写: 使用
    write_file
    重写整个 SKILL.md。
  • 支持文件: 使用
    write_file
    写入 Skill 目录下的
    references/
    、
    templates/
    或
    scripts/
    。
  • 始终提交——repo 内 Skill 是源代码,而不是运行时状态。Frontmatter 发生变化后,应重新运行 docs generator。

Common Pitfalls

常见陷阱

  1. Using
    skill_manage(action='create')
    for an in-repo skill.
    It writes to
    ~/.hermes/skills/
    , not the repo tree. Use
    write_file
    . The reverse direction bites too: a user-local skill written as a real directory under
    ~/.hermes/skills/
    lands outside the
    ~/.agents/skills/
    store the user maintains, so their other agents never see it. And once a skill lives in
    ~/.agents/skills/
    (symlinked into the profile),
    skill_manage
    cannot edit it — both
    patch
    and
    write_file
    fail with "not found in active profile" while
    skills_list
    and
    skill_view
    still show it, because the write target resolves outside the managed tree. Edit those with
    write_file
    /
    patch
    against the real path under
    ~/.agents/skills/<name>/
    , then re-load with
    skill_view
    to confirm.
  2. Trusting the validator's limits as the standard. The validator allows 1024-char descriptions; review rejects anything over 60. The validator doesn't check
    platforms:
    , author format, tests, or docs — review does.
  3. author: Hermes Agent
    on a contributed skill.
    Credit the human first.
  4. Leading whitespace before
    ---
    .
    Validation fails on any leading blank line or BOM.
  5. Description too generic or trigger buried past char 57.
  6. related_skills
    pointing at skills that don't exist in-repo
    (user-local, planned, or in a sibling PR).
  7. Duplicating a peer. Survey the category first; extend rather than sibling.
  8. Skipping the docs generator or pushing its unrelated drift. Both directions are wrong: no regen = orphan skill with no docs page; blind regen = a ballooned diff full of other skills' drift.
  9. Assuming a mid-session write is invisible, or that a visible skill is editable. A skill you place by hand (
    write_file
    + symlink) is visible to
    skill_view
    and
    skills_list
    immediately — mid-session, no reload needed.
    skill_manage
    is the narrower one: it resolves only skills inside the managed tree (see pitfall 1). Confirm which case you are in with
    skill_view(name)
    before assuming either way.
  10. Letting skills accumulate sediment. When adding a rule, remove the old wording it replaces.
  11. Naming an unindexed script as the entry point. A
    scripts/*.mjs
    command runs fine but never appears in
    linked_files
    , so the next session doesn't know it exists. Expose an indexed extension — a
    .sh
    wrapper over
    lib/*.mjs
    (see Scripts, wrappers, and what
    linked_files
    actually indexes) — and document that wrapper as the command.
  12. Baking transient environment state into a skill body. "The vault path is wrong and the directory does not exist", "the bundled build is 9.0.1 not 9.0.2", "binary X is missing" are true today and false the moment the user fixes them — then the skill teaches a stale fact and a future session reports a problem that no longer exists. Write the durable rule and its mechanism instead ("this setting is the one most likely to be stale, and it fails quietly"), and let the preflight script report the current state. Chasing one skill through this rewrite later costs more than writing it right the first time.
  13. Editing a user-local skill with
    skill_manage
    .
    Skills under
    ~/.agents/skills/
    (symlinked into
    ~/.hermes/skills/
    ) resolve outside the managed tree:
    skill_manage
    patch
    /
    write_file
    fail with "not found in active profile" while
    skill_view
    and
    skills_list
    still return them. In a review pass restricted to skill tools there is no fallback — fix those with
    write_file
    /
    patch
    against the real path when a general tool is available, and say so plainly rather than silently skipping the update.
  14. Committing a skill that is a symlink into another checkout. In a repo-backed library, a project-owned skill sits in
    skills/
    as a symlink. Committed as-is it is an absolute link, so a fresh clone stats a path that does not exist and the validator dies with
    ENOENT
    before reporting anything — the CI failure names a missing directory, not the real cause.
    .gitignore
    the path, keep the local symlink, and have the validator skip any entry whose realpath lands outside the library root, listing it as external.
  15. A validator that cannot fail is worse than none. Check the checks: plant a deliberately broken skill (no frontmatter, a link to a missing
    references/
    file) and confirm it reports both and exits non-zero, then delete it. Order matters too — a
    continue
    after the first failing category silently skips every later check on that skill, so run link and secret sweeps before the frontmatter gate.
  16. Expanding
    ~
    only at the start of a value.
    file:~/data/x
    ,
    sqlite:~/db
    and similar keep their tilde, parse without complaint, and fail later at open time with a path that never existed. Expand after a scheme prefix as well (
    (^|:)~(?=/|$)
    ) and pin it with a test that asserts the prefix is gone.
  17. Resolving a credential at module scope.
    const client = new Client(apiKey())
    runs the lookup before argument parsing finishes, so
    --dry-run
    demands a secret it never uses. Resolve on first real call (a getter) — a dry run that needs a key is a portability bug wearing a security costume.
  18. Trusting one green route as proof of a boot. When you verify a multi-surface program, hit at least two that read different stores; a page served from cache or a separate index returning 200 says nothing about the one that opens the database.
  19. Selecting a generated input by glob order. After a run writes
    thing-<timestamp>.json
    , a glob like
    thing-*.json
    also matches the
    thing-test.json
    you wrote for a cheap trial — and
    -test
    sorts last, so the "latest" file is your own stub and every measurement describes the wrong set. Take the newest by mtime, or pass the exact path the run just printed.
  20. Assuming every skill library is symlinked. A library that installs by copying (
    npx skills add
    →
    ~/.agents/skills/<name>
    ;
    muse skills install
    →
    ~/.config/muse/skills/<name>
    , with no loader wiring) leaves the installed copy stale from the first repo edit onward, and it keeps loading, so nothing looks broken. After editing an installed skill, re-install or symlink the source and confirm with
    skill_view
    . Such a library usually also has a catalog manifest: a new skill directory with no manifest entry fails validation, so add both in one commit.
  21. Reading a per-agent install warning as a failed install.
    npx skills add
    fans out to every agent it detects and prints one line per agent; an agent that refuses global installation ("PromptScript does not support global skill installation") is a limitation of that agent, not of the install. Verify the outcome in three places instead of retrying:
    ls -ld ~/.agents/skills/<name>
    (the store),
    ls -l ~/.hermes/skills/<name>
    (the loader symlink the installer creates for you — hand-making it duplicates the installer's work and diverges on the next update), and
    npx skills list -g
    , whose
    Source:
    column proves which repo the copy came from. Then
    diff -rq <repo>/skills/<name> ~/.agents/skills/<name>
    to prove the copy is the source, and
    skill_view(name, file_path='references/…')
    to prove linked files resolve.
  22. Letting one skill exist twice, so neither copy loads. A real directory at
    ~/.hermes/skills/<category>/<name>/
    plus an installed entry at
    ~/.agents/skills/<name>
    (which the loader also reaches as
    ~/.hermes/skills/<name>
    ) collide: both resolve to the same name and path, so
    skill_view
    refuses the pair and the runtime lists the skill while declaring it unloadable. Nothing fails loudly — the symptom is a skill that is present in the index and cannot be read. Find the collisions by intersecting the two name sets before adding a skill, and keep the installed copy: delete the loose directory. The same sweep catches the two other silent library faults — an entry whose
    npx skills list -g
    Source:
    is
    local
    , or a path that no longer exists, can never be updated (
    npx skills update -g
    skips it without a word), and a category directory outside any repo is the only copy of that skill that exists anywhere.
    local
    is not a location: it means the installer holds no record at all.
    ~/.agents/.skill-lock.json
    is the only provenance store, so diff its keys against the installed directories —
    ~/.agents/skills
    minus the lock keys is exactly the set
    update
    will skip forever, and every name in it has to be traced back to a repo by hand. On this machine that set reached 26 of 72 installed skills with nothing visibly wrong: they load,
    skill_view
    returns them, and only the lock count reveals they are frozen. Re-adding each from its library (
    npx skills add <repo> --skill <a> --skill <b> -g --yes
    takes several in one call) restores both the record and the update path.
    Deleting the loose copy is the last step, not the first, and only after a diff: the loose one is often the richer of the two — a hand-maintained directory grows sections and
    references/
    long after the repo copy was normalised — so blind deletion throws away content the repo never had. Merge rather than choose: take the fuller body, keep the repo's frontmatter shape, bump the version, copy any
    references/
    , run the library validator, commit and push, re-install with
    npx skills add
    , then delete the loose directory and prove the result with
    diff -rq
    (repo vs
    ~/.agents/skills/<name>
    ) plus a zero on the collision sweep.
    The store drifts forward as well, so run that diff in both directions before reinstalling: an agent editing
    ~/.agents/skills/<name>
    instead of the source leaves the repo missing sections the install has (
    git push --force
    blocked by an approval hook, with
    git push origin +branch
    as the way through; a new rule added to a
    references/
    file). A blind reinstall deletes those silently, because the store is overwritten from the source. Fold installed-only content back into the repo first, commit, then reinstall, then expect zero mismatches.
  23. Hand-deleting half of an installed skill.
    npx skills remove -g -s <name> -y
    removes the store entry and every agent link the installer wired; deleting
    ~/.agents/skills/<name>
    or the
    ~/.hermes/skills/<name>
    symlink by hand leaves the other half behind, and the loader keeps listing a skill that no longer resolves. Before removing anything whose source is gone, ask the repo whether it still wants the skill:
    git log --all -- skills/<name>
    . A skill the library already removed is a leftover install and safe to delete; one that never existed in the repo lives only in the store, so deleting it is the one action that loses it — preserve it into the repo first.
    -s
    takes one name per invocation:
    -s a,b
    exits 0, prints nothing about either, and removes neither, which reads as a successful removal until you list the store again.
  24. Reading a materialised symlink as drift. A skill kept compact with
    references -> ../../data
    installs as real directories, because the installer dereferences the link.
    diff -rq <repo>/skills/<name> ~/.agents/skills/<name>
    then reports
    Only in …/references: asvs
    ,
    fiasse
    ,
    mastg
    and every sibling, and reinstalling does not clear it — the store is correct. Compare the link's target instead:
    diff -rq "$(readlink -f <repo>/skills/<name>/references)" ~/.agents/skills/<name>/references
    . Do that before re-adding a skill, or you will
    remove
    +
    add
    a healthy copy chasing a phantom and conclude the installer is broken. The same dereference means a symlinked skill is not portable: the store carries the content, the repo carries the pointer.
  25. Editing the installed copy, then wondering why the edit came back.
    ~/.agents/skills/<name>/
    is a copy of the source, not the source: the next
    skills update -g
    overwrites it and the change vanishes with no warning. It is not a rare mistake — an agent fixing a skill mid-task edits the path it just read, and four skills on this machine carried forward edits that never reached the repo (a
    git push origin +branch
    escape hatch, two style rules, an HTML-artifact rule). Treat an edit to an installed copy as a change request against the repo: fold it back, commit, then reinstall. Run
    node scripts/skills_audit.mjs
    in the library repo to see this drift before an update discards it.
  1. 对 repo 内 Skill 使用
    skill_manage(action='create')
    。
    它会写入
    ~/.hermes/skills/
    ,而不是 repo 目录树。请使用
    write_file
    。反方向同样会造成问题:作为真实目录写在
    ~/.hermes/skills/
    下的用户本地 Skill,会落在用户维护的
    ~/.agents/skills/
    存储位置之外,因此其他 Agent 永远看不到它。一旦某个 Skill 位于
    ~/.agents/skills/
    (并 symlink 到 profile),
    skill_manage
    就无法原地编辑它——
    patch
    和
    write_file
    都会报
    not found in active profile
    ,而
    skills_list
    和
    skill_view
    仍然会显示它,因为写入目标解析到了受管树之外。使用
    write_file
    /
    patch
    针对
    ~/.agents/skills/<name>/
    下的真实路径进行编辑,然后用
    skill_view
    重新加载以确认。
  2. 把 validator 的限制当作标准。 Validator 允许 1024 字符的 description;但 review 会拒绝超过 60 字符的内容。Validator 不检查
    platforms:
    、author 格式、测试或文档——review 会检查这些内容。
  3. 在贡献的 Skill 中写
    author: Hermes Agent
    。
    应优先署名贡献者本人。
  4. 在
    ---
    前添加空白字符。
    任何前导空行或 BOM 都会导致验证失败。
  5. Description 过于笼统,或触发条件被埋在第 57 个字符之后。
  6. related_skills
    指向不存在于 repo 内的 Skill
    (用户本地、计划中,或位于另一个 PR 中)。
  7. 重复创建同类 Skill。 先调研 category;应扩展现有 Skill,而不是创建同级 Skill。
  8. 跳过 docs generator,或提交其无关的 drift。 两种做法都不正确:不重新生成会得到没有文档页面的孤立 Skill;盲目重新生成则会得到包含大量其他 Skill drift 的膨胀 diff。
  9. 误以为 session 中途写入不可见,或以为可见的 Skill 一定可编辑。 手动放置的 Skill(
    write_file
    + symlink)会立即出现在
    skill_view
    和
    skills_list
    中——在 session 中途也无需重新加载。
    skill_manage
    的作用范围更窄:它只能解析受管树内的 Skill(见陷阱 1)。在假定属于哪种情况之前,先用
    skill_view(name)
    确认。
  10. 让 Skill 不断积累沉淀。 添加新规则时,应删除它所替代的旧表述。
  11. 将未被索引的 script 命名为入口点。
    scripts/*.mjs
    命令可以正常运行,但永远不会出现在
    linked_files
    中,因此后续 session 不知道它存在。应暴露一个被索引的扩展名——在
    lib/*.mjs
    外提供
    .sh
    wrapper(见“Scripts、wrapper,以及
    linked_files
    实际索引的内容”)——并将这个 wrapper 作为命令写入文档。
  12. 将临时环境状态写进 Skill 正文。 “vault 路径错误且目录不存在”、“内置构建版本是 9.0.1 而不是 9.0.2”、“binary X 缺失”等描述今天可能是真的,但用户修复后就会过时——这样 Skill 会教授过时事实,而未来 session 会报告一个已经不存在的问题。应编写稳定的规则及其机制(“此配置最容易过期,而且会静默失败”),再让 preflight script 报告当前状态。事后为修复一个 Skill 而追查并重写所有相关内容,成本远高于一开始就正确编写。
  13. 使用
    skill_manage
    编辑用户本地 Skill。
    位于
    ~/.agents/skills/
    下的 Skill(symlink 到
    ~/.hermes/skills/
    )会解析到受管树之外:
    skill_manage
    的
    patch
    /
    write_file
    会报
    not found in active profile
    ,而
    skill_view
    和
    skills_list
    仍会返回它们。在仅允许使用 Skill 工具的 review 流程中,没有 fallback——有通用工具时,应使用
    write_file
    /
    patch
    针对真实路径修复,并明确说明,不要悄悄跳过更新。
  14. 将指向另一个 checkout 的 symlink 作为 Skill 提交。 在 repo 支撑的库中,项目专属 Skill 会以 symlink 形式位于
    skills/
    下。照原样提交后,它会成为绝对链接;全新 clone 会对不存在的路径执行 stat,导致 validator 在报告任何问题之前就因
    ENOENT
    终止——CI 失败信息只会提到缺失目录,而不会指出真正原因。应将路径加入
    .gitignore
    ,保留本地 symlink,并让 validator 跳过任何 realpath 落在库根目录之外的条目,将其列为 external。
  15. 一个无法失败的 validator 比没有 validator 更糟。 检查这些检查项:故意放置一个损坏的 Skill(缺少 frontmatter、链接到不存在的
    references/
    文件),确认 validator 会同时报告这两个问题并以非零状态退出,然后将其删除。顺序也很重要——在第一个失败 category 之后执行
    continue
    ,会静默跳过该 Skill 的所有后续检查,因此应在 frontmatter gate 之前运行链接和 secret sweep。
  16. 仅展开值开头的
    ~
    。
    file:~/data/x
    、
    sqlite:~/db
    等值会保留 tilde,解析时不会报错,却在打开文件时失败,因为路径从来不存在。除了值开头,还应在 scheme 前缀之后展开(使用
    (^|:)~(?=/|$)
    ),并添加一个断言前缀已消失的测试。
  17. 在 module scope 中解析 credential。
    const client = new Client(apiKey())
    会在参数解析完成前执行查找,因此
    --dry-run
    会要求一个根本不会使用的 secret。应在第一次真实调用时再解析(使用 getter)——需要 key 的 dry run 是伪装成安全措施的 portability bug。
  18. 将一条绿色路径视为启动正常的证明。 验证一个涉及多个界面的程序时,至少应访问两个读取不同存储位置入口;从缓存返回的页面或独立 index 返回 200,都无法证明真正打开数据库的入口正常。
  19. 按 glob 顺序选择生成的输入。 一次运行写出
    thing-<timestamp>.json
    后,
    thing-*.json
    这样的 glob 也会匹配为低成本试验编写的
    thing-test.json
    ——而
    -test
    排在最后,因此所谓“最新”文件其实是自己的 stub,导致每次测量都描述了错误的数据集。应按 mtime 选择最新文件,或传入运行刚刚打印的准确路径。
  20. 假定每个 Skill 库都是通过 symlink 安装的。 采用复制方式安装的库(
    npx skills add
    →
    ~/.agents/skills/<name>
    ;
    muse skills install
    →
    ~/.config/muse/skills/<name>
    ,且不连接 loader)会让安装副本从首次 repo 编辑开始就保持过期,但它仍会继续加载,因此表面上没有任何异常。编辑已安装的 Skill 后,应重新安装或将源文件 symlink 过去,并用
    skill_view
    确认。此类库通常还有 catalog manifest:如果新 Skill 目录没有 manifest 条目,验证就会失败,因此两者应在同一次 commit 中添加。
  21. 将某个 Agent 的安装警告误认为安装失败。
    npx skills add
    会向检测到的每个 Agent 分发,并针对每个 Agent 打印一行;某个 Agent 拒绝全局安装(“PromptScript does not support global skill installation”)是该 Agent 的限制,而不是安装失败。不要反复重试,而应在三个位置验证结果:
    ls -ld ~/.agents/skills/<name>
    (store)、
    ls -l ~/.hermes/skills/<name>
    (安装程序创建的 loader symlink——手动创建会重复安装程序的工作,并在下次更新时产生分歧),以及
    npx skills list -g
    ,其
    Source:
    列可以证明副本来自哪个 repo。然后执行
    diff -rq <repo>/skills/<name> ~/.agents/skills/<name>
    ,证明副本与源文件一致,并通过
    skill_view(name, file_path='references/…')
    证明关联文件可以解析。
  22. 让同一个 Skill 同时存在两份,导致两份都无法加载。
    ~/.hermes/skills/<category>/<name>/
    下的真实目录与
    ~/.agents/skills/<name>
    下的已安装条目(loader 也会通过
    ~/.hermes/skills/<name>
    访问它)发生冲突:二者解析到相同的 name 和 path,因此
    skill_view
    会拒绝这一对条目,而 runtime 会列出该 Skill,同时声明它无法加载。没有任何环节会大声失败——症状是一个出现在 index 中却无法读取的 Skill。添加 Skill 前,应对两个 name 集合取交集以找出冲突,并保留已安装副本:删除散落的目录。同一次扫描还能发现另外两种静默的库故障——如果某个条目的
    npx skills list -g
    中
    Source:
    为
    local
    ,或者路径已经不存在,它就永远无法更新(
    npx skills update -g
    会悄无声息地跳过它);而位于任何 repo 之外的 category 目录,可能是该 Skill 在任何地方存在的唯一副本。
    local
    并不是一个位置:它表示安装程序完全没有记录。
    ~/.agents/.skill-lock.json
    是唯一的来源记录,因此应将它的 key 与已安装目录进行对比——已安装目录减去 lock key,恰好就是
    update
    会永远跳过的集合,其中每个 name 都必须手动追溯到具体 repo。在这台机器上,该集合曾达到 72 个已安装 Skill 中的 26 个,而且表面上看不出任何问题:它们能够加载,
    skill_view
    也能返回它们,只有 lock 计数才能揭示它们已经冻结。重新从各自库中添加这些 Skill(
    npx skills add <repo> --skill <a> --skill <b> -g --yes
    可在一次调用中添加多个)即可恢复记录和更新路径。
    删除散落副本是最后一步,不是第一步;只有在 diff 之后才能这样做——散落副本往往更丰富,手工维护的目录会在 repo 副本完成标准化后很久仍不断添加章节和
    references/
    ——盲目删除会丢掉 repo 中从未存在过的内容。应合并,而不是直接选择:采用更完整的正文,保留 repo 的 frontmatter 形态,提升 version,复制所有
    references/
    ,运行库 validator,commit 并 push,重新使用
    npx skills add
    安装,然后删除散落目录,并通过
    diff -rq
    (repo 与
    ~/.agents/skills/<name>
    )以及冲突扫描结果为零来证明最终状态。
    Store 也会向前 drift,因此重新安装前应双向执行 diff:如果 Agent 编辑了
    ~/.agents/skills/<name>
    而不是源文件,repo 就会缺少安装副本中已有的章节(
    git push --force
    会被 approval hook 阻止,应改用
    git push origin +branch
    ;或者在
    references/
    文件中新增了一条规则)。盲目重新安装会静默删除这些内容,因为 store 会被源文件覆盖。应先将仅存在于安装副本中的内容合并回 repo,commit,然后重新安装,最后确认零差异。
  23. 手动删除已安装 Skill 的一半。
    npx skills remove -g -s <name> -y
    会同时删除 store 条目和安装程序连接的所有 Agent link;手动删除
    ~/.agents/skills/<name>
    或
    ~/.hermes/skills/<name>
    symlink 会遗留另一半,而 loader 仍会列出已经无法解析的 Skill。删除任何源文件已不存在的条目之前,应先询问 repo 是否仍需要该 Skill:
    git log --all -- skills/<name>
    。库已经移除的 Skill 属于残留安装,可以安全删除;从未存在于 repo、只存在于 store 中的 Skill,删除它是唯一会丢失内容的操作——应先将其保存回 repo。每次调用
    -s
    只能接受一个 name:
    -s a,b
    会以状态 0 退出,不打印任何内容,也不会删除其中任何一个,看起来像安装成功,直到再次列出 store 才发现问题。
  24. 将实体化的 symlink 误认为 drift。 使用
    references -> ../../data
    保持紧凑的 Skill,在安装后会变成真实目录,因为安装程序会解引用 link。此时
    diff -rq <repo>/skills/<name> ~/.agents/skills/<name>
    会报告
    Only in …/references: asvs
    、
    fiasse
    、
    mastg
    以及所有同级目录,而且重新安装也无法清除——store 其实是正确的。应改为比较 link 的目标:
    diff -rq "$(readlink -f <repo>/skills/<name>/references)" ~/.agents/skills/<name>/references
    。在重新添加 Skill 前应先执行此操作,否则会删除并重新添加一个健康副本,追捕一个幻影,并得出安装程序已损坏的结论。同样的解引用行为也意味着 symlink Skill 不可移植:store 携带的是内容,repo 携带的只是指针。
  25. 编辑已安装副本,然后困惑于修改为何又消失了。
    ~/.agents/skills/<name>/
    是源文件的副本,而不是源文件;下一次
    skills update -g
    会覆盖它,且不会发出警告。这并不是罕见错误——Agent 在任务中修复 Skill 时,会编辑刚刚读取的路径,而这台机器上有四个 Skill 携带了从未进入 repo 的修改(一个
    git push origin +branch
    逃生方案、两条风格规则和一条 HTML-artifact 规则)。应将编辑已安装副本视为针对 repo 的变更请求:将其合并回 repo、commit,然后重新安装。在更新覆盖内容之前,可在库 repo 中运行
    node scripts/skills_audit.mjs
    查看这些 drift。

Verification Checklist

验证清单

  • Tier decided deliberately (bundled bar: 5+ sessions/month; else
    optional-skills/
    )
  • File at
    skills/<category>/<name>/SKILL.md
    or
    optional-skills/<category>/<name>/SKILL.md
  • User-local: source committed under the library repo (here
    ~/Developer/kernel/skills/<name>/
    ) and installed with
    npx skills add
    ,
    diff -rq
    clean against
    ~/.agents/skills/<name>
  • Kernel library: name added to the right
    skills.sh.json
    grouping,
    scripts/validate_skills.py
    +
    muse skills validate
    + the unittest suite clean, and the skill installed with
    npx skills add
    (never
    muse skills install
    ) so
    skill_view(name)
    returns it
  • Frontmatter starts at byte 0 with
    ---
    , closes with
    \n---\n
  • name
    ,
    description
    ,
    version
    ,
    author
    ,
    license
    ,
    platforms
    ,
    metadata.hermes.{tags, related_skills}
    all present
  • Description ≤ 60 chars, one sentence, ends with a period, no marketing words
  • author
    credits the human contributor first
  • platforms:
    audited against actual prose/scripts, not copied from a sibling
  • Every
    related_skills
    entry resolves in-repo
  • Body follows the modern section order; commands framed through Hermes tools
  • No machine-local paths anywhere in the file
  • Every command named in the body matches an indexed extension under
    scripts/
    ,
    templates/
    , or
    assets/
    (an unindexed
    .mjs
    needs a
    .sh
    wrapper)
  • Each ordered step has a checkable completion criterion
  • Tests at
    tests/skills/test_<skill>_skill.py
    pass under
    scripts/run_tests.sh
  • Docs regenerated with scope discipline; sidebar has exactly one entry for the slug
  • git add
    + commit on the intended branch; PR opened
  • 已认真决定层级(Bundled 标准:每月 5 次以上 session;否则放入
    optional-skills/
    )
  • 文件位于
    skills/<category>/<name>/SKILL.md
    或
    optional-skills/<category>/<name>/SKILL.md
  • 用户本地版本:源文件已提交到库 repo(此处为
    ~/Developer/kernel/skills/<name>/
    ),并使用
    npx skills add
    安装,且
    diff -rq
    显示其与
    ~/.agents/skills/<name>
    一致
  • Kernel 库:name 已加入正确的
    skills.sh.json
    分组,
    scripts/validate_skills.py
    +
    muse skills validate
    + unittest suite 全部通过,并使用
    npx skills add
    安装(绝不要使用
    muse skills install
    ),使
    skill_view(name)
    能够返回该 Skill
  • Frontmatter 从第 0 个字节开始即为
    ---
    ,并以
    \n---\n
    结束
  • name
    、
    description
    、
    version
    、
    author
    、
    license
    、
    platforms
    、
    metadata.hermes.{tags, related_skills}
    全部存在
  • Description 不超过 60 个字符,为一句话,以句号结尾,不含营销词
  • author
    优先署名人工作者贡献者
  • platforms:
    已根据实际正文和脚本进行审计,而不是复制同级 Skill
  • 每个
    related_skills
    条目都能在 repo 内解析
  • 正文遵循现代章节顺序;命令通过 Hermes 工具表述
  • 文件中任何位置都没有机器本地路径
  • 正文中命名的每个命令都对应
    scripts/
    、
    templates/
    或
    assets/
    下被索引的扩展名(未被索引的
    .mjs
    需要
    .sh
    wrapper)
  • 每个有序步骤都有可核验的完成标准
  • tests/skills/test_<skill>_skill.py
    中的测试可通过
    scripts/run_tests.sh
    运行
  • 已重新生成文档并执行范围控制;sidebar 中 slug 恰好有一个条目
  • 已在预期 branch 上执行
    git add
    + commit;PR 已创建