AI コーディング CLI を何本も併用していると、同じものを 2 つ管理させられます。skills が CLI ごとに別の場所へ散らばり、共通ルールを CLI の数だけ書き写すことになる。どちらも「正本を 1 箇所に置き、各 CLI にはそれぞれの流儀で到達させる」だけで解決します。この記事は、その配線を実際に作るための手順書です。各 CLI が本当にどのファイルを読むかは、推測ではなく実測しました。
上から順に読んで手で実行してもいいですし、章ごとそのまま AI エージェントへ貼って実行させることもできます。危ない操作を踏まないための停止条件を第 8 章に置いてあるので、エージェントに任せる場合はそこも一緒に渡してください。
併用していると、次の 2 つが CLI の数だけ増えます。
<name>/SKILL.md)が CLI ごとに別のディレクトリへ散らばるやることは 2 つだけです。
同じ「作業のルール」を伝えたいだけなのに、受け取る側の名前がそろっていません。
図 1: 同じことを言うために、名前の違うファイルがこれだけ要ります。しかも置き場所も CLI ごとに違います。
ここで素直に「全部作る」と、次の形になります。
図 2 と図 3: ファイルの数は減りません(各 CLI が自分の名前しか見ないので)。減るのは維持する実体の数です。ここを取り違えると「どうせ 2 つ要るなら同じ内容を置けばいい」と考えてしまい、図 2 に戻ります。
「その 1 行、AI はちゃんと読むのか」という当然の疑問は残ります。これは測れます。実際に測った結果は第 6 章にあります。
配線を決める根拠です。canary、つまり「どんなメッセージが来ても決まった文字列だけ返せ」と書いた指示ファイルを置いて、CLI がそれに従うかで判定しました。プロジェクト直下に AGENTS.md と CLAUDE.md(と GEMINI.md)を、それぞれ別の合言葉入りで置いて実行しています。
canary に従ったなら、そのファイルは system prompt に載っていたと言えます。ツールを 1 回も呼ばずに振る舞いが変わるからです(OpenCode では JSON イベントで読み取りツールの呼び出しが 0 件であることを確認しました)。
しかし従わなかったとき、読んでいないとは言えません。読んだうえで「これだけ返せ」という指示を採用しなかっただけかもしれない。筆者はここを取り違えて、いったん「Antigravity は 3 つのどれも読んでいない」と書きました。実際には読んでいました(次章)。
代わりに「矛盾しない事実」を 3 ファイルに 1 つずつ書いて質問する測り方も試しましたが、こちらは使えませんでした。エージェントは rg でファイルを grep して答えてしまうので、自動で読み込まれたのか自分で探しに行ったのかが区別できません。実際 Codex は grep してから答えていました。
| CLI | 従った方 | 補足 |
|---|---|---|
| Claude Code | CLAUDE.md | AGENTS.md だけを置いた場合も従いませんでした。プロジェクトの AGENTS.md は読んでいません |
| Codex | AGENTS.md | |
| OpenCode | AGENTS.md | AGENTS.md があると同じディレクトリの CLAUDE.md は読まれません(次章) |
| Grok | AGENTS.md | |
| GitHub Copilot CLI | 状況で変わる | AGENTS.md だけならそれに従い、AGENTS.md と CLAUDE.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 からはグローバルのユーザールールを使えません |
OpenCode だけは選び方に癖があります。バイナリの中の指示ファイル解決コードを読むと、こうなっていました。
グローバル : [ <configディレクトリ>/AGENTS.md , ~/.claude/CLAUDE.md ]
最初に存在した 1 つを読んで break
プロジェクト : [ AGENTS.md , CLAUDE.md , CONTEXT.md ]
最初に見つかった 1 種類を読んで break
グローバルとプロジェクトは合算されますが、同じ階層の中では先勝ちで排他です。ここから帰結が 2 つ出ます。
<configディレクトリ>/AGENTS.md を作ると、~/.claude/CLAUDE.md へのフォールバックが止まる。作らない方が得AGENTS.md があると、同じリポジトリの CLAUDE.md は OpenCode には届かないcanary に従わなかったので、筆者は最初「Antigravity は AGENTS.md を読んでいない」と判断しました。これは誤りでした。
対話セッションで Antigravity CLI 自身に初回ロードの内容を尋ねると、次のように答えます。
| スコープ | ロードされたファイル | プロンプト上のタグ |
|---|---|---|
| Global(マシン全体) | ~/.gemini/GEMINI.md | <RULE[user_global]> |
| Project(ワークスペース) | AGENTS.md | <RULE[<プロジェクトのパス>/AGENTS.md]> |
あわせて、探索の仕様も説明されました。カレントディレクトリからリポジトリルートに向かって GEMINI.md、AGENTS.md、.agents/rules/*.md を探す、というものです。グローバルは ~/.gemini/GEMINI.md が自動ロードされます。
プロンプト上のタグ名まで挙がっている点が効いています。ファイルを探して見つけただけなら、こういう内部の目印は出てきません。読み込みと遵守は別物で、canary が測れるのは後者だけでした。
canary が返れば「載っている」と言えます。返らなくても「載っていない」とは言えません。否定側を主張するには、別の測り方が要ります。今回は当人に聞くのがいちばん早い、という結果になりました(ただし自己申告なので、タグ名のような内部の痕跡が伴っているかを見てください)。
出典: Antigravity CLI 1.1.22 の対話セッションでの応答。ベンダーの一次ドキュメントではありません。
ここで、この記事を書いている途中に別の穴が見つかりました。Antigravity には skills の棚を繋いでいませんでした。ルールは届いていたのに、skills だけ素通りしていたわけです。今後も使う CLI なので、記事を書く手を止めて配線しました。この章はその記録です。
同梱の 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 と同じ形でした。つまり棚をそのまま繋げます。
確認したところ、ルール側は既に繋がっていました。~/.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 が自分の仕様を持っていないか見た方が速いことがあります。
どこでも構いませんが、CLI の設定ディレクトリの外に置きます。この記事では次のパスで書きます。
%USERPROFILE%\dev\ai-shelf\skills~/dev/ai-shelf/skills既に skills を持っている CLI があるなら、その中身をここへ移動します。コピーだと二重管理が残ります。
リンクを張る前に、張り先が「無い」か「空」かを必ず確認します。中身のあるディレクトリを消してリンクに置き換える操作は絶対にしないでください。
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 つでもあれば、そこで止めて中身を棚へ移してから進みます。
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 を足します。
~/.claude/CLAUDE.md を正本にするのが有利です。第 2 章の表のとおり、このパスは Claude Code が本体として読み、OpenCode がフォールバックで読み、Copilot も公式に参照すると書いています。グローバル層でいちばん到達範囲が広い名前です。
逆に「主流は AGENTS.md だから」とグローバルを寄せるのは得をしません。グローバルの置き場は CLI ごとに違うので、名前を揃えても 1 ファイルにはならず、3 箇所以上の複製になります。
~/.codex/AGENTS.md と ~/.grok/AGENTS.md には、正本を読ませる 1 行を置きます。
# 共通ルール
作業を始める前に `~/.claude/CLAUDE.md` を読み、そこに書かれた共通ルールに従うこと。
このファイルには CLI 固有の補足だけを書く。
この参照は自動ロードではありません。AI が自分で開いて初めて届きます。届くかどうかは第 7 章の方法で測れます。
<configディレクトリ>/opencode/AGENTS.md を作らないでください。作ると OpenCode の ~/.claude/CLAUDE.md へのフォールバックが止まります(第 3 章の先勝ち排他)。
中身は好みですが、索引にして本文を別ファイルへ逃がすと、全 CLI の毎セッションのコンテキストを食わずに済みます。骨格の例です。
# 共通ルール(全 AI CLI 共通の正本)
> このファイルは索引。詳細は各行が指す先を読む。本文をここに書き戻さない。
## ツール選択
(シェルの選び方、専用ツールを優先する話など。1 行ずつ)
## やってほしくないこと
- 指示がないのにビルドしない
- 指示がないのにコミット・プッシュ・タグ付けをしない
- 秘密が同居するファイル(.env / .npmrc など)を全文出力しない
## 出力の形式
(応答の書式、パスの書き方など)
## 詳細ガイドの索引
| いつ読むか | 読むファイル |
|---|---|
| リリース作業のとき | ~/.claude/guides/<name>.md |
索引が肥大しない仕組みを 1 つ入れておくと効きます。行数の上限を検査するスクリプトをコミット前フックに置くだけでも、放っておくと本文が戻ってくるのを止められます。
リポジトリ直下では、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 行で足りるなら、管理する実体が少ない方が楽です。
「参照 1 行は自動ロードではないから届かないのでは」という不安はもっともです。これは測れます。
やり方は、正本にしか書いていない事実を答えないと正解できない質問を、文書に触れない聞き方で投げること。実際に試した結果がこれです。
| 聞き方 | 結果 |
|---|---|
| 質問形式で 3 回 | 3 回とも AGENTS.md のポインタから CLAUDE.md を開き、3 回とも正解した |
| 作業依頼の形で 2 回 この機能を追加したい、実装方針を 3 行で |
2 回とも CLAUDE.md からさらに参照先の台帳まで辿り、「その方針は見送り済み」と答えた。実装案を書き出した回は 0 |
参照方式は実務上機能していました。心配なら自分の環境で同じ測り方をすればいい、というのがこの章の要点です。
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
全部が同じ棚を指していれば成功です。
CLI を新しいセッションで開き直してから確認します。棚に置いた skill が一覧に出るか、起動語で反応するかを見ます。設定を変えた既存セッションには反映されません。
これがいちばん確実です。一時ディレクトリを作り、指示ファイルに合言葉を書いて各 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 文を書き写して」と聞きます。書き写せれば届いています。
この手順をエージェントに実行させるなら、次の条件も一緒に渡してください。どれかに当たったら続行せず確認を求めさせます。
~/.codex/skills を触ることになった~/.claude/CLAUDE.md や ~/.codex/AGENTS.md を上書きすることになった(追記は可、置換は不可)リンクを外すだけです。棚の中身は消えません。リンクでないものは消さないでください(実ディレクトリを消すと中身ごと消えます)。
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
AGENTS.md / CLAUDE.md / .cursor/rules を読むとされていますが、7-3 の canary テストで各自確認してください~/.claude/CLAUDE.md を読むかは未測定です(公式ドキュメントに記載があるという情報にもとづいています)。またプロジェクト側は、置いてあるファイルの数で結果が変わりました。複数の指示ファイルを同居させるなら、Copilot では内容を矛盾させない方が無難です~/.gemini/GEMINI.md を読むとされていますが、実測はできていませんagy-customizations の記述にもとづきます。同梱物なので自己申告よりは強い出典ですが、公開ドキュメントとの突き合わせはしていません。ロード内容(第 3-b 章)の方は対話セッションでの応答なので自己申告です