ᕕ( ᐛ )ᕗ Jimyag's Blog

单一事实源:在一个项目里同时管理 AGENTS、CLAUDE、GEMINI 和 Cursor Rules

· 585 字 · 约 3 分钟

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

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

目标

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

推荐目录

 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

工作流

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

生成命令

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.mdCLAUDE.mdGEMINI.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% 留给工具适配层。

流程图

  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]

#AI 配置 #AGENTS.md #CLAUDE.md #GEMINI.md #Cursor Rules #Skills