Claude Media
Claude CodeでBiomeを使う — 編集直後にcheck --writeをhookで走らせる

Claude CodeでBiomeを使う — 編集直後にcheck --writeをhookで走らせる

Claude Codeがファイルを編集した直後にbiome check --writeを走らせるPostToolUse hookの設定と、直らなかった指摘をClaudeに返す書き方をまとめます。

Biomeをhookに載せると何が固定されるか

Biome(ビオーム)は、フォーマット・Lint・import整理などの修正案(assist actions)を1つのCLIで扱うツールです。biome checkはこの3つをまとめて検査し、--writeを付けると安全に直せる分をその場で書き換えます。これをClaude CodeのPostToolUse hookに載せると、Claudeがファイルを編集するたびに整形とLint修正が走ります。

ESLintとPrettierを別々に回す構成と比べて、hookに書くコマンドが1本で済みます。設定ファイルもbiome.json(またはbiome.jsonc)に寄せられます。手元のプロジェクトにBiomeを入れてある前提で、本記事ではhookの書き方、診断の出力量の絞り方、落とし穴を扱います。言語ごとの自動修正コマンドの比較はPostToolUse hookのLint自動修正の早見表にあります。

biome check --writeが適用する範囲

biome checkは、指定したファイルのフォーマット、Lint、assist actionsをまとめて検査します。PATHを省くとカレントディレクトリ以下が対象です。hookで使うときに関係するフラグを表にします。

フラグ働き
--write(別名--fix)働きフォーマット、安全なLint修正、安全なassist actionsを適用する
--unsafe働き--writeと併用すると、安全でない修正も適用する
--formatter-enabled / --linter-enabled / --assist-enabled働き3つの検査をコマンド単位で切り替える。値はtrueかfalse
--enforce-assist働き必要なassist actionsが適用されないと失敗させる。既定はtrue
--staged働きステージ済みのファイルだけを対象にする。ローカル用
--changed働きデフォルトブランチ(または--sinceの参照)との差分でコミット済みの変更だけを対象にする。CI用

hookで使うのは、実質--writeだけです。--stagedと--changedはGitの状態から対象を決める仕組みで、Claudeが今編集した1ファイルを狙う用途には合いません。--unsafeは後述する理由で、hookでは付けない構成にしています。

最小のhook設定

.claude/settings.jsonに、Edit|WriteにマッチするPostToolUse hookを書きます。コマンド本体はスクリプトに逃がし、パスは${CLAUDE_PROJECT_DIR}で指します。argsを付けるとシェルを介さず実行ファイルを直接起動する書き方(exec form)になり、パスに空白があっても引用符が要りません。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/biome-fix.sh",
            "args": [],
            "timeout": 60
          }
        ]
      }
    ]
  }
}

timeoutは秒単位で、command hookの既定は600秒です。Biomeは普通1ファイルなら短時間で終わるので、npxの初回取得で待たされるケースを見込んで60秒程度に短くしておくと、止まったときに気づきやすくなります。タイムアウトしたhookは出力が捨てられ、判定も返りません。

スクリプト本体 — 1ファイルだけ直して、残りをClaudeに返す

PostToolUseのhookにはstdinでJSONが渡されます。tool_input.file_pathに編集したファイルの絶対パスが入っているので、これをbiome check --writeに渡します。

mkdir -p .claude/hooks
cat > .claude/hooks/biome-fix.sh <<'SH'
#!/bin/bash
file_path=$(jq -r '.tool_input.file_path // empty')
[ -z "$file_path" ] && exit 0
 
output=$(npx @biomejs/biome check --write \
  --no-errors-on-unmatched \
  --files-ignore-unknown=true \
  --diagnostic-level=error \
  --reporter=concise \
  "$file_path" 2>&1)
status=$?
 
if [ "$status" -ne 0 ]; then
  echo "$output" >&2
  exit 2
fi
exit 0
SH
chmod +x .claude/hooks/biome-fix.sh

npxの代わりにネイティブバイナリを使う場合は、npx @biomejs/biomeをbiomeに置き換えます。Biomeの公式CLIページはどちらの呼び出しも並べて載せています。

手順

スクリプトが1回動くときの流れ

  1. 1

    file_pathを取り出す

    jqでstdinのJSONから読みます。空なら対象外の入力なので、何もせずexit 0にします(matcherを広げたときの保険です)。

  2. 2

    check --writeで直す

    整形、安全なLint修正、import整理などが、その場でファイルに書き込まれます。

  3. 3

    失敗したらstderrに書いて終了コード2

    直せない指摘が残ったときだけ、出力をstderrへ流してexit 2にします。PostToolUseでは終了コード2でもツールの実行は取り消せませんが、stderrの内容がClaudeに届きます。

終了コードを0のまま返すと、stderrはデバッグログにしか残らず、Claudeには見えません。指摘を読ませたいなら終了コード2が必要です。この仕組みの一般論はPostToolUse hookでツール実行後の後処理を自動化するにまとめています。

診断の量を絞る4つのフラグ

編集のたびにClaudeへ大量の診断を返すと、文脈を食うだけでなく、本題の指摘が埋もれます。スクリプトに入れたフラグの理由は次のとおりです。

フラグ

スクリプトに入れたフラグの役割

  • --diagnostic-level=error

    表示する診断の最小の重大度を決めます。既定はinfoで、errorにすると警告以下は表示されません。

  • --reporter=concise

    診断を1件1行にまとめる出力形式です。既定のdefaultは診断ごとに複数行の説明が付くため、stderrが長くなります。

  • --files-ignore-unknown=true

    Biomeが認識しないファイル種別で診断を出さなくなります。Markdownや画像を編集したときにhookが赤くなるのを防ぎます。

  • --no-errors-on-unmatched

    処理対象のファイルが1つもないときにエラーを出しません。.gitignoreなどで除外されたパスを渡したときに効きます。

