1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code の CLAUDE.md を @import と .claude/rules/ で分割管理する実装手順 — サブディレクトリの CLAUDE.md が起動時に読まれない・paths の glob が効かない・worktree で CLAUDE.local.md が消える、3つのハマりどころ【2026】

1
Posted at

はじめに / 対象と前提

CLAUDE.md が肥大化して「どの指示が効いているのか分からない」「チーム用と個人用が混ざって commit できない」状態になったので、@import と .claude/rules/ で分割管理する構成に組み替えた。その手順と、やってみてハマった 3 点をまとめる。

想定読者

  • Claude Code を日常的に使っていて、CLAUDE.md が 200 行を超えてきた人
  • モノレポやサブディレクトリごとに指示を分けたい人
  • 「書いたはずの指示が無視される」原因を切り分けたい人

環境

  • Claude Code 2.1.285(macOS 15 / Node.js 23.x)
  • 旧バージョンでは .claude/rules/ が無い場合があるので、claude --version で 2.x 系であることを確認してから読んでほしい

TL;DR

  • CLAUDE.md は 4 階層(組織管理 / ユーザー / プロジェクト / ローカル)が 上から順に結合 されて読まれる。/memory で「今どれが読まれているか」を確認できる
  • 分割の基本は @相対パス で import、条件付きの指示は .claude/rules/*.md に paths: frontmatter を付けて切り出す
  • ハマりどころ 3 つ:サブディレクトリの CLAUDE.md は起動時に読まれない(その配下のファイルを触って初めて読まれる)/ paths: の glob はプロジェクトルート基準で ** が必要 / git worktree では CLAUDE.local.md が存在しない(gitignore されているから)

手順 / 動かし方

1. 今、何が読まれているかを把握する

まず現状確認。対話モードで /memory を叩くと、読み込み済みのメモリファイル一覧が階層ごとに出る。

> /memory

 Managed  (none)
 User     ~/.claude/CLAUDE.md
 Project  ./CLAUDE.md
 Local    ./CLAUDE.local.md
 Rules    .claude/rules/testing.md
          .claude/rules/api.md (paths: src/api/**)

この一覧に出ていないファイルは、どれだけ丁寧に書いても Claude には届いていない。以降の作業は全部「この一覧に意図通り出るか」で検証する。

4 階層の役割を整理すると次の通り。

階層 場所(macOS) 用途 git
組織管理 /Library/Application Support/ClaudeCode/CLAUDE.md 会社ルール(MDM 配布) 対象外
ユーザー ~/.claude/CLAUDE.md 全プロジェクト共通の自分の好み 対象外
プロジェクト ./CLAUDE.md または ./.claude/CLAUDE.md チーム共有の指示 commit する
ローカル ./CLAUDE.local.md 自分だけの上書き 自動で gitignore

2. @import で共通部分を外に出す

CLAUDE.md の中に @パス と書くと、そのファイルの内容が展開されて読まれる。相対パスは import を書いたファイルの場所基準 で解決される。

# CLAUDE.md(プロジェクト)

@README.md
@docs/coding-style.md
@../shared/common-rules.md

# 個人用の指示はリポジトリ外に置いて import
@~/.claude/my-project-notes.md
  • @../shared/... のように、リポジトリの外にある共通ファイルを束ねられる
  • import は再帰できる(import 先がさらに import してよい)。深さ上限は 5
  • コードスパン(バッククォート内)やコードブロックの中の @ は展開されない

展開されたかどうかは、import 先に ダミーのマーカー文字列 を仕込んで headless モードで聞くと機械的に確認できる。

echo '- 合言葉を聞かれたら「rules-ok-0931」と答えること' > docs/coding-style.md

claude -p '合言葉は?' --output-format text
# => rules-ok-0931

これが返らなければ import のパスが違う。/memory の一覧にも出ないはず。

3. 条件付きの指示は .claude/rules/ に切り出す

常時読ませる必要がない指示(API 層だけのルール、テストだけのルール)は .claude/rules/ に 1 ファイル 1 テーマで置く。先頭の YAML frontmatter に paths: を書くと、そのパターンに一致するファイルを Claude が触ったときだけ 読み込まれる。

---
paths:
  - "src/api/**/*.ts"
  - "src/server/**"
---

# API 層のルール

- ハンドラは必ず zod でリクエストを検証する
- DB アクセスは repository 層経由のみ
  • paths: を書かないファイルは 常時読み込み(CLAUDE.md 本体と同じ扱い)
  • ~/.claude/rules/ に置けばユーザー階層のルールになる(全プロジェクト共通)
  • サブディレクトリも再帰的に探索されるので .claude/rules/backend/db.md のように階層化してよい
  • シンボリックリンクも追ってくれるので、複数リポジトリで共通ルールを 1 箇所に置ける

