project-forge
AGENTS.md1 starsChanged 5 days agoMITView on GitHub
本文件在每次会话开始时自动注入上下文。它是本 skill 与 AI 之间的接口契约,是项目的 唯一权威约定;任何修改必须遵守本文,并以「自检全绿 + 文档同步 + 提交存档」为完成标准。 做任何事,先想清楚「根本」在哪里,再动手。追求一次解决一类问题,而不是反复处理 同一个问题的表象;追求整体清晰、可维护、可演进,而不是越堆越乱。这是所有行动的 最高准则,本文的一切方法都是它的落地方式。 - 治本,不治标:遇到问题先找根因,想清楚「怎样才算真正解决」再动手;修一次, 就要让这一类问题不再复发——只把表面压下去,不算解决; - 写原则,不写死:能归为通则的,绝不写成个案特例;能用结构和机制表达的, 绝不靠写死的细节——能做成配置、参数、规则表或自动探测的,就不把具体值、具体 路径、具体名字硬编码进代码,环境变了、需求变了,代码不用重写;但动态化要真的 服务于这个目的,不是为动态而动态。不依赖特定细节的做法,环境怎么变都不会失效 ——通则即稳定; - 融入结构,不打补丁:任何新增(能力、规则、流程、工具用法)都要找到它在整体 中的位置并融入进去,而不是在边上再挂一块;同一个问题第二次出现,说明上次治的 是标,回头治根; - 保持清朗,不堆屎山:所有产物——方案、清单、目录、文件、流程、代码——都要 结构清晰、职责分明、可维护可演进,改动时知道该动哪一处,而不是牵一发动全身; 宁可多想一步,也不留下一堆难以理解、难以修改的堆积物。每次改动后自问:这次 之后,还能不能在不弄坏别处的前提下接着往下写?答不上来,就是屎山又长高了 一层,回退一步重做。 先尽全力想出最优解,这是默认动作:把请求做到能力范围内最好,直接说清最佳方案 是什么、为什么是它,不偷懒,不把决定权随手丢回去。 给一个还是给几个,现场判断,标准只有一条:这个选择是否取决于只有使用者自己 知道的实际情况。不是——只给最优解;是——给出几个方案让他挑。多方案必须是方向 不同、各自完整可用的平行路线(「都行得通,只是取向不一样」),不是从保守到激进 的梯子,也不拿明知不成立的选项凑数——一个稳妥但不成立的方案没有任何价值。不搞 「永远给一个」或「永远给三个」的套路。 一次想到底:设计必须一步到位、充分考虑清楚,不分期、不写「第一版先这样,然后 观察某某再调整」这类试探性措辞。(这条管*方案设计*;代码实现仍按「最小可用、 增量扩展」走,两者不矛盾。) 取舍要说明影响,且始终是 UX(使用者用起来顺不顺手)、DX(以后改、以后加功能 累不累)、AX(后续接手的人能不能看懂、能不能安全改动)三件事同时变好,并且不 破坏任何已有东西。需要取舍时说清楚对日常使用和后续维护的实际影响;普通取舍自己 决定,差别明显、或以后很难撤回的决定才交给使用者选。 前三条动手前问,第四条动手后问: - 简单成熟,不造轮子:优先选简单、成熟、被验证过的方案;动手前先问 「已有的能不能用」,避免过度设计、重复造轮子; - 先看再动:修改或新增前,先检查项目现有代码、依赖与文档,摸清现状 与既有约定,再动手; - 最小可用,增量扩展:从最小可用版本起步,按真实需要逐步扩展,不为 未来需求提前叠加复杂度; - 模块清晰,职责单一,复用已有:模块边界清晰、职责单一;优先复用 已有能力,能并入现成的就不另起炉灶; - 为长期维护决策:架构决策考虑长期维护成本,避免只解决眼前问题的 临时方案——今天的临时补丁,是明天要还的债; - 小改动,保稳定:修改前先理解现有实现,尽量小范围改动,确保已有 功能稳定;能少动就不多动; - 高效稳定:既不慢也不脆——不在每次运行都重复做白工,也不让一次 小异常把整个流程带崩; - 修故障修根因,不糊一层:先查清错误为什么发生再动手,修完这一类问题 就不该再来;不许用「加个判断挡住它」「出错就重试一次」把症状往后推。 被替换掉的旧逻辑一并清干净,不留两套做法并存; - 不修好一个、弄坏一个:每改一处,重新确认跟它相关的功能还正常; 改完就宣布完成、不去看别处,等于把一个故障换成了一个更隐蔽的故障; - 做完必须反复实测:不能靠「看起来对了」——代码真跑一遍,改完确认真的 好了、别处也没坏,脚本、配置、文档同样要过一遍;来回多验几遍,涉及好几处 的地方每处都验。判断标准:拿得出实际跑过的结果才算完成,拿不出就是没做完;…
- Commits and pushes
In this repositoryOne other file it gives its agents.
- project-forgeSkill
The file
Files
project-forge — 项目说明(会话自动注入)
本文件在每次会话开始时自动注入上下文。它是本 skill 与 AI 之间的接口契约,是项目的 唯一权威约定;任何修改必须遵守本文,并以「自检全绿 + 文档同步 + 提交存档」为完成标准。
<!-- project-forge:kernel:start -->行事总纲(最高准则,优先于一切具体方法)
做任何事,先想清楚「根本」在哪里,再动手。追求一次解决一类问题,而不是反复处理 同一个问题的表象;追求整体清晰、可维护、可演进,而不是越堆越乱。这是所有行动的 最高准则,本文的一切方法都是它的落地方式。
四条准则
- 治本,不治标:遇到问题先找根因,想清楚「怎样才算真正解决」再动手;修一次, 就要让这一类问题不再复发——只把表面压下去,不算解决;
- 写原则,不写死:能归为通则的,绝不写成个案特例;能用结构和机制表达的, 绝不靠写死的细节——能做成配置、参数、规则表或自动探测的,就不把具体值、具体 路径、具体名字硬编码进代码,环境变了、需求变了,代码不用重写;但动态化要真的 服务于这个目的,不是为动态而动态。不依赖特定细节的做法,环境怎么变都不会失效 ——通则即稳定;
- 融入结构,不打补丁:任何新增(能力、规则、流程、工具用法)都要找到它在整体 中的位置并融入进去,而不是在边上再挂一块;同一个问题第二次出现,说明上次治的 是标,回头治根;
- 保持清朗,不堆屎山:所有产物——方案、清单、目录、文件、流程、代码——都要 结构清晰、职责分明、可维护可演进,改动时知道该动哪一处,而不是牵一发动全身; 宁可多想一步,也不留下一堆难以理解、难以修改的堆积物。每次改动后自问:这次 之后,还能不能在不弄坏别处的前提下接着往下写?答不上来,就是屎山又长高了 一层,回退一步重做。
方案与决策
先尽全力想出最优解,这是默认动作:把请求做到能力范围内最好,直接说清最佳方案 是什么、为什么是它,不偷懒,不把决定权随手丢回去。
给一个还是给几个,现场判断,标准只有一条:这个选择是否取决于只有使用者自己 知道的实际情况。不是——只给最优解;是——给出几个方案让他挑。多方案必须是方向 不同、各自完整可用的平行路线(「都行得通,只是取向不一样」),不是从保守到激进 的梯子,也不拿明知不成立的选项凑数——一个稳妥但不成立的方案没有任何价值。不搞 「永远给一个」或「永远给三个」的套路。
一次想到底:设计必须一步到位、充分考虑清楚,不分期、不写「第一版先这样,然后 观察某某再调整」这类试探性措辞。(这条管方案设计;代码实现仍按「最小可用、 增量扩展」走,两者不矛盾。)
取舍要说明影响,且始终是 UX(使用者用起来顺不顺手)、DX(以后改、以后加功能 累不累)、AX(后续接手的人能不能看懂、能不能安全改动)三件事同时变好,并且不 破坏任何已有东西。需要取舍时说清楚对日常使用和后续维护的实际影响;普通取舍自己 决定,差别明显、或以后很难撤回的决定才交给使用者选。
动手前后四问(适用于任何任务)
前三条动手前问,第四条动手后问:
- 我这是在治标,还是在治本?
- 这个做法能覆盖一类问题,还是只覆盖眼前这一个?
- 做完之后,整体是更清晰了,还是更臃肿了?
- 我实际跑过了吗,凭什么说它真的成了?
工程落地纪律(改代码、写文档、新增能力时的具体法则)
- 简单成熟,不造轮子:优先选简单、成熟、被验证过的方案;动手前先问 「已有的能不能用」,避免过度设计、重复造轮子;
- 先看再动:修改或新增前,先检查项目现有代码、依赖与文档,摸清现状 与既有约定,再动手;
- 最小可用,增量扩展:从最小可用版本起步,按真实需要逐步扩展,不为 未来需求提前叠加复杂度;
- 模块清晰,职责单一,复用已有:模块边界清晰、职责单一;优先复用 已有能力,能并入现成的就不另起炉灶;
- 为长期维护决策:架构决策考虑长期维护成本,避免只解决眼前问题的 临时方案——今天的临时补丁,是明天要还的债;
- 小改动,保稳定:修改前先理解现有实现,尽量小范围改动,确保已有 功能稳定;能少动就不多动;
- 高效稳定:既不慢也不脆——不在每次运行都重复做白工,也不让一次 小异常把整个流程带崩;
- 修故障修根因,不糊一层:先查清错误为什么发生再动手,修完这一类问题 就不该再来;不许用「加个判断挡住它」「出错就重试一次」把症状往后推。 被替换掉的旧逻辑一并清干净,不留两套做法并存;
- 不修好一个、弄坏一个:每改一处,重新确认跟它相关的功能还正常; 改完就宣布完成、不去看别处,等于把一个故障换成了一个更隐蔽的故障;
- 做完必须反复实测:不能靠「看起来对了」——代码真跑一遍,改完确认真的 好了、别处也没坏,脚本、配置、文档同样要过一遍;来回多验几遍,涉及好几处 的地方每处都验。判断标准:拿得出实际跑过的结果才算完成,拿不出就是没做完;
- 不立碑,不写履历:不为一次意外新增永久规则或纪念性注释——规则要来自 真实的结构需要,而不是来自一次意外的惊吓;代码、脚本、配置、文档、文件名 只写它现在做什么,不写改过什么、谁改的、旧版本什么样,也不写 「以前是…改成…」这类履历——履历放提交记录里。判断标准:一个从没看过 这个项目的人,只看这一份能不能正确理解?不能,就是里面混进了过时的东西。
任务编排、搜索调研、文件整理……一切具体方法,都是这条总纲的落地方式。
本文件的定位与编辑规则
本文件的地位:随仓库走,是项目的契约,不是某一个人的私有笔记。它描述 「这个项目是什么、要求什么」,而不是「谁在维护它」。这条区分决定了本文能写什么:
| 能写(对任何接手者都成立) | 不能写(随人而变) |
| 机制、架构、不变量、硬性规范 | 某个账号的用户名、邮箱、发布作用域 |
| 构建 / 测试 / 发布该做什么、按什么顺序 | 推送目标、发布目标这些取值 |
| 为什么这样要求(意图与边界) | 一次性的版本号、日期、某人当时的决定 |
操作者取值一律现场取:正文里凡是需要具体值的地方(仓库地址、项目标识、远端、 署名、发布目标),都从项目自身的权威来源读取,而不是写在本文里。可用的权威来源 随技术栈不同而不同,常见的几类:
- 清单与元数据文件:项目标识、版本、依赖、发布范围、入口与产物声明;
- 版本控制配置:远端地址、当前分支、提交署名(
git remote -v、git config user.name、git config user.email); - 账号登录状态:当前登录的账号与作用域(各生态的
whoami一类命令); - 项目已有的约定文件:既有文档、既有配置、既有目录结构所体现的事实。
上面只是常见类别,不是固定清单——项目里实际存在哪几个,就以哪几个为准;一个都 不存在时,说明该项目还没建立这些事实,那就先建立,再回填本文。项目是这些事实的 唯一权威来源:换人、换远端、换发布目标时,改这些真实配置即可,本文不需要跟着改, 也不会失准。
载体名称不写死:不同技术栈的清单文件、配置文件名各不相同。本文只写「从哪里取」, 不写「叫什么名字」;需要举例时举例,但举例不是规范——接手者用什么技术栈,就以那个 技术栈的权威来源为准。
这条只管本文,不管给人看的文档。README 与 CONTRIBUTING 面向使用者和贡献者, 要能直接复制粘贴,因此必须写具体地址与命令;易主时跟着改一次即可(改动集中在 安装命令与反馈渠道几处)。区分标准是读者是谁:读者是 AI(需要推理、会读项目) 就现场取值,读者是人(需要照做)就给现成命令。
别人 fork 之后怎么办:直接沿用即可。本文的机制与规范描述的是这个项目的做法, 对任何接手者同样成立;而所有「取值」都是现场读的,会自动指向他自己的仓库与账号。 他若想改工作方式(例如换分支模型、换 CI),按下面的编辑规则改本文即可——这正是本文 「是活的」的含义。因此本文放在仓库里的意义不只是备份:它是接手者(以及接手者的 AI)理解这个项目的最短路径;哪天仓库只剩本文和代码,也不会有「只有原作者才知道」的 隐性约定。
本文件是活的:它随项目演进被更新、增删、重构,不是一份永不变化的快照。会话中 若发现本文与代码现状不一致,以「本文意图 + 代码现状」为准,并按下面规则修正本文。
AI 有权编辑本文件。出现以下情况时,AI 应直接更新本文件(并提交到版本控制), 而不是绕过它、只做口头约定、或把特殊处理写死进代码:
- 规则过时或与代码行为不符;
- 用户要求调整项目约定;
- 发现本文无法覆盖的新场景,且该场景会反复出现;
- 本文表述不清、导致理解歧义。
编辑本文件的小规矩:
- 面向 AI 阅读:专业、准确、可执行;不写面向终端用户的科普内容(用户要求大白话时, 在对话中解释,不写进本文件)。
- 与代码一致:修改项目行为后,若本文描述失准,须同步修正;发现过时描述应主动更新。
- 写意图不写快照:优先表达「为什么」与「边界」,让未来会话能推理到未覆盖的情形; 避免堆砌一次性补丁式的例外条款,避免记录无意义的日期 / 版本号。
- 精炼:不重复、不冗余;结构清晰(标题层级 / 表格 / 代码块)。
- 引用他节的条目数不写数字:写「那一节里那几件」,不写「那一节的三件事」——条目会 随表格增减,而这类相对引用没有任何检查盯得住。确需指路时给节名,条目数以那一节为准。
- 遵守项目硬性规范(禁 emoji 等);涉及用户可见能力或命令变化时,同步更新 README。
文档同步:README 面向人(能力 / 安装 / 使用 / 命令),本文件面向 AI(机制 / 规范 / 流程),两者描述同一项目。AI 修改项目或本文件后,应检查 README 是否仍准确, 不匹配时同步更新并提交。
<!-- project-forge:kernel:end -->环境与上下文(以实际环境为准,不依赖固定值)
- 本 skill 不绑定宿主。 它用的是通用的
SKILL.md格式:入口文件加references/、templates/、scripts/三个同级目录(scripts/dev/只服务本仓库,见「用户包与仓库 自用」);脚本是纯 Node,不调用宿主接口。因此凡支持该格式的宿主都能加载它。 不要把某个具体宿主的名字、环境变量或 skill 根路径写成前提——写进本文件、README 或任何产出,都会在换宿主那天变成假话。 - frontmatter 的字段分两档:
name与description是必需且各家共通的核心字段, 缺任一整份会被丢弃;其余是可选的宿主扩展(whenToUse、user-invocable、metadata等),加之前先读该宿主的解析实现,并且只加真的会被用起来的字段——不认识的宿主会 忽略它们,未实测过的宿主不做保证。驼峰旧键(userInvocable一类)会让整份被丢弃, 一律用连字符形式。 - 位置由本文件所在目录直接推导,不写死绝对路径;skill 整体迁移后相对关系不变。
- 资源引用一律用相对路径(形如
references/<文件名>.md)。宿主会把它们解析到 skill 自己的目录下,这是这套格式的一部分,也是它跨宿主可用的原因之一。写占位示例时用尖 括号包住可变部分,这样自检不会把它当成一个真实存在的文件。 - 目录名必须与 frontmatter 的
name逐字相同。 - 本项目零依赖:
scripts/只用运行环境内置模块,只用 JavaScript。不引入第三方 依赖,也不引入第二个运行时——它是被 AI 在各种陌生项目里调用的工具,不能要求先装 东西。README 的「开始之前」明说运行要求只有 Node 一项、不要求任何别的东西,那条承诺 由preflight.mjs守住(它检查scripts/下只有.mjs)。 - 运行要求只有一项:Node.js 16 或更高(下限来自
String.prototype.replaceAll等 语言特性)。唯一的例外是两个要联网的脚本(release-notes.mjs、check-badges.mjs) 用了 Node 18 才有的全局fetch,跑它们要 18+。git可选——没有它的能力会如实报告 并跳过,不会假装成功。README 的「开始之前」一节是这条要求的权威表述(例外也写在 那里),改动运行要求时两处一起改。 - 改
SKILL.md、references/、templates/、scripts/后下一次调用即生效, 无需重启服务。
项目定位
一个通用的「项目锻造」skill:给任意项目目录,自己勘察现状、自己判断该做哪几件事, 把版本管理、远端仓库、文档套装、发布通道一次配到位;收尾时问用户要不要把它留下的 痕迹清干净。
它只做治理,不生成项目本身:不选技术栈、不创建源码与框架配置文件(那是脚手架的事)。 治理跑在骨架就绪之后——对空目录说「初始化项目」时,先让项目存在,再走本 skill。
它必须适用于各种技术栈(Node、Python、Rust、Go、Java、纯文档目录),插件类项目
(宿主加载代码:已有专章见 references/plugins/,无专章按 references/plugin-project.md
的判定协议处理),以及能力目录类项目(宿主加载指令:判据见 references/skill-project.md,
专章索引与生长规则另起一份,因为两边的判定问题没有一条答案是相同的),还有 skill 项目本身。
判断依据永远是勘察出的事实,不是预设的清单。
本项目自己就是它的第一个用户:本仓库的版本管理、文档与持续集成,都是用它自己的标准 配起来的。这不是宣传语,是验收方式——它教别人怎么做,自己就得那么做。
架构与关键不变量
四层结构(职责单一,互不复述)
| 层 | 目录 | 职责 | 读者 |
| 入口 | SKILL.md | 原则、能力矩阵、执行流程、硬门控 | AI,每次调用都读 |
| 决策 | references/ | 按情境读的判据与方法 | AI,按需读全文 |
| 落盘 | templates/ | 可直接起步的文件骨架;agents-kernel.md 逐字注入 | 脚本与人 |
| 工具 | scripts/ | 勘察、注入、自检、交棒勘察 | 命令行 |
references/ 与 templates/ 必须分开:前者读来决策,后者拿来落盘。混在一起
最容易出的错是 AI 把本该逐字复制的内核「改写」一遍;分成两个目录从结构上压住了这个错。
关键不变量
- 单一真相源:通用内核只在
templates/agents-kernel.md里存在一份,项目里的AGENTS.md由scripts/compose-agents.mjs注入。不得手抄——手抄必然漂移,preflight.mjs会报不一致。 - 入口不复述细节:
SKILL.md只写原则与指针,细节一律在references/。两处都写 机制,就一定会有两处不一致的那天。 - 判定基于事实:能力矩阵的输入是勘察脚本的产出。脚本读不到的字段一律留空, 不编造——空的含义是「这项事实不存在」,此时的正确动作是去读项目或问用户。
- 零依赖:
scripts/只用内置模块。这条由preflight.mjs自动检查。 - 不写死取值:所有文档里凡是需要具体值的地方(账号、路径、版本号、仓库地址), 一律写「从哪里取」。项目自身是这些事实的唯一权威来源。
- 只读先行:勘察未完成不写任何文件。这条是 skill 的第一硬不变量,它自己必须先做到。
- 幂等:所有脚本重复执行结果相同;
compose-agents.mjs刷新内核标记之间的内容, 标记之外按节合并——标着「这里要人写」的节保留既有内容,纯生成内容按当前项目事实 重新求值(所以建好远端之后要再跑一次)。 - 包边界由结构与门禁保证:哪些文件进用户包由结构规则(
.gitattributes的export-ignore与scripts/lib/protocol.mjs的REPO_ONLY_*)决定,门禁拿真导出 与受控文件全集对账——不得靠人工清单(见「用户包与仓库自用」)。 - 能拆干净:本 skill 在目标项目里留下的每一样东西都要能被找出并撤掉。痕迹的判定
只认证据(标识符、模板指纹、落点路径),不认记忆;拆除是不可逆动作,未经用户
明确确认不许做。判据与顺序见
references/handoff.md。
交棒:把自己留下的痕迹清掉
skill 跑完一个项目之后,目标项目里会留着它的痕迹:标识符、未替换的模板占位符、注入 残留。留着不算交付——但拆也要用户拍板。机制分这几处,各司其职:
| 位置 | 承担什么 |
scripts/handoff.mjs | 只读勘察:找出痕迹、给出分类与依据;--verify 判「已经干净」 |
references/handoff.md | 交棒与撤销的判据、顺序、验收——判断归它,扫描归脚本 |
SKILL.md 的 P8 与硬不变量 | 收尾必问:不问就收工,等于把痕迹永久留下 |
references/survey.md 的「接着上次做」 | 交棒这件事的机器载体:重跑会话靠它知道交棒问过没有 |
脚本只读、协议给判据:扫描归机器(不会遗漏、不会误伤),取舍归人(不会乱搞)。 两边分开,是因为它们失败的方式不同——脚本可能读漏,但读漏了不会删错;协议可能漏项, 但拿它去删错东西比不删更糟。
用户包与仓库自用
仓库里的文件分两类:随用户包分发的与只服务本仓库的。分野由结构与门禁共同保证, 不靠任何人工清单——清单要人记得改,漏一行就开始说假话。
| 类别 | 放哪 | 谁用 |
| 用户包里有 | SKILL.md、references/、templates/、scripts/ 顶层与 lib/、README*、LICENSE、assets/ | 拿到包的人,以及在目标项目上跑本 skill |
| 仓库里另有 | AGENTS.md、CONTRIBUTING.md、RELEASING.md、docs/、.github/、两个点文件、scripts/dev/ | 只服务本仓库 |
结构规则在哪:.gitattributes 的 export-ignore 是那份规则的文本形态,
scripts/lib/protocol.mjs 的 REPO_ONLY_DIRS / REPO_ONLY_FILES 是它的代码形态,
两处由 preflight.mjs 核对一致。新增自用文件只做一件事:放进已有的那几处
(自用脚本进 scripts/dev/,工作记录进 docs/work/,协作设施进 .github/)——
那一整目录本来就不进包,不用改任何清单。要新增一处独立目录,才需要两处一起改。
为什么不逐文件列表:一张「本仓库还有哪些文件」的表,读者是谁都说不清——用户读它没用
(那些文件他拿不到),维护者读它也没用(东西在他手边)。而它必须有人记得改,漏一行的症状
是「文件在包里但没人知道」。结构规则不会漏:它描述的是哪几处不进包,那几处在一年里
可能一次都不变。找东西用目录本身:scripts/ 顶层随包、scripts/dev/ 自检与工具、
scripts/lib/ 共用模块、docs/ 发布说明与工作记录。
包集合判据不靠表:候选来自真跑一次的导出(git archive,见 packageMembers),
与受版本控制的文件全集对账——每个文件要么在包里、要么命中结构规则;两处都不占的那个
文件就是「没有归属」,门禁当场报出来。这一条抓的正是「清单漏一行」那类问题,而它不需要
任何人维护。不自己翻译 .gitattributes:那等于维护第二份真相,实测 git check-attr
与 git archive 对同一个文件给出过两个答案,只有后者是对的。
包自洽性:包内的相对引用必须落在包内——.mjs 的相对 import 与文档里的 Markdown 链接
指向包外即红。templates/ 不参与链接检查:那些骨架将要落到别的项目里,其中的相对链接
指向那个项目的文件。共用的实现要放在随包分发的模块里,不能放在 scripts/dev/。
持续集成对真产出物再核对一次:git archive <版本> | tar -t 列出条目,交给
preflight.mjs --package <清单> --at <版本>。判据与对象同源——内容也从那个版本读,
否则核的是「今天的规矩」与「那天的产出物」。
本仓库是这套规范的第一个用户
本文件不是「给别人看的说明」,它就是这套规范的实例:references/ 与 templates/ 里写的
做法,本仓库必须自己做到;做不到时先改自己,再改规范(硬性规范第 6 条)。落地方式是下面
这张同步对照——改了一边,另一边一起看(判据:两边说的还是同一件事吗)。
| 这次动了 | 必须一起看 |
templates/agents-project.md | 本文件的机制段;references/docs-set.md 的结构表 |
templates/contributing.md、release-spec.md | CONTRIBUTING.md、RELEASING.md(本仓库的实例) |
templates/work-item.md、references/collaboration.md | docs/work/ 里的实际记录 |
| 落盘模板 → 本仓库实例 | templates/issue-bug-report.md→.github/ISSUE_TEMPLATE/bug_report.md;issue-feature-request.md→feature_request.md;pull-request-template.md→.github/pull_request_template.md;ci-check.yml→.github/workflows/check.yml;ci-release.yml→release.yml |
任何 references/ 里的判据 | 本文件复述它的那一段;scripts/review.mjs 的对应检查 |
| 发布链路的任何一处(说明放哪、怎么发) | RELEASING.md、docs/release-notes/、.github/workflows/release.yml 三处一起 |
README.md 的结构或能力 | 与 README.en.md 成对改 |
README 卡片(assets/readme/*.svg)
手写 SVG,改它要量留白与纵向节奏。判据、断言核对方式与验收命令见
CONTRIBUTING.md 的「README 卡片」一节——那是操作手册,本文件不复述。
构建与验证(改完必跑,全绿才算完成)
本项目没有构建步骤,验证就是自检加实跑:
node scripts/dev/preflight.mjs --no-behavior # 结构自检:引用完整、内核一致、硬性规范、用户包边界
node scripts/dev/selftest.mjs # 行为自检:造 fixture 实跑,断言判定结果
node scripts/compose-agents.mjs . --check # 内核逐字一致、标记恰好一对、生成文件不缺节
node scripts/sync-toc.mjs README.md README.en.md --check # README 目录与标题是否同步
git archive --format=tar HEAD | tar -t > package-list.txt && node scripts/dev/preflight.mjs --package package-list.txt --at HEAD # 产出物与结构规则核对(与 check.yml 同一批)preflight.mjs 检查的是「坏了就一定会出问题」的结构性质,不是风格偏好:
| 类 | 查什么 |
| 可被宿主识别 | frontmatter 合法、name 与目录名一致、description 不被截断 |
| 引用不断链 | 全文引用的每个文件真实存在;每份 reference 有标题与「何时读本文件」节 |
| 单一真相源 | AGENTS.md 内核与模板逐字一致;agents-kernel.md 具备必需章节 |
| 零依赖 | scripts/ 只有内置模块、每个脚本能解析——扫磁盘不扫清单,刚建好还没登记的也过一遍 |
| 会跑的都真跑 | 勘察、注入、自检、目录同步在只读/幂等模式下跑一遍;崩溃与语法错都在这里暴露 |
| 用户包边界 | 结构规则核对、受控文件归属、包自洽性(包内相对引用必须落在包内),见「用户包与仓库自用」 |
| 数量自称与可达性 | 硬不变量与硬门控的数量与实物对账;每个 references//templates/ 文件与每个脚本都要有人指得到 |
| 硬性规范 | 全文无 emoji、BOM、构建机私有路径 |
release-notes.mjs 只覆盖「半成品被拒」(真实发送要凭据,靠发版 CI 兜底);
render-svg.mjs 依赖本机浏览器,不被自检覆盖,改卡片时手工跑。
selftest.mjs 是另一半,同样不可省:静态检查看不出「判定写错了」——survey 把 CMake
项目判成纯文档目录时,引用与格式全都正常。这类错误只能靠造 fixture 实跑发现。
副本里的 selftest.mjs 换成「只打印摘要行」的替身(skillClone 的默认行为):
不做替身会形成 preflight → selftest → preflight 套娃,实测让整套自检多花一分半。
判定逻辑由主检出那一趟覆盖。
慢要测出来,不要猜:preflight.mjs --timing 打印各步骤耗时;只想验结构加
--no-behavior(它会明说行为判定没被检查过)。并排比改前改后用
node scripts/dev/bench.mjs。
本仓库自己也过一遍交付门禁:node scripts/review.mjs . 预期「无缺失」。报了 [缺]
就改真东西,不要加 flag 把它压下去;本机状态目录(被忽略、不进版本库)落在
[待问] 不算缺失。凭据形状的夹具一律运行时拼出(selftest.mjs 顶部的 FAKE_*),
所以不需要 --secrets-reviewed;git clone 不复制仓库级署名,全新克隆里报「提交署名
缺失」是真话。
改动 templates/agents-kernel.md 后必须重新注入并提交,否则自检会红。
派生内容用工具同步,不手工维护
README.md 里有两样东西完全由别处决定,因此不手写:
| 内容 | 由什么决定 | 怎么同步 |
| 目录 | 各节标题 | node scripts/sync-toc.mjs README.md README.en.md(--check 只校验) |
| 徽章 | 外部服务返回的内容 | node scripts/check-badges.mjs README.md README.en.md(写之前先验一遍) |
目录由工具插在成对标记之间,标记之外一个字节都不动;加节、改名、删节之后重跑即同步
(不要手工编辑标记之间的内容,下次生成会覆盖它),preflight.mjs 与持续集成里都跑
它的 --check。这与「写意图不写快照」是同一个道理:快照会过期,派生关系不会。
双语说明文档成对维护:README.md(权威)与 README.en.md 是同一份文档的两个版本,
改任何一份都算用户可见变化。改一份时必须打开另一份一起看,改完要能回答「另一份同步了吗」;
不一致时以中文权威版为准。不要机械对译。
徽章不进 CI:它依赖网络与外部服务,放进 CI 会因对方抖动而误报。验证走两条路:
写之前按上表那条命令验一遍;review.mjs 每次运行也会联网验(查不完时如实报「未核对」,
不读成通过)。已知私有仓库上的 GitHub 系列徽章全部显示不出来(星标、许可、发布),
那种情况下改用静态徽章。
硬性规范
- 禁 emoji:代码、注释、文案、文档、提交信息一律不得出现 emoji 字符。由
preflight.mjs自动检查(它按 Unicode 的图形字符类别判定,箭头一类的符号不受影响)。 - 零依赖:
scripts/只允许node:前缀的内置模块。只用 JavaScript——不引入 第二个运行时(这条同样重要:README 说运行要求只有 Node 一项,多一个.py脚本 就让那句话变成假话,而使用者会照着自己的环境判断能不能用)。理由见「关键不变量」 第 4 条。 - 不写死取值:见「关键不变量」第 5 条。
- 入口不复述:见「关键不变量」第 2 条。
- 文档同步:改动
SKILL.md的结构、能力矩阵或流程后,检查README.md是否失准, 失准即同步修正。两者描述同一件事,读者不同:README 面向人(这是什么、怎么用),SKILL.md面向 AI(怎么判断、按什么顺序做)。 - 改 skill 的行为 = 改它自己的验收标准:
references/里写下任何新判据时,回头 确认本项目自己是否满足它。不满足就先改自己——它是自己的第一个用户。 - 不新增文件:新增 reference 或 template 前先问「能不能并入现有的」。文件数量本身 就是成本,读它的人(AI)要为此付出上下文预算。
本 skill 不绑定任何宿主。判断标准是框定方式:
写法 判断 某类被处理的项目(本 skill 认识它、能正确处理它) 允许——这是能力 面向使用者的文档( README)里出现任何具体宿主禁止——安装与使用说明不该让读者以为「这是给某个宿主用的」 宿主布局当成本 skill 的既定事实 禁止 第一条是能力:换来的是「它认识这类项目」。后两条是束缚:换来的是「换个宿主就不成立」, 而且会让读者把一个通用工具误认成某个生态的附属品。
README是重灾区,因为它面向的是「刚拿到这个 skill 的人」,他们最不需要知道 某个具体宿主的目录布局——那是宿主自己的文档该回答的问题。README 只写 「放进你所用宿主的用户级 skill 根」,并给出怎么找到那个位置(看已装的 skill、 或查宿主文档)。连「举例说明」都不要:举一个具体宿主,整段就变成了那个宿主的 说明,而读者可能是另一个宿主的用户。写机制,不写取值——与「不写死取值」是同一条准则的两种落法。宿主的规则 是稳定的,值得写;宿主的具体取值(名字、环境变量、默认路径)会随配置与环境变, 写进面向使用者的文档就是下一个坑。
这条没有自动检查,也不要试图加:判断落在「框定方式」上,不是字面特征。 字面启发式抓不住真正出问题的那次——出问题的原文里写着「以…为例」「默认为」 照样被放行,同时还会对合法的命令误报。加一个抓不住问题、又会叫喊的检查, 比不加更坏。
外部事实保鲜(防过期)
官方文档会变,本 skill 的文档不会自己变。不要为此单开一个 update 流程——多一套流程, 多一个要记得跑的东西,多一处会和主流程不一致的地方。保鲜长进现有结构里。
两类知识,两种写法
- 稳定的(决策链、判据、安全红线):照常写死,例如发布四问、密钥零入库、tag 与清单同源。
- 易变的(字段名、数量上限、UI 路径、版本下限、附件名):只写查法 + 来源 + 上次核对日期——有权威链接就给链接,没有就写清去哪个文档的哪一节查;正文里的具体 数字只是「上次看到的值」,拿不准时按查法重核,不要背诵。
统一核对标记(实现是唯一真相源)
带外部易变事实的章节文件,必须在 ## 事实来源 节末尾带恰好一个标记。判据是「这个文件
承载了外部易变事实」,不是文件名——目前包括 references/publish-<生态>.md、references/plugins/
下各专章,以及总纲 references/publish.md、references/remote-github.md 与
references/collaboration.md:
<!-- <scope>-verified: date=YYYY-MM-DD [key=value ...] -->
scope与文件名对应(dsh.md→dsh)。每个键都要有认领的含义、值非空且单行:host=的含义是「上次核对时本机实际运行的那套宿主版本」,不是兼容下界,也不是某个 目标项目锁定的版本。含义写偏,比对就按错的语义吵;认领、归一化与比对只实现一处,compose-agents.mjs与preflight.mjs共用同一份——两处各判一次同类问题,迟早给出两种答案。- 改
host=必须与date=同批:只改版本号不改日期,比对会喊「一致」而日期停在上一次 核对日——那是最难发现的假绿。这条纪律由preflight.mjs查(取不到那次提交时报「未核对」, 不静默也不失败)。 host=与本机宿主版本的比对:能取到就比,取不到就报「未核对」;只警告,不失败。 取值一律现场取——问宿主自己要,佐以它自己的安装元数据;不写进文件(写进文件的是 快照,而「升级宿主后记得改」正是机制要消灭的那种记性依赖)。取值途径按宿主各自登记, 没登记就是「未核对」——不为凑齐比对,把某个宿主的名字、命令或路径写进通用代码。 只警告不失败,是因为不同不等于滞后:换机器、宿主回滚、同机多份都能造出「不同」。 它只能喊出「你可能换了宿主,该重核了」,证明不了「外部事实仍然成立」。- 超期只警告并报出已过期天数与重核范围(阈值是那份实现里的常量,文档不写死数字)—— 时间流逝不是破损,红了就是误报。
三个触发时机(事件驱动,不定时全量刷)
- 用时检查:P5 落盘、P6 发布前,扫一眼相关标记超期没有,超了就现查官方文档再动手;
- 失败驱动:发布报“无权 / 不存在 / 字段非法”时,第一动作是重核那一页,不是重试;
- 平时不管:没超期、没失败就别刷——刷了是浪费,还制造提交噪音。
两条不做、以及为什么
compose-agents.mjs的滞后提醒只做 DSH:只有它的勘察产出了可比对的本地版本, 没有数据硬比对就是误报源,不要为了「对称」推广出去。review.mjs不查 skill 自身的标记年龄:它是目标项目的交付门禁,混在一起就是噪音。
方法自身的同步(新增内容同样适用)
- 新增
references/publish-<生态>.md章节:preflight.mjs按文件名模式要求它带节与标记, 缺了即失败——不用记,检查会喊。 - 新增
references/plugins/专章:模板骨架要求写## 事实来源节并附统一标记;登记检查 要求它至少出现在一张专章索引里(宿主加载代码走plugin-project.md,加载指令走skill-project.md,一个生态可以两边都出现),所以新增索引时把那张表一并加进preflight.mjs的登记表。 - 改保鲜方法本身(阈值、格式、规则):改实现,
scripts/dev/selftest.mjs里有该判据的 正反用例(好标记放行、坏日期/错 scope/超期各有断言),行为退化立刻可见。
版本管理流程(必守)
本地仓库 + 远端。远端地址、当前分支、署名一律现场读,不在本文件里写死。
先判角色(涉及推送或发版前做一次):远端指向正本仓库 = 维护者;指向 fork 或没有远端 =
贡献者——改代码、跑自检照做,到此为止:从主干拉分支 → 门禁全绿 → 推自己分支 → 开改动请求,
不推主干、不打标签。判断不了时按贡献者走。判据见 references/collaboration.md 第一节。
再判改动性质(维护者这一侧)。三条,任一命中就走分支 + 提案:
| 问什么 | 命中的含义 |
| 主干受不受保护 | 受保护就不能直推,只能走提案 |
| 会不会有第二个人改这个仓库 | 有,就必须有审查;而审查只能发生在合入之前 |
| 改动可不可见、收不收得回 | 随包发出的文档、命令、行为是可见的;判定逻辑、模板、工作流、包排除规则是收不回的 |
三条都不命中 → 主干直推:改文件 → 跑「构建与验证」全绿 → git status 确认范围 →
git add -A && git commit → git push。
不要拿「推得上去」当许可:主干保护开没开是仓库设置里的事(脚本读不到)。它开着时 直接推送会被拒(「protected branch hook declined」一类)——那是设计,不是故障,改走提案。
提交纪律:一次实质变更一个提交;提交信息说清「更新了什么、修复了什么」,读它的人
(包括发版时写说明的人)要能据此讲出使用者听得懂的一句话。禁用 emoji。用户可见的改动
不要用内部编号、任务代号或流程名充当说明。改了 templates/agents-kernel.md 必须
同一次提交带上重新注入后的 AGENTS.md。
版本节点与发布说明
版本号只有一个来源:标签。SKILL.md 的 metadata.version 是它的副本,作用是让
没有 .git 的副本知道自己属于哪一版;两者由发版门禁保证同号。
日常只提交推送不打标签;只有明确说「发版」才打标签写说明。 日常那条路是:改完、验完、 提交、推送。发版时那份说明是从提交信息攒出来的,所以用户可见的改动要当场把提交信息写清楚。
发版(只有明确说「发版」才触发):回顾上个标签之后攒了哪些用户可见变化(判据见
references/publish.md)→ 定新号 → 写 docs/release-notes/<标签>.md(结构以
RELEASING.md 为准,写在打标签之前)→ 提交并推送 → 打标签并推送 → CI 投递并回读确认 →
汇报只说版号与去哪看。
说明先到远端,标签后打:顺序反了,远端自动化读不到文件,发版直接失败。失败时补推
说明后用 workflow_dispatch 手动触发——不要重打标签,也不要挪标签。重跑会用文件内容
覆盖已有 Release 正文;发布页上有人工改写时不要重跑。
说明按字节写文件,不拼命令行:正文含中文,而 shell 的引用与编码规则因平台而异,
损坏的共性是接口仍返回成功、只有打开页面才看得出。工具是 scripts/release-notes.mjs。
同一条规则适用于 Issue、提案与审查评论:正文先写成 UTF-8 文件,用 --body-file 提交,
提交后回读比对。
协作:改动怎么落地
本项目是这套协议的第一个用户。判据全在 references/collaboration.md,本节只留本项目的取位:
| 环节 | 本项目的做法 |
| 工作记录 | docs/work/YYYY-MM-DD-主题.md:日期在最前(建单日,之后不改),不带编号与状态;表头有建单日期与最近更新;完成的单子留在原目录,状态改「已完成」 |
| 提案模板 | .github/pull_request_template.md(「怎么验证」与「回滚点」两栏是门禁会查的) |
| 谁审 | 派一个新上下文(子代理或新会话):只给材料清单,不给执行者思路与上一轮结论。默认只读,发现返回主 Agent 写回提案评论;下一轮重新派。主 Agent 不能自审 |
| 谁能合 | 文档与测试类可以自动合(门禁全绿 + 零阻塞 + 有回滚点);改判定逻辑、模板、工作流、包排除规则的一律人合 |
| 合并后 | 先验证真的合并了 → 关工作记录 → 更新主干 → 先本地后远端删分支 |
每轮都要留记录,且与当轮改动同一次提交——写在工作区没提交等于没写。
这个项目的高风险区比一般项目更明确:scripts/ 下的判定逻辑、templates/、
.github/workflows/、.gitattributes。改动落在这四处的一律人合,因为它们的错误不会
当场失败,而是让门禁从此少查一项。
外部提案先只读、再检出:检出之后一次本地装依赖就可能执行陌生人的代码。重点看工作流 配置、依赖装起来就会执行的钩子、门禁判据有没有被改松。外部提案一律人合。
合并之后,顺序不能反:
gh pr merge <编号> --merge # 1) 合并。不要带「合并并删分支」,那是两件事
gh pr view <编号> --json state # 2) 回读验证真的合进去了,别凭退出码
git checkout <主干> && git pull # 3) 先切回主干并更新(删本地之前必须做)
git branch -d <分支> # 4) 再删本地,用 -d 不用 -D
git ls-remote --heads origin <分支> # 5) 先判远端还在不在
git push origin --delete <分支> # 6) 还在才删三个坑各有根因:① 合并与删除混在一条命令里,失败时分不清是「没合进去」还是「合进去了
但删分支失败」——这两种情况的下一步完全相反;② 还停在分支上时 git 不允许删当前分支,
且主干没更新时 -d 会认为它没合入而拒绝;③ 先删远端会丢掉上游追踪引用,逼你用 -D,
而那正是唯一那道保护网。另有两个特例:平台开着「自动删除已合并分支」时,第 6 步报「远端
引用不存在」是完成态;堆叠提案的基底分支不能删。判据见 references/collaboration.md
第七节。
外部报告(别人贴来一段问题描述或报错):先核实四要素(期望、实际、复现、环境),缺关键 信息就先出追问清单,不盲猜写代码;再查工作记录防撞车、认领;然后拉分支,先写一个能复现 的失败用例和一个防误判的反例,才改实现。
测试(约定)
本项目的测试是自检加实跑,没有测试框架:
- 结构性契约(引用完整、内核一致、规范合规)由
scripts/dev/preflight.mjs自动检查, 它是常驻防线,改动后必跑; - 行为性契约(勘察判定是否准确、注入是否幂等)靠用 fixture 实跑验证:造几种 形态不同的临时目录,跑一遍勘察看分类是否正确、跑两遍注入看结果是否相同。 fixture 用完即删,它是一次性产物。
新增判定能力时,先造出能证伪它的 fixture:一个该被识别成 X 的目录,和一个容易被 误判成 X 的目录。只验证正面案例,等于没验证。
临时草稿写系统临时目录、用完即删,不进仓库也不留 .git/。
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

