マルチリポジトリのワークスペースに合わせた Claude Code の構成

Dev.to / 2026/3/27

💬 オピニオンTools & Practical Usage

要点

  • Claude Code はリポジトリごとに動作するため、マルチリポジトリのマイクロサービス・ワークスペースでは、隣接するリポジトリ、共有ライブラリ、インフラストラクチャに関する認識がありません。各セッションでレイアウトを改めて提示しない限り、その状態が続きます。
  • 提案されている解決策は、「ブートストラップ」(ワークスペースルート)リポジトリを作成することであり、そこにリポジトリ・マニフェスト、ディレクトリツリーのコンテキストファイル、そして pull/search/status のような共通のリポジトリ横断アクション用タスクを含めます。
  • ブートストラップリポジトリは、組織レベルの CLAUDE.md と、チームごとの CLAUDE.md(それぞれ)を保持し、新しく参加したチームメンバーがコードや共有インフラがどこにあるかを理解できるようにします。
  • 記事では、mani.yaml と、ドメインごとのマニフェスト(例:orders、shipping、analytics、shared、infra)によって、すべてのリポジトリがどのように組み合わさるかを定義する、階層化されたディレクトリ構造を示しています。

Claude Code は一度に 1 つのリポジトリだけを理解します。多くのチームでは、その数は 30 あります。

マイクロサービス、共有ライブラリ、インフラストラクチャ・アズ・コード、フロントエンドアプリ、データパイプラインがすべて別々の git リポジトリに分かれています。Claude Code を 1 つ起動して別のものについて尋ねても、文脈がありません。ワークスペースの存在を知りません。

そこで、複数リポジトリにまたがって動くように、私がどう設定してきたかを紹介します。

複数リポジトリのワークスペースにおいて、組織・チーム・リポジトリの文脈がレイヤーのように積み重なる様子を示す3つの半透明レイヤー

問題

orders/order-service で Claude Code を起動しても、隣に orders/orders-ui があることや、共有ライブラリが shared/ にあること、データチームの Spark ジョブが analytics/ にあることを Claude Code は知りません。すべてのセッションは、あなたがワークスペースの構成を説明するところから始まります。

同じ問題は、新しくチームに加わった人でも起きます。1 つのリポジトリをクローンするものの、他にどんなリポジトリがあるのか、それらがどう関係しているのか、共有インフラがどこにあるのかを知りません。

ワークスペースのルートとしてのブートストラップリポジトリ

私が行き着いた方針は、他のすべてのリポジトリの上に置かれ、ワークスペースのルートになるブートストラップリポジトリです。そこにはアプリケーションコードは含めません。含めるのは次のものです:

  1. リポジトリマニフェスト:すべてのリポジトリ、置き場所、役割を列挙します
  2. コンテキストファイル:Claude Code がディレクトリツリーから読み取るもの
  3. タスク:リポジトリをまたぐ共通操作(全部を pull、全部を検索、ステータス確認など)

リポジトリ管理には mani を使っていますが、考え方はどんなツールでも同じです。シェルスクリプトとリポジトリの一覧で同様のことができます。

ディレクトリ構造

workspace/
  mani.yaml                  # 製品ごとの設定を取り込む
  CLAUDE.md                  # 組織レベルのコンテキスト
  mani.d/
    orders.yaml              # 注文管理(3階層)
    shipping.yaml            # 輸送・物流(3階層)
    analytics.yaml           # データプラットフォーム(Spark, Airflow, APIs)
    assist.yaml              # エージェント型 AI システム(FastAPI, LangGraph, React)
    shared.yaml              # 共有ライブラリとサービス
    infra.yaml               # インフラリポジトリ
  orders/
    CLAUDE.md                # チームレベルのコンテキスト(ブートストラップで追跡)
    order-service/           # Spring Boot(gitignored)
    payment-service/         # Spring Boot(gitignored)
    orders-ui/               # React(gitignored)
    reporting-service/       # Spring Boot + PostgreSQL(gitignored)
    pricing-engine/          # Vert.x(Spring Bootではない)(gitignored)
  shipping/
    CLAUDE.md
    shipment-service/        # Spring Boot + MongoDB
    shipping-ui/             # Angular
    carrier-service/         # Spring Boot(リアクティブ)
  analytics/
    CLAUDE.md
    airflow-dags/            # Python、Airflow
    spark-jobs/              # EMR 上での PySpark
    metrics-service/         # Kotlin、Micronaut
    dashboard-ui/            # React
  assist/
    CLAUDE.md
    agent-service/           # FastAPI + LangGraph
    conversation-service/    # Spring Boot + WebSocket
    chat-ui/                 # React + ストリーミングチャット
  shared/
    CLAUDE.md
    react-lib/
    java-commons/
    feature-toggles/
  infra/
    CLAUDE.md
    terraform-modules/
    ci-templates/
    cluster/

