约定落在六个固定路径:仓库说明 CLAUDE.md、规则 .claude/rules/*.md、钩子 .claude/settings.json 的 hooks 字段。放错路径不报错,只是永远不生效。
刚接手要建第一版,直接看六处落点各管什么。
建的每一个文件,都能指到 ls -R .claude 输出里的一行,并且能被一次实测证明它真的生效吗?
答「不能」,说明文件在那里但没被读。要做的是把它挪到正确路径,不是再写一份说明。
按一到六步建,顺序不要跳:先让助手读到约束(仓库说明、规则),再给它固定的动作(命令、技能),最后才给独立角色和硬拦截。前一步没建好,后一步都在补窟窿。
CLAUDE.md,它每次会话都读。只写整仓成立、每次改动都成立的短句,上限 200 行。.claude/rules/*.md,前置元数据写 paths:,匹配到才加载;不写 paths 的每次会话都加载。.claude/skills/<名称>/SKILL.md 并加一行 disable-model-invocation: true,只有人输入 / 才执行。.claude/skills/<名称>/SKILL.md。它靠 description 决定什么时候加载,正文只留步骤。.claude/agents/*.md 里把只读角色写死成 tools: Read, Grep, Glob,验收时它改不了文件。.claude/settings.json 的 hooks 字段,事件名用 PreToolUse。这是唯一不依赖助手自觉的一层。边界:这一步只适用于本次任务的验收时,不要写进前两处,写进当次对话或 可执行规约。写进根文件,下一次不相关的任务也会读到它。
三套工具最容易混的不是概念,是文件名和字段名。改仓库前先在这张表里按「同一个用途」对一遍三处写法。表里没列的项,在自己仓库里建一次,用 ls 加一次实测确认;不要照另一个工具的文件名猜。
| 用途 | Claude Code | Cursor | TraeCode |
|---|---|---|---|
| 仓库说明 | CLAUDE.md(.claude/CLAUDE.md 也读);根目录可写 @AGENTS.md 引入已有文件 | AGENTS.md,每次对话读仓库根 | AGENTS.md,默认不读,要在「设置 → 规则 → 导入设置」打开开关 |
| 规则 | .claude/rules/*.md,前置元数据 paths: | .cursor/rules/*.mdc,globs: 加 alwaysApply: false | .trae/rules/*.md,alwaysApply: false 加 globs: |
| 命令(新写法) | .claude/skills/<名称>/SKILL.md 加 disable-model-invocation: true | .cursor/skills/<名称>/SKILL.md 加同一字段 | .trae/commands/<名称>.md,文件名就是调用名 |
| 命令(旧写法) | .claude/commands/*.md 仍加载;两边同名时技能优先 | .cursor/commands/*.md 仍加载 | 无旧写法 |
| 技能 | .claude/skills/<名称>/SKILL.md,正文留步骤 | .cursor/skills/<名称>/SKILL.md,长说明放同目录 reference.md | .trae/skills/<名称>/SKILL.md |
| 子智能体 | .claude/agents/*.md,用 tools: Read, Grep, Glob 限权 | .cursor/agents/*.md,用 readonly: true | .trae/agents/*.md,用 tools 加 disallowedTools |
| 钩子 | .claude/settings.json 的 hooks 字段,事件名 PreToolUse | .cursor/hooks.json(version: 1),事件名 preToolUse 小写 p | .trae/hooks.json(version: 1),matcher 是正则 |
个人位置与提交边界也分三套:Claude Code 里把本机内容放 CLAUDE.local.md 与 .claude/settings.local.json,并把这两个文件加进 .gitignore,另有 ~/.claude/ 下的规则、技能、子智能体、全局 settings.json;Cursor 没有本地覆盖文件;TraeCode 的本机共用位置是 ~/.trae-cn/ 下的 user_rules、commands、skills、agents、hooks.json。
下面的树按顺序对应上一节六步,表里没有的路径不要新建。
注释写明每个位置的作用与提交边界,照抄时删掉注释。
仓库根/
├── CLAUDE.md # 仓库说明,每次会话读,≤200 行
├── CLAUDE.local.md # 个人偏好,写进 .gitignore
├── AGENTS.md # 已有给 Cursor / TraeCode 用的说明时保留
├── .gitignore # 内含 CLAUDE.local.md 与 .claude/settings.local.json
└── .claude/
├── settings.json # hooks 字段 + permissions.deny,提交
├── settings.local.json # 本机钩子,不提交
├── rules/
│ ├── backend.md # paths: backend/**/*.go
│ └── frontend.md # paths: frontend/src/**/*.ts[x]
├── skills/
│ ├── check-scope/SKILL.md # 命令:disable-model-invocation: true
│ └── trace-retrieval/SKILL.md
├── agents/
│ └── verifier.md # tools: Read, Grep, Glob
└── hooks/
└── block-env.py # 被 settings.json 里的 command 调用
根 CLAUDE.md 要复用已有的 AGENTS.md 时,在第一行写 @AGENTS.md,不要复制正文——复制会让同一条约束出现在两处,改一处漏一处。
这四种都不报错,只是行为没变。按「现象 → 原因 → 修法」对号入座,改完用该行的修法命令复核一次。
| 现象 | 原因 | 修法 |
|---|---|---|
在 Claude Code 里另建了 hooks.json,改 .env 时没有任何拦截 | Claude Code 的钩子在 .claude/settings.json 的 hooks 字段里,单独的 hooks.json 不会被读 | 把配置搬进 .claude/settings.json,再跑 python3 -c "import json;json.load(open('.claude/settings.json'))" 确认 JSON 合法 |
| 钩子配了但一次没触发,脚本里的日志从没出现 | 事件名写成了 preToolUse(小写 p),Claude Code 用的是 PreToolUse;写错不报错,只是不触发 | 改成 PreToolUse,并在脚本第一行向 /tmp/hook.log 追加一行,确认它被执行过 |
| 仓库说明写到 300 行,助手仍然漏掉「SQL 必须绑定 scope_id」 | 根文件超过上限,靠近末尾的句子在实际加载里被稀释 | wc -l CLAUDE.md,超过 200 行就删;只对某一端成立的句子移进 .claude/rules/ 的 paths |
| 子智能体验收时把源码改了 | tools 里写了 Edit 或 Write,或写了 Bash 而脚本能改文件 | tools 只留 Read, Grep, Glob,删掉 Write、Edit、Bash |