

.claude/ をチーム共有のハーネスとして整備すると一気に解決します。本記事はその全体像を、実際にあるモノレポで運用している .claude/ の構造を題材に解説する連載の第1回です。Claude Code を一人で使っているうちは、設定ファイルは ~/.claude/settings.json で十分です。permissions を緩めて、自分の好みのフックを少しだけ足せば、それで動きます。
しかしチームでの利用が始まると、一気に話が変わります。
「A さんの環境ではすぐ実行できたのに、B さんは毎回 permission の確認ダイアログが出る」
「Claude が terraform/prod を terraform apply しようとしてヒヤッとした」
「コミットメッセージに Co-Authored-By: Claude が紛れ込んでいて、本番ブランチに残ってしまった」
「.env をうっかり Read されて、シークレットが画面に出てしまった」
これらは「Claude Code 本体の機能だけでは防ぎきれないが、.claude/ の設計次第で全部止められる」種類の問題です。
本連載では、ある AI 系プロダクト群を抱えるモノレポで実運用している .claude/ を題材に、チームで共有するハーネスをどう設計するかを 4 回に分けて整理します。
Claude Code の .claude/ がチーム運用上どんな役割を果たすか
.claude/ を構成する 5 つの要素(settings / hooks / agents / commands / skills)の責務分担
「土台 → ガード → 拡張」の 3 層モデルで設定を組み立てる考え方
個人 override(settings.local.json)とチーム共有のラインの引き方
Claude Code をチームに導入したが、各自の設定が散らかってきている開発者
「permissions.deny だけだと漏れがあるな」と気づき始めた人
subagent / skill / slash command の使い分けをまだ整理できていない人
Claude Code が 1.0 以降
リポジトリのルートに .claude/ を作って commit する権限がある
公式ドキュメント Claude Code: Settings / Hooks / Subagents / Skills を一度眺めていると読みやすい
Claude Code には設定の階層があります。雑にまとめると次の 3 段です。
ユーザー設定 ~/.claude/settings.json — 個人ごと、全プロジェクト共通
プロジェクト共有設定 <repo>/.claude/settings.json — リポジトリにコミットされ、全員に効く
プロジェクトローカル設定 <repo>/.claude/settings.local.json — gitignore 対象、個人 override
問題が起きやすいのは「プロジェクト固有のルールをユーザー設定に書いてしまう」ケースです。たとえば「このリポジトリの terraform/dev と terraform/prod には絶対 apply するな」というルールを個人の設定で運用していると、次に入ったメンバーには伝わりません。
逆に、Claude Code 自身は CLAUDE.md(リポジトリに置く指示書)からそのルールを読み取って従ってくれますが、LLM 任せのガードは確率的にすり抜けることがあります。とくに長い会話や、複数 agent を経由する操作では危険です。
そのため、チーム運用ではこうなります。
意図 はテキスト(CLAUDE.md)で LLM に伝える
守らせたい不変条件 はコード(hooks / permissions)で機械的に止める
反復作業 は slash command / skill で型化する
これを実現する箱が .claude/ です。
運用していて落ち着いた構造はこうです。
.claude/
├── settings.json # 土台:permissions・env・hooks・statusLine
├── settings.local.json.example # 個人 override のテンプレ(実体は gitignore)
├── hooks/ # ガード層:起動時 context、Pre/PostToolUse での介入
├── agents/ # 拡張層:用途特化の subagent 定義
├── commands/ # 拡張層:薄い slash command
└── skills/ # 拡張層:多段手順を文脈ごと束ねる playbook
これを「土台 → ガード → 拡張」の 3 層として捉えると、何をどこに書くかの判断が一気にラクになります。
┌─────────────────────────────────────────────────┐
│ 拡張層: agents / commands / skills │
│ 「やりたいこと」を型として提供する │
├─────────────────────────────────────────────────┤
│ ガード層: hooks │
│ 「やってはいけないこと」を機械的に止める │
├─────────────────────────────────────────────────┤
│ 土台: settings.json │
│ permissions / env / hook 配線 / status line │
└─────────────────────────────────────────────────┘
大事なのは、下層ほど「守り」、上層ほど「攻め」であることです。ガード層が薄いと拡張層がいくら充実してもチームに展開できません。一方、ガードだけ充実していて拡張が無いと、Claude Code を使う旨味が出ません。
まずは土台の settings.json です。実運用ではだいたいこの 4 要素を書きます。
一番効くのは permissions です。allow / deny / ask の 3 つに分かれていて、それぞれ役割が違います。
{
"permissions": {
"allow": [
"Bash(git:*)", "Bash(gh:*)", "Bash(make:*)",
"Bash(poetry:*)", "Bash(python:*)", "Bash(pytest:*)",
"Bash(docker compose:*)", "Bash(docker:*)",
"Bash(ls:*)", "Bash(cat:*)", "Bash(grep:*)",
"WebFetch", "WebSearch"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/.env)",
"Read(./**/secrets/**)",
"Bash(terraform apply:*)",
"Bash(terraform destroy:*)",
"Bash(rm -rf /*)",
"Bash(git push --force:*)"
],
"ask": [
"Bash(git push:*)",
"Bash(git reset --hard:*)",
"Bash(docker volume rm:*)"
],
"defaultMode": "auto"
}
}
ポイントは 3 つあります。
第一に、allow は「許可」ではなく「確認を省略するリスト」だと考えるのがコツです。Bash(grep:*) を allow しておくと、grep が走るたびに毎回ダイアログが出るのを抑えられます。逆に、副作用がある操作は absolutely allow に入れません。
第二に、deny は黄金パターンを最初に並べます。.env / secrets/ の Read 遮断、terraform apply|destroy の遮断、rm -rf / 系の遮断、git push --force の遮断は、ほぼどのプロジェクトでも入れて損がありません。
第三に、ask には「ほとんどの場合 OK だが、念のため確認したい」操作を入れます。git push や docker volume rm などです。allow にすると怖い、deny にすると不便、という操作の置き場所です。
環境変数も settings.json で持てます。シークレットは入れず、プロジェクトを識別するための定数だけ持つのが安全です。
{
"env": {
"CLAUDE_CODE_PROJECT": "my-monorepo"
}
}
hooks は次節で詳しく扱いますが、設定上は「どのイベントで」「どのスクリプトを呼ぶか」を配線するだけです。
{
"hooks": {
"SessionStart": [{ "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh", "timeout": 10 }] }],
"PreToolUse": [
{ "matcher": "Read", "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/secret-guard.sh", "timeout": 5 }] },
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/bash-guard.sh", "timeout": 5 }] }
],
"PostToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/strip-claude-signature.sh", "timeout": 5 }] }
]
}
}
matcher で「どの tool に対する hook か」を絞れます。Read と Bash で別のスクリプトを当てているのがポイントです。
CLI 下部に出る 1 行のステータス表示です。ブランチ名や環境マーカーを出しておくと、「dev に向いた環境で作業中」を見落としづらくなります。
{
"statusLine": {
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/statusline.sh"
}
}
チーム共有 settings.json は最小公倍数で書きます。個人ごとに変えたい設定(モデル指定、特殊な permission の追加など)は settings.local.json に置き、こちらは gitignore します。雛形だけ settings.local.json.example としてコミットしておくと、新メンバーがコピーして使えます。
cp .claude/settings.local.json.example .claude/settings.local.json
# 自分用の設定を編集
hooks は .claude/hooks/*.sh の単なる shell スクリプトです。標準入力で JSON が来て、終了コードと標準エラー出力で Claude Code に応答します。
運用していて効いたのはこの 4 種類です。
SessionStart: 起動時にブランチ・dirty 状態・ENV マーカーを 1 行で表示し、「いま自分が触っている環境」を視認できるようにする
PreToolUse(Read): .env や secrets/ を Read しようとしたら exit 2 で止める(permissions の deny を二重化)
PreToolUse(Bash): terraform apply を terraform/dev terraform/prod に対して打ったら止める、develop / main への force push を止める、rm -rf / 系を止める
PostToolUse(Bash): git commit の後で Co-Authored-By: Claude 等の署名が混入していたら exit 1 で警告し、amend を促す
設計上のポイントは「exit 2 でブロック、exit 1 で警告」という入出力契約です。これは Claude Code 公式の hooks 仕様で、ブロックしたいときは exit 2 と stderr メッセージ、ブロックはせず警告だけ出したいときは exit 1 を使います。
詳細な実装と落とし穴は連載第 2 回で深掘りします。ここでは「LLM への指示(CLAUDE.md)だけに頼らず、機械的に止められるところは止めておく」というスタンスだけ覚えておいてください。
agents/ に置くのは subagent の定義です。各エージェントは .md ファイルに YAML frontmatter + 本文(システムプロンプト)で書きます。
---
name: code-reviewer
description: Pre-PR code reviewer for this monorepo. Use proactively after a meaningful set of edits, before opening a PR, or when the user asks for a "review", "second opinion", or "is this safe to merge".
model: inherit
tools: Read, Grep, Glob, Bash
---
You are an experienced reviewer for this monorepo. You read the diff, then the surrounding code, then form an opinion.
## Scope of every review
- Correctness, stack-specific rules, security, test coverage
- Repo hard rules: no edits under terraform/dev/, no removed columns/tables/endpoints
## Workflow
1. Run `git diff origin/develop...HEAD` to see the change set.
2. For each non-trivial hunk, open the file and read at least 30 lines of context.
3. Group findings by severity (must-fix / should-fix / nit) with file:line citations.
4. End with a one-sentence verdict.
運用していて重要だったのは次の 3 点です。
description がエージェント起動のトリガー文になる、ということ。メインの Claude が「いま subagent に投げるべきか」を判断するときに読むのは description です。ここに「いつ呼ぶべきか」を具体的に書けば書くほど、自動的に呼ばれる精度が上がります。逆にここが曖昧だと、せっかく定義しても呼ばれません。
tools で権限を絞ること。たとえばコードレビュー専門の agent には Write/Edit を渡しません。誤って修正してしまうリスクを物理的に消せます。
メインの context を汚さないこと。大量のファイルを読むレビュー作業をメインのスレッドで走らせると、その後の作業の context window を圧迫します。subagent に投げて、結果のサマリだけ受け取る運用にすると context が綺麗に保てます。
用途ごとに分けると、ある程度のサイズのプロジェクトでは次のような構成に落ち着きます。
code-reviewer — PR 前のコードレビュー
security-reviewer — 秘密情報・IAM・LLM プロンプトインジェクションなど security 観点
pipeline-debugger — 特定サブシステム(例:データパイプライン)の専門デバッガ
test-writer — テストコードの追加
第 3 回で、それぞれの description と tools をどう設計するかを掘ります。
最後の層は slash command と skill です。両方とも .md ファイル + frontmatter ですが、責務が違います。
.claude/commands/lint.md のような薄いラッパーです。/lint と打つと中身が実行されます。
---
description: Run ruff lint + format check on the API package.
allowed-tools: Bash(make:*), Bash(poetry:*), Bash(ruff:*)
---
Run the project lint:
!`make lint`
If anything fails, summarize the diagnostics and propose specific fixes referencing file:line. Do not auto-format unless the user asks.
本質的には「よく使うコマンド + 軽い指示」をまとめたショートカットです。/fmt, /test-api, /up, /down, /seed のような開発フローに 1 行で乗るものは全部 command に寄せます。
一方 skill は「この手順を踏むときの判断基準・落とし穴・コマンド群」を 1 つの playbook としてまとめる単位です。たとえば「データパイプラインの A→B→C を一気通貫でトレースするとき、どの順序でどのログを見て、何があったら次にどうするか」を skill にします。
---
name: pipeline-trace
description: Trace a Pipeline A→B→C run end-to-end against the local docker-compose stack. Use when a document upload does not produce expected output and you need to localise the failure to A, B, or C.
allowed-tools: Bash(make:*), Bash(docker:*), Bash(curl:*), Bash(jq:*), Read, Grep
---
# Pipeline trace
You will follow one document through the integrated pipelines container.
## Pre-flight
... 以下、手順とトラブルシュート表
もう 1 つ大事な属性が disable-model-invocation: true です。これを付けると「モデルが自動で発火することはなく、ユーザが明示的に呼んだときだけ動く」になります。破壊的操作を伴う skill(例:ローカル DB と Blob の wipe & re-seed)はこれを必ず付けます。
だいたいこういう判断軸で分けます。
1 〜 2 行で済む / 副作用が小さい → command(例:/lint, /fmt)
多段手順がある / 判断分岐がある / 落とし穴を覚えておきたい → skill(例:パイプライントレース、リリースノート生成)
破壊的 / 明示呼び出し限定にしたい → skill + disable-model-invocation: true
この境界線の話は連載第 4 回で詳しく扱います。
運用に入ってから効いたコツを並べておきます。
deny は早めに広く貼る。「絶対やられたくないこと」を後回しにすると、必ず一度はやらかします
hooks は短く、ブロック条件を明示する。長い hook はデバッグが大変な上、起動時に毎回走るので体感速度に効きます
subagent の description に「いつ呼ぶか」を必ず書く。これがメイン Claude の判断材料です
command と skill を混ぜない。/foo を打ったときに「単発実行なのか playbook なのか」が予測できなくなるとチームに浸透しません
個人 override は settings.local.json.example を一緒にコミットしておく。新メンバーが何を上書きしていいかが分かりません
Claude Code をチーム運用するなら .claude/ を「土台 → ガード → 拡張」の 3 層で設計するのが整理しやすい
土台は settings.json(permissions / env / hooks 配線 / status line)
ガード層は hooks(exit 2 でブロック / exit 1 で警告 の入出力契約)
拡張層は agents(context を汚さない専門家) + commands(薄いラッパー) + skills(多段 playbook)
LLM への指示(CLAUDE.md)と機械的なガード(hooks / permissions)を二重化することで、確率的なすり抜けを防ぐ
次回(第 2 回)は ガード層 = hooks に深く踏み込みます。
bash-guard.sh で破壊コマンドを未然に止める実装
secret-guard.sh で .env Read を二重化する設計
strip-claude-signature.sh で commit 痕跡を検知する PostToolUse の使い方
session-start.sh で起動時に context を 1 行注入するパターン
「exit 2 でブロック / exit 1 で警告」契約の落とし穴
ハンズオン形式で進めます。