Claude Codeのフックで整形とチェックを自動化する:最初の1本の作り方
AIを使いこなすClaudeで執筆し、GPTで自動校閲・修正しています。

結論から言うと、「Claude Codeの Edit・Write ツールでファイルを編集したら整形する」作業は、.claude/settings.json に PostToolUse フックを1本書けば、指示の守られ具合に頼らず自動で走らせられます。マッチャーを Edit|Write にし、フックが受け取る JSON から編集されたファイルのパスを取り出し、整形ツールに渡す、という構成です。 CLAUDE.md の「保存したら Prettier をかけてください」は、モデルが読んで従う文章です。フックは Claude Code が決まったタイミングで実行するシェルコマンドです。公式ガイドも、フックは「LLM が実行を選ぶかどうかに依存せず、特定のアクションが必ず起きる」ようにする仕組みだと説明しています(Automate actions with hooks)。 この記事では、整形+簡単なチェックを行う最初の1本を作り、動かないときの切り分け表まで示します。 なお、記事内の設定・スクリプトは説明用の例で、私が実行して検証したものではありません。あなたのリポジトリのツール構成(Prettier、ESLint など)に合わせて、最初は小さなテスト用ファイルで確認してください。
この記事の目次
1. フックに任せる作業と、指示に残す作業を分ける
フックに向くのは「機械的に判定でき、毎回同じ結果になる作業」です。
- 編集したファイルの整形、フック向きか:向く、理由:ツールが決まった結果を返す
- 編集したファイル単体のリント、フック向きか:向く、理由:失敗したら内容を Claude に返せる
- 設計方針の遵守、命名の好み、フック向きか:向かない、理由:判断が要るので CLAUDE.md や依頼文で伝える
- 全テストの実行、フック向きか:条件付き、理由:遅いと編集のたびに待たされる。まず整形だけで始める
整形のように結果が一意に決まる作業から始めると、失敗時の原因も追いやすくなります。
2. 最小の設定:PostToolUse で編集後に走らせる
PostToolUse は、ツール呼び出しが成功した後に発火するイベントです。入力には tool_name や tool_input(ツールへの引数)が JSON で渡されます。編集ツールなら、tool_input.file_path に対象ファイルが入ります。マッチャーはツール名に対して働き、大文字小文字を区別します。英数字と | などだけで書いたものは、完全一致の一覧として扱われます(Hooks reference)。
設定は次のような場所に書けます(同じ参照先の表より)。
- ~/.claude/settings.json:自分の全プロジェクト用(共有されない)
- .claude/settings.json:そのプロジェクト用(コミットしてチームで共有できる)
- .claude/settings.local.json:そのプロジェクト用(Claude Code がこのファイルへ設定を保存すると gitignore される)
最初は .claude/settings.local.json で試し、動いたら .claude/settings.json へ移すのが安全です。共有設定のフックはチームメンバーの環境でもシェルコマンドとして動くため、中身をレビューしてからコミットしてください。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PROJECT_DIR}/.claude/hooks/format.sh\"",
"timeout": 30
}
]
}
]
}
}${CLAUDE_PROJECT_DIR} はセッションを開始したプロジェクトのルートを指すプレースホルダーです。timeout を短めに置くと、整形が固まったときに待ち続けずに済みます(既定は600秒)。
3. 整形とチェックのスクリプト例
次が .claude/hooks/format.sh の例です(説明用・未実行)。Bash・jq・npm と、プロジェクトに導入済みの Prettier・ESLint を使います。対象拡張子だけを処理し、整形は黙って行い、リントの指摘だけを標準出力の JSON(hookSpecificOutput.additionalContext)で Claude に返します。PostToolUse は編集後に動くため、編集を取り消すことはできません(Hooks reference)。
#!/bin/bash
# 説明用の例(未実行)。Prettier / ESLint が導入済みのプロジェクト向け
command -v jq >/dev/null 2>&1 || { echo "jq が必要です" >&2; exit 1; }
input=$(cat)
file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty') || exit 1
# パスが取れない、またはファイルが無ければ何もしない
[ -z "$file" ] && exit 0
[ -f "$file" ] || exit 0
# 依存パッケージとビルド出力は対象外
case "$file" in
*/node_modules/*|node_modules/*|*/dist/*|dist/*) exit 0 ;;
esac
# 対象拡張子だけ処理する
case "$file" in
*.ts|*.tsx|*.js|*.jsx|*.json|*.css) ;;
*) exit 0 ;;
esac
workdir=$(printf '%s' "$input" | jq -r '.cwd // "."') || exit 1
cd "$workdir" || exit 1
# 1) 整形(出力は捨てる。失敗は非ブロッキングのエラーとして記録される)
if ! npx --no-install prettier --write "$file" >/dev/null 2>&1; then
echo "prettier が失敗しました: $file" >&2
exit 1
fi
# 2) リント(JS/TSのみ)。指摘や実行エラーを JSON で Claude に返す
case "$file" in
*.ts|*.tsx|*.js|*.jsx)
if ! out=$(npx --no-install eslint "$file" 2>&1); then
message=$(printf 'ESLint の確認結果: %s\n%s\n' "$file" "$out" | head -n 30)
jq -n --arg message "$message" '{
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: $message
}
}' || exit 1
fi
;;
esac
exit 0この例は、整形失敗時に exit 1 で非ブロッキングのエラーを報告し、リントの指摘があれば JSON を出して exit 0 で返します。additionalContext は Claude が次の応答で読むコンテキストに追加されますが、通常のチャットメッセージとしては表示されません。
作成後は実行権限を付けます。
chmod +x .claude/hooks/format.sh動かす前に手で試す
Claude Code を介さず、プロジェクトのルートでフックと同じ形の JSON を流し込めば、スクリプト単体の挙動を確認できます。
echo '{"tool_input":{"file_path":"src/sample.ts"}}' | .claude/hooks/format.sh
echo "exit code: $?"src/sample.ts は、わざとインデントを崩したテスト用ファイルに置き換えてください。整形後の内容と終了コード0を確認します。リントの指摘がある場合も終了コードは0で、標準出力に additionalContext を含む JSON が出ます。続いて Claude Code から同じファイルを編集させ、指摘が Claude に伝わるか確認してください。
4. Claude Code に作らせる依頼文と、人が確認すること
フックの設定ファイルも Claude Code に書かせられます。渡す情報は「使っているツールと、対象にする範囲」です。
このリポジトリに、ファイル編集後に自動で整形するフックを追加してください。
- 使うイベント: PostToolUse、マッチャーは Edit|Write
- 整形: npx --no-install prettier --write(このリポジトリは Prettier 導入済み)
- リント: JS/TS のみ npx --no-install eslint、指摘は標準出力の JSON(hookSpecificOutput に hookEventName: "PostToolUse" と additionalContext)で返し、終了コード0
- 対象外: node_modules と dist 配下、画像などのバイナリ
- スクリプトは .claude/hooks/format.sh、設定は .claude/settings.local.json に書く
- 実際に動かす前に、設定とスクリプトの内容を私に見せてください任せる範囲は設定とスクリプトの下書きまでです。人が確認するのは次の点です。
- スクリプトが編集対象のファイル以外を書き換えたり、削除したりしないか
- 外部へ通信するコマンドが入っていないか
- テスト用ファイルで、整形が実際に効くか(手で試す方法を使う)
- .claude/settings.json に移す前に、他のメンバーの環境でも同じツールが入っているか
5. 止まったときの切り分け表
まず Claude Code で /hooks を開き、作ったフックが一覧に出るか確認します。選ぶと定義されている場所を確認できます。一覧に出ないなら、設定ファイルの記述と置き場所を確認します。通常は変更が自動反映されますが、数秒待っても出ない場合はセッションを再起動してください。管理設定でプロジェクトのフックが禁止されている場合もあります(公式の切り分け手順)。ログを見たいときは claude --debug で起動し、フック名やイベント名で検索します。
- /hooks に表示されない、確認すること:JSON の構文、ファイルの置き場所、hooks キーの階層、対処:構文エラーを直す。既存の hooks があるときは、イベント名を兄弟キーとして足す
- 表示されるが発火しない、確認すること:マッチャーの綴りと大文字小文字(Edit、対処:Write)、正確に書き直す。if を使っているなら、パターンの階層(src/ と /src/**)も見る
- jq: command not found、確認すること:which jq、対処:jq を入れるか、コマンドを絶対パスで書く
- 発火するが整形されない、確認すること:手で試す方法で単体実行、対処:npx --no-install は未導入のツールでは失敗する。依存を導入するかパスを直す
- hook error が会話に出る、確認すること:終了コード、起動失敗、タイムアウト、JSON の構文や形式、対処:ログの原因を直す。PostToolUse は編集後に動くため、失敗しても編集は取り消せない
- JSON を出すフックが効かない、確認すること:--debug のログで JSON の解析エラーや Hook output does not start with { を確認、対処:標準出力は JSON オブジェクト1つだけにする。プロファイルの文字出力は対話時だけにし、additionalContext が hookSpecificOutput 内にあるかも確認する
- 遅い・途中で止まる、確認すること:timeout と処理時間、対処:対象を絞る、タイムアウトを調整する、重い処理は非同期フックにする
- bash の sed などで変えたファイルが整形されない、確認すること:マッチャーが Edit、対処:Write だけか、想定どおり。マッチャーはツール名に対して働くので、Bash 経由の変更は対象外
指摘を返すフックを入れたあと、Claude が同じ指摘を何度も直そうとして長引くことがあれば、リントの範囲を狭めるか、head -n 30 のように返す量を絞ってください。
6. 次に足す順番
最初の1本が安定して動いてから、1つずつ足します。
- 整形だけ(まずここ)
- 編集ファイル単体のリント
- 型チェックなどの少し重い処理(遅ければ非同期にする)
同時に複数を足すと、止まったときにどのフックが原因か切り分けにくくなります。1つ足すたびに、手で試す方法と /hooks の確認を繰り返してください。

