はじめに / 対象と前提
Claude Code の CLAUDE.md は「プロジェクトのルールをAIに読ませるファイル」として使っている人が多いと思うが、実は 複数箇所に置いたものが階層的にマージされる 仕組みになっている。さらに @path/to/file.md という記法でファイルを読み込む import 機能もある。
この記事では、この階層読み込みと @import を実際に動かして、以下の3つのハマりどころを踏み抜いた記録を残す。
想定読者:Claude Code を業務や個人開発で使っていて、CLAUDE.md を単一ファイルとしてしか使ったことがない人。
前提環境:
- Claude Code v2.x 系(2026年8月時点の最新系列)
- macOS / Linux どちらでも再現可能(パス表記のみ差分あり)
TL;DR
-
CLAUDE.mdは enterprise → project → user の順で複数箇所から読み込まれ、後から読まれたものが優先される(上書きではなく "併記" に近い挙動) -
@path/to/file.mdで他ファイルを読み込める。相対パスは「読み込み元ファイルの場所」基準で解決される(cwd 基準ではない) - import の再帰は無限には潜らない。深いネストや循環参照は途中で止まるため、意図せず一部だけ読み込まれないケースがある
手順 / 動かし方
1. 階層読み込みの実態を確認する
Claude Code は起動時に以下の場所を順に探して CLAUDE.md を読み込む(存在するものだけ)。
# 1. エンタープライズポリシー(管理者が配置、通常は個人開発では無関係)
/Library/Application Support/ClaudeCode/CLAUDE.md # macOS の場合
# 2. プロジェクトルート(git管理下に置くことが多い)
./CLAUDE.md
# 3. ユーザーグローバル設定
~/.claude/CLAUDE.md
実際にどれが読み込まれているかは、セッション中に確認できる。
# プロジェクトルートに最小限のCLAUDE.mdを置く
cat > ./CLAUDE.md <<'EOF'
# Project Rules
- コミットメッセージは日本語で書く
EOF
# ユーザーグローバルにも別ルールを置く
cat > ~/.claude/CLAUDE.md <<'EOF'
# Global Rules
- 常にテストを書いてからコードを書く
EOF
この状態で Claude Code を起動すると、両方のルールが同時に有効になる。片方が片方を上書きするわけではなく、両方がコンテキストに乗る。「プロジェクト側に書いたのにグローバル側の指示が優先されて困る」という相談をたまに見かけるが、実態は上書きではなく併記なので、矛盾するルールを両方に書くと プロンプト内で指示が衝突する。これが1つ目のハマりどころ。
回避策:同じ観点のルールは1箇所にまとめる。プロジェクト固有の話はプロジェクト側、個人の作業スタイルはユーザー側、と役割を分けて書く。
2. @path で他ファイルを import する
CLAUDE.md の中で以下のように書くと、別ファイルの中身をその場に展開できる。
# CLAUDE.md
@docs/architecture.md
@../shared/coding-rules.md
## プロジェクト概要
...
大規模プロジェクトで CLAUDE.md が肥大化するのを防ぐのに便利で、チーム共通ルールを shared/ に切り出して複数プロジェクトから import する、という構成もよくやる。
mkdir -p /tmp/claude-import-demo/shared
cat > /tmp/claude-import-demo/shared/coding-rules.md <<'EOF'
## 共通コーディングルール
- 型は明示する
EOF
mkdir -p /tmp/claude-import-demo/project-a
cat > /tmp/claude-import-demo/project-a/CLAUDE.md <<'EOF'
@../shared/coding-rules.md
## project-a 固有ルール
- APIキーは環境変数から読む
EOF
project-a で Claude Code を起動すると shared/coding-rules.md の内容が展開された状態で読み込まれる。
ハマりどころ
① 相対パスの基準がcwdではなくファイルの場所
@../shared/coding-rules.md
このパスは 「Claude Code を起動したディレクトリ」からの相対パスではなく、「この @import を書いている CLAUDE.md 自身の場所」からの相対パス として解決される。
自分がハマったのは、サブディレクトリの docs/CLAUDE.md から @../../shared/x.md のように書いたつもりが、実際には docs/CLAUDE.md から見た相対パスなので1階層分ズレていたケース。エラーメッセージは特に出ず、単に該当ファイルが読み込まれず静かに無視されるため、「ルールを書いたのに反映されない」状態になって原因究明に時間を溶かした。
回避策:import 元のファイルパスを起点に、実際に ls で辿って確認してから書く。可能な限り深いネストからの @../../ の多用は避け、import される側のファイルをプロジェクトルート直下に集約する。
② 循環参照・深いネストは途中で止まる
a.md が b.md を import し、b.md が a.md を import する、というような循環を作ると当然ながら無限ループにはならず、途中で読み込みが打ち切られる。同様に、import の連鎖が一定階層より深くなると、それ以上は展開されない。
<!-- a.md -->
@b.md
<!-- b.md -->
@a.md
こういう構成を意図的に作ることはまず無いが、「共通ルールファイルを跨いで相互参照させる」ような設計(例:architecture.md と conventions.md が互いにリンクし合う)にすると、知らないうちに循環に近い構造になりやすい。自分の場合、3つのファイルが A→B→C→A の形で循環していて、Cの内容だけがコンテキストに乗っていないことに気づかず数日運用してしまった。
回避策:import の依存関係は 一方向の木構造(ルート CLAUDE.md → 各トピック別ファイル、トピック別ファイル同士は import し合わない)に設計する。
③ import ファイル内のコードブロックが誤認識される
@path.md の記法はテキスト中のどこに書いても展開対象として認識されるため、コードブロック内で @sample.md のような文字列をサンプルとして書いた場合も import 扱いされてしまうことがある。
## 使用例
以下のように書くとimportできます:
\`\`\`markdown
@example-config.md
\`\`\`
このケースで example-config.md という実ファイルが存在しない場合は無視されるだけだが、たまたま同名ファイルがプロジェクト内に存在すると、説明用のサンプルのつもりが実際に中身が展開されてしまう。ドキュメント内で @import の書き方自体を説明したいときに注意が必要。
回避策:説明用のサンプルコードでは @ の直後にゼロ幅スペースを挟む、または `@path.md`(インラインコード)ではなく画像や別の記法で示すなど、実際の import 記法と区別がつく書き方にする。
背景・補足
なぜこの仕様になっているかというと、CLAUDE.md は「チームで共有しつつ、個人の作業スタイルも上乗せできる」ことを狙った設計になっているからだと考えられる。enterprise → project → user という順序も、組織のポリシーを土台にしつつ、プロジェクト固有ルール、さらに個人の癖を積み重ねられるようにするためのものだろう。
この階層構造を理解しておくと、「チーム全体のルールは project 側、自分だけのメモは user 側」ときれいに分離でき、CLAUDE.md が1つの巨大ファイルに肥大化するのを防げる。
まとめ
-
CLAUDE.mdは enterprise/project/user の複数箇所から 併記される、上書きではない -
@pathの相対パスは import元ファイルの場所基準、cwd基準ではないので要注意 - 循環参照・深いネストは静かに打ち切られる。import 構造は一方向の木にする
- 読み込まれない・反映されないときはエラーが出ないことが多いので、実際に
lsでパスを辿って確認するのが結局一番早い