モノレポ Claude Code CLAUDE.md AGENTS.md Nx Turborepo Bazel pnpm workspaces トークン CI/CD 開発者ツール 2026

モノレポ × AIツール:50以上のパッケージを抱える大規模構成のパターン集(2026年版)

The Prompt Shelf ·

小規模なモノレポでAIコーディングツールを動かすのは設定の問題だ。50以上のパッケージになると、アーキテクチャの問題になる。

この違いは重要だ。30〜50パッケージを超えた時点で、素朴なアプローチ(ルートに1つのCLAUDE.md、1つのAGENTS.md、共通設定ひとつ)は測定可能なレベルでAIの出力品質を下げ始める。コンテキストが希薄になり、トークン消費が膨れ上がり、AIは自信満々に間違ったパッケージに間違ったコーディング規約を適用するようになる。コードレビューで違和感として現れてくるが、原因をすぐに特定できないことが多い。

このガイドは、Claude Code モノレポ設定ガイドモノレポにおける AGENTS.md ガイドの深掘り版だ。あの2本は基礎を扱っている。こちらは、基礎だけでは足りなくなった段階で効いてくるパターンを扱う。

具体的なトレードオフを持つ7つの設定パターン、Nx・Turborepo・pnpm workspaces・Bazel それぞれへのマッピング、トークン計測手法、そしてCI/CD統合(多くのチームが設定の欠陥を発見する場所)まで解説する。

なぜ規模が変わると話が変わるのか

パターンの説明に入る前に、何が壊れるのかを具体的に押さえておく。

コンテキスト希薄化の問題

Claude Code のコンテキストウィンドウは大きいが無限ではなく、セッション開始時に読み込まれるすべてのもの(CLAUDE.md ファイル群、会話履歴、読み込んだファイル、ツール出力)で共有される。小規模なモノレポでは、潤沢に設定しても CLAUDE.md 関連で消費するのは 15,000〜20,000 トークン程度で、残りを実作業に使える。

50以上のパッケージのリポジトリで素朴に CLAUDE.md を積み上げると、最初のツール呼び出し前の時点で 60,000〜80,000 トークンを消費することがある。コストが増えるだけでなく、実際のコードベースのコンテキストが設定の指示に押し出される。AIはコーディング規約については自信満々になり、関連コードを実際に読む能力が落ちる。

メンテナンス漂流の問題

小規模なモノレポは手作業での設定更新でなんとかなる。大規模リポジトリはそうはいかない。50のパッケージがあってテストランナーをリネームしたら、50の AGENTS.md を更新することになる。1ヶ月後には半数が間違った内容になっている。更新が完全に伝播しなかった、スクリプトを実行するのを忘れた、テンプレートに従わずに新しいパッケージが追加された、といった理由で。

この規模での設定漂流は、問題になるまで見えない。そして大抵、極めて微妙な形で問題になる。AIがわずかに古い規約を適用し、コードレビューで小さな不整合が出始め、新しいパッケージが古いパッケージと見た目が違う理由を誰もすぐに説明できない。

ワークスペースとツールのミスマッチ問題

Nx、Turborepo、pnpm workspaces、Bazel はそれぞれ実行モデルが異なり、AIツールの動作もどれを使っているかによって変わる。20パッケージのリポジトリではうまく機能する Turborepo 向け AGENTS.md パターンが、Nx ワークスペースでは混乱した出力を生むことがある。依存グラフの解決方法が違い、タスクキャッシュの動作が違い、単一パッケージのビルドを実行する「正しい」コマンドが、依存チェーンをトリガーするかどうかによって変わるからだ。

7つの設定パターン

これらは排他的ではない。実際のリポジトリでは2〜3のパターンを組み合わせることが多い。各パターンの構造、適切な用途、失敗するポイントを順に整理する。

パターン1:シングルルート設定

構造:

monorepo/
└── CLAUDE.md(またはAGENTS.md)  # すべてを1ファイルに

機能する条件: 15パッケージ未満、全体で同一技術スタック、小規模チーム。

機能しなくなる条件: 20パッケージを超えたとき、スタックが混在するとき(TypeScriptフロントエンドの隣にPythonサービス、Rust CLIなど)、ルートファイルが500行を超えたとき。

