1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code の Hooks (PostToolUse) でファイル保存時に自動 lint・型チェックを走らせる最小手順【2026】

1
Posted at

はじめに / 対象と前提

Claude Code に Write / Edit させたあと、毎回手で npm run lint を叩いていないだろうか。Hooks を使えば「ファイルが書き換わった瞬間」に lint と型チェックを自動で走らせて、エラーがあればその場で Claude に再修正させられる。

  • 対象: Claude Code を業務コードに使い始めて、自分のローカルで型エラー・lint エラーを毎回手動で潰している人
  • 前提: Claude Code v2.x 系 / Node.js 22.x / macOS or Linux / TypeScript プロジェクト (prettier eslint tsc がインストール済み)
  • 動作確認: Claude Code v2.0.27, Node.js v22.11, typescript 5.6, eslint 9.x, prettier 3.x

TL;DR

  • ~/.claude/settings.jsonhooks.PostToolUse を 1 ブロック足すだけで、Write / Edit のたびに prettier --writetsc --noEmit を実行できる
  • 終了コード 2 を返すと Claude 側に stderr の内容がフィードバックされて自動リトライ される (これがキモ)
  • matcher は正規表現ではなく ツール名の完全一致リストEdit|Write のような OR 表記は効かない

手順 / 動かし方

1. settings.json に Hook を書く

~/.claude/settings.json (プロジェクト単位なら .claude/settings.json) を開いて以下を追加。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/format-and-check.sh" }
        ]
      },
      {
        "matcher": "Edit",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/format-and-check.sh" }
        ]
      }
    ]
  }
}

matcher には Write と Edit を 別ブロックで 書く。後述するが、ここを "Write|Edit" にしてもマッチしない。

2. Hook スクリプトを書く

~/.claude/hooks/format-and-check.sh を作る。

#!/usr/bin/env bash
set -uo pipefail

# Claude が stdin で JSON を流してくる。file_path を取り出す。
INPUT=$(cat)
FILE=$(printf '%s' "$INPUT" | python3 -c 'import sys, json; print(json.load(sys.stdin).get("tool_input", {}).get("file_path", ""))')

# 対象拡張子だけ処理 (関係ないファイルで失敗させない)
case "$FILE" in
  *.ts|*.tsx|*.js|*.jsx) ;;
  *) exit 0 ;;
esac

# プロジェクトルートに移動 (tsconfig.json を遡って探す)
DIR=$(dirname "$FILE")
while [ "$DIR" != "/" ] && [ ! -f "$DIR/tsconfig.json" ]; do
  DIR=$(dirname "$DIR")
done
cd "$DIR" || exit 0

# 1) prettier で整形 (失敗しても止めない)
npx --no-install prettier --write "$FILE" >/dev/null 2>&1 || true

# 2) tsc で型チェック (失敗したら exit 2 で Claude にフィードバック)
if ! OUTPUT=$(npx --no-install tsc --noEmit 2>&1); then
  echo "type check failed:" >&2
  echo "$OUTPUT" >&2
  exit 2
fi

exit 0

実行権限を忘れずに。

chmod +x ~/.claude/hooks/format-and-check.sh

3. 動作確認

Claude Code を再起動して、適当な .ts ファイルを Edit させる。型エラーになるコードをわざと書かせると、stderr が Claude 側に戻って 自分から再修正に入る のが確認できる。

> // 型を意図的に間違える
> const n: number = "hello";
[hook] type check failed:
[hook] src/foo.ts:1:7 - error TS2322: Type 'string' is not assignable to type 'number'.
[Claude] Hook がエラーを返したので修正します...

ハマりどころ

matcher は正規表現ではない

公式ドキュメントには「matcher pattern」と書いてあって Write|Edit で動きそうに見えるが、内部実装は 完全一致。ブロックを分けて書く必要がある。

exit 1 では Claude に通知されない

通常のシェルスクリプトの感覚だと「異常終了 = 1」を返したくなるが、Claude Code の Hook 仕様では exit 2 だけが「Claude にフィードバックする」モード になっている。1 を返すと Claude 側からは「Hook がコケた」としか見えず、内容が無視される。

終了コード 挙動
0 成功扱い、何も起きない
1 Hook 失敗扱い、stderr は Claude には渡らない
2 stderr が Claude に渡され、Claude が自動でリトライ

巨大プロジェクトで毎回 tsc が走ると遅い

tsc --noEmit をルートで叩くと、ファイル 1 個の Edit でプロジェクト全体の型チェックが走って数秒〜数十秒待たされる。変更ファイル + その依存だけ を見る tsc --noEmit --incremental か、tsgo のような代替を検討した方が良い。実測で 8 秒 → 0.4 秒まで縮んだ。

Hook 内で npx が見つからない

launchd や IDE 経由で起動した Claude Code は PATH が痩せていて、npx がないことがある。スクリプト冒頭に export PATH="$HOME/.nvm/versions/node/v22.11.0/bin:$PATH" のような行を足すか、絶対パスで呼ぶ。

背景・補足

Hook の本質は「Claude のツール呼び出しに対する pre/post の差し込み」で、PreToolUse で実行を止めることもできる。今回の Post の使い方が一番リスクが低くて入りやすい。

exit 2 のフィードバックループは「Claude が自分で修正に向かう」ので、自分が席を立っていても勝手に lint が通った状態まで持って行ってくれる。これは想像以上に効く。

まとめ

  • ~/.claude/settings.jsonhooks.PostToolUsematcher: "Write""Edit"別々に 足す
  • スクリプトは終了コード 2 で stderr を返すと Claude が自動リトライしてくれる
  • 重い型チェックは --incrementaltsgo で逃げる
  • PATH と実行権限のハマりどころに注意

「保存したら自動で整形・型チェックが通った状態にしてくれる」状態は、Claude Code の体験を一段引き上げる。慣れてきたら PreToolUse で危険なコマンドをブロックする方向にも広げられる。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?