製品の下にある各インデント付きディレクトリ(order-service/orders-ui/spark-jobs/ など)は別々の git リポジトリで、リポジトリ管理ツールがクローンし、ブートストラップリポジトリが gitignore します。それぞれのレベルにある CLAUDE.md ファイルはブートストラップリポジトリで追跡されます。

文脈の3つのレイヤー

Claude Code は CLAUDE.md ファイルを探すためにディレクトリツリーを上へたどっていきます。orders/order-service で起動すると、次を読み取ります:

  1. orders/order-service/CLAUDE.md(リポジトリレベル:そのリポジトリでコミット)
  2. orders/CLAUDE.md(チームレベル:ブートストラップでコミット)
  3. workspace/CLAUDE.md(組織レベル:ブートストラップでコミット)

各レイヤーは、他のレイヤーが提供している内容を繰り返さずに、追加の文脈を付け足します。

レイヤー1:組織

組織レベルの CLAUDE.md には、どこにでも当てはまることをまとめます:

  • これが複数リポジトリのワークスペースであるという警告(git 操作の前に git rev-parse --show-toplevel を確認する)
  • リポジトリの見つけ方(マニフェストファイルを指す)
  • 存在する製品と、それらが所有するもの
  • リポジトリをまたぐ共通操作

短く保ちます。どのリポジトリにいるかに関わらず、Claude は毎セッションそれを読みます。

レイヤー2:チーム

チームレベルの CLAUDE.md には、そのグループ内のリポジトリで共有される慣習をまとめます。内容は製品タイプによって変わります:

3階層の製品(orders や shipping など)なら、例えば:

  • バックエンドスタック(Java 21、Spring Boot 3.5、Gradle、MongoDB)
  • フロントエンドスタック(React 19、Vite、TypeScript)
  • 各もののビルドおよびテストコマンド
  • 例外は 1 つだけ(pricing engine は Spring Boot ではなく Vert.x を使う)

データプラットフォーム(analytics など)なら、例えば:

  • オーケストレーション(Airflow DAG。async-job-service 経由で非同期ジョブとして起動)
  • 処理(EMR 上での PySpark、ECS 上でコンテナ化した Python ジョブ)
  • マルチリージョン対応(パイプラインはリージョンごとの設定で、リージョンごとに実行される)

エージェント型システム(assist など)なら、例えば:

  • エージェントフレームワーク(オーケストレーションに FastAPI + LangGraph)
  • 支えとなるサービス(永続化は Spring Boot、ストリーミングは WebSocket)
  • フロントエンド(ストリーミングの UI パターンを使った React)

ここにはリポジトリを列挙しない方がいいと学びました。リストはすぐ陳腐化します。代わりに、Claude に参照先を伝えます:「このグループのリポジトリは mani.d/orders.yaml で定義されています。各プロジェクトには desc フィールドがあります。最新のリストはこのファイルを確認してください。」

レイヤー3:リポジトリ

これは各リポジトリにあり、それを所有するチームによって維持されます。ビルドコマンド、アーキテクチャノート、テスト手順、このコードベースに固有の内容。これは標準的な Claude Code の使い方で、特に新しいことはありません。

マニフェスト内のプロジェクト説明

リポジトリマニフェストの 1 行説明は、発見(ディスカバリ)に大きく効きます。Claude がマニフェストを読み込むと、クローンしたり探検したりしなくても各リポジトリが何をするか分かります。

projects:
  order-service:
    desc: 注文のライフサイクル管理とフルフィルメント
    url: git@gitlab.com:acme/order-service.git
    path: orders/order-service
    tags: [orders, java]

返却形式: {"translated": "翻訳されたHTML"}pricing-engine:
    desc: Vert.x のリアルタイム価格設定エンジン
    url: git@gitlab.com:acme/pricing-engine.git
    path: orders/pricing-engine
    tags: [orders, java]

  orders-ui:
    desc: 受注管理とレポーティングのための React UI
    url: git@gitlab.com:acme/orders-ui.git
    path: orders/orders-ui
    tags: [orders, ui]

desc フィールドは維持コストがほとんどかからず、Claude が推測したり問い合わせたりすることを防いでくれます。

リポジトリをまたいだタスク

mani のようなリポジトリマネージャを使うと、リポジトリをまたいで実行するタスクを定義できます:

