![[Claude Code] CwdChanged / FileChanged hook で direnv を自動化する](https://images.ctfassets.net/ct0aopd36mqt/3KBTm8tdpO9RJJuaVvVzod/a9964bb03097b448b2327edc6920bf9f/Claude.png?w=3840&fm=webp)
[Claude Code] CwdChanged / FileChanged hook で direnv を自動化する
Claude Code の CwdChanged / FileChanged フック:direnv連携で環境変数管理を自動化する
Claude Codeのv2.1.83で新たに追加された CwdChanged と FileChanged フックイベントを使うと、
ディレクトリ移動やファイル変更をトリガーに処理を自動実行できます。
この記事ではdirenvと組み合わせて環境変数を自動でロードする処理を確認してみました。
背景:Claude Code のフックシステム
Claude Code には、特定のイベントが発生した際にシェルを実行する「フック(Hooks)」機能があります。
従来は以下のようなイベントが利用可能でした。
| 従来のイベント | トリガー条件 |
|---|---|
PreToolUse |
ツール実行前 |
PostToolUse |
ツール実行後 |
Notification |
通知送信時 |
Stop |
レスポンス完了時 |
SubagentStop |
サブエージェント完了時 |
いろいろなフックタイミングがありますが、ディレクトリ移動や
ファイル変更には対応していませんでした。
新しいフックイベント
今回追加された2つのイベントにより、ディレクトリ移動などの環境変化に反応できるようになりました。
| イベント | トリガー条件 | 主な用途 |
|---|---|---|
CwdChanged |
作業ディレクトリが変わったとき | direnv連携、SDK切り替え、仮想環境の有効化 |
FileChanged |
指定ファイルが変更されたとき(matcherでフィルタ可能) |
.env変更時の環境再読み込み、設定ファイル変更の検知 |
Claude Code & direnv
direnv は、ディレクトリごとに異なる環境変数を自動でロード・アンロードするツールです。
各ディレクトリに .envrc ファイルを配置し、そこに cd するだけで環境変数が切り替わります。
通常のターミナルでは、cd するたびに direnv のシェルフックが発火し、環境変数が自動でロードされます。
しかし、Claude Code の Bash ツールは非インタラクティブシェルで実行されるため、
direnvのシェルフックが動作しません。
なので、結果として以下のような問題がおこります。
# Claude Code の Bash ツールで実行
% cd api/
% echo $DB_HOST
# → 空(環境変数がロードされていない)
# 毎回手動で環境変数をロードする必要がある
% set -a && source .env && [ -f .env.local ] && source .env.local && set +a
% echo $DB_HOST
# → localhost
モノレポ構成でパッケージごとに異なる環境変数を持つプロジェクトの場合、
この手動ロードが繰り返し発生します。
確認:モノレポでのdirenv
本記事で使用するサンプルプロジェクトは以下のようなモノレポ構成です。
my-project/
├── .env # ルート共通(DB接続情報等)
├── .env.local # ルートローカル(Git管理外)
├── .envrc # direnv設定
│
├── backend/ # バックエンドAPI
│ ├── .env # API固有の設定(認証、外部サービス等)
│ ├── .env.local
│ └── .envrc
│
├── infra/ # インフラ(IaC)
│ ├── .env # クラウド関連の設定
│ ├── .env.local
│ └── .envrc
│
└── e2e/ # E2Eテスト
├── .env
├── .env.local
└── .envrc
各パッケージの .envrc は同一構成で、4段階のオーバーライドを行います。
# backend/.envrc(他パッケージも同様)
dotenv ../.env # 1. ルート共通環境変数
dotenv ../.env.local # 2. ルートローカル環境変数
dotenv .env # 3. パッケージ共通環境変数
dotenv .env.local # 4. パッケージローカル環境変数(最優先)
この構成により、各パッケージは共通のDB接続情報を継承しつつ、固有の設定を追加しています。
| パッケージ | 固有の環境変数 |
|---|---|
| ルート | DB_HOST, DB_PORT, DB_PASSWORD 等 |
| backend/ | API_KEY, AUTH_DOMAIN, STORAGE_BUCKET 等 |
| infra/ | CLOUD_PROFILE, DEPLOY_REGION 等 |
Setup
1. hook script作成
% mkdir -p .claude/hooks
ディレクトリ移動時に direnv を実行する
.claude/hooks/on-cwd-changed.shシェルを作成します。
#!/bin/bash
# CwdChanged hook: direnvで環境変数を自動ロード
if command -v direnv &> /dev/null && [ -f .envrc ]; then
direnv allow .envrc 2>/dev/null
eval "$(direnv export bash)"
direnv export bash >> "$CLAUDE_ENV_FILE"
fi
また、.env ファイル変更時に環境を再ロードする
.claude/hooks/on-env-changed.shシェルも作成。
#!/bin/bash
# FileChanged hook: .envファイル変更時に環境変数を再ロード
if command -v direnv &> /dev/null && [ -f .envrc ]; then
eval "$(direnv export bash)"
direnv export bash >> "$CLAUDE_ENV_FILE"
fi
実行権限を付与。
% chmod +x .claude/hooks/on-cwd-changed.sh .claude/hooks/on-env-changed.sh
2. settings.json にフックを登録
.claude/settings.local.jsonか.claude/settings.jsonに以下を追加します。
{
"hooks": {
"CwdChanged": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/on-cwd-changed.sh",
"async": true,
"timeout": 10
}
]
}
],
"FileChanged": [
{
"matcher": "^\\.envrc$|^\\.env$|^\\.env\\.local$",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/on-env-changed.sh",
"async": true,
"timeout": 10
}
]
}
]
}
}
hook scriptのパスには $CLAUDE_PROJECT_DIR 環境変数を使った絶対パスを指定します。
hookは現在の作業ディレクトリで実行されるため、cd api/ 後にフックが発火すると
相対パス .claude/hooks/... は api/.claude/hooks/... を探しに行って失敗します。
また、FileChanged の matcher は、変更ファイルの basename に対する正規表現(regex)です。
3. CLAUDE_ENV_FILE
CLAUDE_ENV_FILE は Claude Code が提供する特殊な環境変数で、ここに書き出された export 文が後続の Bash 実行に反映されます。SessionStart、CwdChanged、FileChanged の3つのフックイベントでのみ利用可能です。
※参照
CLAUDE_ENV_FILE is available for SessionStart, CwdChanged, and FileChanged hooks.
Other hook types do not have access to this variable.
% direnv export bash >> "$CLAUDE_ENV_FILE"
direnv export bash の出力は以下のような形式です。
export DB_HOST=$'localhost';
export DB_PORT=5432;
export API_KEY=$'sk-dev-xxxxxxxxxxxx';
export AUTH_DOMAIN=$'dev.example.com';
# ...
この出力を CLAUDE_ENV_FILE に追記することで、Claude Code のシェル環境に環境変数が注入されます。
Test Results
フックなし(従来の動作)
| 操作 | DB_HOST | API_KEY | 結果 |
|---|---|---|---|
| ルートに移動 | 未設定 | 未設定 | 環境変数なし |
cd backend/ |
未設定 | 未設定 | 環境変数なし |
cd infra/ |
未設定 | 未設定 | 環境変数なし |
Claude Code の Bash ツールではどこに移動しても環境変数は一切ロードされず、
毎回 set -a && source .env && ... の手動実行が必要。
フックあり(CwdChanged + direnv)
| 操作 | DB_HOST | API_KEY | AUTH_DOMAIN | 結果 |
|---|---|---|---|---|
| ルートに移動 | localhost |
未設定 | 未設定 | ルートの .env のみロード |
cd backend/ |
localhost |
sk-dev-xxx... |
dev.example.com |
ルート + backend/ の .env がロード |
cd infra/ |
localhost |
未設定 | 未設定 | ルート + infra/ の .env がロード |
ディレクトリ移動だけで、そのパッケージに必要な環境変数が自動的に揃います。
backend/ に移動すれば認証関連の設定が、
infra/ に移動すればそこにある設定が自動でロードされます。
CwdChanged と FileChanged はdirenv以外にも活用できます。
Nodeバージョンの自動切り替えをしたり、Python仮想環境の自動有効化したりなど、
いろいろ使えます。
# .claude/hooks/on-cwd-changed.sh
if [ -f .node-version ]; then
NODE_VERSION=$(cat .node-version)
export PATH="$HOME/.nvm/versions/node/v${NODE_VERSION}/bin:$PATH"
echo "export PATH=\"$HOME/.nvm/versions/node/v${NODE_VERSION}/bin:\$PATH\"" >> "$CLAUDE_ENV_FILE"
fi
# .claude/hooks/on-cwd-changed.sh
if [ -f .venv/bin/activate ]; then
source .venv/bin/activate
echo "export PATH=\"$(pwd)/.venv/bin:\$PATH\"" >> "$CLAUDE_ENV_FILE"
echo "export VIRTUAL_ENV=\"$(pwd)/.venv\"" >> "$CLAUDE_ENV_FILE"
fi
セキュリティ上の考慮
direnv allowを自動実行するため、信頼できるリポジトリでのみ使用してください.envrcに悪意のあるコマンドが含まれていると、自動実行されるリスクがあります- チームで共有する場合は
.claude/hooks/をリポジトリに含め、settings.local.jsonは個人設定としてGit管理外にすることを推奨します
Reference
Summary
v2.1.81でClaude Code に追加された CwdChangedと FileChangedのhookイベントについて確認してみました。
モノレポ構成のプロジェクトにおいては毎回 source .envをしていた環境変数設定が、
自動で設定されるようになりました。
これ以外にもパスによる設定切り替えが簡単にできるので、便利です。