トークンコストの特性: セッションごとに固定オーバーヘッド。予測可能だがスケールしない。

# CLAUDE.md — ルート(シングル設定パターン)

## スタック
全パッケージ:TypeScript、Node.js 22、pnpm workspaces。

## ビルド
pnpm build — すべてのパッケージを依存順にビルド(Turborepo経由)

## テスト
pnpm test — 全パッケージでVitestを実行

## ルール
- 生のSQLは禁止。ORM(packages/dbはPrisma、それ以外はDrizzle)を使う。
- 全パッケージはnamed exportのみ。default exportは使わない。
- テストは必須。テストのないpublic APIは存在してはならない。

判断: ここから始めるのは問題ない。20パッケージに到達してからではなく、到達する前に移行パスを計画しておくこと。


パターン2:パッケージ別設定(フラット)

構造:

monorepo/
├── CLAUDE.md                  # ルート:横断事項のみ
├── packages/
│   ├── api/CLAUDE.md          # パッケージ固有
│   ├── web/CLAUDE.md
│   ├── mobile/CLAUDE.md
│   └── [他48個]/CLAUDE.md

機能する条件: 15〜40パッケージ、パッケージごとに独立したチーム、明確なオーナーシップの境界。

機能しなくなる条件: 50以上のパッケージでのメンテナンスコストが高くなる。横断的なルール変更のたびに全ファイルを更新する必要がある。新しいパッケージが既存のテンプレートからずれていく。

トークンコストの特性: 変動的だが許容範囲。エージェントはルートと現在操作中のパッケージのみを読み込むため、パッケージ総数に関係なくセッションごとのコストは上限がある。

# 現在の階層で読み込まれるトークン数を確認する
# 作業しているパッケージディレクトリから実行する
find . -maxdepth 1 -name "CLAUDE.md" -print -exec wc -w {} \;
cd ../.. && find . -maxdepth 1 -name "CLAUDE.md" -print -exec wc -w {} \;

判断: 最もよく推奨されるパターン。50以上の独立したファイルのメンテナンス負荷が解決する問題より大きくなるまでは機能する。


パターン3:階層型継承

構造:

monorepo/
├── CLAUDE.md                  # 第1層:全体共通
├── packages/
│   ├── CLAUDE.md              # 第2層:パッケージカテゴリのデフォルト
│   ├── api/CLAUDE.md          # 第3層:パッケージ固有の上書き
│   ├── web/CLAUDE.md
│   └── cli/CLAUDE.md
├── apps/
│   ├── CLAUDE.md              # 第2層:アプリのデフォルト
│   └── dashboard/CLAUDE.md   # 第3層:アプリ固有
└── services/
    ├── CLAUDE.md              # 第2層:サービスのデフォルト
    └── auth-service/CLAUDE.md # 第3層:サービス固有

機能する条件: 40〜100パッケージ、複数の明確なカテゴリ(アプリ、パッケージ、サービス)、パッケージではなくカテゴリで組織されたチーム。

機能しなくなる条件: カテゴリの境界があいまいなとき、またはパッケージがカテゴリのデフォルトを頻繁に上書きするとき(多くの上書きはカテゴリの境界が間違っていることを示す)。

トークンコストの特性: セッションごとに3層が読み込まれる:ルート(〜200語)+ カテゴリ(〜150語)+ パッケージ(〜200語)= 〜550語 / 〜750トークン。非常に管理しやすい。

重要な設計原則:各層は上位層との重複がゼロであるべきだ。カテゴリレベルの CLAUDE.md には、そのカテゴリの全パッケージには適用されるが他カテゴリのパッケージには適用されないルールのみを書く。

# packages/CLAUDE.md — カテゴリ層(ライブラリのみ)

## ライブラリパッケージのルール
- すべてのpublic APIはJSDocでドキュメントを書く
- パッケージはNode.js 18以上を対象とし、ブラウザは対象外
- フレームワーク依存(React、Express等)は禁止 — utilsのみ
- tsupでバンドル:`pnpm build`
- 公開設定はpackage.jsonの`exports`フィールドで

## テスト(ライブラリパッケージ)
- Vitestで単体テスト
- public APIはブランチカバレッジ100%
- Node.js 18と22でテスト(CIでマトリックス)
# packages/utils/CLAUDE.md — パッケージ層(上記ライブラリ層を継承)

