CLI ツールに「保存時」の概念は存在しない。だが、.claude/settings.json の hooks で Write ツール実行後にコマンドを挟み込めば、手動でテストや整形を回す回数が劇的に減る。最初は「テスト出力が溢れて操作不能」になったが、フィルタとログ吐き出し設定で実用レベルに収まった。CLI 特有の罠と、コピペで動く設定ファイルの実物を共有する。
前提:CLI 特有の「保存」イベントとフックの仕組み
Claude Code は対話型 CLI であり、VS Code や JetBrains のようにファイルシステムウォッチで「保存」を検知するわけではない。代わりに、Claude がリポジトリのファイルに書き込む内部ツール Write(新規作成)や Edit(差分更新)の実行完了をフックする。hooks はモデル(Claude AI)の推論結果とは無関係に、ハーネス(CLI 本体)がイベントを検知してシェルコマンドを実行する。設定はプロジェクト直下の .claude/settings.json に JSON 形式で定義する。ユーザー全体設定(~/.claude/settings.json)と混同すると、リポジトリ間で意図せぬコマンドが実行されるため注意。
実際の手順:settings.json とシェル連携
自動フォーマットとテスト実行を仕込むための最小構成は以下の通り。PostToolUse イベントでツール実行完了を検知し、シェルコマンドを実行する。テストは全量走らせると CLI が応答待ちになるため、差分テストと出力リダイレクトを強制する設計にする。
{
"hooks": {
"PostToolUse": [
{
"tool": "Write",
"command": "sh -c 'npx prettier --write \"{{filepath}}\" && npx eslint \"{{filepath}}\" --fix >> /tmp/claude_hook_log.txt 2>&1 || true'"
},
{
"tool": "Edit",
"command": "sh -c 'npx prettier --write \"{{filepath}}\" && npx eslint \"{{filepath}}\" --fix >> /tmp/claude_hook_log.txt 2>&1 || true'"
}
]
},
"permissions": {
"allow": [
"Bash(npx prettier *)",
"Bash(npx eslint *)",
"Bash(npx vitest --run *)",
"Bash(cat /tmp/claude_hook_log.txt)"
]
}
}
permissions の allow は必須ではないが、付けると CLI が「このコマンドを実行してもよいか?」と確認ダイアログを出さなくなる。hooks と組み合わせる場合、ダイアログ待ちでフックが中断されるため、自動化を完遂するには許可リストへ登録する。{{filepath}} はツールが渡すパスプレースホルダーであり、Edit では複数ファイルが半角スペース区切りで渡される場合がある。その場合は for f in $filepath; do ...; done などでループさせる必要がある。
効果(体感の before / after)
- before: 実装後に
npm run lint -- --fixとnpm run testを手動で打ち込み、ターミナルの出力をスクロールして確認。週に 30 回以上。 - after: ファイルが書き込まれると裏側で整形・lint が走り、結果は
/tmp/claude_hook_log.txtに蓄積。手動コマンド入力が 0 に。 - 体感でレビューでのフォーマット指摘が 90% 減った。テスト失敗時も
|| true付きで CLI がフリーズしないため、失敗ログはcat /tmp/claude_hook_log.txtで都度確認する運用にした。コミット前の確認フローが「実装 → hooks 処理完了 → 次プロンプト入力」に統一され、回し込みの手間が大幅に削れた。
つまずき・ハマりどころ
最もハマるのが「CLI 側で実行される」という仕様と出力バッファの限界だ。hooks は AI が「テスト不要」と判断してもコマンドは実行される。また、テストコマンドが対話モードやウォッチモードで落ちると CLI が応答待ちでフリーズする。必ず --run や --no-watch を付け、非対話型を強制すること。さらに、標準出力が CLI の内部バッファ容量(通常数 MB)を超えると、途中から出力が切れてログが不完全になる。そのため、>> /tmp/claude_hook_log.txt 2>&1 でファイルにリダイレクトし、tail -n 50 /tmp/claude_hook_log.txt などで末尾だけ確認する設計が必須。デバッグ時は claude hooks コマンドで現在登録されているフック一覧を確認し、command の実行権限が permissions で許可されているか確認すると早い。
向かないケース・注意点
大規模リポジトリ(テストファイルが数千個以上)での全量テスト実行は避ける。CLI の応答遅延が実用に耐えなくなり、次のプロンプト入力まで数分以上待たされることになる。また、IDE プラグイン(Prettier/ESLint 拡張)を併用している場合、二重実行で競合する可能性がある。その際は IDE 側の「保存時にフォーマット」を無効化するか、hooks で IDE 側で既に処理済みかを判定するスクリプトを書き込む必要がある。設定項目やイベント名はバージョンで変わりうる。執筆時点の公式仕様は https://docs.claude.com/en/docs/claude-code を参照。
まとめ + 次の一歩
-
hooksは CLI 側のフックであり、AI の推論とは無関係にコマンドを実行する。 -
Write/Edit直後のフォーマットは安定して効くが、テスト実行は出力リダイレクトと差分指定が必須。 - 完全自動化するには
permissionsでテストコマンドをallowし、確認ダイアログを排除する。 - 次の一歩:
claude hooksで現在登録されたフックを確認し、npm testの代わりにプロジェクト固有のバッチコマンドに書き換える。公式ドキュメントのhooksセクションでイベント仕様を再確認してから本番リポジトリに適用すること。
筆者について / About the author
pendorix — 生成AI・LLM エンジニア(日本在住 / JLPT N1)。SESで約9年、Python・Java を中心に開発。現在はローカルLLM・RAG・生成AI実装に注力し、実測値と動くコードで検証した知見を発信しています。LLM / 生成AI 実装・DX支援のお仕事のご相談を歓迎します。
pendorix — GenAI / LLM engineer based in Japan (JLPT N1). ~9 years building software (Python / Java); now focused on local LLMs, RAG, and shipping practical GenAI systems — sharing hands-on, benchmarked findings. Open to LLM / GenAI implementation & DX opportunities.
GitHub: https://github.com/pendorix