Claude Code の Hook で format lint test を実行する

🚲
note

Claude Code に追加された Hook 機能を使って、これまで CLAUDE.md や githooks で実現していたツールの実行を Claude Code の設定に移してみる。format は公式でも利用例として紹介されている。

今回は format は編集時 PostToolUse 、lint と test は git commit の PreToolUse で実行したいと考え、最終的には次のように設定した:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit|MultiEdit",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.file_path | select(endswith(\".js\") or endswith(\".ts\") or endswith(\".jsx\") or endswith(\".tsx\")) or endswith(\".json\")) or endswith(\".css\"))' | xargs -r bun biome format --write"
      }]
    }],
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "jq -r '.tool_input.command // \"\"' | grep -q \"^git commit\" && (bun run lint 1>/dev/null; [ $? -eq 1 ] && echo '{\"decision\":\"block\",\"reason\":\"Lint errors found. Fix them before committing.\"}' && exit 2) || true"
      }, {
        "type": "command",
        "command": "jq -r '.tool_input.command // \"\"' | grep -q \"^git commit\" && (bun run test 1>/dev/null; [ $? -eq 1 ] && echo '{\"decision\":\"block\",\"reason\":\"Tests failed. Fix them before committing.\"}' && exit 2) || true"
      }]
    }]
  }
}

format と lint には biome を使っていて、 test は monorepo でパッケージごとに異なるツールを利用している。設定のポイントとして、例えば biome lint の Lint Error は Exit code が 1 なのでそのままでは git commit が止まらない。そのため PreToolUse では、コマンドがエラーだったときに詳細な JSON と Exit code 2 を返すようにしている。少し読みやすくするとこうなる:

jq -r '.tool_input.command // \"\"' | grep -q \"^git commit\"
    && (bun run lint 1>/dev/null; 
        [ $? -eq 1 ] 
        && echo '{\"decision\":\"block\",\"reason\":\"Lint errors found. Fix them before committing.\"}'
            && exit 2)
|| true

これで今のところうまく動いているよう見える。これが便利かはまだ体感できていないので、すぐにやめる可能性はある。

yaakai.to