## スコープ
文字列ユーティリティ、日付フォーマット、データ変換ヘルパー。
このパッケージはゼロ依存であり、そのまま維持する。

## 制約
- 非同期関数は禁止 — 同期変換のみ
- すべてのexportはtree-shakeable
- 関数名は動詞-名詞パターン:formatDate、parseAmount、slugify

判断: 大規模組織に最適なパターン。層の分離を維持する規律が必要だが、スケール時に効果を発揮する。


パターン4:ワークスペース対応ツール設定

構造:

monorepo/
├── CLAUDE.md
├── .claude/
│   ├── agents/
│   │   ├── nx-task-runner.md    # Nx専用エージェント
│   │   ├── build-verifier.md
│   │   └── cross-package-auditor.md
│   └── settings.json
└── packages/
    └── [AGENTS.mdを持つパッケージ群]

機能する条件: Nx、Turborepoなどのワークスペースレベルのツールを使用しており、それらを目的特化のAIエージェントでラップしたいチーム。

機能しなくなる条件: エージェントがビルドツールのバージョンに過度に依存しているとき。Nx 18向けのエージェント設定はNx 19へのアップグレード後にコマンドAPIが変わった場合は正しく機能しないことがある。

このパターンは CLAUDE.md の階層よりも、ワークスペースのオーケストレーション層を理解するサブエージェントに重点を置く。価値はエージェントにあり、パッケージ別ファイルにはない。

---
name: nx-task-runner
description: Nxタスクの実行、依存グラフの理解、影響を受けるパッケージの確認、Nxキャッシュの動作のデバッグが必要な場合に使用する。Nxコマンドとプロジェクトグラフを理解している。
tools: Bash, Read, Glob
model: sonnet
maxTurns: 10
---

このモノレポのNxビルドシステムを理解している。

## 主要コマンド
- `nx show project <name>` — プロジェクト設定とターゲットを表示
- `nx graph` — ブラウザで依存グラフを開く(CI:--file=output.jsonを使用)
- `nx affected --target=build --base=main` — 変更されたものだけビルド
- `nx run-many --target=test --all` — すべてをテスト
- `nx reset` — ビルドが古い出力を返す場合にNxキャッシュをクリア

## キャッシュの動作
Nxはインプットハッシュでタスク出力をキャッシュする。変更がビルド出力に反映されない場合:
1. `nx show project <name>` でキャッシュ可能な操作を確認する
2. ターゲットに `cache: true` がある場合、古い出力が返される可能性がある
3. `nx reset` でローカルキャッシュをすべてクリアする

## やってはいけないこと
- `nx deploy``nx affected --target=deploy` は絶対に実行しない — デプロイはCIを経由する
- ユーザーの明示的な指示なしに nx.json を変更しない

判断: 成熟したワークスペースツールを持つチームには強力。規模の小さいセットアップやNxの実行モデルに不慣れなチームにはオーバーエンジニアリングになる。


パターン5:ドメイン駆動構造

構造:

monorepo/
├── CLAUDE.md                     # 組織全体
├── domains/
│   ├── billing/
│   │   ├── CLAUDE.md             # 決済ドメインのルール
│   │   ├── billing-api/
│   │   ├── billing-ui/
│   │   └── billing-worker/
│   ├── identity/
│   │   ├── CLAUDE.md             # 認証ドメインのルール
│   │   ├── auth-service/
│   │   └── user-management/
│   └── analytics/
│       ├── CLAUDE.md             # 分析ドメインのルール
│       └── [パッケージ群]

機能する条件: プロダクト単位のチーム、コードベースにDDDがすでに導入されている、技術的なコンテキストと同じくらいビジネスコンテキストが必要なAIアシスタント。

機能しなくなる条件: インフラやユーティリティなど、技術的な懸念事項がドメインにきれいにマッピングされないとき。

ドメインレベルの CLAUDE.md が持つ「なぜ」という情報は、パッケージ別ファイルが見落としやすい部分だ。純粋に技術的なパッケージ設定では書けないビジネスルール、データオーナーシップの境界、ドメイン間の統合契約を説明できる。

