hermes-agent-skill-authoring
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAuthoring Hermes-Agent Skills (in-repo)
编写 Hermes-Agent Skill(仓库内)
Overview
概览
There are two places a SKILL.md can live:
- User-local, repo-backed: authored skills belong in a git repo — on this machine (remote
~/Developer/kernel) underponti-studios/kernel, the same library described in 3. Any olderskills/<name>/checkout is obsolete; do not write there.~/Developer/skillsis the install store every agent reads, and~/.agents/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 —~/.hermes/skills/<name>the entry before you assume an edit landed. A skill whose source of truth is another project repo stays a symlink there and isls -ld — 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:.gitignore.references/skill-library-repo.md - In-repo (this skill is about this case): or
skills/<category>/<name>/SKILL.mdinside the hermes-agent repo — committed, shipped with the package. Useoptional-skills/<category>/<name>/SKILL.md+write_file.git adddoes NOT target this tree.skill_manage(action='create') - Kernel library: (remote
~/Developer/kernel), underponti-studios/kernel, catalogued inskills/<name>/. Validated byskills.sh.json(catalog parity, links, name/directory agreement),python3 scripts/validate_skills.py(format), andmuse skills validate <path>. Install and update withpython3 -m unittest discover -s tests:npx skillswritesnpx skills add ponti-studios/kernel --skill <name> -g --yesand creates the~/.agents/skills/<name>symlink itself,~/.hermes/skills/<name>refreshes, andnpx skills update -g --yesprints each entry'snpx skills list -g.Source:owns format only —muse skills validatecopies tomuse skills installand 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:~/.config/muse/skills/<name>.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 有两个存放位置:
- 用户本地、由 repo 支撑: 编写的 Skill 应归属于一个 git repo——在这台机器上,是 (远程仓库
~/Developer/kernel)下的ponti-studios/kernel,也就是第 3 项所述的同一个库。任何较旧的skills/<name>/checkout 都已废弃;不要在那里写入。~/Developer/skills是每个 Agent 都会读取的安装存储位置,而~/.agents/skills/<name>保存安装程序创建的 loader symlink。该条目是指向其源文件的 symlink(编辑会立即生效)还是副本(重新安装前编辑不可见),取决于安装方式;在假定编辑已经落盘前,先对该条目运行~/.hermes/skills/<name>。如果某个 Skill 的事实源是另一个项目 repo,它会在那里保持为 symlink,并被ls -l忽略——validator 会将其报告为 external 并跳过,因为 CI 中没有另一个项目的 checkout。当用户指定某个 skills 目录时,使用该目录。目录布局、validator 设计和跨框架可移植性:.gitignore。references/skill-library-repo.md - Repo 内(本 Skill 讨论的就是这种情况): hermes-agent repo 内的 或
skills/<category>/<name>/SKILL.md——应提交到版本控制,并随 package 一同交付。使用optional-skills/<category>/<name>/SKILL.md+write_file。git add不针对这棵树。skill_manage(action='create') - Kernel 库: (远程
~/Developer/kernel),位于ponti-studios/kernel下,并收录在skills/<name>/中。通过skills.sh.json验证(catalog 一致性、链接、名称/目录一致性)、通过python3 scripts/validate_skills.py验证格式,并通过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>symlink;~/.hermes/skills/<name>会刷新安装;npx skills update -g --yes会打印每个条目的npx skills list -g。Source:只负责格式——muse skills validate会将文件复制到muse skills install,且不会连接 loader,因此它不是安装程序。选择安装程序前,先阅读 repo 自身的 README/AGENTS 安装章节。机制与接线方式:~/.config/muse/skills/<name>。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 or
skills/(useoptional-skills/for small edits,patchfor rewrites;write_filestill works for patch on in-repo skills, but not forskill_manage)create - Don't use for: personal skills — on this machine those live in with a symlink into
~/.agents/skills/(see Overview), where~/.hermes/skills/cannot edit them in placeskill_manage
- 用户要求“在这个 branch / repo / commit 中添加 Skill”
- 你要提交一个可复用 workflow,并且它应随 hermes-agent 一同交付
- 你正在编辑 或
skills/下的现有 Skill(小改动使用optional-skills/,重写使用patch;write_file仍可对 repo 内 Skill 执行 patch,但不能用于skill_manage)create - 不适用于:个人 Skill——在这台机器上,个人 Skill 位于 ,并通过 symlink 指向
~/.agents/skills/(见概览);~/.hermes/skills/无法原地编辑它们skill_manage
Decide the Tier First: Bundled vs Optional
先决定层级:Bundled 与 Optional
- Bundled () — 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.
skills/<category>/ - Optional () — niche, vertical-specific (blockchain, gaming, finance, one app), recurring-job/task skills, or anything heavy. Installed via
optional-skills/<category>/.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 even if it "feels productivity"). Confirm existing categories with and don't invent new top-level categories casually.
autonomous-ai-agents/search_files(pattern='*', target='files', path='skills')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 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.
When to UseSplit 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 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.
--stage- Bundled()——日常主力行为,对许多用户类型都有广泛用途,且占用空间少。硬性标准:你必须能坦然地说“一个用户每月会加载它 5 次以上”。
skills/<category>/ - Optional()——小众、垂直领域专用(区块链、游戏、金融、单一应用)、周期性 Job/任务 Skill,或任何较重的 Skill。通过
optional-skills/<category>/安装。hermes skills install official/<category>/<skill>
拿不准时,选择 Optional。 后续升级为 Bundled 很容易,降级则会制造 churn。“任何可能需要它的人都会觉得有用”是支持 Optional 层级的理由,而不是支持 Bundled 的理由。
根据工具本质上是什么来选择 category,而不是根据它给人的感觉(即使一个 AI-agent CLI 让人感觉属于生产力工具,也应放入 )。使用 确认现有 category,不要随意创建新的顶层 category。
autonomous-ai-agents/search_files(pattern='*', target='files', path='skills')不允许使用 router / index / hub Skill。 如果一个 Skill 的核心内容只是指向同级 Skill 的路由表,它就会增加一层间接跳转,并重复同级 Skill 自身的 触发条件。如果去掉“改为加载 Skill X”之类的指针后,这个 Skill 就所剩无几,那么不要编写它——catalog 和每个同级 Skill 的触发条件已经承担了这项工作。
When to Use按输出形态拆分,而不是按 pipeline 阶段拆分。 如果一个 Skill 会生成九种产物,那么当每种输出都有自己的形态、验证方式和消费方时,它应当拆成多个 Skill:Agent 加载它时,会为八个根本用不到的 prompt 付出成本,而真正的触发条件(“根据这个脚本撰写一篇 LinkedIn 帖子”)也会被埋在 参数之后。将形态相同的阶段归为一组——长视频和短视频都属于口语化 prose——而字段构成与其他程序之间契约的生产产物(shot list、manifest、render spec)应保留在独立的 Skill 中,因为这些文件由 renderer 而不是人来读取。拆分时,应按 export 名称从原文件中切出 prompts 和 schemas,并在删除源内容前针对每个常量断言字节完全相等;重新手动输入 prompt 是一种无人能从 diff 中发现的静默产品变更。
--stageRequired Frontmatter
必需的 Frontmatter
Validator source of truth: . Validator hard requirements:
tools/skill_manager_tool.py::_validate_frontmatter- Starts with as the first bytes (no leading blank line).
--- - Closes with before the body.
\n---\n - Parses as a YAML mapping.
- field present.
name - field present (validator ceiling 1024 chars — but see the repo hardline below, which is much stricter).
description - 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 的事实源:。Validator 的硬性要求:
tools/skill_manager_tool.py::_validate_frontmatter- 前几个字节必须以 开始(前面不能有空行)。
--- - 在正文前必须以 结束 frontmatter。
\n---\n - 必须能解析为 YAML mapping。
- 必须包含 字段。
name - 必须包含 字段(validator 上限为 1024 个字符——但 repo 下文的 hardline 标准严格得多)。
description - 结束 之后必须有非空正文。
---
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)
descriptiondescription
规则(HARDLINE——validator 的 1024 不是标准)
description- ≤ 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:
Bad: (240 chars — rejected in review)
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, ...- 不超过 60 个字符。 一句话。以句号结尾。
- 说明能力,而不是实现方式,也不要重复 Skill 名称。
- 不使用营销词(“powerful”、“comprehensive”、“seamless”、“advanced”)。
- System prompt 的 Skill index 会在 57 个字符加 处截断——触发条件/能力说明必须在这个窗口内自洽完整。
... - 如果 中包含
description,请用双引号包裹,否则 YAML 会将其解析为 mapping,并导致 docs generator 崩溃。引号不计入 60 个字符。:
好:
坏:(240 个字符——review 会拒绝)
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, ...author
rules
authorauthor
规则
author- Credit the human first, then "Hermes Agent" as secondary collaborator: .
Ben Barclay (benbarclay), Hermes Agent - Never alone for contributed skills — credit the human, not the tool, even (especially) when an agent drafted the text.
author: Hermes Agent - Maintainer-authored skills: .
Teknium (teknium1), Hermes Agent
- 先署名贡献者本人,再将 “Hermes Agent” 作为次要协作者:。
Ben Barclay (benbarclay), Hermes Agent - 对贡献的 Skill,绝不能只写 ——应署名贡献者,而不是工具,即使(尤其是)文本由 Agent 起草。
author: Hermes Agent - Maintainer 编写的 Skill:。
Teknium (teknium1), Hermes Agent
related_skills
rules
related_skillsrelated_skills
规则
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: (and
search_files(pattern='<name>', target='files', path='skills')).optional-skills/
- 每个条目都必须解析到 PR 所在同一 tree state 中的现有 repo 内 Skill。不要引用仅处于计划阶段、存在于另一个 PR,或仅存在于 中的 Skill。
~/.hermes/skills/ - 逐项验证:(以及
search_files(pattern='<name>', target='files', path='skills'))。optional-skills/
Platform Gating: audit, don't trust
Platform Gating:进行审计,不要臆测
platforms:| Skill uses only… | |
|---|---|
| Hermes tools + stdlib Python + cross-platform CLIs | |
bash pipelines, | |
| |
| |
POSIX-only signals to search for in : , , , , , , liveness checks, hardcoded . Default posture: fix cross-platform first (, , ); gate narrower only when the dependency is genuinely platform-bound, and say why in .
scripts/fcntltermiosptyos.forkos.killpgsignal.SIGKILLos.kill(pid, 0)/tmp/proc/etctempfile.gettempdir()pathlib.Pathpsutil.pid_exists## Pitfallsplatforms:| Skill 仅使用…… | |
|---|---|
| Hermes 工具 + stdlib Python + 跨平台 CLI | |
bash pipeline、 | |
| |
| |
在 中搜索 POSIX-only 信号:、、、、、、 存活检查,以及硬编码的 、、。默认策略是优先修复跨平台兼容性(、、);只有在依赖确实受平台约束时,才使用更窄的门控,并在 中说明原因。
scripts/fcntltermiosptyos.forkos.killpgsignal.SIGKILLos.kill(pid, 0)/tmp/proc/etctempfile.gettempdir()pathlib.Pathpsutil.pid_exists## PitfallsSize Limits
大小限制
- Full SKILL.md: ≤ 100,000 chars enforced (), but target ~100 lines for a simple skill, ~200 for a complex one. Peer skills sit at 8-14k chars.
MAX_SKILL_CONTENT_CHARS - Bulky or branch-specific material goes in ,
references/*.md, ortemplates/— pointed to from SKILL.md, not inlined.scripts/ - Don't expect the model to inline-write parsers or non-trivial logic every call — ship a helper script in and reference it by path.
scripts/
- 完整 SKILL.md 不得超过 100,000 个字符(由 强制执行),但目标应是:简单 Skill 约 100 行,复杂 Skill 约 200 行。同类 Skill 通常为 8-14k 字符。
MAX_SKILL_CONTENT_CHARS - 冗长或与特定分支有关的内容应放入 、
references/*.md或templates/,并从 SKILL.md 指向它们,而不是内联。scripts/ - 不要指望模型在每次调用时都临时编写 parser 或非平凡逻辑——应在 中交付 helper script,并通过路径引用。
scripts/
Body Structure (modern section order)
正文结构(现代章节顺序)
undefinedundefined<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
terminalHow to Run — canonical invocation through the terminal
tool
terminalQuick 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: , , , , , , , , , , . Do NOT name shell utilities the agent already has wrapped ( → , → , / → , / → ). A CLI-wrapper skill should frame invocations as — bare shell prose ("run ") is a review-blocking non-conformance. If the skill depends on an MCP server, name it and document setup in Prerequisites.
terminalread_filewrite_filepatchsearch_filesweb_searchweb_extractbrowser_navigatevision_analyzedelegate_taskcronjobgrepsearch_filescatread_filesedawkpatchfindlssearch_files target='files'terminal(command="{tool} ...", timeout=...)foo --version当 Skill 需要某项能力时,应在反引号中写出正确的 Hermes 工具:、、、、、、、、、、。不要写 Agent 已经封装的 shell 工具( → , → ,/ → ,/ → )。CLI-wrapper Skill 应将调用表述为 ——裸 shell 叙述(“运行 ”)属于 review 阻断级不合规。如果 Skill 依赖 MCP server,应写出其名称,并在 Prerequisites 中记录安装方式。
terminalread_filewrite_filepatchsearch_filesweb_searchweb_extractbrowser_navigatevision_analyzedelegate_taskcronjobgrepsearch_filescatread_filesedawkpatchfindlssearch_files target='files'terminal(command="{tool} ...", timeout=...)foo --versionNever use machine-local paths
绝不要使用机器本地路径
Write repo-relative paths (, ). A path baked into a committed skill breaks for every other user and is an instant review flag.
skills/...tools/skill_manager_tool.py/home/<you>/...应编写 repo 相对路径(、)。将 之类的路径写进已提交的 Skill,会使其在其他所有用户机器上失效,并立即触发 review 标记。
skills/...tools/skill_manager_tool.py/home/<you>/...Scripts, wrappers, and what linked_files
actually indexes
linked_filesScripts、wrapper,以及 linked_files
实际索引的内容
linked_filestools/skills_tool.py::_LINKED_FILE_SPECSskill_viewlinked_files| Directory | Globs indexed | Recursive |
|---|---|---|
| | no |
| | yes |
| | yes |
| | no |
.mjs.cjs.tsx.zsh.jsx.mjsawaitimportscripts/
my-command.sh # indexed — the public entry point, named in SKILL.md
lib/my-command.mjs # the implementation, privatebash
#!/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 , 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.
.mjsTemplates follow the same table: a config template named , any or is invisible in even though SKILL.md can link to it by path. Give a template an indexed extension (, , ) when a future session must discover it; otherwise accept that only the path in SKILL.md leads there, and say so.
vitest.config.ts*.toml*.jsonclinked_files.yml.json.shBundling scripts that live elsewhere. Symlinking () 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.
ln -sfnPointing 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 : 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.
lib/resolve.mjsOutput 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 , 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 as a table () — a future session needs to know what it can override without reading the source.
resolve.mjs~/…SKILL.md## Configuration| Variable | Default | Used for |Secrets follow the same order as paths: environment first, then one user-level config file, and nothing else — no repo , no , no per-tool search path. Every extra location is a question the next debugger has to answer, and the answer changes per machine.
.env~/.localrcOne parser, imported not copied. Put the parse rule in the project's own config package and have each skill it from the build (), 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.
await import()packages/config/dist/index.jsKeep the file flat (, 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 section carries the key/env/default table.
key: value#~## ConfigurationProve 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 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.
scripts/check-*.shMake the paid step dry-run by default, requiring an explicit go-ahead flag () 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 () 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.
--yes--only <id>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_SPECSskill_viewlinked_files| 目录 | 被索引的 Glob | 是否递归 |
|---|---|---|
| | 否 |
| | 是 |
| | 是 |
| | 否 |
.mjs.cjs.tsx.zsh.jsx.mjsawaitimportbash
#!/usr/bin/env bash
set -euo pipefail
exec node "$(dirname "${BASH_SOURCE[0]}")/lib/my-command.mjs" "$@"应将 wrapper 作为命令写入文档,而不是 ,这样后续 session 才能在 index 中找到该能力并运行正确的命令。包装独立命令(CLI);不要包装供其他 scripts 导入的 module。
.mjs模板遵循同一张表:名为 的 config template,以及任何 或 ,在 中都不可见,即使 SKILL.md 可以按路径链接它。如果未来 session 必须发现某个 template,请为其使用被索引的扩展名(、、);否则只能接受只有 SKILL.md 中的路径能够指向它,并明确说明这一点。
vitest.config.ts*.toml*.jsonclinked_files.yml.json.sh打包位于其他位置的 scripts。 将外部 script symlink()到 Skill 目录,可以保留唯一的 canonical file,并且它会通过 Skill 路径被索引和运行;但复制到另一台机器后,Skill 将不再可移植。真实副本可移植,却会发生 drift。请有意识地选择,并记录这一取舍,以便在 SKILL.md 中体现。
ln -sfn让 Skill 指向外部代码。 当 Skill 包装另一个项目的源代码,而该源代码以后可能被移植进 Skill 时,应将位置只放在一个 中:先检查环境变量覆盖值,再使用默认路径;两者都无法解析时,抛出的消息应同时列出两个候选位置。每个 script 都应从它导入,Skill 中不得有其他文件写出 repo 路径——这样移植只需修改一行,而不是重写整个实现。
lib/resolve.mjs输出目录同样属于配置。 同一规则适用于 Skill 写入的所有位置,而不只是它读取的代码:render inbox、每次调用的 usage log 目录、草稿目录、发布根目录都是个人数据位置。每个位置都应拥有自己的环境变量,以旧路径作为默认值,通过同一个 解析,并且脚本内部不得出现字面量 。如果一个 Skill 硬编码了六个这样的路径,它就只能在一台机器上运行;而失败并不明显,因为默认值恰好在那台机器上存在。随后,应在 Skill 自己的 SKILL.md 中以 表格()列出这些变量——未来 session 必须无需阅读源码就能知道可以覆盖哪些配置。
resolve.mjs~/…## Configuration| Variable | Default | Used for |Secrets 遵循与路径相同的顺序:首先是环境变量,然后是一个用户级 config file,除此之外没有其他位置——不要使用 repo 、 或 per-tool 搜索路径。每增加一个位置,下一位调试者就多一个问题,而答案还会因机器而异。
.env~/.localrc只保留一个 parser,并导入它,不要复制它。 将解析规则放在项目自身的 config package 中,让每个 Skill 从构建产物()执行 ,与代码库其他部分的导入方式保持一致。复制的 parser 起初完全相同,但第一次修复后就会分叉,而副本位于 skills 中——这是人们最后才会检查的地方。这会带来两个结果:未构建的 package 必须明确失败并指出构建命令;config module 自身的路径不能来自它所解析的文件,因此应通过一个环境变量加单一默认值解析该条目,其余内容全部从文件读取。
packages/config/dist/index.jsawait import()保持文件结构扁平(、 注释、可选引号、展开后的 ),将每个 key 映射到对应环境变量,并忽略未知 key,避免较新的文件破坏较旧的工具。优先级为 flag → 环境变量 → 文件 → 内置默认值,Skill 的 章节应包含 key/env/default 对照表。
key: value#~## Configuration验证优先级,不要想当然。 用三种方式运行同一次 dry run——仅文件、环境变量覆盖文件、没有配置——并确认每次解析结果都随之变化。只使用默认值运行一次不能证明任何可移植性:默认值恰恰会掩盖测试机器上的查找错误。
先移植行为一致的版本,再进行修改。 将代码移入 Skill 时,应使其精确复现原实现输出,并用原实现自己的 golden artifacts 证明一致性——相同输入、字节完全一致的结果,同时对照已记录的 expected files 和上游构建的实时运行结果。只有在建立一致性后,才能叠加修改;一旦一致性成立,后续每个差异都是有意决策,而不是翻译错误,并且可以指出哪个差异来自哪一侧。从那时起,应让测试 harness 同时覆盖两个方向:原模式下的 parity(移植后仍与上游一致),以及新模式下的不变量(输出确定性,且重写要消除的特定模式确实不再存在)。如果某项修改本来就是要改变输出,应有意重新录制 golden files 并明确说明;绝不能为了让检查通过而修改 expected file。这里有两项保护措施曾捕获仅靠 parity 检查会漏掉的真实 bug:对新模式输出执行不变量断言,以及拒绝让新模式静默回退到旧代码路径。
交付免费的 preflight。 任何会花钱、会写入用户数据,或依赖可能过期的配置的 Skill,都应提供 :解析每项依赖、打印实际解析结果(使用哪个 binary、哪个 config path、哪个 model、哪个 voice)、不产生费用,并且只在发生真实故障时以非零状态退出。可选依赖缺失时应发出警告而不是失败,否则这个检查会因“狼来了”而被忽略。“这次运行究竟用了哪个 ffmpeg / 哪个 vault?”是后续 session 无法从代码中回答的问题。
scripts/check-*.sh默认将付费步骤设为 dry-run,必须显式提供 go-ahead flag()才能消费资源:按照 provider 自己公布的费率(而不是自行推导的估算值)打印单项目和总成本,并提供单项目路径(),这样迭代一个 prompt 时只产生一个项目的成本,而不是整批成本。默认启用的脚本背后若隐藏着昂贵阶段,对用户而言是隐患,而不是便利。
--yes--only <id>验证连接点,而不只是各个阶段。 在一条链路中——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.
- Optimize for process predictability. If a line does not change behavior, cut it.
- Choose the right context load. The description is paid for every turn; details go in the body or linked references.
- End steps with completion criteria. Checkable and, when it matters, exhaustive: "every modified file accounted for" beats "summarize changes."
- Co-locate rules with the concept they govern.
- Use strong leading words ("tight loop," "root cause," "regression test") over long repeated explanations.
- 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 始终遵循同一套有用的纪律。
- 优化流程可预测性。 如果某一行不会改变行为,就删掉它。
- 选择正确的 context load。 每轮对话都会产生成本;细节应放入正文或关联引用。
description - 以完成标准结束步骤。 标准应当可核验,必要时还应穷尽:“所有修改过的文件均已交代清楚”优于“总结一下变更”。
- 将规则与其所约束的概念放在一起。
- 使用有力的引导词(“tight loop”、“root cause”、“regression test”),而不是反复进行长篇解释。
- 删减重复内容和无效操作。 “谨慎处理”和“使用最佳实践”不会改变模型行为——应将其替换为可核验标准,或者直接删除。
Tests and Docs (required for repo skills)
Repo Skill 必需的测试与文档
- Tests live at — stdlib + pytest +
tests/skills/test_<skill>_skill.pyonly, no live network. Run viaunittest.mock. (The genericscripts/run_tests.sh tests/skills/test_<skill>_skill.py -qpassing proves nothing about YOUR skill.)tests/tools/test_skill_manager_tool.py - Docs regen: run , then apply scope discipline — the generator rewrites EVERY auto-gen page.
python website/scripts/generate-skill-docs.pyeverything 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-linegit checkout --insertion (verify withwebsite/sidebars.ts— exactly one hit, or the page is an orphan).search_files(pattern='<your-slug>', path='website/sidebars.ts') - (only if the skill needs new env vars): one clearly delimited commented block; touch nothing else in the file.
.env.example
- 测试位于 ——仅使用 stdlib + pytest +
tests/skills/test_<skill>_skill.py,不访问实时网络。通过unittest.mock运行。(通用的scripts/run_tests.sh tests/skills/test_<skill>_skill.py -q通过,并不能证明你的 Skill 是正确的。)tests/tools/test_skill_manager_tool.py - 重新生成文档: 运行 ,然后执行范围控制——生成器会重写每一个自动生成页面。对不属于你的内容执行
python website/scripts/generate-skill-docs.py;最终 diff 只能显示你的 SKILL.md、对应的一个 Skill 文档页面、一行 catalog 条目,以及git checkout --中的一行插入项(使用website/sidebars.ts验证——必须恰好命中一次,否则页面会成为孤立页面)。search_files(pattern='<your-slug>', path='website/sidebars.ts') - (仅当 Skill 需要新增环境变量时):添加一个边界清晰、带注释的代码块;不要修改该文件中的其他任何内容。
.env.example
Workflow
工作流
- Survey peers in the target category with and read 2-3 peer SKILL.md files to match tone and structure. Prefer extending an existing skill over creating a narrow sibling.
search_files(target='files') - Decide tier and category (see above). When in doubt, optional — and ask before pushing rather than defaulting.
- Draft with to
write_file(orskills/<category>/<name>/SKILL.md).optional-skills/... - Validate locally:
Also verify everypython
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_000entry exists in-repo.related_skills - Add tests + regen docs (previous section).
- Git add + commit on the active branch; open a PR.
- Note: the CURRENT session's skill loader is cached — /
skill_viewwill not see the new skill until a new session. This is expected, not a bug.skills_list
- 使用 调研目标 category 中的同类 Skill,并阅读 2-3 个同类 SKILL.md 文件,以匹配语气和结构。优先扩展现有 Skill,而不是创建一个狭窄的同类 Skill。
search_files(target='files') - 决定层级和 category(见上文)。拿不准时选择 Optional,并先询问再推进,而不是擅自决定。
- 使用 编写
write_file(或skills/<category>/<name>/SKILL.md)。optional-skills/... - 在本地进行验证:
还应验证每个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条目都存在于 repo 内。related_skills - 添加测试并重新生成文档(见上一节)。
- 在当前 branch 上执行 + commit;创建 PR。
git add - 注意: 当前 session 的 Skill loader 使用的是缓存——在新的 session 启动前,/
skill_view不会看到新 Skill。这是预期行为,不是 bug。skills_list
Editing Existing In-Repo Skills
编辑现有的 Repo 内 Skill
- Small fix: works on in-repo skills, as does
skill_manage(action='patch', ...).patch - Major rewrite: the whole SKILL.md.
write_file - Supporting files: to
write_file,references/, ortemplates/under the skill dir.scripts/ - Always commit — in-repo skills are source, not runtime state. Re-run the docs generator when frontmatter changed.
- 小修复: 可用于 repo 内 Skill,
skill_manage(action='patch', ...)也可以。patch - 大改写: 使用 重写整个 SKILL.md。
write_file - 支持文件: 使用 写入 Skill 目录下的
write_file、references/或templates/。scripts/ - 始终提交——repo 内 Skill 是源代码,而不是运行时状态。Frontmatter 发生变化后,应重新运行 docs generator。
Common Pitfalls
常见陷阱
-
Usingfor an in-repo skill. It writes to
skill_manage(action='create'), not the repo tree. Use~/.hermes/skills/. The reverse direction bites too: a user-local skill written as a real directory underwrite_filelands outside the~/.hermes/skills/store the user maintains, so their other agents never see it. And once a skill lives in~/.agents/skills/(symlinked into the profile),~/.agents/skills/cannot edit it — bothskill_manageandpatchfail with "not found in active profile" whilewrite_fileandskills_liststill show it, because the write target resolves outside the managed tree. Edit those withskill_view/write_fileagainst the real path underpatch, then re-load with~/.agents/skills/<name>/to confirm.skill_view -
Trusting the validator's limits as the standard. The validator allows 1024-char descriptions; review rejects anything over 60. The validator doesn't check, author format, tests, or docs — review does.
platforms: -
on a contributed skill. Credit the human first.
author: Hermes Agent -
Leading whitespace before. Validation fails on any leading blank line or BOM.
--- -
Description too generic or trigger buried past char 57.
-
pointing at skills that don't exist in-repo (user-local, planned, or in a sibling PR).
related_skills -
Duplicating a peer. Survey the category first; extend rather than sibling.
-
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.
-
Assuming a mid-session write is invisible, or that a visible skill is editable. A skill you place by hand (+ symlink) is visible to
write_fileandskill_viewimmediately — mid-session, no reload needed.skills_listis the narrower one: it resolves only skills inside the managed tree (see pitfall 1). Confirm which case you are in withskill_managebefore assuming either way.skill_view(name) -
Letting skills accumulate sediment. When adding a rule, remove the old wording it replaces.
-
Naming an unindexed script as the entry point. Acommand runs fine but never appears in
scripts/*.mjs, so the next session doesn't know it exists. Expose an indexed extension — alinked_fileswrapper over.sh(see Scripts, wrappers, and whatlib/*.mjsactually indexes) — and document that wrapper as the command.linked_files -
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.
-
Editing a user-local skill with. Skills under
skill_manage(symlinked into~/.agents/skills/) resolve outside the managed tree:~/.hermes/skills/skill_manage/patchfail with "not found in active profile" whilewrite_fileandskill_viewstill return them. In a review pass restricted to skill tools there is no fallback — fix those withskills_list/write_fileagainst the real path when a general tool is available, and say so plainly rather than silently skipping the update.patch -
Committing a skill that is a symlink into another checkout. In a repo-backed library, a project-owned skill sits inas 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
skills/before reporting anything — the CI failure names a missing directory, not the real cause.ENOENTthe path, keep the local symlink, and have the validator skip any entry whose realpath lands outside the library root, listing it as external..gitignore -
A validator that cannot fail is worse than none. Check the checks: plant a deliberately broken skill (no frontmatter, a link to a missingfile) and confirm it reports both and exits non-zero, then delete it. Order matters too — a
references/after the first failing category silently skips every later check on that skill, so run link and secret sweeps before the frontmatter gate.continue -
Expandingonly at the start of a value.
~,file:~/data/xand 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 (sqlite:~/db) and pin it with a test that asserts the prefix is gone.(^|:)~(?=/|$) -
Resolving a credential at module scope.runs the lookup before argument parsing finishes, so
const client = new Client(apiKey())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.--dry-run -
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.
-
Selecting a generated input by glob order. After a run writes, a glob like
thing-<timestamp>.jsonalso matches thething-*.jsonyou wrote for a cheap trial — andthing-test.jsonsorts 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.-test -
Assuming every skill library is symlinked. A library that installs by copying (→
npx skills add;~/.agents/skills/<name>→muse skills install, 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~/.config/muse/skills/<name>. 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.skill_view -
Reading a per-agent install warning as a failed install.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:
npx skills add(the store),ls -ld ~/.agents/skills/<name>(the loader symlink the installer creates for you — hand-making it duplicates the installer's work and diverges on the next update), andls -l ~/.hermes/skills/<name>, whosenpx skills list -gcolumn proves which repo the copy came from. ThenSource:to prove the copy is the source, anddiff -rq <repo>/skills/<name> ~/.agents/skills/<name>to prove linked files resolve.skill_view(name, file_path='references/…') -
Letting one skill exist twice, so neither copy loads. A real directory atplus an installed entry at
~/.hermes/skills/<category>/<name>/(which the loader also reaches as~/.agents/skills/<name>) collide: both resolve to the same name and path, so~/.hermes/skills/<name>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 whoseskill_viewnpx skills list -gisSource:, or a path that no longer exists, can never be updated (localskips it without a word), and a category directory outside any repo is the only copy of that skill that exists anywhere.npx skills update -gis not a location: it means the installer holds no record at all.localis the only provenance store, so diff its keys against the installed directories —~/.agents/.skill-lock.jsonminus the lock keys is exactly the set~/.agents/skillswill 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,updatereturns them, and only the lock count reveals they are frozen. Re-adding each from its library (skill_viewtakes several in one call) restores both the record and the update path.npx skills add <repo> --skill <a> --skill <b> -g --yesDeleting 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 andlong 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 anyreferences/, run the library validator, commit and push, re-install withreferences/, then delete the loose directory and prove the result withnpx skills add(repo vsdiff -rq) plus a zero on the collision sweep.~/.agents/skills/<name>The store drifts forward as well, so run that diff in both directions before reinstalling: an agent editinginstead of the source leaves the repo missing sections the install has (~/.agents/skills/<name>blocked by an approval hook, withgit push --forceas the way through; a new rule added to agit push origin +branchfile). 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.references/ -
Hand-deleting half of an installed skill.removes the store entry and every agent link the installer wired; deleting
npx skills remove -g -s <name> -yor the~/.agents/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:~/.hermes/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.git log --all -- skills/<name>takes one name per invocation:-sexits 0, prints nothing about either, and removes neither, which reads as a successful removal until you list the store again.-s a,b -
Reading a materialised symlink as drift. A skill kept compact withinstalls as real directories, because the installer dereferences the link.
references -> ../../datathen reportsdiff -rq <repo>/skills/<name> ~/.agents/skills/<name>,Only in …/references: asvs,fiasseand every sibling, and reinstalling does not clear it — the store is correct. Compare the link's target instead:mastg. Do that before re-adding a skill, or you willdiff -rq "$(readlink -f <repo>/skills/<name>/references)" ~/.agents/skills/<name>/references+removea 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.add -
Editing the installed copy, then wondering why the edit came back.is a copy of the source, not the source: the next
~/.agents/skills/<name>/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 (askills update -gescape 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. Rungit push origin +branchin the library repo to see this drift before an update discards it.node scripts/skills_audit.mjs
-
对 repo 内 Skill 使用。 它会写入
skill_manage(action='create'),而不是 repo 目录树。请使用~/.hermes/skills/。反方向同样会造成问题:作为真实目录写在write_file下的用户本地 Skill,会落在用户维护的~/.hermes/skills/存储位置之外,因此其他 Agent 永远看不到它。一旦某个 Skill 位于~/.agents/skills/(并 symlink 到 profile),~/.agents/skills/就无法原地编辑它——skill_manage和patch都会报write_file,而not found in active profile和skills_list仍然会显示它,因为写入目标解析到了受管树之外。使用skill_view/write_file针对patch下的真实路径进行编辑,然后用~/.agents/skills/<name>/重新加载以确认。skill_view -
把 validator 的限制当作标准。 Validator 允许 1024 字符的 description;但 review 会拒绝超过 60 字符的内容。Validator 不检查、author 格式、测试或文档——review 会检查这些内容。
platforms: -
在贡献的 Skill 中写。 应优先署名贡献者本人。
author: Hermes Agent -
在前添加空白字符。 任何前导空行或 BOM 都会导致验证失败。
--- -
Description 过于笼统,或触发条件被埋在第 57 个字符之后。
-
指向不存在于 repo 内的 Skill(用户本地、计划中,或位于另一个 PR 中)。
related_skills -
重复创建同类 Skill。 先调研 category;应扩展现有 Skill,而不是创建同级 Skill。
-
跳过 docs generator,或提交其无关的 drift。 两种做法都不正确:不重新生成会得到没有文档页面的孤立 Skill;盲目重新生成则会得到包含大量其他 Skill drift 的膨胀 diff。
-
误以为 session 中途写入不可见,或以为可见的 Skill 一定可编辑。 手动放置的 Skill(+ symlink)会立即出现在
write_file和skill_view中——在 session 中途也无需重新加载。skills_list的作用范围更窄:它只能解析受管树内的 Skill(见陷阱 1)。在假定属于哪种情况之前,先用skill_manage确认。skill_view(name) -
让 Skill 不断积累沉淀。 添加新规则时,应删除它所替代的旧表述。
-
将未被索引的 script 命名为入口点。命令可以正常运行,但永远不会出现在
scripts/*.mjs中,因此后续 session 不知道它存在。应暴露一个被索引的扩展名——在linked_files外提供lib/*.mjswrapper(见“Scripts、wrapper,以及.sh实际索引的内容”)——并将这个 wrapper 作为命令写入文档。linked_files -
将临时环境状态写进 Skill 正文。 “vault 路径错误且目录不存在”、“内置构建版本是 9.0.1 而不是 9.0.2”、“binary X 缺失”等描述今天可能是真的,但用户修复后就会过时——这样 Skill 会教授过时事实,而未来 session 会报告一个已经不存在的问题。应编写稳定的规则及其机制(“此配置最容易过期,而且会静默失败”),再让 preflight script 报告当前状态。事后为修复一个 Skill 而追查并重写所有相关内容,成本远高于一开始就正确编写。
-
使用编辑用户本地 Skill。 位于
skill_manage下的 Skill(symlink 到~/.agents/skills/)会解析到受管树之外:~/.hermes/skills/的skill_manage/patch会报write_file,而not found in active profile和skill_view仍会返回它们。在仅允许使用 Skill 工具的 review 流程中,没有 fallback——有通用工具时,应使用skills_list/write_file针对真实路径修复,并明确说明,不要悄悄跳过更新。patch -
将指向另一个 checkout 的 symlink 作为 Skill 提交。 在 repo 支撑的库中,项目专属 Skill 会以 symlink 形式位于下。照原样提交后,它会成为绝对链接;全新 clone 会对不存在的路径执行 stat,导致 validator 在报告任何问题之前就因
skills/终止——CI 失败信息只会提到缺失目录,而不会指出真正原因。应将路径加入ENOENT,保留本地 symlink,并让 validator 跳过任何 realpath 落在库根目录之外的条目,将其列为 external。.gitignore -
一个无法失败的 validator 比没有 validator 更糟。 检查这些检查项:故意放置一个损坏的 Skill(缺少 frontmatter、链接到不存在的文件),确认 validator 会同时报告这两个问题并以非零状态退出,然后将其删除。顺序也很重要——在第一个失败 category 之后执行
references/,会静默跳过该 Skill 的所有后续检查,因此应在 frontmatter gate 之前运行链接和 secret sweep。continue -
仅展开值开头的。
~、file:~/data/x等值会保留 tilde,解析时不会报错,却在打开文件时失败,因为路径从来不存在。除了值开头,还应在 scheme 前缀之后展开(使用sqlite:~/db),并添加一个断言前缀已消失的测试。(^|:)~(?=/|$) -
在 module scope 中解析 credential。会在参数解析完成前执行查找,因此
const client = new Client(apiKey())会要求一个根本不会使用的 secret。应在第一次真实调用时再解析(使用 getter)——需要 key 的 dry run 是伪装成安全措施的 portability bug。--dry-run -
将一条绿色路径视为启动正常的证明。 验证一个涉及多个界面的程序时,至少应访问两个读取不同存储位置入口;从缓存返回的页面或独立 index 返回 200,都无法证明真正打开数据库的入口正常。
-
按 glob 顺序选择生成的输入。 一次运行写出后,
thing-<timestamp>.json这样的 glob 也会匹配为低成本试验编写的thing-*.json——而thing-test.json排在最后,因此所谓“最新”文件其实是自己的 stub,导致每次测量都描述了错误的数据集。应按 mtime 选择最新文件,或传入运行刚刚打印的准确路径。-test -
假定每个 Skill 库都是通过 symlink 安装的。 采用复制方式安装的库(→
npx skills add;~/.agents/skills/<name>→muse skills install,且不连接 loader)会让安装副本从首次 repo 编辑开始就保持过期,但它仍会继续加载,因此表面上没有任何异常。编辑已安装的 Skill 后,应重新安装或将源文件 symlink 过去,并用~/.config/muse/skills/<name>确认。此类库通常还有 catalog manifest:如果新 Skill 目录没有 manifest 条目,验证就会失败,因此两者应在同一次 commit 中添加。skill_view -
将某个 Agent 的安装警告误认为安装失败。会向检测到的每个 Agent 分发,并针对每个 Agent 打印一行;某个 Agent 拒绝全局安装(“PromptScript does not support global skill installation”)是该 Agent 的限制,而不是安装失败。不要反复重试,而应在三个位置验证结果:
npx skills add(store)、ls -ld ~/.agents/skills/<name>(安装程序创建的 loader symlink——手动创建会重复安装程序的工作,并在下次更新时产生分歧),以及ls -l ~/.hermes/skills/<name>,其npx skills list -g列可以证明副本来自哪个 repo。然后执行Source:,证明副本与源文件一致,并通过diff -rq <repo>/skills/<name> ~/.agents/skills/<name>证明关联文件可以解析。skill_view(name, file_path='references/…') -
让同一个 Skill 同时存在两份,导致两份都无法加载。下的真实目录与
~/.hermes/skills/<category>/<name>/下的已安装条目(loader 也会通过~/.agents/skills/<name>访问它)发生冲突:二者解析到相同的 name 和 path,因此~/.hermes/skills/<name>会拒绝这一对条目,而 runtime 会列出该 Skill,同时声明它无法加载。没有任何环节会大声失败——症状是一个出现在 index 中却无法读取的 Skill。添加 Skill 前,应对两个 name 集合取交集以找出冲突,并保留已安装副本:删除散落的目录。同一次扫描还能发现另外两种静默的库故障——如果某个条目的skill_view中npx skills list -g为Source:,或者路径已经不存在,它就永远无法更新(local会悄无声息地跳过它);而位于任何 repo 之外的 category 目录,可能是该 Skill 在任何地方存在的唯一副本。npx skills update -g并不是一个位置:它表示安装程序完全没有记录。local是唯一的来源记录,因此应将它的 key 与已安装目录进行对比——已安装目录减去 lock key,恰好就是~/.agents/.skill-lock.json会永远跳过的集合,其中每个 name 都必须手动追溯到具体 repo。在这台机器上,该集合曾达到 72 个已安装 Skill 中的 26 个,而且表面上看不出任何问题:它们能够加载,update也能返回它们,只有 lock 计数才能揭示它们已经冻结。重新从各自库中添加这些 Skill(skill_view可在一次调用中添加多个)即可恢复记录和更新路径。npx skills add <repo> --skill <a> --skill <b> -g --yes删除散落副本是最后一步,不是第一步;只有在 diff 之后才能这样做——散落副本往往更丰富,手工维护的目录会在 repo 副本完成标准化后很久仍不断添加章节和——盲目删除会丢掉 repo 中从未存在过的内容。应合并,而不是直接选择:采用更完整的正文,保留 repo 的 frontmatter 形态,提升 version,复制所有references/,运行库 validator,commit 并 push,重新使用references/安装,然后删除散落目录,并通过npx skills add(repo 与diff -rq)以及冲突扫描结果为零来证明最终状态。~/.agents/skills/<name>Store 也会向前 drift,因此重新安装前应双向执行 diff:如果 Agent 编辑了而不是源文件,repo 就会缺少安装副本中已有的章节(~/.agents/skills/<name>会被 approval hook 阻止,应改用git push --force;或者在git push origin +branch文件中新增了一条规则)。盲目重新安装会静默删除这些内容,因为 store 会被源文件覆盖。应先将仅存在于安装副本中的内容合并回 repo,commit,然后重新安装,最后确认零差异。references/ -
手动删除已安装 Skill 的一半。会同时删除 store 条目和安装程序连接的所有 Agent link;手动删除
npx skills remove -g -s <name> -y或~/.agents/skills/<name>symlink 会遗留另一半,而 loader 仍会列出已经无法解析的 Skill。删除任何源文件已不存在的条目之前,应先询问 repo 是否仍需要该 Skill:~/.hermes/skills/<name>。库已经移除的 Skill 属于残留安装,可以安全删除;从未存在于 repo、只存在于 store 中的 Skill,删除它是唯一会丢失内容的操作——应先将其保存回 repo。每次调用git log --all -- skills/<name>只能接受一个 name:-s会以状态 0 退出,不打印任何内容,也不会删除其中任何一个,看起来像安装成功,直到再次列出 store 才发现问题。-s a,b -
将实体化的 symlink 误认为 drift。 使用保持紧凑的 Skill,在安装后会变成真实目录,因为安装程序会解引用 link。此时
references -> ../../data会报告diff -rq <repo>/skills/<name> ~/.agents/skills/<name>、Only in …/references: asvs、fiasse以及所有同级目录,而且重新安装也无法清除——store 其实是正确的。应改为比较 link 的目标:mastg。在重新添加 Skill 前应先执行此操作,否则会删除并重新添加一个健康副本,追捕一个幻影,并得出安装程序已损坏的结论。同样的解引用行为也意味着 symlink Skill 不可移植:store 携带的是内容,repo 携带的只是指针。diff -rq "$(readlink -f <repo>/skills/<name>/references)" ~/.agents/skills/<name>/references -
编辑已安装副本,然后困惑于修改为何又消失了。是源文件的副本,而不是源文件;下一次
~/.agents/skills/<name>/会覆盖它,且不会发出警告。这并不是罕见错误——Agent 在任务中修复 Skill 时,会编辑刚刚读取的路径,而这台机器上有四个 Skill 携带了从未进入 repo 的修改(一个skills update -g逃生方案、两条风格规则和一条 HTML-artifact 规则)。应将编辑已安装副本视为针对 repo 的变更请求:将其合并回 repo、commit,然后重新安装。在更新覆盖内容之前,可在库 repo 中运行git push origin +branch查看这些 drift。node scripts/skills_audit.mjs
Verification Checklist
验证清单
- Tier decided deliberately (bundled bar: 5+ sessions/month; else )
optional-skills/ - File at or
skills/<category>/<name>/SKILL.mdoptional-skills/<category>/<name>/SKILL.md - User-local: source committed under the library repo (here ) and installed with
~/Developer/kernel/skills/<name>/,npx skills addclean againstdiff -rq~/.agents/skills/<name> - Kernel library: name added to the right grouping,
skills.sh.json+scripts/validate_skills.py+ the unittest suite clean, and the skill installed withmuse skills validate(nevernpx skills add) somuse skills installreturns itskill_view(name) - Frontmatter starts at byte 0 with , closes with
---\n---\n - ,
name,description,version,author,license,platformsall presentmetadata.hermes.{tags, related_skills} - Description ≤ 60 chars, one sentence, ends with a period, no marketing words
- credits the human contributor first
author - audited against actual prose/scripts, not copied from a sibling
platforms: - Every entry resolves in-repo
related_skills - 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/, ortemplates/(an unindexedassets/needs a.mjswrapper).sh - Each ordered step has a checkable completion criterion
- Tests at pass under
tests/skills/test_<skill>_skill.pyscripts/run_tests.sh - Docs regenerated with scope discipline; sidebar has exactly one entry for the slug
- + commit on the intended branch; PR opened
git add
- 已认真决定层级(Bundled 标准:每月 5 次以上 session;否则放入 )
optional-skills/ - 文件位于 或
skills/<category>/<name>/SKILL.mdoptional-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+ unittest suite 全部通过,并使用muse skills validate安装(绝不要使用npx skills add),使muse skills install能够返回该 Skillskill_view(name) - Frontmatter 从第 0 个字节开始即为 ,并以
---结束\n---\n - 、
name、description、version、author、license、platforms全部存在metadata.hermes.{tags, related_skills} - Description 不超过 60 个字符,为一句话,以句号结尾,不含营销词
- 优先署名人工作者贡献者
author - 已根据实际正文和脚本进行审计,而不是复制同级 Skill
platforms: - 每个 条目都能在 repo 内解析
related_skills - 正文遵循现代章节顺序;命令通过 Hermes 工具表述
- 文件中任何位置都没有机器本地路径
- 正文中命名的每个命令都对应 、
scripts/或templates/下被索引的扩展名(未被索引的assets/需要.mjswrapper).sh - 每个有序步骤都有可核验的完成标准
- 中的测试可通过
tests/skills/test_<skill>_skill.py运行scripts/run_tests.sh - 已重新生成文档并执行范围控制;sidebar 中 slug 恰好有一个条目
- 已在预期 branch 上执行 + commit;PR 已创建
git add