
在同一个仓库里同时支持 Claude Code、Codex、Gemini CLI、Cursor 时，最容易遇到的问题是：同一条规则写了四份，最后四份不一致。

本文给一个可落地方案：**单一事实源 + 多工具适配层**。

## 目标

1. 只维护一份核心规则。
2. 允许各工具保留少量差异补丁。
3. 自动生成目标文件，减少手工编辑。
4. 子目录可以加增量规则，不污染全局。

## 推荐目录

```text
docs/ai/
  core.md
  tooling/
    claude.md
    codex.md
    gemini.md
    cursor.md
  scopes.list
  scopes/
    content/blog.md
scripts/
  sync-ai-configs.sh
```

## 工作流

1. 在 `docs/ai/core.md` 写所有工具共享规则（单一事实源）。
2. 在 `docs/ai/tooling/*.md` 写各工具专属补丁。
3. 在 `docs/ai/scopes.list` 列出需要子目录增量规则的路径。
4. 在 `docs/ai/scopes/*.md` 写对应子目录的增量规则。
5. 运行脚本生成目标文件。

## 生成命令

```bash
# 只预览，不落盘
scripts/sync-ai-configs.sh

# 实际写入
scripts/sync-ai-configs.sh --apply

# 强制覆盖已有手工文件（谨慎）
scripts/sync-ai-configs.sh --apply --force
```

## 脚本全文（可直接复制）

<details>
<summary>点击展开 sync-ai-configs.sh</summary>

```bash
#!/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."
```

</details>

## 生成结果

脚本会生成或更新：

- 根目录：`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% 共用单文件，原因有三个：

1. 工具语义不同，同一句规则执行效果可能不同。
2. 各工具有专属能力（rules/policy/skills），需要少量补丁。
3. 版本迭代快，强耦合会增加回归风险。

实践上，建议共享 80%-90%，剩下 10%-20% 留给工具适配层。

## 流程图

```mermaid
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]
```

