CARLOS LASTRES お問い合わせ

Claude Codeのフックで、自分のミスを自分で直させる設定

textlintで日本語の表記ゆれを直させ、テストが通るまで完了させない。Claude Codeのフックで自己修正の仕組みを作る方法を解説します。

Claude Codeに作業を任せると、最後に「完了しました」と丁寧に報告してくれます。ただ、その「完了」が本当に完了なのかは別の話です。テストが落ちたまま、表記がバラバラのまま、きれいな要約だけが返ってくることがあります。この記事では、フックという仕組みを使って、Claudeが自分のミスに自分で気づき、直してから報告するように設定する方法を紹介します。

フックとは何か

フックは、決まったタイミングでClaude Codeが自動で実行するコマンドです。プロジェクトの .claude/settings.json に一度書けば、あとは毎回必ず動きます。プロンプトに「必ずテストを実行してください」と書いても、それはお願いにすぎません。フックはお願いではなく、仕組みとして強制されます。

自己修正でよく使うイベントは次の4つです。名前は大文字小文字まで正確に書く必要があります。

  • PostToolUse:ファイル編集などのツール実行が成功した直後
  • PostToolUseFailure:ツール実行が失敗した直後
  • Stop:Claudeが返答を終えようとしたとき
  • SessionStart:セッションの開始、再開、/clear、コンパクト時

ポイントは「Claudeへの返し方」です。PostToolUse では終了コード2で終わると、標準エラーに書いた内容がそのままClaudeに届きます。Stop では {"decision": "block", "reason": "..."} を出力すると、Claudeは作業を続け、理由を読んで直しにいきます。SessionStart では標準出力がそのままコンテキストに入ります。

日本語ドキュメントの表記ゆれを自動で直させる

日本の現場でまず効くのは、コードよりも文章のチェックかもしれません。仕様書やヘルプページをClaudeに書かせると、「です・ます」と「だ・である」が混ざったり、「サーバ」と「サーバー」が揺れたりします。これは人が毎回指摘するより、textlint に任せたほうが確実です。

準備として、プロジェクトにtextlintと日本語向けのルールプリセットを入れます。

npm install --save-dev textlint textlint-rule-preset-ja-technical-writing
echo '{ "rules": { "preset-ja-technical-writing": true } }' > .textlintrc.json

次に .claude/hooks/check_ja.py を作ります。Claudeが編集したMarkdownだけをチェックし、問題があれば終了コード2で本人に突き返します。

#!/usr/bin/env python3
# 編集されたMarkdownをtextlintで確認し、指摘をClaudeに返す
import json, os, subprocess, sys

data = json.load(sys.stdin)
path = data.get("tool_input", {}).get("file_path", "")
if not path.endswith(".md"):
    sys.exit(0)

os.chdir(os.environ.get("CLAUDE_PROJECT_DIR", "."))
r = subprocess.run(["npx", "textlint", path], capture_output=True, text=True)
if r.returncode != 0:
    print("textlintの指摘があります。すべて直してから次に進んでください。\n" + r.stdout[-3000:], file=sys.stderr)
    sys.exit(2)

Claudeは日本語の指摘文もきちんと読めるので、「文末表現が混在しています」といったエラーを受け取ると、その場で書き直します。人がレビューする前に、機械的な指摘はすべて消えている状態になります。

テストが落ちている間は「完了」させない

コードを扱うプロジェクトなら、Stopフックが一番効きます。.claude/hooks/finish_gate.py として保存してください。

#!/usr/bin/env python3
# 変更があるのにテストが落ちていれば、終了を一度だけ止める
import json, os, subprocess, sys

data = json.load(sys.stdin)
os.chdir(os.environ.get("CLAUDE_PROJECT_DIR", "."))

if not subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True).stdout.strip():
    sys.exit(0)

t = subprocess.run(["npm", "test", "--silent"], capture_output=True, text=True)
if t.returncode == 0:
    sys.exit(0)

if data.get("stop_hook_active"):
    print(json.dumps({"systemMessage": "再試行後もテストが失敗しています。確認をお願いします。"}, ensure_ascii=False))
    sys.exit(0)

print(json.dumps({"decision": "block",
    "reason": "テストが失敗しています。原因を直し、npm testを再実行してから終了してください。\n" + (t.stdout + t.stderr)[-3000:]},
    ensure_ascii=False))

stop_hook_active は、Claudeがすでにフックの指示で作業を続けているときにtrueになります。この確認を入れておくと、再試行は一度だけで、それでもだめなら人に戻ってきます。無限ループの心配がなくなります。

設定ファイルにまとめる

{
  "hooks": {
    "PostToolUse": [
      { "matcher": "Edit|Write",
        "hooks": [{ "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/check_ja.py\"" }] }
    ],
    "Stop": [
      { "hooks": [{ "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/finish_gate.py\"" }] }
    ],
    "SessionStart": [
      { "matcher": "startup|resume|clear|compact",
        "hooks": [{ "type": "command", "command": "cat \"$CLAUDE_PROJECT_DIR/.claude/lessons.md\" 2>/dev/null || true" }] }
    ]
  }
}

最後のSessionStartは、.claude/lessons.md に書いた「過去の失敗から学んだルール」を毎回読み込ませるためのものです。compact を入れておくと、長い作業で会話が圧縮されたあとにも読み直されます。設定後はClaude Codeで /hooks と打ち、登録されているか確認しましょう。

学びは人が選んでから残す

失敗の記録から自動でルールを書き換えさせる方法もありますが、私はおすすめしません。誰も読まないルール集は、少しずつずれていきます。週に一度、次のように頼むくらいがちょうどいいです。

今週のgit logと差分を読み、同じ箇所を直し直した手戻りを探してください。
多かった原因を3つ挙げ、それぞれについて、.claude/lessons.md に追加する一行ルールを提案してください。
ファイルは20行以内に保ち、書き込む前に差分を見せて私の承認を待ってください。

数週間守られて効果があったルールは CLAUDE.md に移し、lessons.mdからは消します。

注意点

  • フックはあなたの権限で動くシェルコマンドです。ネットで見つけたスクリプトも、必ず中身を読んでから登録してください。
  • Stopフックは返答のたびに走ります。重いテスト全体ではなく、数十秒で終わる範囲に絞ってください。
  • 一時的にすべて止めたいときは、設定に "disableAllHooks": true を入れます。

最初はtextlintのフックか、テストのStopフックのどちらか一つだけで十分です。「完了しました」の中身が変わるのを一度体験すると、他の作業にも広げたくなるはずです。イベントや出力形式の詳細は、公式のフックリファレンスにまとまっています。

関連ガイド

← AIガイド一覧へ