← ishizakahiroshi.com
手順書 2026-08-30

Codex / Claude Code / OpenCode で skills と共通ルールを 1 本にまとめる

AI コーディング CLI を何本も併用していると、同じものを 2 つ管理させられます。skills が CLI ごとに別の場所へ散らばり、共通ルールを CLI の数だけ書き写すことになる。どちらも「正本を 1 箇所に置き、各 CLI にはそれぞれの流儀で到達させる」だけで解決します。この記事は、その配線を実際に作るための手順書です。各 CLI が本当にどのファイルを読むかは、推測ではなく実測しました。

この手順書の使い方

上から順に読んで手で実行してもいいですし、章ごとそのまま AI エージェントへ貼って実行させることもできます。危ない操作を踏まないための停止条件を第 8 章に置いてあるので、エージェントに任せる場合はそこも一緒に渡してください。

1. 何を解決するか

併用していると、次の 2 つが CLI の数だけ増えます。

やることは 2 つだけです。

そもそもの困りごと: 読むファイル名が AI ごとに違う

同じ「作業のルール」を伝えたいだけなのに、受け取る側の名前がそろっていません。

CLAUDE.md AGENTS.md GEMINI.md copilot-instructions.md .cursor/rules CONTEXT.md

図 1: 同じことを言うために、名前の違うファイルがこれだけ要ります。しかも置き場所も CLI ごとに違います。

ここで素直に「全部作る」と、次の形になります。

図 2: 全部に本文を置く つらい

CLAUDE.mdルール本文(146 行)
AGENTS.mdルール本文(146 行・同じ内容)
GEMINI.mdルール本文(146 行・同じ内容)
ルールを 1 つ足すたびに 3 箇所を直す。直し忘れると、CLI によって言うことが変わる。ズレを見つけるための検査も別途要る。

図 3: 1 本を正本にして、ほかは指すだけ これでよい

AGENTS.md1 行「CLAUDE.md を読め」
CLAUDE.mdルール本文(146 行)
ここだけを編集する
GEMINI.md1 行「CLAUDE.md を読め」
copilot-instructions.md1 行「CLAUDE.md を読め」
直すのは 1 箇所だけ。ほかのファイルは 1 行のまま一生変わらないので、ズレようがない。検査も要らない。

図 2 と図 3: ファイルのは減りません(各 CLI が自分の名前しか見ないので)。減るのは維持する実体の数です。ここを取り違えると「どうせ 2 つ要るなら同じ内容を置けばいい」と考えてしまい、図 2 に戻ります。

「その 1 行、AI はちゃんと読むのか」という当然の疑問は残ります。これは測れます。実際に測った結果は第 6 章にあります。

2. 各 CLI が実際に何を読むか(実測)

配線を決める根拠です。canary、つまり「どんなメッセージが来ても決まった文字列だけ返せ」と書いた指示ファイルを置いて、CLI がそれに従うかで判定しました。プロジェクト直下に AGENTS.mdCLAUDE.md(と GEMINI.md)を、それぞれ別の合言葉入りで置いて実行しています。

この測り方の限界 従わない = 読んでいない、ではない

canary に従ったなら、そのファイルは system prompt に載っていたと言えます。ツールを 1 回も呼ばずに振る舞いが変わるからです(OpenCode では JSON イベントで読み取りツールの呼び出しが 0 件であることを確認しました)。

しかし従わなかったとき、読んでいないとは言えません。読んだうえで「これだけ返せ」という指示を採用しなかっただけかもしれない。筆者はここを取り違えて、いったん「Antigravity は 3 つのどれも読んでいない」と書きました。実際には読んでいました(次章)。

代わりに「矛盾しない事実」を 3 ファイルに 1 つずつ書いて質問する測り方も試しましたが、こちらは使えませんでした。エージェントは rg でファイルを grep して答えてしまうので、自動で読み込まれたのか自分で探しに行ったのかが区別できません。実際 Codex は grep してから答えていました。

