AGENTS.md 作单一真源(CLAUDE.md 薄壳共存)
把一个工程的项目上下文归约成「AGENTS.md 作单一真源(SSOT)、对多个 agent 兼容」——CLAUDE.md
或已有的 AGENTS.md 经两条路径收敛到同一份工具无关的 AGENTS.md,各 agent 各用各的入口加载它
(原生读 AGENTS.md 的 / 经薄壳 CLAUDE.md → @AGENTS.md 的),不必维护两份。
设计与原理
核心是让 AGENTS.md 成为单一真源:所有项目上下文只在这里维护一份,CLAUDE.md 退化成引入它的
薄壳(加少量 Claude 专属逃生舱)。AGENTS.md 是跨 agent 的事实标准——原生读它的 agent 都原生识别,
无需额外配置;不原生读 AGENTS.md 的 agent 则经薄壳 CLAUDE.md → @AGENTS.md 引入同一份内容,
且支持递归 @import,故 AGENTS.md 内的 @MEMORY/MEMORY.md 也会被自动展开。
L2 记忆层用 @MEMORY/MEMORY.md 收口(R2;此理由只在此讲一次,Step 2 / 3 均引用本段):
MEMORY.md(索引文件)放在 MEMORY/ 下,AGENTS.md 用 @MEMORY/MEMORY.md 单行引入——会自动展开
@import 的 agent(递归 import / 原生支持 @path)会把索引正文展开到上下文;不展开 @import 的
agent 仅把它当文本。故 AGENTS.md 顶部挂一条强制 Read
指令(对所有 agent 一视同仁):凡 @ 引用都用 Read 读——不展开的据此 Read MEMORY/MEMORY.md 拿到
索引,展开的读了也无害(从「段内逐点补指引」升级为「一条通则兜底」,不再绑定具体 agent)。
为什么不把 MEMORY.md 索引内联进 AGENTS.md 正文:内联要双写(MEMORY.md 改一处就得回贴 AGENTS.md,
必漂移)、推高 L1 词数、把记忆 SSOT 从 MEMORY.md 分裂成“MEMORY.md + AGENTS.md”双源。而自动展开
@import 的 agent 本就把索引读入,不展开的由顶部指令补回——不值得为不展开的少数内联。
记忆跟 repo 走(R6):跨会话记忆的真源是 repo 根 MEMORY/,不是 agent 私有 memory(如
~/.claude/...)——私有路径不随仓迁移 / 不进 git / 多 agent 各写分裂。故迁前无 MEMORY/ 时在 repo 下
新建最小 MEMORY/MEMORY.md 索引(R6),不跳过、不省略记忆段。
完整分层模型(L1 常驻 / L2 记忆)与段落分层决策树见 references/layering.md
——Step 1 给段落分类时读它;改写规则 R1–R6 + 路径 2 诊断清单见
references/rewrite-rules.md——Step 2 / 3 / 4 对照执行。
兼容性矩阵(按加载行为分类,不逐家点名——是否自动展开 @import 已由顶部强制 Read 指令通吃):
| 层 | 原生读 AGENTS.md 的 agent | 经薄壳 CLAUDE.md → @AGENTS.md 的 agent |
|---|---|---|
| L1 正文 | 原生读 | 经薄壳 @AGENTS.md 引入 |
| L2 记忆索引 | @MEMORY/MEMORY.md 一行;顶部强制 Read 指令通吃展开与否 |
同左(薄壳递归展开后同效) |
| L2 记忆正文 | 按需 Read | 按需 Read |
| 触发式 rule | 不涉及(全读);部分 agent 自家触发式 rule 目录本 skill 不生成 | 同左 |
薄壳结构 + 三家信息流:
AGENTS.md 唯一真源(工具无关,人工维护的唯一目标)
└── ## 跨会话记忆(索引) @MEMORY/MEMORY.md(顶部强制 Read 指令兜底不展开 @import 的 agent)
CLAUDE.md 薄壳(自动生成,不需要人工维护)
├── @AGENTS.md 引入全部共用内容(递归展开里面的 @MEMORY/MEMORY.md)
└── <!-- Claude Code 专属 --> 几行无法泛化的工具绑定内容(如有)
任一 agent 启动 → 读 AGENTS.md → L2 按上表展开或按顶部强制 Read 指令读——Step 1 段落分层决策树见
references/layering.md。
输入 / 输出
输入
| 输入 | 必需 | 说明 |
|---|---|---|
| 项目根目录 | 是 | 默认 cwd;两条路径之一:含 CLAUDE.md / 含 AGENTS.md(都没有→先用 /init) |
MEMORY/ |
否 | 存在则一并去品牌化(Step 3)+ 在 AGENTS.md 加 @MEMORY/MEMORY.md;不存在则在 repo 下新建(R6) |
| 源文件快照 | 自动 | Step 1 快照源(CLAUDE.md / AGENTS.md)到 .migration-backup/(临时目录),供 Step 5 覆盖率比对,验证通过即删 |
输出
<project-root>/
├── AGENTS.md 新/改:工具无关真源(原生读 / 经薄壳 CLAUDE.md 引入),
│ 含「## 跨会话记忆(索引)」段:@MEMORY/MEMORY.md(顶部强制 Read 指令在文件顶部)
├── CLAUDE.md 薄壳(@AGENTS.md + <!-- Claude Code 专属 --> 逃生舱)——让 Claude Code 也
│ 加载同一份 AGENTS.md(含递归 @MEMORY/MEMORY.md 展开);有现有 CLAUDE.md
│ 则改写,纯 AGENTS.md 项目则新建最小薄壳
└── MEMORY/ 新/改:存在则 MEMORY.md + 各 slug 正文按 R1 去品牌;不存在则建最小 MEMORY.md(R6)
两条路径(前置检测路由)
precheck.py 检测项目状态,路由到两条归约路径之一——两条共享后半段(去品牌 R1 + R3–R5 + L2 走
@MEMORY/MEMORY.md 的 R2 + 覆盖率验证),只前半段(源提取)不同:
| 项目状态 | 路径 | 前半段(源提取) |
|---|---|---|
有 CLAUDE.md,无 AGENTS.md |
1 迁移 | 扫描 CLAUDE.md 段落分类 → 去品牌(现有流程) |
有 AGENTS.md(可能 +CLAUDE.md) |
2 规范化 | 诊断现有 AGENTS.md(品牌残留?记忆段形式?)+ 合并并存 CLAUDE.md,冲突让用户裁定;清单见 references/rewrite-rules.md |
| 都没有(裸项目) | 硬阻塞 | 不在本 skill 范围——先用 agent 的 /init 生成初始 CLAUDE.md / AGENTS.md 再来 |
两条都收敛到同一份工具无关 AGENTS.md SSOT,并产出 CLAUDE.md 薄壳让不原生读 AGENTS.md 的 agent 也加载同一份内容。
执行原则 / 边界
- 只动上下文文件(
CLAUDE.md/AGENTS.md/MEMORY/)——不碰迁移权限 (如.claude/settings.local.json)、MCP 配置、scripts//references/ - 不删
CLAUDE.md——本 skill 保留薄壳共存;彻底删需用户显式确认 - 不生成 agent 专属触发式 rule 文件(如
.qoder/rules/)——官方多未文档化,交给用户在 目标 agent IDE 配置
改写规则 R1–R6(摘要)
- R1 工具无关化:去品牌、不改事实。完整替换表(
Claude Code→AI coding agent、claude -p→agent CLI等,含"何时保留工具名"判定)见references/rewrite-rules.md—— Step 2 / 3 改写时对照。 - R2 记忆索引
@import收口:AGENTS.md 的## 跨会话记忆(索引)段用单行@MEMORY/MEMORY.md引入索引——不展开@import的 agent 由 AGENTS.md 顶部强制 Read 指令兜底(见layering.md骨架), 段内不再单挂指引。不内联索引行——理由见上方「设计与原理」L2 记忆层段(Step 2 引用同一 R2 模板)。 - 记忆写统一(默认,见 R6):R2 解决读统一;写统一(agent 把新记忆写
MEMORY/而非私有 memory)是默认,连同 repo-local + 存在性一起收口到 R6(禁私有 memory + 最小MEMORY.md模板 + 写入规约)。详见references/rewrite-rules.mdR6。 - R3 行宽不变:保持原文行宽约束(如
.markdownlint.jsoncMD013)——完整约束见references/rewrite-rules.mdR3。 - R4 MEMORY 改写:MEMORY 正文按 R1 去品牌,目录结构 / 文件数不变(仅改措辞,不合并 / 拆分 /
删除条目)——完整约束见
references/rewrite-rules.mdR4。 - R5 逃生舱:无法泛化的工具专属内容(如脚本硬编码
claude -p子进程),在 AGENTS.md 写泛化版、 在 CLAUDE.md 薄壳尾部追加具体实现。判定标准:去掉工具名后读者无法执行该操作 → 进逃生舱。 - R6 MEMORY 仓 repo-local + 存在性:跨会话记忆真源 = repo 根
MEMORY/,禁写 agent 私有 memory (~/.claude/...)。迁前无MEMORY/→ 在 repo 下建MEMORY/MEMORY.md最小索引(不跳过、不省略 记忆段);「仓库规约」段默认含写入规约。完整约束 + 模板见references/rewrite-rules.mdR6。
运行约束
- 幂等:重复运行覆盖已有
AGENTS.md,不产生重复文件;.migration-backup/为临时快照目录, Step 5 收尾即删,不留在项目里
工作流 / 步骤
贯穿全程:分类表(Step 1)、逃生舱内容(Step 4)两处交互确认点,不要静默决断。
Step 0:前置检查 + 路径判定(跑 scripts/precheck.py)
python3 scripts/precheck.py <project-root>
报告项目状态并按上方路由表判定路径;同时报告 MEMORY/ 是否存在。路径 2 修改现有 AGENTS.md
(规范化 / 合并并存 CLAUDE.md)时停下来让用户确认合并策略,绝不静默覆盖。
Step 1:快照 + 源提取(按路径分支)
- 快照源文件到
.migration-backup/(路径 1 快照CLAUDE.md;路径 2 快照原AGENTS.md,并存CLAUDE.md时一并快照)——Step 5 覆盖率比对要用,这是唯一机会;该目录仅为临时用途, Step 5 收尾时删除。 - 按路径提取源内容:
- 路径 1:解析
CLAUDE.md所有##段落,跑references/layering.md分层决策树。 - 路径 2:对现有
AGENTS.md跑references/rewrite-rules.md的规范化诊断清单 (品牌残留?有无 L2 记忆内联?有无 MEMORY 支持?);若CLAUDE.md并存,一并解析按层决策树合并。
- 路径 1:解析
- 输出分类 / 诊断表,展示给用户确认 / 调整再继续。
Step 2:生成 AGENTS.md(LLM,核心步)
- 按
references/layering.md的 AGENTS.md 骨架组织 L1 内容 (项目定位 / 仓库规约 / 常用命令 / 高层结构 / 注意事项)。- 路径 1:CLAUDE.md 段落去品牌改写入骨架。
- 路径 2:在现有 AGENTS.md 基础上规范化(改记忆段为
@MEMORY/MEMORY.md、去品牌残留、 合并 CLAUDE.md 内容去重),冲突口径让用户裁定。
- 加
## 跨会话记忆(索引)段落(R2):单行@MEMORY/MEMORY.md——段内不再挂段内指引(不展开@import的 agent 由顶部强制 Read 指令兜底)。无MEMORY/时先在 repo 下建MEMORY/MEMORY.md最小索引(R6)再放本段——不再因「无 MEMORY/」省略。完整段落模板见references/rewrite-rules.mdR2。 - 应用 R1(去品牌)+ R3(行宽)。
- 控制 L1 正文词数(预算 + 记忆索引不计入的规则见
references/layering.md); 超出把详细内容下沉到 L2(MEMORY/)。 - 自检:
grep -iE "claude code|\.claude/" AGENTS.md应无命中(去品牌通过)grep -nE '^@MEMORY/MEMORY\.md$' AGENTS.md应有 1 行命中(R2 引用;MEMORY/ 现为默认存在,见 R6)- 顶部应有强制 Read 指令 blockquote(H1 后、首个
##前;含@+ Read 特征——所有 AGENTS.md 必备)
Step 3:MEMORY(LLM)——存在则去品牌,不存在则建仓
- 存在:应用 R4——MEMORY 正文按 R1 去品牌,目录结构 / 文件数量不变。自检
grep -riE "claude code|Claude Code|\bCC\b" MEMORY/应无命中(\bCC\b避免误伤缩写)。 - 不存在:应用 R6——在 repo 下新建
MEMORY/MEMORY.md最小索引(模板见references/rewrite-rules.mdR6)。新文件无品牌可去,只建仓。
Step 4:生成 CLAUDE.md 薄壳
让不原生读 AGENTS.md 的 agent(经 CLAUDE.md 加载)也能用上同一份:有现有 CLAUDE.md(路径 1;路径 2 并存场景)则改写成薄壳;
纯 AGENTS.md 项目(路径 2 无 CLAUDE.md)则新建最小薄壳。按 references/rewrite-rules.md
的薄壳模板:顶部「薄壳声明」(点明 AGENTS.md 是单一真源、勿在此编辑共用部分)+ @AGENTS.md +
<!-- Claude Code 专属 --> 逃生舱(Step 2 识别出的 TOOL_SPECIFIC 内容,按 R5 处理)。没有逃生舱内容就省略注释块。
逃生舱内容展示给用户确认。 自检口径(行数上限、@AGENTS.md 存在、薄壳声明、无大段正文)见
references/rewrite-rules.md 薄壳验证段。
Step 5:覆盖率验证(跑 scripts/coverage.py)
python3 scripts/coverage.py <project-root>
拿 Step 1 快照的原 CLAUDE.md,逐行 token 比对 AGENTS.md + 薄壳 CLAUDE.md
(内置去品牌归一化,让 Claude Code→agent 后仍能匹配),输出未匹配行清单 + 覆盖率 %。
未匹配行 ≠ 一定丢失——可能是去品牌改写或合理下沉;逐条与用户确认是丢失还是改写。
覆盖率 100% 是目标。最后跑 markdownlint 检查所有新 / 改文件 0 error。
收尾删除 .migration-backup/:flag 全部逐条确认完毕 + markdownlint 0 error 后,快照使命完成——
rm -rf .migration-backup/,不把临时目录留在用户项目里(git 项目原文随时可从历史恢复)。
有未决 flag / 用户还要回滚比对时先留着,下轮验证通过再删。
参考样例
端到端最小示例(路径 1 迁移)。输入:项目根只有一份 CLAUDE.md。
用户:"这个项目只有 CLAUDE.md,帮我迁成多 agent 都能读的 AGENTS.md"
precheck.py报 [路径 1] → 快照CLAUDE.md到.migration-backup/(Step 1)- 按分层决策树分类段落 → 去品牌(R1)写入 AGENTS.md 骨架,挂
@MEMORY/MEMORY.md(R2)- 无
MEMORY/→ 建最小MEMORY/MEMORY.md(R6)- 生成薄壳
CLAUDE.md(@AGENTS.md+ 逃生舱注释块,Step 4 展示确认)coverage.py比对 100% 目标 → markdownlint 0 error →rm -rf .migration-backup/
参考文件
references/layering.md—— 分层模型 + 段落分层决策树(Step 1 / 2)references/rewrite-rules.md—— R1–R6 完整规则 + 路径 2 诊断清单(Step 2 / 3 / 4)eval/trigger_eval.json—— 触发评估集(description 触发优化用的 20 条{query, should_trigger}查询,非行为评估集)