Claude Codeで開発していると、こんな問題に遭遇しませんか?
- CLAUDE.mdに「uvを使って」や「uvの具体的な環境構築ガイド」を書いても、ルールを無視してpipを使われてしまう
- 仮想環境をアクティベートするのを忘れ、Python実行でエラーが頻発する。そして別のActivateされていた環境が汚される
- 長いやり取りの途中で、AIがプロジェクトのルールを忘れてしまう
CLAUDE.mdに詳細な環境構築手順やuvパッケージマネージャーの使用を記載していても、Claude Codeが一貫してそのルールを守ってくれることはなかったです。
しかし、Claude Code Hooksを使うことで、指示忘れの問題を解決できました!
この記事では、実際にPython環境のパッケージマネージャーを強制的にuv
に統一するためのHooksスクリプトを公開し、Hooksのメリット等を解説します。
例: pip installをブロックしてuv addに変える
なぜ指示が守られないのか
CLAUDE.mdやLLMに指示を与えるだけだと、Transformerの仕組み上完全にルールが守られることはありません。
問題1:コンテキストの忘却
長いやり取りの途中で、LLMは以下のような問題を起こします。
- プロジェクトの環境設定ルールを忘れる
- 仮想環境のアクティベートを忘れる
- pipではなくuvを使うというルールを忘れる
問題2:暗黙の動作
Claude Codeは、ユーザーが明示的に指示しない限り、以下のような動作をすることが多いです。
-
pip install
を実行してしまう - グローバルのPython環境を使用してしまう
-
.venv
環境をアクティベートしないままコマンドを実行してしまう
これはTransformerモデルの宿命で、uvよりもpipの方が圧倒的に学習データが多いと推測されるため仕方ないです。
問題3:指示の一貫性
CLAUDE.mdに記載された指示は強制力がありません。
## Package Management
- ONLY use uv, NEVER pip
- Installation: uv add package
- Running tools: uv run tool
- Upgrading: uv add --dev package --upgrade-package package
- FORBIDDEN: uv pip install, @latest syntax
※ ModelContextProtocolのPython-SDKのCLAUDE.mdから引用
Claude Code Hooksという救世主!
Claude Code Hooksは、Claude Codeのライフサイクルの特定のタイミング(ツール実行前後など)で自動実行されるユーザー定義のシェルコマンドです。LLMによる実行判断に依存せずにClaude Codeの動作をコントロールし、特定のアクションが常に実行されるようにします。
Claude Code Hooksを使うことで、これらの問題を解決できます!
- 強制力がある: AIの動作を物理的にブロックできる
- 一貫性がある: 長いやり取りでも確実にルールが適用される
- 自動化される: ユーザーが何度も指示する必要がない
例えばClaude Code Hooksを利用することで、python sample.py
がブロックされ、uv run python sample.py
に変換できます。
それによりプロジェクト内の仮想環境でコードを実行することが可能です!
実際のHooksスクリプト(全公開)
以下私が作成し、実際に使用している「Python環境をuvに統一する」Hooksスクリプトです。
HooksのJSON OUTPUTを活用して作成しています。
-
pip install/uninstall
をuv add/remove
にコマンドを置き換える - Pythonファイルの実行を
uv run python
を用いて仮想環境で実行する - 環境構築が行われていなかったら、
uv
で環境構築を行う -
requirements.txt
をもとにuv
で環境構築する
#!/bin/bash
input=$(cat)
if [ -z "$input" ]; then
echo '{"decision": "approve"}'
exit 0
fi
tool_name=$(echo "$input" | jq -r '.tool_name' 2>/dev/null || echo "")
command=$(echo "$input" | jq -r '.tool_input.command // ""' 2>/dev/null || echo "")
file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.path // ""' 2>/dev/null || echo "")
current_dir=$(pwd)
if [[ "$tool_name" == "Bash" ]]; then
case "$command" in
pip\ *|pip3\ *)
pip_cmd=$(echo "$command" | sed -E 's/^pip[0-9]? *//' | xargs)
case "$pip_cmd" in
install\ *)
packages=$(echo "$pip_cmd" | sed 's/install//' | sed 's/--[^ ]*//g' | xargs)
if [[ "$pip_cmd" =~ -r\ .*\.txt ]]; then
req_file=$(echo "$pip_cmd" | sed -n 's/.*-r \([^ ]*\).*/\1/p')
echo "{
\"decision\": \"block\",
\"reason\": \"📋 requirements.txtからインストール:\\n\\n✅ 推奨方法:\\nuv add -r $req_file\\n\\nこれにより:\\n• requirements.txt内のすべての依存関係をpyproject.tomlに追加\\n• uv.lockファイルを自動生成/更新\\n• 仮想環境を自動的に同期\\n\\n💡 制約ファイルがある場合:\\nuv add -r $req_file -c constraints.txt\\n\\n📌 注意: この方法が最も確実で、バージョン指定も正しく処理されます\"
}"
exit 0
fi
if [[ "$pip_cmd" =~ --dev ]] || [[ "$pip_cmd" =~ -e ]]; then
echo "{
\"decision\": \"block\",
\"reason\": \"🔧 開発依存関係をインストール:\\n\\nuv add --dev $packages\\n\\n編集可能インストール: uv add -e .\"
}"
exit 0
fi
echo "{
\"decision\": \"block\",
\"reason\": \"📦 パッケージをインストール:\\n\\nuv add $packages\\n\\n💾 'uv add' はpyproject.tomlに依存関係を保存します\\n🔒 uv.lockで再現可能な環境を保証\\n\\n💡 特殊なケース:\\n• URLからのインストール: パッケージを手動でダウンロードしてから追加\\n• 開発版: uv add --dev $packages\\n• ローカルパッケージ: uv add -e ./path/to/package\"
}"
exit 0
;;
uninstall\ *)
packages=$(echo "$pip_cmd" | sed 's/uninstall//' | sed 's/-y//g' | xargs)
echo "{
\"decision\": \"block\",
\"reason\": \"🗑️ パッケージを削除:\\n\\nuv remove $packages\\n\\n✨ 依存関係も自動的にクリーンアップされます\"
}"
exit 0
;;
list*|freeze*)
echo '{
"decision": "block",
"reason": "📊 パッケージ一覧を確認:\n\n• プロジェクト依存関係: cat pyproject.toml\n• ロックファイル詳細: cat uv.lock\n• インストール済み一覧: uv tree\n• requirements.txt形式でエクスポート: uv export --format requirements-txt\n\n💡 'uv tree'はプロジェクトの依存関係ツリーを表示します"
}'
exit 0
;;
*)
echo "{
\"decision\": \"block\",
\"reason\": \"🔀 pipコマンドをuvで実行:\\n\\nuv pip $pip_cmd\\n\\n💡 パッケージのインストール/削除には 'uv add/remove' を使用してください\"
}"
exit 0
;;
esac
;;
python*|python3*|py\ *)
args=$(echo "$command" | sed -E 's/^python[0-9]? //' | xargs)
if [[ "$args" =~ ^-m ]]; then
module=$(echo "$args" | sed 's/-m //')
case "$module" in
pip\ *)
pip_cmd=$(echo "$module" | sed 's/pip //')
if [[ "$pip_cmd" =~ ^install ]]; then
packages=$(echo "$pip_cmd" | sed 's/install//' | sed 's/--[^ ]*//g' | xargs)
if [[ "$pip_cmd" =~ -r\ .*\.txt ]]; then
req_file=$(echo "$pip_cmd" | sed -n 's/.*-r \([^ ]*\).*/\1/p')
echo "{
\"decision\": \"block\",
\"reason\": \"📋 requirements.txtからインストール:\\n\\n✅ 推奨方法:\\nuv add -r $req_file\\n\\n💡 これによりすべての依存関係がpyproject.tomlに追加されます\"
}"
else
echo "{
\"decision\": \"block\",
\"reason\": \"📦 パッケージをインストール:\\n\\nuv add $packages\\n\\n💡 'uv add' はpyproject.tomlに依存関係を保存します\"
}"
fi
else
echo "{
\"decision\": \"block\",
\"reason\": \"🔀 pipコマンドをuvで実行:\\n\\nuv pip $pip_cmd\\n\\n💡 パッケージ管理には 'uv add/remove' を使用してください\"
}"
fi
exit 0
;;
*)
echo "{
\"decision\": \"block\",
\"reason\": \"uvでモジュールを実行:\\n\\nuv run python -m $module\\n\\n🔄 uvは自動的に環境を同期してから実行します。\"
}"
exit 0
;;
esac
fi
echo "{
\"decision\": \"block\",
\"reason\": \"uvでPythonを実行:\\n\\nuv run python $args\\n\\n✅ 仮想環境のアクティベーションは不要です!\"
}"
exit 0
;;
esac
fi
echo '{"decision": "approve"}'
設定方法
1. Hooksのシェルスクリプトを.claude/hooks/
配下に置く
ファイル名はenforce-uv.sh
にしてますが、なんでもOKです。
2. シェルスクリプトに実行権限を付与する
chmod +x path/to/enforce-uv.sh
3.プロジェクトの .claude/settings.json
に以下の設定を追加する
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/enforce-uv.sh"
}
]
}
]
}
}
Hooksを使うと得られるメリット
1. CLAUDE.mdのコンテキストを大幅削減
従来はCLAUDE.mdに詳細な環境構築手順やコマンド例を記載していましたが、Hooksによる物理的な制御により、環境に関する指示をほぼ不要にできました。その他のコンテキストも減らせます!
Before(CLAUDE.mdが長大)
## Package Management
- ONLY use uv, NEVER pip
- Installation: uv add package
- Running tools: uv run tool
- Virtual environment: always use uv-created .venv
- ... (詳細な説明が数十行)
After(Hooksで自動制御)
## Environment
- Python package manager: uv
2. ルール忘れの解消①: pipの物理的ブロックによる確実性
物理的にpipコマンドを阻止することで、pip install
が実行される前にブロックを一貫して行えるようになりました。
3. ルール忘れの解消②: プロジェクト環境の自動チェック・修正
今回公開したHooksにより以下が自動化されます。
- pyproject.toml の存在確認: プロジェクトが適切に初期化されているかチェック
- .venv 仮想環境の検証: uv環境が作成されているか自動確認
- 自動的な環境構築提案: 不備があれば適切なuvコマンドを提案
これにより、指定のプロジェクト内のuv環境でPythonが確実に実行されるようになりました。
まとめ
Claude Code Hooksを使うことで、これまでCLAUDE.mdだけでは指示を忘れてしまい解決できなかったPythonの環境構築の問題を解決できました!
Pythonプロジェクトでuvを使用している方は、ぜひこのHooks設定を試してみてください。一度設定すれば、pipコマンドや仮想環境に悩まされることは減ると思います!(まだ不完全かもしれませんが…)
Hooksを使うとこれまでコンテキストエンジニアリングで解決していた問題の大部分を、決定論的に解決することができます。様々な用途に使えると思いますので、ぜひClaude Code Hooksを使ってみてください!(むしろ色んな使い方知りたい!)
X(@gota_bara)もやってますので、是非フォローしてください!
Views: 0