CLI従った方補足
Claude CodeCLAUDE.mdAGENTS.md だけを置いた場合も従いませんでした。プロジェクトの AGENTS.md は読んでいません
CodexAGENTS.md
OpenCodeAGENTS.mdAGENTS.md があると同じディレクトリの CLAUDE.md読まれません(次章)
GrokAGENTS.md
GitHub Copilot CLI状況で変わるAGENTS.md だけならそれに従い、AGENTS.mdCLAUDE.md の 2 つなら CLAUDE.md に従いました。GEMINI.md を足して 3 つにすると、どれにも従わなくなりました(2 回とも)。読んでいないという意味ではなく、矛盾する 3 つを前に採用をやめた、という読みです
Gemini CLI未測定認証で止まりました。個人向けの無料枠は「このクライアントはサポート対象外。Antigravity へ移行を」というエラーになります
Antigravity(agy従わないが読んでいるcanary には従いませんでしたが、対話セッションで自分のコンテキストを尋ねると、グローバルの ~/.gemini/GEMINI.md とプロジェクトの AGENTS.md<RULE[...]> というタグ付きで読み込んでいると答えました。詳細は次章
Cursor Agent CLI未測定無料プランでモデル指定が拒否され、実行できませんでした

グローバル指示ファイルの置き場は CLI ごとに違います。統一された 1 パスは存在しません。

CLIグローバル指示ファイル
Claude Code~/.claude/CLAUDE.md
OpenCode<configディレクトリ>/AGENTS.md。無ければ ~/.claude/CLAUDE.md へフォールバック
GitHub Copilot CLI~/.copilot/copilot-instructions.md。公式ドキュメントには .claude/CLAUDE.md も使うと記載あり(未測定)
Codex~/.codex/AGENTS.md
Grok~/.grok/ 配下の AGENTS.md など
Gemini CLI~/.gemini/GEMINI.md
Antigravity(agy~/.gemini/GEMINI.md(Gemini CLI と同じファイル)
Cursor Agent CLI無し。CLI からはグローバルのユーザールールを使えません

3. OpenCode の癖(先勝ちで排他)

OpenCode だけは選び方に癖があります。バイナリの中の指示ファイル解決コードを読むと、こうなっていました。

グローバル : [ <configディレクトリ>/AGENTS.md , ~/.claude/CLAUDE.md ]
             最初に存在した 1 つを読んで break

プロジェクト : [ AGENTS.md , CLAUDE.md , CONTEXT.md ]
             最初に見つかった 1 種類を読んで break

グローバルとプロジェクトは合算されますが、同じ階層の中では先勝ちで排他です。ここから帰結が 2 つ出ます。

3-b. Antigravity は読んでいた(測り方を間違えた話)

canary に従わなかったので、筆者は最初「Antigravity は AGENTS.md を読んでいない」と判断しました。これは誤りでした。

対話セッションで Antigravity CLI 自身に初回ロードの内容を尋ねると、次のように答えます。

スコープロードされたファイルプロンプト上のタグ
Global(マシン全体)~/.gemini/GEMINI.md<RULE[user_global]>
Project(ワークスペース)AGENTS.md<RULE[<プロジェクトのパス>/AGENTS.md]>

あわせて、探索の仕様も説明されました。カレントディレクトリからリポジトリルートに向かって GEMINI.mdAGENTS.md.agents/rules/*.md を探す、というものです。グローバルは ~/.gemini/GEMINI.md が自動ロードされます。

プロンプト上のタグ名まで挙がっている点が効いています。ファイルを探して見つけただけなら、こういう内部の目印は出てきません。読み込みと遵守は別物で、canary が測れるのは後者だけでした。

教訓

canary が返れば「載っている」と言えます。返らなくても「載っていない」とは言えません。否定側を主張するには、別の測り方が要ります。今回は当人に聞くのがいちばん早い、という結果になりました(ただし自己申告なので、タグ名のような内部の痕跡が伴っているかを見てください)。

出典: Antigravity CLI 1.1.22 の対話セッションでの応答。ベンダーの一次ドキュメントではありません。

3-c. 気づいたので配線した

ここで、この記事を書いている途中に別の穴が見つかりました。Antigravity には skills の棚を繋いでいませんでした。ルールは届いていたのに、skills だけ素通りしていたわけです。今後も使う CLI なので、記事を書く手を止めて配線しました。この章はその記録です。

Antigravity の探索仕様

同梱の skill agy-customizations が仕様の正本でした。~/.gemini/antigravity-cli/builtin/skills/agy-customizations/ にあります。読むと、discovery は 3 系統でした。

スコープ場所備考
Workspace.agents/.agent/ / _agents/ / _agent/ も可)cwd からリポジトリルートへ遡って探す。チームで共有する用
階層ルールGEMINI.md / AGENTS.md / .agents/rules/*.md同じく cwd からルートへ遡り、見つけたものを読む
グローバル~/.gemini/config/マシン全体。この下の skills/ が利用者の棚

customization の種類は Rules / Skills / Plugins / Hooks / MCP Servers の 5 つで、Skills は skills/<name>/SKILL.md という他の CLI と同じ形でした。つまり棚をそのまま繋げます。

足りなかったのは 1 本だけだった

確認したところ、ルール側は既に繋がっていました。~/.gemini/GEMINI.md に共通ルールの正本を読ませる参照行が入っていて、これは Gemini CLI 用に整備したものです。Antigravity は同じファイルを読むので、そのまま効いていました。実際、対話セッションでは <RULE[user_global]> として載っていることが確認できます。

足りなかったのは skills の棚だけでした。1 本張って解決です。

New-Item -ItemType Junction `
  -Path (Join-Path $env:USERPROFILE '.gemini\config\skills') `
  -Target <共有棚のパス>

張ったあとに確認します。

agy --print "あなたが今使える skill の総数を教えて"
  → 53 個

共有棚のディレクトリ数と一致しました。棚がそのまま見えています。

この節から持ち帰るもの

ルールが届いていることと、skills が届いていることは別です。今回は「ルールは繋がっているから大丈夫だろう」と流していて、記事を書くために各 CLI を並べ直したときに初めて気づきました。CLI を増やしたときは、2 系統それぞれを確認してください。

もう 1 つ。同梱の skill が仕様書になっていることがあります。Antigravity は agy-customizations がそれでした。ドキュメントを Web で探す前に、手元の CLI が自分の仕様を持っていないか見た方が速いことがあります。

4. 手順 A: skills の共有棚を作る

A-1. 正本の置き場を決める

どこでも構いませんが、CLI の設定ディレクトリのに置きます。この記事では次のパスで書きます。

既に skills を持っている CLI があるなら、その中身をここへ移動します。コピーだと二重管理が残ります。

A-2. 張り先の中身を先に確認する

リンクを張る前に、張り先が「無い」か「空」かを必ず確認します。中身のあるディレクトリを消してリンクに置き換える操作は絶対にしないでください。

Windows:

$paths = @('.claude\skills','.agents\skills','.cursor\skills','.config\opencode\skills','.copilot\skills','.gemini\config\skills')
foreach ($rel in $paths) {
  $p = Join-Path $env:USERPROFILE $rel
  if (Test-Path $p) {
    $item = Get-Item $p -Force
    $count = @(Get-ChildItem $p -Force -ErrorAction SilentlyContinue).Count
    "{0,-28} type={1,-10} entries={2}" -f $rel, ($item.LinkType ?? 'dir'), $count
  } else { "{0,-28} 無し" -f $rel }
}

macOS / Linux:

for rel in .claude/skills .agents/skills .cursor/skills .config/opencode/skills .copilot/skills .gemini/config/skills; do
  p="$HOME/$rel"
  if [ -e "$p" ]; then
    printf '%-28s %s entries=%s\n' "$rel" "$( [ -L "$p" ] && echo symlink || echo dir )" "$(ls -A "$p" 2>/dev/null | wc -l)"
  else
    printf '%-28s 無し\n' "$rel"
  fi
done

entries が 0 でないものが 1 つでもあれば、そこで止めて中身を棚へ移してから進みます。

A-3. リンクを張る

Windows はディレクトリジャンクションを使います。管理者権限も開発者モードも要りません。既存があればスキップする書き方にしてあります。

$src = Join-Path $env:USERPROFILE 'dev\ai-shelf\skills'
if (-not (Test-Path $src)) { New-Item -ItemType Directory -Path $src -Force | Out-Null }
foreach ($rel in @('.claude\skills','.agents\skills','.cursor\skills','.config\opencode\skills','.copilot\skills','.gemini\config\skills')) {
  $p = Join-Path $env:USERPROFILE $rel
  if (Test-Path $p) { "skip (既にある): $rel"; continue }
  $parent = Split-Path $p -Parent
  if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null }
  New-Item -ItemType Junction -Path $p -Target $src | Out-Null
  "created: $rel"
}

macOS / Linux はシンボリックリンクです。

src="$HOME/dev/ai-shelf/skills"
mkdir -p "$src"
for rel in .claude/skills .agents/skills .cursor/skills .config/opencode/skills .copilot/skills .gemini/config/skills; do
  p="$HOME/$rel"
  if [ -e "$p" ]; then echo "skip (既にある): $rel"; continue; fi
  mkdir -p "$(dirname "$p")"
  ln -s "$src" "$p"
  echo "created: $rel"
done

注意 触ってはいけない場所

~/.codex/skills にはリンクを張らないでください。Codex 同梱のシステム skill が入っており、丸ごと差し替えると隠れてしまいます。Codex には ~/.agents/skills 経由で届きます。

~/.gemini/antigravity-cli/builtin/skills も同じです。Antigravity 同梱の skill が入っています。利用者の棚は ~/.gemini/config/skills 側です。

ファイル名は SKILL.md(大文字)にします。小文字の skill.md は認識されないことがあります。

使う CLI が Codex + Claude Code + OpenCode の 3 つだけなら、必要なリンクは ~/.claude/skills~/.agents/skills<configディレクトリ>/opencode/skills の 3 本です。Antigravity も使うなら ~/.gemini/config/skills を足します。

5. 手順 B: 共通ルールの正本を 1 本にする

B-1. どれを正本にするか

~/.claude/CLAUDE.md を正本にするのが有利です。第 2 章の表のとおり、このパスは Claude Code が本体として読み、OpenCode がフォールバックで読み、Copilot も公式に参照すると書いています。グローバル層でいちばん到達範囲が広い名前です。

逆に「主流は AGENTS.md だから」とグローバルを寄せるのは得をしません。グローバルの置き場は CLI ごとに違うので、名前を揃えても 1 ファイルにはならず、3 箇所以上の複製になります。

B-2. 参照だけを置く CLI

~/.codex/AGENTS.md~/.grok/AGENTS.md には、正本を読ませる 1 行を置きます。

# 共通ルール

作業を始める前に `~/.claude/CLAUDE.md` を読み、そこに書かれた共通ルールに従うこと。
このファイルには CLI 固有の補足だけを書く。

この参照は自動ロードではありません。AI が自分で開いて初めて届きます。届くかどうかは第 7 章の方法で測れます。

注意 作ってはいけないファイル

<configディレクトリ>/opencode/AGENTS.md を作らないでください。作ると OpenCode の ~/.claude/CLAUDE.md へのフォールバックが止まります(第 3 章の先勝ち排他)。

B-3. 正本の骨格

中身は好みですが、索引にして本文を別ファイルへ逃がすと、全 CLI の毎セッションのコンテキストを食わずに済みます。骨格の例です。

# 共通ルール(全 AI CLI 共通の正本)

> このファイルは索引。詳細は各行が指す先を読む。本文をここに書き戻さない。

## ツール選択
(シェルの選び方、専用ツールを優先する話など。1 行ずつ)

## やってほしくないこと
- 指示がないのにビルドしない
- 指示がないのにコミット・プッシュ・タグ付けをしない
- 秘密が同居するファイル(.env / .npmrc など)を全文出力しない

## 出力の形式
(応答の書式、パスの書き方など)

## 詳細ガイドの索引
| いつ読むか | 読むファイル |
|---|---|
| リリース作業のとき | ~/.claude/guides/<name>.md |

索引が肥大しない仕組みを 1 つ入れておくと効きます。行数の上限を検査するスクリプトをコミット前フックに置くだけでも、放っておくと本文が戻ってくるのを止められます。

B-4. リポジトリ単位の指示ファイル

リポジトリ直下では、CLAUDE.md を正本にして AGENTS.md にポインタを置く形が扱いやすいです。第 2 章のとおり Claude Code は AGENTS.md を読まず、Codex と OpenCode は AGENTS.md を読むので、どちらの入口も塞ぎません。

# Agent Entry Point

このリポジトリの作業ルールは `CLAUDE.md` にある。作業前に読むこと。

- 概要とタスク別の索引: ./CLAUDE.md
- 設計の正本: ./docs/<design>.md

個人のグローバル設定はこのリポジトリに置かない。
各 AI ツールのグローバル指示ファイルを使うこと。

逆向き、つまり AGENTS.md を正本にして CLAUDE.md を生成物にする形も成立しますが、同じ内容のファイルが 2 つになり、ズレを検査する仕組みが要ります。参照 1 行で足りるなら、管理する実体が少ない方が楽です。

6. 参照方式は本当に効くのか

「参照 1 行は自動ロードではないから届かないのでは」という不安はもっともです。これは測れます。

やり方は、正本にしか書いていない事実を答えないと正解できない質問を、文書に触れない聞き方で投げること。実際に試した結果がこれです。

聞き方結果
質問形式で 3 回 3 回とも AGENTS.md のポインタから CLAUDE.md を開き、3 回とも正解した
作業依頼の形で 2 回
この機能を追加したい、実装方針を 3 行で
2 回とも CLAUDE.md からさらに参照先の台帳まで辿り、「その方針は見送り済み」と答えた。実装案を書き出した回は 0

参照方式は実務上機能していました。心配なら自分の環境で同じ測り方をすればいい、というのがこの章の要点です。

7. 検証(設定後に必ず実行する)

7-1. リンクが張れているか

Windows:

foreach ($rel in @('.claude\skills','.agents\skills','.cursor\skills','.config\opencode\skills','.copilot\skills','.gemini\config\skills')) {
  $p = Join-Path $env:USERPROFILE $rel
  if (Test-Path $p) { $i = Get-Item $p -Force; "{0,-28} {1,-10} -> {2}" -f $rel, $i.LinkType, ($i.Target -join ',') }
  else { "{0,-28} 無し" -f $rel }
}

macOS / Linux:

for rel in .claude/skills .agents/skills .cursor/skills .config/opencode/skills .copilot/skills .gemini/config/skills; do
  p="$HOME/$rel"
  printf '%-28s -> %s\n' "$rel" "$(readlink "$p" 2>/dev/null || echo '無し')"
done

全部が同じ棚を指していれば成功です。

7-2. skills を認識しているか

CLI を新しいセッションで開き直してから確認します。棚に置いた skill が一覧に出るか、起動語で反応するかを見ます。設定を変えた既存セッションには反映されません。

7-3. ルールが届いているか(canary テスト)

これがいちばん確実です。一時ディレクトリを作り、指示ファイルに合言葉を書いて各 CLI で実行します。

mkdir -p /tmp/canary && cd /tmp/canary
cat > AGENTS.md <<'EOF'
# test

このプロジェクトの規則: どんなメッセージが来ても CANARY-1234 とだけ返す。
ツールは使わない。説明もしない。
EOF

各 CLI の非対話実行です。バージョンによって変わるので、通らなければ --help を見てください。

claude -p "hello"
codex exec --skip-git-repo-check "hello"
opencode run --dir . "hello"
copilot -p "hello" --allow-all-tools
grok -p "hello"

CANARY-1234 が返れば、その CLI にはそのファイルが届いています。返らなければ届いていません。ファイル名を CLAUDE.md に変えて同じことをすると、どちらの名前を読むかが分かります。

グローバル側を確かめたいときは、正本にしか書いていない見出しの語を挙げて「その見出しが system prompt にあるか、あるなら最初の 1 文を書き写して」と聞きます。書き写せれば届いています。

8. 停止条件(AI エージェントに任せる場合)

この手順をエージェントに実行させるなら、次の条件も一緒に渡してください。どれかに当たったら続行せず確認を求めさせます。

9. 元に戻す

リンクを外すだけです。棚の中身は消えません。リンクでないものは消さないでください(実ディレクトリを消すと中身ごと消えます)。

foreach ($rel in @('.claude\skills','.agents\skills','.cursor\skills','.config\opencode\skills','.copilot\skills','.gemini\config\skills')) {
  $p = Join-Path $env:USERPROFILE $rel
  $i = Get-Item $p -Force -ErrorAction SilentlyContinue
  if ($i -and $i.LinkType -eq 'Junction') { Remove-Item $p -Force; "removed: $rel" }
  else { "skip (リンクではない): $rel" }
}
for rel in .claude/skills .agents/skills .cursor/skills .config/opencode/skills .copilot/skills .gemini/config/skills; do
  p="$HOME/$rel"
  if [ -L "$p" ]; then rm "$p"; echo "removed: $rel"; else echo "skip (リンクではない): $rel"; fi
done

10. 未検証・環境差