# domains/billing/CLAUDE.md

## ドメイン概要
決済ドメインはサブスクリプションのライフサイクル、請求書生成、支払い処理を管理する。
このドメインはサブスクリプション状態の正式な情報源を所有している。他のドメインは
決済テーブルに直接アクセスするのではなく、決済APIに問い合わせる必要がある。

## ビジネスルール
- 冪等性キーなしで決済を処理しないこと — 二重請求はP0インシデントになる
- 価格変更はすべて price_override 承認ワークフローを経由する(billing-api/docs/pricing.md 参照)
- サブスクリプションのキャンセルはソフトデリートで90日間保持する(法的要件)
- 税率計算はレートサービスを使用する — 税率をハードコードしないこと

## パッケージの責務
- billing-api:HTTPAPIとWebhookハンドラー(Stripeイベント)
- billing-ui:顧客向けサブスクリプション管理UI
- billing-worker:非同期の請求書生成とダニング処理

## データの機密性
全決済データはPCI-DSSスコープに含まれる。カード番号、CVV、完全なPANをログに記録しないこと。
カード保有者データはStripeが管理する — 我々はトークンのみを保存する。

判断: チームがドメインで組織されているなら、構造への投資は価値がある。ドメインレベルのファイルにあるビジネスコンテキストは、ドメインロジックのタスクでAIの出力品質を明らかに向上させる。


パターン6:生成設定(テンプレートベース)

構造:

monorepo/
├── scripts/
│   ├── templates/
│   │   ├── library-package.claude.md.tpl
│   │   ├── app-package.claude.md.tpl
│   │   └── service-package.claude.md.tpl
│   └── generate-configs.ts
├── packages/
│   └── [生成されたCLAUDE.mdを持つパッケージ群]

機能する条件: 50以上のパッケージ、強力なエンジニアリングプラットフォームチーム、一貫したテンプレートに従うパッケージ、大量のオンボーディング。

機能しなくなる条件: ジェネレーターがエッジケースを処理しないとき、またはチームが「今回だけ」の手動上書きを追加し始めて将来の再生成を妨げるとき。

テンプレートアプローチはメンテナンス漂流の問題を機械的に解決する。設定はテンプレートから再生成され、乖離はCIでフラグが立ち、テンプレートへの更新は自動的にすべてに伝播する。

// scripts/generate-configs.ts
import { readFileSync, writeFileSync, existsSync } from "fs";
import { join } from "path";
import { glob } from "glob";

const TEMPLATE_MAP: Record<string, string> = {
  library: "templates/library-package.claude.md.tpl",
  app: "templates/app-package.claude.md.tpl",
  service: "templates/service-package.claude.md.tpl",
};

interface PackageJson {
  name: string;
  "ai-config"?: { type: "library" | "app" | "service"; overrides?: string };
}

async function generateConfigs() {
  const packageJsonPaths = await glob("packages/*/package.json");

  for (const pkgJsonPath of packageJsonPaths) {
    const pkg: PackageJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
    const packageType = pkg["ai-config"]?.type ?? "library";
    const templatePath = TEMPLATE_MAP[packageType];
    const packageDir = pkgJsonPath.replace("/package.json", "");

    let template = readFileSync(templatePath, "utf-8");
    template = template.replace("{{PACKAGE_NAME}}", pkg.name);
    template = template.replace("{{PACKAGE_DIR}}", packageDir);

    // 手動上書きセクションを保持する
    const overridesMarker = "## 手動上書き(再生成時に保持)";
    const existingConfig = existsSync(join(packageDir, "CLAUDE.md"))
      ? readFileSync(join(packageDir, "CLAUDE.md"), "utf-8")
      : "";
    const existingOverrides = existingConfig.includes(overridesMarker)
      ? existingConfig.split(overridesMarker)[1]
      : "";

    const output = template + (existingOverrides ? `\n${overridesMarker}\n${existingOverrides}` : "");
    writeFileSync(join(packageDir, "CLAUDE.md"), output);
    console.log(`生成完了: ${packageDir}/CLAUDE.md`);
  }
}

generateConfigs();

CIでの乖離チェック:

# .github/workflows/ai-config-check.yml
- name: AI設定の一貫性チェック
  run: |
    pnpm ts-node scripts/generate-configs.ts
    if [ -n "$(git diff --name-only | grep CLAUDE.md)" ]; then
      echo "CLAUDE.mdファイルがテンプレートと同期していない。scripts/generate-configs.tsを実行してコミットすること。"
      exit 1
    fi

判断: 50以上のパッケージには必須。テンプレートとジェネレータースクリプトへの初期投資は、数ヶ月で元が取れる。


パターン7:ハイブリッド(生成ベース + 手動上書き)

構造:

monorepo/
├── packages/
│   └── special-package/
│       └── CLAUDE.md
│           # 生成されたセクション(自動更新)
│           # --- 手動上書き(保持) ---
│           # テンプレートに合わない独自のルール

これはパターン6に明示的な脱出口を追加したものだ。ジェネレーターがベースを管理し、各ファイルの末尾にある明確にマークされたセクションが再生成をまたいで保持される。

必要な規律:手動上書きセクションは本当に例外的なケースのみを対象にする。ほとんどのパッケージで上書きを書いている場合は、テンプレートが間違っている。

# CLAUDE.md — packages/legacy-payments
# 生成元:scripts/templates/service-package.claude.md.tpl
# 最終再生成:2026-04-15
# 生成バージョン:2.4.1
# 生成されたセクションを直接編集しないこと。`pnpm generate:configs`で更新する。

## [生成コンテンツ以下]
[...標準サービスパッケージの内容...]

## 手動上書き(再生成時に保持)
このパッケージはレガシーPHPバックエンドで決済処理を行っている(移行中 — Q3 2026目標)。
Node.jsのデータベースパターンを提案しないこと。PHPサービスはservices/legacy-php/にある。

決済フローを変更する際は、services/legacy-php/ARCHITECTURE.mdを最初に読むこと。
統合ポイントはREST API — レガシーデータベーステーブルには直接アクセスしないこと。

判断: 大規模モノレポに最も堅牢なパターン。一貫性の問題(テンプレート)と例外が現実に存在するという問題(上書きセクション)の両方を処理する。


パターン比較表

パターン最適な用途パッケージ数メンテナンスコストセッションごとのトークン
シングルルート均質な小規模リポジトリ< 15非常に低い固定、多い
パッケージ別フラット独立チーム15〜40中程度低い、上限あり
階層型カテゴリ組織リポジトリ40〜100中程度低い、3層
ワークスペース対応ビルドツール利用が多いチーム任意高い(エージェント)低い
ドメイン駆動DDDコードベース20〜80中程度低い、ドメイン上限
生成設定プラットフォーム主導の組織50+低い(自動化)低い
ハイブリッド(生成+上書き)複雑な大規模リポジトリ50+非常に低い低い

ツール別ウォークスルー

上記のパターンはツールに依存しない。以下では各パターンが具体的なワークスペースツールにどうマッピングされるかを説明する。

Nx ワークスペース

Nxが他と異なる重要な点がひとつある:AIツールが自動的には読めないリッチなプロジェクトグラフを持っている。Claude は nx graph を実行して出力を推論することができないため、CLAUDE.md と AGENTS.md でプロジェクトグラフを人間が読める形で説明する必要がある。

推奨:階層型(パターン3)+ ワークスペース対応エージェント(パターン4)

# CLAUDE.md — ルート(Nxワークスペース)

## Nxの基本
- ワークスペースマネージャー:pnpm
- ビルドオーケストレーター:Nx 19(ターゲットのデフォルトはnx.json参照)
- 影響を受けるもののみ実行:`nx affected --target=<target> --base=main`
- 単一プロジェクト:`nx run <project>:<target>`
- Nxキャッシュをリセット:`nx reset`

## プロジェクト組織
- ライブラリ:libs/(内部、非公開)
- アプリケーション:apps/(デプロイ可能)
- 共有ユーティリティ:libs/shared/

## 依存ルール(ESLint @nx/enforce-module-boundariesで強制)
- apps/はlibs/からのインポートが可能
- libs/feature-*はlibs/data-access-*とlibs/ui-*からインポート可能
- libs/data-access-*はlibs/feature-*からインポート不可
- libs/ui-*はlibs/data-access-*からインポート不可
- アプリケーション間の境界を越えたインポートは禁止

