明明开头就是三个横杠,为什么报 missing frontmatter
在 Windows 上给 agency-agents 加一个自己的 agent,或者只是把某个现成的 agent markdown 拿编辑器打开改两行再存回去,很容易撞上一个让人怀疑人生的报错:校验脚本告诉你这个文件没有 frontmatter,可你把文件拖到编辑器里,第一行清清楚楚就是 ---。
这篇把这类报错拆成五步来讲。事实全部来自 agency-agents 仓库的 scripts/lint-agents.sh 等脚本源码与 tools.json、divisions.json,我们只读了源码,没有安装或运行过其中任何一个 agent、任何一个脚本,所以下文不会出现任何「跑起来是什么样」的描述。
一、现象长什么样
典型的触发路径是这样的:你在 Windows 下新建或编辑了一个 agent 的 .md,编辑器按平台默认把行尾存成了 CRLF;或者 Git 的换行转换设置让检出到工作区的文件带上了 \r。然后你跑仓库自带的校验,得到的结论是「frontmatter 缺失」这一类的硬错误。
之所以看起来矛盾,原因在 lint-agents.sh 的源码注释里写得很直白:行尾多出来的那个 \r 会让后面的 frontmatter 检查报出令人困惑的 missing frontmatter ---,明明文件开头就是 ---。换句话说,报错文案指向的是 frontmatter,真正的病灶在行尾——你眼睛看到的 --- 和脚本读到的字符串,差了一个不可见字符。
正因为这个错误信息会误导人,仓库干脆在所有 frontmatter 检查之前,单独加了一道「拒绝 CRLF 行尾」的检查,级别是 ERROR,仓库标准是 LF,依据写在 .gitattributes 里。换句话说,这道检查就是专门拦在误导性报错前面的:它先以 CRLF 的名义把行尾问题喊出来,你就不必再去猜那句 frontmatter 提示到底在说什么。源码注释里「明明文件开头就是 ---」那句话,描述的正是没有这道拦截时读者会陷入的困惑——所以看到 CRLF 报错反而是好事,它已经把根因直接告诉你了。
二、怎么确认真的是行尾问题
先给可执行的判定动作,不要凭感觉改文件。
动作一:只对这一个文件跑校验。 lint-agents.sh 的用法是 ./scripts/lint-agents.sh [file ...],给了文件就只查这些文件,不给参数才扫描全部 agent 目录。
./scripts/lint-agents.sh engineering/engineering-ai-engineer.md
单文件跑的意义在于把噪音压掉:全库扫描时几百个文件的 WARN 混在一起,你很难判断哪一条对应你刚改的那个文件。
动作二:看它报的是哪一类。 校验里 ERROR 与 WARN 是两回事,只有 ERROR 才是硬失败:
| 检查 | 级别 | 说明 |
|---|---|---|
| 拒绝 CRLF 行尾 | ERROR | 排在所有 frontmatter 检查之前,仓库标准是 LF |
| frontmatter 分隔符存在 | ERROR | 取首尾两个 --- 之间的内容 |
| 必填字段齐全 | ERROR | name、description、color 缺一不可 |
| 推荐章节存在 | WARN | Identity、Core Mission、Critical Rules,大小写不敏感 |
| 正文有实质内容 | WARN | 正文词数少于 50 就警告 |
| 章节标题能映射到两个目标文件 | WARN | 见后文 SOUL.md / AGENTS.md 的分流 |
如果输出指向 CRLF,判定结束,就是行尾。如果指向 frontmatter 而你又是在 Windows 下编辑过这个文件的,那 \r 依然是第一嫌疑。
动作三:用通用手段直接看行尾。 这一步属于通用的 Git 与编辑器排查手段,不是 agency-agents 官方文档的内容:多数编辑器状态栏会显示当前文件是 LF 还是 CRLF,Git 侧则可以检查仓库与全局的换行转换配置是不是把文件在检出时改写了。这两处一对,基本能坐实。
顺带提醒 Windows 读者一句:install.sh 的注释里写明支持的平台是 Linux、macOS(需要 bash 3.2 以上)以及 Windows 的 Git Bash / WSL。也就是说这些脚本本来就预期你在类 Unix 的 shell 环境里跑,而不是在原生 cmd 或 PowerShell 里,环境选错的话你会遇到另一批完全不同的问题。
三、源码语义给出的处置
处置本身没有玄机:把文件转成 LF,与仓库 .gitattributes 声明的标准对齐,让第一道检查放行,后面的 frontmatter 检查才有意义。
值得强调的是修的顺序。\r 没清掉之前,你对 frontmatter 做的任何修改都可能是在治标:你以为字段写错了,反复调 name 和 description 的写法,其实脚本压根还没读到字段那一层。先让行尾正确,再看它还报不报 frontmatter,这是两次不同的判断,别合成一次做。
清完行尾之后,真正要看的是必填三项有没有齐:name、description、color。这三个字段的缺失是 ERROR 级;样例文件里出现的 emoji 和 vibe 不在必填列表里,属于可选字段,其中 vibe 是一句话人设标语,会被 app 一类的目录工具消费。
另外,这套检查不是只在你本机跑。仓库把「配置漂移」当成明确的敌人,check-divisions.sh 会校验 division 列表与磁盘目录、convert.sh 和 lint-agents.sh 里的 AGENT_DIRS 数组、以及 lint-agents.yml 的路径过滤器保持一致,不一致就让构建失败。所以一个带 CRLF 的文件即使在你本地被你忽略掉,走到 CI 也一样过不去。
四、处置后怎么验证
第一,重跑同一条单文件校验命令,确认 ERROR 归零。判定标准很明确:ERROR 才决定成败,WARN 不会让检查失败。看到「推荐章节缺失」或「正文词数不足」这类警告,不必回头再折腾行尾。
第二,确认没有把 WARN 当成新的失败去追。 这里有一条容易误伤的:校验会把每个 ## 级标题按关键词分流,命中 identity、learning 与 memory、communication、style、critical rule、rules you must follow 的标题归到 SOUL.md,其余全部归到 AGENTS.md,两边都必须至少有一个标题,否则各报一条 WARN。这条分流规则和 convert.sh 转成 OpenClaw 工作区格式时的拆分逻辑是同一套——你给章节起的标题不只是排版,它实际上是路由。但它仍然只是 WARN,不影响这次修复的结论。
第三,如果这个 agent 是你新写的,再过一遍原创性检查。 check-agent-originality.sh 会把候选 agent 与整个花名册做实体中性化后的 8 词 shingle 重叠率比对,默认阈值是 ORIGINALITY_FAIL=40(达到或超过该百分比退出码 1)与 ORIGINALITY_WARN=20(达到或超过则警告但不失败),两个值都可以用环境变量覆盖。源码注释给出的库内校准数据是:现有 agent 库里同一对之间最差相似度约 1.5%,中位数 0%——所以双位数就是强异常。它依赖 python3,缺了会以退出码 2 直接退出。
顺序上先结构后原创性:行尾和 frontmatter 是格式问题,重复度是内容问题,混着修只会互相干扰。
五、什么情况说明不是这个原因
这一节才是排查文章相对「报错大全」的增量。下面几种情况看起来相邻,但根因都不是行尾。
报错指向必填字段,而不是分隔符。 如果输出说的是 name / description / color 里某一项缺失,那就是字段问题,跟 \r 没关系,补字段即可。
分隔符本身不成对。 检查取的是首尾两个 --- 之间的内容。如果你只写了开头那个、第二个 --- 漏了或者位置不对,内容取不出来,报错一样会归到 frontmatter 这一类。这种情况下把行尾转成 LF 不会有任何变化。
只剩 WARN。 推荐章节缺失、正文词数少于 50、章节标题没能同时映射到两个目标文件,这三类都是警告。它们不会让检查失败,也说明你的 frontmatter 已经被正确读到了。
文件根本不在 agent 目录里。 divisions.json 的 _note 写明并非每个顶层目录都是 division:integrations/ 是 convert.sh 写出的每工具转换产物而不是源 agent,strategy/ 放 playbook 与 runbook、没有 agent frontmatter,examples/ 与 scripts/ 同样被排除。一个目录要成为 division,必须至少包含一个带 frontmatter 的 agent 文件。你在这些目录里新建的 markdown,不会按 agent 被扫到,自然也谈不上 frontmatter 报错。
校验全绿,但工具里「像没装」。 这是另一条完全不同的线索。convert.sh 渲染 Cursor 的 .mdc 时,frontmatter 里的 globs: "" 和 alwaysApply: false 是写死的,也就是转换出来的规则默认不会自动生效,需要在 Cursor 里按需引用。装完没反应多半是这个,跟行尾、跟 frontmatter 校验都无关。
找不到某个 agent 的独立文件。 这要看 tools.json 里的 installKind:per-agent 是每个 agent 一份产物,roster 是所有 agent 合并成一个文件(Aider 的 CONVENTIONS.md、Windsurf 的 .windsurfrules 属于这一类),plugin 则是一个构建产物、不能按 agent 渲染成字符串(Hermes 是唯一一个)。在 roster 类工具里翻不到单个 agent 文件,是产物形态决定的,不是文件坏了。
改完的内容没生效在目标目录。 链路是源 agent markdown 经 convert.sh 渲染进 integrations/,再由 install.sh 拷到各工具的配置目录。源文件改了但产物没重新生成,看到的自然还是旧的。想在写盘前先看一眼计划,install.sh 提供了 --dry-run,只打印计划不写任何东西;--link 则是用符号链接代替拷贝,改动会自动传播。在往 ~/.claude/agents/ 这类共享目录批量写文件之前,先 --dry-run 看一眼是很划算的习惯。
最后提醒一句边界:这套校验管的是结构与重复度,它检查 frontmatter 齐不齐、有没有 CRLF、和已有 agent 撞不撞车,但它不评估提示词写得好不好用。全绿只代表格式过关。
顺带一个背景数字:这个仓库带 frontmatter 的 agent markdown 共 255 个,分布在 17 个 division,tools.json 覆盖 16 种工具;仓库在 2026-08-09 的快照里有 140729 个 star。star 数只说明关注度,不能由它推出质量、稳定性或是否适合你的项目。
延伸阅读
本文依据 agency-agents 官方仓库(github.com/msitarzewski/agency-agents)的 README、
tools.json、divisions.json、scripts/ 下的安装与校验脚本整理,核对日 2026-08-09。
本文内容为仓库源码与文档口径,我们没有安装或运行过其中任何 agent。
目录、脚本与安装路径随上游更新而变动,请以仓库最新内容与 ./scripts/install.sh --help 的实际输出为准。