之前讲 CLAUDE.md 时说过两条:每个文件控制在 200 行以内;内容多了可以拆成几个文件再引入。
但「拆」没解决上下文的问题:拆成十个文件,启动时还是十个全部加载。
还有一种方案:.claude/rules/ 加上 paths,拆开之后,有的常驻,有的只在需要时才出现。
这一节看 ECC 怎么用这套机制,组织 20 多个语言和框架的规则包。
一、Rules 是什么

这是 Claude Code 的原生功能,不是 ECC 发明的:
| 位置 | 作用范围 |
|---|---|
<项目>/.claude/rules/*.md |
当前项目 |
~/.claude/rules/*.md |
本机所有项目 |
这两个目录下的 .md 文件(子目录也会递归读取),会像 CLAUDE.md 一样在会话启动时加载进上下文。你可以把它理解成「拆成很多份的 CLAUDE.md」。
和 CLAUDE.md 的关键区别是多了一个 paths 开关,第三部分讲。
ECC 的 rules 结构:
rules/
├── common/ # 通用规则:10 个文件,共 559 行
│ ├── coding-style.md # 编码风格
│ ├── development-workflow.md # 开发流程
│ ├── git-workflow.md # 提交规范
│ ├── testing.md # 测试要求
│ ├── security.md # 安全检查
│ ├── code-review.md # 审查清单
│ ├── agents.md # 子代理派工单(本章 第3节)
│ ├── hooks.md
│ ├── patterns.md
│ └── performance.md
├── typescript/ # 各语言扩展
├── python/
├── golang/
└── …… # 共 20 多个语言/框架包
安装时放在 ~/.claude/rules/ecc/ 下面。多一层 ecc/ 目录做命名空间,免得和你自己的规则文件重名。
二、两层结构:通用 + 语言

1. common 只写原则,不写具体代码
比如 common/coding-style.md 开头:
## Immutability (CRITICAL)
ALWAYS create new objects, NEVER mutate existing ones:
WRONG: modify(original, field, value) → changes original in-place
CORRECT: update(original, field, value) → returns new copy with change
## 不可变性(关键)
始终创建新对象,绝不修改已有对象:
错误:modify(original, field, value) → 就地修改原对象
正确:update(original, field, value) → 返回带有改动的新副本
用的是伪代码,不绑定任何语言。后面是 KISS、DRY、YAGNI 这些通用原则。
2. 语言包继承并覆盖
每个语言规则文件的第一行,都写明它扩展的是哪个通用文件:
> This file extends [common/coding-style.md](../common/coding-style.md)
> with TypeScript/JavaScript specific content.
两者冲突时,语言规则优先:具体的覆盖一般的。比如通用规则要求「不要修改原对象」,但 Go 的习惯就是用指针接收者直接修改结构体,golang/ 的规则就可以覆盖这一条。
这里有个细节要想清楚。各层 CLAUDE.md 不会互相覆盖,而是全部拼在一起,规则打架时 Claude 可能随机使用其中的规则。
Rules 也一样,Claude Code 不会替你处理覆盖关系。
ECC 的办法是用文字写明覆盖关系:语言规则里直接写「这一条覆盖通用规则的某某原则」,通用规则里可能被覆盖的条目也标注 Language note。
模型读到这些说明,就知道冲突时该听谁的,这个方法挺好的可以借鉴。
三、paths:让常驻规则按需出现

语言规则文件的头部都有这么一段:
---
paths:
- "**/*.ts"
- "**/*.tsx"
- "**/*.js"
- "**/*.jsx"
---