Turborepo ワークスペース

Turborepoの重要な差別化要素は turbo.json のパイプライン設定(パッケージ間ではなくタスク間の依存グラフ)だ。AI設定はこれを反映する必要がある。

推奨:階層型(パターン3)

# CLAUDE.md — ルート(Turborepoワークスペース)

## Turborepoパイプライン
タスクの依存関係はturbo.jsonで定義されている。主要なパイプライン:
- buildは上流のbuildに依存(^build)
- testはローカルのbuildに依存(build)
- lintは依存なし(並列実行)

## タスクの実行
- 全パッケージ:`pnpm turbo <task>`
- 単一パッケージ:`pnpm turbo <task> --filter=<package>`
- 依存ありフィルター:`pnpm turbo <task> --filter=<package>...`
- ドライラン(実行予定を確認):`pnpm turbo build --dry-run`

## キャッシュの動作
Turborepoはコンテンツハッシュでキャッシュする。キャッシュミスが起きる条件:
- パッケージのソースファイルが変更された
- turbo.jsonのinputsにリストされた依存関係が変更された
- package.jsonまたはturbo.jsonが変更された

キャッシュミスを強制する:`pnpm turbo build --force`
ローカルキャッシュを削除する:`rm -rf .turbo`

pnpm ワークスペース(オーケストレーターなし)

一部のリポジトリはNxやTurborepoなしでpnpm workspacesを使っている。20パッケージ未満や、シンプルさを優先するリポジトリで一般的だ。

推奨:パッケージ別フラット(パターン2)または階層型(パターン3)

# CLAUDE.md — ルート(pnpm workspaces、オーケストレーターなし)

## ワークスペース設定
- パッケージマネージャー:pnpm 9
- ワークスペース定義:pnpm-workspace.yaml
- ビルドオーケストレーターなし — スクリプトはパッケージごとに独立して実行

## 依存グラフの不在
Nx/Turborepoとは異なり、タスク依存グラフは存在しない。
パッケージを間違った順序でビルドするとエラーになる。
既知のビルド順序:shared-types → utils → [その他すべて]
shared-typesを変更した場合は、utilsと全コンシューマーを手動で再ビルドすること。

「依存グラフがない」という明示的な記述が重要だ。Claudeはワークスペース設定からビルド順序を推論できない。明示することで、ビルドが失敗するような一般的なエラークラスを防げる。

Bazel

Bazelはハーメティックビルド、リモート実行、多言語サポートが重要な大規模組織で使用される。他のツールよりも大幅に複雑で、AI設定はその複雑さを反映する必要があるが、圧倒的にならないようにする。

推奨:ドメイン駆動(パターン5)+ ワークスペース対応エージェント(パターン4)

BazelのBUILDファイルが依存グラフの正式な情報源であり、CLAUDE.mdの階層ではない。AI設定はこれを明示的に認識する必要がある。

# CLAUDE.md — ルート(Bazelワークスペース)

## ビルドシステム
BazelとBzlmod(MODULE.bazel)。npm/pnpm/yarnは使用しない。

## 主要コマンド
- ターゲットをビルド:`bazel build //path/to/package:target`
- すべてビルド:`bazel build //...`
- 依存パッケージを検索:`bazel query "rdeps(//..., //path/to:lib)" --output=label`
- 依存関係を検索:`bazel query "deps(//path/to:target)" --output=label`

## やってはいけないこと
- `npm install` は絶対に実行しない — 依存関係はMODULE.bazelでBazelが管理する
- bazel-out/内の生成ファイルを変更しない
- bazel-out/を含むパスをハードコードしない

トークン計測

最適化するためには計測が必要だ。AI設定ファイルからの実際のトークン消費量を計測する手法を紹介する。

セッション開始時のコンテキスト読み込み量を計測する

#!/bin/bash
# measure-context-load.sh
# 作業しているパッケージディレクトリから実行して
# AIツールがセッション開始時に読み込む量をシミュレートする

PACKAGE_DIR="${1:-.}"
TOTAL_WORDS=0