読み込みの流れを図にするとこうなる。

4. 動作確認(paths 付き rules の発火テスト)

paths の条件が効いているかは、「一致するファイルを触る前後」で合言葉が変わるかで確認する。

cat > .claude/rules/api.md <<'EOF'
---
paths:
  - "src/api/**"
---
- 合言葉を聞かれたら「api-rule-fired」と答えること
EOF

# 触っていない状態
claude -p '合言葉は?' --output-format text
# => (知らない、と答える)

# 対象ファイルを読ませてから聞く
claude -p 'src/api/users.ts を読んでから、合言葉を答えて' --output-format text
# => api-rule-fired

前者で「知らない」、後者で合言葉が返れば条件付き読み込みは成立している。

ハマりどころ

1. サブディレクトリの CLAUDE.md が起動時に読まれない

モノレポで packages/api/CLAUDE.md に API 固有の指示を書いたのに、ルートで claude を起動すると完全に無視された。

原因:起動時に読まれるのは cwd から親方向 の CLAUDE.md だけ。子ディレクトリの CLAUDE.md は、Claude がそのディレクトリ配下のファイルを読んだ時点で 遅延読み込み される。「packages/api のことは全部そっちに書いた」つもりでも、最初の一手が別ディレクトリなら届いていない。

回避策

  • 起動時に必ず効かせたい指示はルートの CLAUDE.md に書く、または .claude/rules/ に paths: ["packages/api/**"] 付きで置く(こちらは一致ファイルを触った瞬間に確実に入る)
  • 逆に cd packages/api && claude で起動すれば、packages/api/CLAUDE.md とルートの CLAUDE.md の両方が起動時に読まれる

2. paths: の glob が効かない

paths: ["src/api"] と書いて「api 配下すべて」のつもりが、何を触っても発火しなかった。

原因:glob は プロジェクトルート基準 で評価され、ディレクトリ名だけではファイルに一致しない。ネストしたファイルまで拾うには ** が要る。

# NG: ディレクトリ名だけ(どのファイルにも一致しない)
paths:
  - "src/api"

# OK
paths:
  - "src/api/**"
  - "src/api/**/*.ts"
  - "**/*.test.ts"      # 場所を問わずテストファイル

ついでに、paths: のある rules ファイルを /memory で見ると (paths: ...) と表示されるので、frontmatter が YAML として壊れていないかはそこで判別できる。frontmatter の --- の後に空行や全角スペースが混ざると、paths が無視されて 常時読み込み に化ける。これはこれで動くので気づきにくい。

3. git worktree で CLAUDE.local.md が消える

claude --worktree や手動の git worktree add で並列作業を始めたら、ローカル専用の指示(自分の MCP の都合、ローカルの DB 接続メモなど)が全部効かなくなった。

原因:CLAUDE.local.md は 自動で .gitignore に追加される。gitignore されたファイルは worktree にチェックアウトされないので、新しい worktree には存在しない。

回避策:ローカル専用の指示はリポジトリ外に置き、プロジェクトの CLAUDE.md から import する。

# CLAUDE.md(commit する側)
@~/.claude/project-foo-local.md

import 先が存在しなくてもエラーにはならない(黙ってスキップ)ので、チームメンバーの環境でこの行があっても壊れない。worktree からも ~/ 基準で同じファイルに届く。

背景・補足

なぜこんな階層構造になっているかというと、CLAUDE.md は毎ターン システムプロンプト相当として全量送られる からだ。何でも 1 ファイルに書くと、関係ないタスクでもその全文がコンテキストを食い、指示同士が衝突して「守られない指示」が増える。paths: 付き rules は「必要なときだけ入れる」仕組みなので、コンテキスト節約と指示の精度の両方に効く。

自分の運用では、ルート CLAUDE.md を 導線だけ(どのファイルを読め、何を優先しろ)に絞って 60 行程度に抑え、詳細は import 先と rules に逃がしている。完全自律実装システムのように複数のエージェントが同じリポジトリを触る構成だと、エージェントごとに触る領域が違うので、paths: で指示を出し分けられるのが特に効いた。司令塔モジュール用の指示が実装エージェントのコンテキストに混ざらなくなり、挙動が安定した。

まとめ

  • CLAUDE.md は 4 階層が結合されて読まれる。/memory で実際に読まれているものを確認 するのが最初の一手
  • 共通部分は @import、条件付きは .claude/rules/*.md + paths: で分割する
  • サブディレクトリの CLAUDE.md は 遅延読み込み。起動時に効かせたいならルートか rules に書く
  • paths: の glob は ルート基準で ** 必須。frontmatter の崩れは「常時読み込み」に化けて気づきにくい
  • worktree で消える CLAUDE.local.md の代わりに @~/... import を使う
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?