tasks:
  update-repos:
    desc: すべてのリポジトリの最新を取得する
    target: all
    cmd: |
      current=$(git rev-parse --abbrev-ref HEAD)
      if [[ -n $(git status -s) ]]; then
        git fetch origin $branch
        echo "FETCHED (汚れた作業ツリーが $current にある)"
      elif [[ "$${current}" != "$branch" ]]; then
        git fetch origin $branch
        echo "FETCHED ($current ブランチ上だが $branch ではない)"
      else
        git pull --rebase origin $branch
      fi

これは、クリーンでデフォルトブランチ上にあるリポジトリでは最新を pull し、進行中の作業があるリポジトリでは fetch するだけ(触らない)ようにします。どちらのケースでもデータはローカルで利用可能なので、次回の pull は高速です。

他にも便利なタスク:すべてのリポジトリでの検索、未コミットの変更があるリポジトリの確認、CI パイプラインの起動。

チーム単位の CLAUDE.md ファイル向け gitignore のトリック

ブートストラップリポジトリは、すべてのサブリポジトリディレクトリを gitignore しています。しかし、チーム単位の CLAUDE.md ファイルは、それらと同じディレクトリ内のブートストラップで追跡(管理対象化)する必要があります。修正方法:

# dir/* を dir/ の代わりに使うと例外が効く
orders/*
!orders/CLAUDE.md

orders/ はディレクトリ全体を無視します(git は中を見ません)。orders/* はその中のすべてを無視しますが、特定のファイルを除外できます。

スキル、フック、コマンド

Claude Code は、リポジトリの .claude/ ディレクトリで設定された skills, hooks, and custom commands をサポートしています。これらはこれまでずっとリポジトリ単位で動いていました。ブートストラップ構造により、さらに 2 つの階層が追加されます:

組織(Org)レベル(ブートストラップリポジトリの .claude/ 内):

  • すべてのリポジトリにまたがって動作するスキル。作業中のディレクトリからプロジェクトキーを自動検出して、ワークスペース内の任意のリポジトリに対して SonarQube を問い合わせるスキルを 1 つ用意しています。
  • 事前コミットフック(秘密情報検出の gitleaks。ブートストラップリポジトリ自体に適用)。
  • ブランチ移行がまだ必要なリポジトリを監査する、チームをまたぐ操作のためのシェルスクリプト。

チームレベル(各チームの CLAUDE.md、または追跡される設定の中):

  • チーム内のすべてのリポジトリに適用されるビルド規約。ただし組織全体には適用しない。Spring Boot サービスが 10 個あるチームなら、共有の Gradle convention プラグインを 1 回だけチームの CLAUDE.md に記述できます。

リポジトリレベル(各リポジトリで、これまで通り):

  • リポジトリ固有のスキル、フック、コマンド。ここは何も変わりません。

階層化されているので、組織レベルで SonarQube のスキルを 1 回書くだけで、どのリポジトリでも動作します。チームレベルで ./gradlew spotlessApply を 1 回だけドキュメント化すれば、そのチームのすべてのリポジトリがそのコンテキストを継承します。

部分的および完全なチェックアウト

全員がワークスペース全部を必要とするわけではありません。私が一緒に働く多くの開発者は、チームのリポジトリだけをクローンします:

workspace/
  mani.yaml
  CLAUDE.md
  orders/
    CLAUDE.md
    order-service/
    payment-service/
    orders-ui/

それでも組織レベルとチームレベルの CLAUDE.md ファイルは取得されます。Claude Code は依然としてチームの規約を理解し、マニフェストを通じて組織の残りを見つける方法を知っています。

複数チームにまたがって働くプラットフォームエンジニアやアーキテクトは、すべてをクローンします。その場合は、あらゆるレベルで完全なコンテキストが得られます。

リポジトリマネージャが両方を扱います。チームでリポジトリにタグを付けて選択的にクローンしたり(mani sync --tags orders)、すべてクローンしたりできます(mani sync)。いずれの場合でも、各レベルの CLAUDE.md がすでに配置されているため、階層化されたコンテキストは機能します。

これで得られるもの

ワークスペース内のどのリポジトリでも誰かが Claude Code を起動すると、すでに次のことを把握しています:

  • そのリポジトリが何をするのか、どうビルドするのか
  • 同じチームに他にどんなリポジトリがあり、それらがどう関連しているか
  • 共有ライブラリ、インフラ、およびデプロイ設定へどう移動するか
  • 共通の規約と例外

試してみたいなら小さく始めてください。ブートストラップリポジトリを作り、ワークスペースのレイアウトを記した CLAUDE.md を追加し、1 行の説明付きでマニフェストにリポジトリを列挙します。構造が役立つことが分かってきたら、チームレベルのコンテキストやリポジトリ横断タスクも追加できます。