# cwdからリポジトリルートへ歩きながらCLAUDE.mdを収集する
dir="$PACKAGE_DIR"
while [ "$dir" != "/" ]; do
  if [ -f "$dir/CLAUDE.md" ]; then
    words=$(wc -w < "$dir/CLAUDE.md")
    echo "$dir/CLAUDE.md: $words語"
    TOTAL_WORDS=$((TOTAL_WORDS + words))
  fi
  dir=$(dirname "$dir")
done

echo "---"
echo "合計語数: $TOTAL_WORDS"
echo "推定トークン数(語数 × 1.3): $((TOTAL_WORDS * 13 / 10))"
echo ""
if [ $TOTAL_WORDS -gt 2000 ]; then
  echo "警告:コンテキスト読み込み量が多い。トリミングまたは@参照の使用を検討すること。"
fi

全パッケージで実行して外れ値を見つける:

# CLAUDE.mdの読み込み量が最も多いパッケージを見つける
for pkg in packages/*/; do
  total=$(bash measure-context-load.sh "$pkg" 2>/dev/null | grep "合計語数" | awk '{print $2}')
  echo "$total $pkg"
done | sort -rn | head -20

パッケージ別のトークン予算の目標値

AI出力の品質を維持しながらコンテキストを管理するための実用的な目標値:

コンテキストレベル目標語数目標トークン数
グローバル(~/.claude/CLAUDE.md< 200語< 260トークン
リポジトリルートCLAUDE.md< 400語< 520トークン
カテゴリ/ドメインCLAUDE.md< 300語< 390トークン
パッケージCLAUDE.md< 300語< 390トークン
セッションごとの合計< 1,000語< 1,300トークン

読み込まれたすべてのファイルで1,000語を超えると、AIの注意がコーディング規約と実際のコードで分割される。規約が勝ち始め、コード品質が微妙な形で低下する。


CI/CD統合パターン

CIはAIツール設定がストレステストされる場所だ。ローカルでは問題なく動く設定が、作業ディレクトリ、環境変数、利用可能なコンテキストが異なるCIで失敗することが多い。

パターンA:プリフライト設定バリデーション

AIを使ったCIタスクが実行される前に、設定ファイルが読み込み可能で予算内であることを検証する:

# .github/workflows/ai-config-validate.yml
name: AI設定バリデーション
on: [push, pull_request]

jobs:
  validate-configs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: CLAUDE.mdの語数チェック
        run: |
          max_words=400
          failed=0
          for f in $(find . -name "CLAUDE.md" -not -path "*/node_modules/*"); do
            words=$(wc -w < "$f")
            if [ "$words" -gt "$max_words" ]; then
              echo "FAIL: $f は $words語(上限: $max_words語)"
              failed=1
            else
              echo "OK: $f ($words語)"
            fi
          done
          exit $failed

パターンB:PRチェックでのパッケージスコープのAIレビュー

PRが packages/api/ を変更した場合、そのパッケージに関連するコンテキストのみを読み込むパッケージスコープのAIレビューを実行する:

# .github/workflows/ai-pr-review.yml
name: AI PRレビュー
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  ai-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: スコープ付きAIレビューを実行
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          # パッケージディレクトリにcdすることで、
          # Claudeは関連するCLAUDE.mdファイルのみを読み込む
          cd packages/${{ matrix.package }}
          claude --no-interactive --max-turns 5 \
            "packages/${{ matrix.package }}のmainに対するgit diffをレビューしてください。CLAUDE.mdの規約違反、テスト不足、明らかなバグを確認し、PASS/FAIL、発見した問題、深刻度を含む構造化レポートを出力してください。"

パターンC:生成設定の乖離検出

パターン6または7(生成設定)を使っている場合、設定がテンプレートと乖離したときにビルドを失敗させるCIチェックを追加する:

- name: AI設定の同期を検証する
  run: |
    # 全設定を再生成する
    node scripts/generate-configs.js

    # CLAUDE.mdまたはAGENTS.mdが変更された場合は失敗
    if git diff --name-only | grep -E "(CLAUDE|AGENTS)\.md"; then
      echo "エラー:AI設定ファイルがテンプレートと同期していない。"
      echo "実行してください:node scripts/generate-configs.js && git add -A && git commit -m 'chore: AI設定を同期'"
      exit 1
    fi

    echo "すべてのAI設定ファイルがテンプレートと同期しています。"

よくある問題とFAQ

Q:60パッケージあって、ルートのCLAUDE.mdが800語に近づいている。何を最初に削るべきか?

ルールより先に例を削る。設定ファイルの例(「良いコミットメッセージの例を示す」)は役立つように感じるが、ルール自体から明らかな情報のために多くのトークンを消費する。例を参照ドキュメント(@docs/examples.md)に移動し、CLAUDE.mdの階層にはルールのみを残す。

例の次に、ツールでカバーできるルールを探す。リンターがルールを強制しているなら、CLAUDE.mdでそれを伝える必要はない。リンターがそれを検出し、エラー出力がClaudeに十分なコンテキストを与える。

Q:AGENTS.mdとCLAUDE.mdは同じディレクトリに共存すべきか?

はい、そして異なる目的を果たす。CLAUDE.mdはインタラクティブセッションでClaude Codeが読む。AGENTS.mdはOpenAIのCodex、GoogleのGemini CLI、その他のAGENTS.mdを探すツールが読む。チームが複数のAIツールを使っている場合は両方を維持する。内容の〜80%は共有でき、ツール固有のセクションが異なる。

Q:サブエージェントが間違ったパッケージのCLAUDE.mdを選び続ける。どう修正するか?

ほぼ確実にサブエージェントの description フィールドの問題だ。descriptionはClaudeがどのエージェントを呼び出すかの主要なシグナルだ。「このエージェントは何をするか」ではなく「Claudeはいつこのエージェントを選ぶべきか」に答える必要がある。

悪い例:"データベース操作とPrismaマイグレーションを処理する。"

良い例:"データベーススキーマの変更、Prismaマイグレーションの作成、データベースクエリのパフォーマンスデバッグ時に使用すること。一般的なAPIルート開発には使用しないこと。"

「使用しないこと」の節は誤ったルーティングを防ぐのに驚くほど効果的だ。

Q:TurborepoからNxへ移行中。AI設定でどう対応するか?

ルートのCLAUDE.mdに移行状態を明示的に文書化する:

## ビルドシステム(移行中:Turborepo → Nx)
移行進行中。完了目標:2026年Q3。

現状:
- packages/ — まだTurborepoを使用(pnpm turbo <task>)
- apps/ — Nxに移行済み(nx run <project>:<target>)

パッケージがどちらのシステムを使っているか不明な場合は、
パッケージルートのnx.jsonを確認すること。
nx.jsonがあればNx、なければTurborepo。

「移行中」の明示的なドキュメントは、そうしなければ数時間かかるデバッグが必要なAIのミスを防ぐ。

Q:廃止予定のパッケージのAI設定をどう扱うか?

パッケージのCLAUDE.mdの先頭に廃止通知を追加する:

# CLAUDE.md — packages/legacy-auth
# 非推奨:このパッケージはpackages/auth-v2に置き換えられる
# 新機能を追加しないこと。
# 重要でないバグはここでは修正せず、auth-v2で修正すること。
# 削除予定:2026年Q3

[...メンテナンス目的の残りの設定...]

廃止の明示的な指示は重要だ。それがないと、Claudeはコードが有効でテストが通るため、廃止されたパッケージに機能を追加しようとする。

Q:.claude/settings.jsonはコミットすべきか?

はい、制限付きで。チーム全体の標準を強制するプロジェクトレベルの設定(拒否されるコマンド、モデル選択、環境変数)をコミットする。APIキー、個人MCPサーバー、個人設定などはコミットしない。それらには .claude/settings.local.json を使い、.gitignore に追加する。

50以上のパッケージのモノレポでは、プロジェクト設定ファイルにはよく以下が含まれる:

{
  "model": "sonnet",
  "permissions": {
    "deny": [
      "Bash(npm install:*)",
      "Bash(yarn install:*)",
      "Bash(git push --force:*)",
      "Bash(bazel run :deploy:*)",
      "Bash(terraform apply:*)"
    ]
  },
  "env": {
    "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "8000",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  }
}

拒否リストは大規模モノレポでは特に価値がある。混乱したAIが広範な影響を持つコマンド(mainへのforce push、本番デプロイ)を誤って実行する可能性があるからだ。


関連記事

Related Articles

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules