lint 报了一屏:哪些必须修,哪些可以先放着

2026-08-09

给 agency-agents 提一个新 agent,或者只是改了一个已有 agent 的正文,仓库里等着你的第一道关是 scripts/lint-agents.sh。这个脚本一次性会吐出好几类条目,而且 ERROR 和 WARN 是混在同一份输出里的。第一次面对它的人通常会做两件事之一:要么全部埋头改完,要么看一眼觉得”都是警告”就提交了。这两种做法都不太对——因为这个脚本里恰好有一条 WARN,后果比另外两条 WARN 严重得多。

下面按排查的顺序来:先说现象,再说怎么确认自己碰到的是哪一类,然后是源码给出的处置,处置完怎么验证,最后是哪些情况根本不该往 lint 上赖。

一、现象:一次校验出来的条目分不出轻重

lint-agents.sh 脚本头部自述了它要校验的三件事:frontmatter 必须存在且包含必填字段(这一类是 ERROR)、推荐章节只给警告(WARN)、文件必须有实质内容。用法是:

./scripts/lint-agents.sh [file ...]

给了文件就只查这些文件,不给参数则扫描所有 agent 目录。仓库里带 frontmatter 的 agent markdown 有 255 个,分布在 17 个 division(数字为 2026-08-09 的仓库快照)——不带参数扫全库,条目自然不会少。

问题就出在这里:条目多,级别混排,而两个级别的实际含义完全不同。ERROR 是会让检查失败的,WARN 不会。所以”哪些必须修”这个问题,第一步不是逐条看内容,而是先按级别把这一屏切成两半。

二、怎么确认:把五道检查按级别对号入座

脚本一共五道检查(加上一道前置的行尾检查),级别在源码里是写死的,不随文件内容变化。你只要知道自己被哪一道拦住,就知道它是不是阻塞项:

检查级别说的是什么
拒绝 CRLF 行尾ERROR仓库标准是 LF,见 .gitattributes
frontmatter 分隔符存在ERROR取首尾两个 --- 之间的内容
必填字段齐全ERRORname / description / color
推荐章节存在WARNIdentityCore MissionCritical Rules,大小写不敏感匹配
正文有实质内容WARN正文词数小于 50 就警告
章节标题能映射到两个目标文件WARN见下一节

可执行的判定动作有三个。

第一,缩小范围只查你改的那个文件。 直接把路径当参数传进去:

./scripts/lint-agents.sh engineering/engineering-ai-engineer.md

全库扫描适合做审计,定位单个文件的问题时它只会淹没你。

第二,如果报的是 frontmatter 相关的 ERROR,先怀疑行尾而不是内容。 源码注释专门解释了这个坑:行尾多一个 \r,会让后面的 frontmatter 检查报出令人困惑的”missing frontmatter ---“——明明你打开文件,第一行就是 ---。所以判定动作是:先看这个文件的行尾是不是 CRLF,而不是反复检查那三个字段的拼写。这一条在 Windows 上尤其容易中招,编辑器或 Git 配置换掉行尾,文件看起来一点毛病没有。

第三,必填字段就照着源码那一行核。 lint-agents.sh 里写的是:

REQUIRED_FRONTMATTER=("name" "description" "color")

只有这三个是必填。仓库样例里还会出现 emojivibe——vibe 是一句话人设标语,被 app 一类的目录工具消费——但它们不在这个数组里,缺了不报 ERROR。有人看着样例照抄,以为五个字段都得齐,其实没有必要。

三、处置:三条 ERROR 全修,三条 WARN 分开对待

三条 ERROR 没有商量余地,它们决定检查是否通过:行尾统一成 LF;文件开头和 frontmatter 结尾各有一行 ---namedescriptioncolor 三个键齐全。

三条 WARN 就要分开看了。

“推荐章节存在”这条,是排版建议。 它匹配的是 Identity、Core Mission、Critical Rules 三类标题,大小写不敏感。缺了不会让检查失败。如果你写的 agent 结构上确实没有这几节,先放着不修是可以接受的。

“正文词数小于 50”这条,当成信号看。 它警告的是文件基本没内容。一个正文只有几十个词的 agent,提示词本身大概率也没写完。它不阻塞,但它在提醒你这个文件还没写好。

“章节标题能映射到两个目标文件”这条,别当成排版建议。 这是本文真正想说的那一条。classify_header_target() 会把每个 ## 级标题分流到两个目标:命中 identity、learning + memory、communication、style、critical rule、rules you must follow 这几个关键词的标题去 SOUL.md,其余全部去 AGENTS.md。lint 要求两边都至少有一个标题,否则各报一条 WARN,措辞是 no section headers map to SOUL.md in convert.sh... to AGENTS.md in convert.sh

注意警告文案里那半句 in convert.sh。这两个文件名对应的是 convert.sh 里 OpenClaw 格式的工作区结构:openclaw-workspace 这个 format 会给一个 agent 生成一个目录,正文按 ## 标题关键词拆成 SOUL.md 和 AGENTS.md 两个文件,默认桶是 agents。换句话说,你给章节起的标题名,实际上是在给转换产物做路由。这是一处看起来只是排版、实际上有下游后果的设计——如果所有标题都落到同一个桶,转换出来的另一个文件里就没有内容可写。

所以处置口径是:如果你的目标工具链里包含 OpenClaw,这条 WARN 应当按硬伤处理;如果完全用不到那条转换路径,它才退回成建议。判断依据不在 lint 里,在你打算装到哪几个工具。

四、处置后怎么验证

先重跑单文件 lint。 还是那条带路径的命令,确认原来报的条目消失。这是最直接的一步。

再跑原创性检查。 scripts/check-agent-originality.sh 与 lint 是两把不同的尺子:它把候选 agent 和整个已有花名册做对比,用实体中性化后的 8 词 shingle 重叠率打分,把 vietnam/chinese/douyin/tiktok/korean 这类专有名词中性化掉,让”换个国家名的换皮 agent”藏不住。两个默认阈值是 ORIGINALITY_FAIL=40(达到或超过这个百分比视为重复,退出码 1)和 ORIGINALITY_WARN=20(达到或超过给警告,不失败),都可以用环境变量覆盖。源码注释给出的库内校准结论是:现有 agent 库里同一对之间最差相似度约 1.5%,中位数 0%——所以双位数就已经是强异常,默认阈值留了很宽的误判余地。这个脚本依赖 python3,缺了直接退出码 2;给文件参数就只查这些文件(CI 用于变更文件),不给参数则全库两两互查。

如果你动的不只是一个 agent 文件,还要跑对应的一致性脚本。 仓库把”配置漂移”当成明确的敌人,几个脚本各守一段:

脚本守什么
check-divisions.shdivision 列表要与磁盘目录、convert.shlint-agents.sh 里的 AGENT_DIRSlint-agents.yml 的路径过滤器一致
check-tools.shtools.json 要与 install.shALL_TOOLSconvert.sh 的转换器集合一致,条目缺 id/label/kebab/format/installKind/dest 就失败
check-runbooks.shrunbook 里引用的每个 slug 必须能解析到真实存在的 agent 文件

新建 division 的正确顺序,divisions.json_note 里写得很清楚:建目录 → 在 divisions.json 加条目 → 跑 scripts/check-divisions.sh → 按它的报错去更新它指向的地方。顺序反过来做,你就是在跟脚本的报错捉迷藏。

以上脚本的职责划分来自仓库源码与 divisions.json_note,我们没有逐项运行过,以仓库最新内容与各脚本源码里的用法说明为准。

五、什么情况说明不是 lint 的问题

这一节比前面几节更值钱,因为它能省掉你在错误方向上的时间。

lint 全绿,但 Cursor 里”像没装”。 这跟校验没关系。convert.sh 生成的 .mdc 文件里,alwaysApply: falseglobs: "" 是写死的,也就是说转换出来的 Cursor 规则默认不会自动生效,需要在 Cursor 里按需引用。往 lint 上找原因是找不到的。

装完在目标工具里翻不到。 先看落点。install.sh 的注释里,opencodecursoraiderwindsurf 这四个写的是当前目录,其余多为家目录(例如 Claude Code 是 ~/.claude/agents/)。你在哪个目录下执行,决定了是给这个项目装还是给整个账户装。这是安装 scope 的问题,不是文件格式的问题。

Aider 或 Windsurf 里没法只装某一个 agent。 答案在 tools.jsoninstallKind:这两个是 roster,所有 agent 会合并成一个文件(分别是 CONVENTIONS.md.windsurfrules),产物形态本来就不是一 agent 一文件。Hermes 更特殊,它是 plugin,是一个构建出来的产物,不能按 agent 渲染成字符串,只能走 CLI。这些都与 lint 无关。

agent 装上了,但提示词效果不理想。 这条要说得直白些:lint 检查的是结构与重复度,不评估提示词写得好不好用。它不会告诉你这个人设是不是有效,也不会告诉你这段职责描述模型听不听得懂。别指望校验脚本承担质量评审的职责。

被校验的文件根本不是 agent。 integrations/convert.sh 写出的转换产物,strategy/ 放的是没有 agent frontmatter 的 playbook 与 runbook,examples/scripts/ 同理——这四个目录在 check-divisions.sh 里通过 NON_DIVISION_DIRS 被排除。如果你手动指定路径去 lint 这些目录下的文件,报出来的 frontmatter 缺失是意料之中的,不是 bug。

在 Windows 上脚本压根跑不起来。 install.sh 的注释列出的平台是 Linux、macOS(需要 bash 3.2+)、Windows 的 Git Bash / WSL。在 PowerShell 或 cmd 里直接执行 .sh,那是运行环境的问题,跟脚本内容无关。

最后补一句节奏上的建议:真要往 ~/.claude/agents/ 这类共享目录写东西之前,install.sh 有个 --dry-run,只打印计划不落盘。lint 管的是单个文件写得对不对,--dry-run 管的是这一批文件会落到哪儿——两件事都确认过,再动真格。

延伸阅读


本文依据 agency-agents 官方仓库(github.com/msitarzewski/agency-agents)的 README、 tools.jsondivisions.jsonscripts/ 下的安装与校验脚本整理,核对日 2026-08-09。 本文内容为仓库源码与文档口径,我们没有安装或运行过其中任何 agent。 目录、脚本与安装路径随上游更新而变动,请以仓库最新内容与 ./scripts/install.sh --help 的实际输出为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。