診断の表示件数には--max-diagnosticsもあり、既定は20件です。数を絞りたいときは数値を渡し、制限を外したいときはnoneを渡します。1ファイルだけを対象にするhookでは20件で足りることが大半ですが、大量のLint違反が出る既存ファイルを初めて触るときは、先頭の20件しか返らない点を覚えておくと混乱しません。

警告も失敗扱いにしたい場合は--error-on-warningsを足します。このフラグは、警告の診断が1件でも出ると終了コードを失敗にします。警告を失敗扱いにするか、表示から外すかは別の方針なので、どちらにするかを先に決めます。

--unsafeを付けない理由

--writeが適用するのは「安全な」修正に限られます。--unsafeを足すと、安全と言い切れない修正やassist actionsまで書き換えます。コードの意味が変わりうる変更が、確認なしにClaudeの編集直後に入る構成になるため、hookでは付けません。必要なら人間が手元でbiome check --write --unsafeを実行し、差分を見てからコミットする運用が扱いやすくなります。

既存のESLint・Prettier設定から移す

ESLintとPrettierを使っているプロジェクトは、Biomeの設定を手書きせずに、設定の取り込みコマンドで下書きを作れます。biome migrateは引数なしだと、Biome自体の破壊的変更に伴う設定更新を表示するだけで、ファイルを書き換えません。--writeを付けて初めて適用されます。

npx @biomejs/biome init
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write

biome initはカレントディレクトリに設定ファイルを作ります(--jsoncを付けるとbiome.jsonc)。migrate eslintはカレントディレクトリのESLint設定とignore設定を、migrate prettierはPrettierの設定とignore設定を取り込みます。ESLintの--include-inspiredを付けると、ESLintのルールに着想を得たBiomeのルールも移行対象に入ります。--include-nurseryではnurseryルールも含まれます。

移行したあとは、旧ツールのhookを残したままBiomeのhookを足さないようにします。同じファイルに2系統の整形と検査が走ると、指摘も二重にClaudeへ返ります。ESLint 9のflat configへ進める手順はClaude CodeでESLintのflat config移行を進めるで扱っています。

効かないケースと落とし穴

PostToolUse hookの仕様から、次のケースでは編集してもBiomeが走りません。

  • Bashコマンドがファイルを書き換えたとき: Edit|Writeにマッチするhookは、Bashコマンドや外部プロセスによる書き換えでは動きません。ディスク上の変更に反応させたいときはFileChangedイベントを使う選択肢があります
  • 失敗したツール呼び出し: PostToolUseはツールが成功したあとにだけ動きます。失敗時の処理はPostToolUseFailureの領分です(PostToolUseFailure hookでツール失敗時だけ動くリカバリを書く)
  • Biomeが対応しない拡張子: --files-ignore-unknown=trueを付けていれば診断は出ず、素通りします

もう1つ、設定ファイルの探索先は固定したほうが安全です。--config-pathを渡さなければ、Biomeはbiome.jsonかbiome.jsoncを探します。Claudeが作業ディレクトリを移すとhook入力のcwdも変わるため、参照する設定を1つに決めたいときは--config-pathにプロジェクトルートを渡します。環境変数BIOME_CONFIG_PATHでも同じ指定ができます。

CLAUDE.mdに添える規約

hookが整形とLint修正を引き受けるので、Claudeには「整形を手でやり直さない」と伝えておくと余計な編集が減ります。次は一例です。

## フォーマットとLint
 
- 保存時にhookが `biome check --write` を実行する。インデントや引用符の整形を手で直さない
- hookのstderrに指摘が出たら、その行だけを直す。`biome-ignore` コメントで黙らせる前に理由を説明する
- `--unsafe` は付けない

二つ目の箇条にあるbiome-ignoreは、Biomeが診断を個別に抑止するコメントです。Claudeが警告を黙らせる近道として使うのを抑えるため、理由の説明を求める書き方にしています。

Claude Codeを起動せずにhookだけ試す

hookはstdinのJSONを読むだけなので、同じ形のJSONを流し込めば、Claude Codeなしで動作を確かめられます。パスは手元の実在ファイルに置き換えます。

printf '{"tool_name":"Write","tool_input":{"file_path":"%s/src/index.ts"}}' "$PWD" \
  | .claude/hooks/biome-fix.sh; echo "exit=$?"

整形だけで済むファイルならexit=0で、ファイルの内容が書き換わります。意図的に未使用変数などの違反を残したファイルなら、診断がstderrに出てexit=2になるはずです。Biome単体の挙動を先に確かめたいときは、公式CLIページが示す標準入力の形が使えます。

echo 'let a;' | npx @biomejs/biome check --stdin-file-path=file.js --write

--stdin-file-pathは、標準入力のコードを処理して結果を標準出力に書く指定です。渡したパスは設定の選択と入力種別の判定に使われ、実在するファイルでなくても構いません。この形はファイルを書き換えないため、hookの本体には使わず、設定が効いているかの確認専用にします。

まとめ

Biomeのhookは、biome check --writeを1本呼ぶだけで整形、Lint修正、import整理を編集直後に固定できます。肝は、直らなかった指摘だけを終了コード2とstderrでClaudeに返す部分です。--diagnostic-level=errorと--reporter=conciseで出力を絞り、--unsafeは人間の手元に残す。この分担が、Claudeの文脈を汚さずに運用しやすい形です。

この記事を共有:XはてブLinkedIn