在同一个仓库里同时支持 Claude Code、Codex、Gemini CLI、Cursor 时,最容易遇到的问题是:同一条规则写了四份,最后四份不一致。
本文给一个可落地方案:单一事实源 + 多工具适配层。
- 只维护一份核心规则。
- 允许各工具保留少量差异补丁。
- 自动生成目标文件,减少手工编辑。
- 子目录可以加增量规则,不污染全局。
1
2
3
4
5
6
7
8
9
10
11
12
|
docs/ai/
core.md
tooling/
claude.md
codex.md
gemini.md
cursor.md
scopes.list
scopes/
content/blog.md
scripts/
sync-ai-configs.sh
|
- 在
docs/ai/core.md 写所有工具共享规则(单一事实源)。
- 在
docs/ai/tooling/*.md 写各工具专属补丁。
- 在
docs/ai/scopes.list 列出需要子目录增量规则的路径。
- 在
docs/ai/scopes/*.md 写对应子目录的增量规则。
- 运行脚本生成目标文件。
1
2
3
4
5
6
7
8
|
# 只预览,不落盘
scripts/sync-ai-configs.sh
# 实际写入
scripts/sync-ai-configs.sh --apply
# 强制覆盖已有手工文件(谨慎)
scripts/sync-ai-configs.sh --apply --force
|
点击展开 sync-ai-configs.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
AI_DIR="$ROOT_DIR/docs/ai"
APPLY=0
FORCE=0
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=1; shift ;;
--force) FORCE=1; shift ;;
*) echo "Usage: $0 [--apply] [--force]" >&2; exit 2 ;;
esac
done
require_file() { [[ -f "$1" ]] || { echo "Missing file: $1" >&2; exit 1; }; }
read_file() { cat "$1"; }
is_generated_file() {
[[ -f "$1" ]] || return 1
head -n 1 "$1" | rg -q "AI-CONFIG: GENERATED"
}
write_target() {
local target="$1" content="$2"
mkdir -p "$(dirname "$target")"
if [[ -f "$target" && $FORCE -ne 1 ]]; then
if ! is_generated_file "$target"; then
echo "SKIP (manual file): ${target#$ROOT_DIR/}"
return 0
fi
fi
if [[ $APPLY -eq 1 ]]; then
printf "%s\n" "$content" > "$target"
echo "WRITE ${target#$ROOT_DIR/}"
else
echo "PLAN ${target#$ROOT_DIR/}"
fi
}
build_root_doc() {
local tool="$1"
cat <<EOF
<!-- AI-CONFIG: GENERATED. DO NOT EDIT DIRECTLY. -->
# $(tr '[:lower:]' '[:upper:]' <<<"$tool") Instructions
$(read_file "$AI_DIR/core.md")
$(read_file "$AI_DIR/tooling/$tool.md")
EOF
}
build_scope_doc() {
local tool="$1" scope="$2" scope_file="$3"
cat <<EOF
<!-- AI-CONFIG: GENERATED. DO NOT EDIT DIRECTLY. -->
# Scope Instructions: $scope
本文件仅定义当前目录的增量规则,不重复全局规则。
$(read_file "$scope_file")
$(read_file "$AI_DIR/tooling/$tool.md")
EOF
}
build_cursor_rule() {
local scope="$1" scope_file="$2"
cat <<EOF
---
description: Generated scope rule for $scope
globs:
- $scope/**
alwaysApply: false
---
<!-- AI-CONFIG: GENERATED. DO NOT EDIT DIRECTLY. -->
# Cursor Scope Rule: $scope
$(read_file "$scope_file")
EOF
}
require_file "$AI_DIR/core.md"
require_file "$AI_DIR/scopes.list"
require_file "$AI_DIR/tooling/claude.md"
require_file "$AI_DIR/tooling/codex.md"
require_file "$AI_DIR/tooling/gemini.md"
require_file "$AI_DIR/tooling/cursor.md"
write_target "$ROOT_DIR/AGENTS.md" "$(build_root_doc codex)"
write_target "$ROOT_DIR/CLAUDE.md" "$(build_root_doc claude)"
write_target "$ROOT_DIR/GEMINI.md" "$(build_root_doc gemini)"
while IFS= read -r scope || [[ -n "$scope" ]]; do
[[ -z "$scope" || "$scope" =~ ^# ]] && continue
scope_id="${scope//\//-}"
scope_file="$AI_DIR/scopes/${scope}.md"
require_file "$scope_file"
write_target "$ROOT_DIR/$scope/AGENTS.md" "$(build_scope_doc codex "$scope" "$scope_file")"
write_target "$ROOT_DIR/$scope/CLAUDE.md" "$(build_scope_doc claude "$scope" "$scope_file")"
write_target "$ROOT_DIR/$scope/GEMINI.md" "$(build_scope_doc gemini "$scope" "$scope_file")"
write_target "$ROOT_DIR/.cursor/rules/generated-${scope_id}.mdc" "$(build_cursor_rule "$scope" "$scope_file")"
done < "$AI_DIR/scopes.list"
[[ $APPLY -eq 0 ]] && echo -e "\nDry run only. Use --apply to write files."
|
脚本会生成或更新:
- 根目录:
AGENTS.md、CLAUDE.md、GEMINI.md
- 子目录:
<scope>/AGENTS.md、<scope>/CLAUDE.md、<scope>/GEMINI.md
- Cursor 规则:
.cursor/rules/generated-<scope>.mdc
说明:
- 默认会跳过手工文件(非模板生成文件),避免误覆盖。
- 目标文件头部有
AI-CONFIG: GENERATED 标记,便于识别。
如果 content/blog 有特殊约束,只需要写增量:
- 文章要有明确日期锚点。
- 能用表格/列表时优先表格/列表。
- 工具能力对比要优先引用官方链接。
这样全局规则继续在根目录生效,子目录只补差异。
可以共享大部分内容,但不建议 100% 共用单文件,原因有三个:
- 工具语义不同,同一句规则执行效果可能不同。
- 各工具有专属能力(rules/policy/skills),需要少量补丁。
- 版本迭代快,强耦合会增加回归风险。
实践上,建议共享 80%-90%,剩下 10%-20% 留给工具适配层。
flowchart TD
A[docs/ai/core.md<br/>单一事实源] --> B[docs/ai/tooling/*.md<br/>工具差异补丁]
A --> C[docs/ai/scopes/*.md<br/>子目录增量]
B --> D[scripts/sync-ai-configs.sh]
C --> D
D --> E[AGENTS.md]
D --> F[CLAUDE.md]
D --> G[GEMINI.md]
D --> H[.cursor/rules/generated-*.mdc]
E --> I[本地/CI 使用]
F --> I
G --> I
H --> I
I --> J[CI 漂移检查<br/>sync 后 git diff --exit-code]