Claude Code Hooks の使い方をブランド QA の型にする発想

Hooks は Claude Code のライフサイクルに処理を差し込む仕組みです。決められたイベントで自前のスクリプトが走ります。対象はユーザー入力・ツール実行前後・セッション境界です。

公式ドキュメントには 20 種類以上のイベントが並びます。ブランド QA で使いやすいのは五つです。役割はこの形で分けます。

  • PreToolUse — 保存前の差し止め
  • PostToolUse — 保存後の指摘
  • SessionStart — 基準の注入
  • UserPromptSubmit — 入力段階の予防
  • Stop — 送信前の最終確認

CLAUDE.md や Skills は「基準を読ませる」層です。Hooks は「基準に反したら止める・拾う」層で、役割が分かれます。制作の全体像は Claude Code をクリエイティブに使う実装ガイド にまとめています。本記事はその中で「ブランド基準を Hooks で強制する」実装だけを取り出します。

Hooks の置き場所とスコープ

Hooks の設定は JSON で書きます。置ける場所は次の三つです。

  • ユーザーホームの ~/.claude/settings.json — 全プロジェクト共通
  • プロジェクト直下の .claude/settings.json — 一つのプロジェクト、Git 共有可
  • プロジェクト直下の .claude/settings.local.json — 個人用、gitignore 対象

ブランド QA の基準はチームで共有したい領域です。そこで、プロジェクト直下の .claude/settings.json に寄せます。案件ごとにトーンが違うので、ブランドごとにリポジトリを切る運用が扱いやすい設計です。

プラグインの hooks/hooks.json にも書けます。複数のブランドで同じ QA を使い回すなら、社内プラグインとして配布する形が候補になります。

SessionStart で基準を注入する

SessionStart はセッションが開始または再開された時に一度だけ走ります。ブランド QA では「セッションのたびに現在の基準を読み込ませる」用途に向きます。

CLAUDE.md は常時プロンプトに載る仕様です。膨らむと文脈を食います。辞書やチェックリストが大きい場合は SessionStart から都度読み込ませます。そのほうが文脈が軽く済みます。JSON の設定はこの形です。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "cat ${CLAUDE_PROJECT_DIR}/.claude/brand/glossary.md"
          }
        ]
      }
    ]
  }
}

コマンドの標準出力は additionalContext として Claude に渡ります。書き手は毎回同じ基準を貼り直す手間から解放されます。ブランド辞書を更新したら、次のセッションから即反映されます。

PreToolUse で禁止語彙の保存を止める

PreToolUse はツールが実行される前に走ります。Write や Edit を叩く前に、ブランド基準に反する内容が含まれていないかを検査する。含まれていれば止める。ブランド QA の中でいちばん強い一手です。

matcher に Edit|Write を書き、if に Edit(*.md) を書きます。md ファイルを触る Edit / Write だけに hook を絞れます。設定はこの形です。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "if": "Edit(*.md)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/brand-guard.sh"
          }
        ]
      }
    ]
  }
}

hook のスクリプトは stdin から JSON を受け取ります。tool_input.content (Write) や tool_input.new_string (Edit) に書き込み内容が入ります。禁止語彙一覧と grep して、当たれば JSON で差し止めます。

#!/bin/bash
INPUT=$(cat)
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // ""')
HITS=$(echo "$CONTENT" | grep -Ef "${CLAUDE_PROJECT_DIR}/.claude/brand/banned.txt" || true)

if [ -n "$HITS" ]; then
  jq -n --arg reason "禁止語彙を検出: $HITS" '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: $reason
    }
  }'
  exit 0
fi
exit 0

permissionDecision: "deny" を返すと、その Write / Edit は実行されません。理由文が Claude 側に返るので、次のターンで書き直しが走ります。書き手は原稿を汚す前の段階で止められます。

同じ運用は Claude Code でブランドコンテンツを大量制作する に書きました。生成側と QA 側で同じ banned.txt を共有すれば、量を出しても基準が崩れません。

PostToolUse で表記揺れを拾う

PostToolUse はツールが成功した後に走ります。原稿が実際に書き込まれた状態で、腰を据えた検査を回せる場所です。差し止めるほどではない指摘をここで拾います。表記揺れや canonical 形からのずれ、社名の綴り違いが対象です。

設定は PreToolUse と同じ形で、matcher と if を絞れます。スクリプト側では stdin から tool_input.file_path を受け取る。書き込まれたファイル全体を走査する。

#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path')

# canonical 形の対照表 (誤 → 正) を tsv で持つ
awk -F'\t' 'NR==FNR{map[$1]=$2; next}
{ for (k in map) if (index($0, k)) print FILENAME":"FNR": "k" → "map[k] }' \
  "${CLAUDE_PROJECT_DIR}/.claude/brand/canonical.tsv" "$FILE"

PostToolUse は差し止めができません。標準出力の一覧が Claude と書き手に共有される形になります。「置き換え候補が三つあります」という指摘だけを渡す。実際に直すかは書き手の判断に残せます。

色と書体版は Claude Design でブランドガイドラインを固定する と並びます。片方は言葉の canonical、片方は視覚の canonical です。Hooks と Skills の考え方を横に展開できます。

UserPromptSubmit で入力段階から予防する

UserPromptSubmit はユーザーがプロンプトを送った時に走ります。指示を出した瞬間に、その指示自体をブランド基準で検査できます。

たとえば「派手なキャッチコピーを作って」といった依頼です。社内基準の外側で受けたくない場合が対象になります。UserPromptSubmit で入力を読む。方向性が基準から外れていれば permissionDecision: "deny" を返す。この流れが組めます。

書き手にとっては「そもそも走らせない」で終わる形です。後段の差し止めよりも消費トークンが少なく済みます。ただし予防を強くしすぎると書き手のリズムを削ります。対象は「絶対に避けたい依頼型」に絞るのが穏当です。

量産の運用は Claude Code でブログ記事を一括生成する にまとめました。生成側と入力予防側を同じリポジトリで共有すると、後で基準が食い違いません。

exit code と JSON decision の使い分け

Hooks の返し方は二つあります。exit code の数字で意思表示するか、標準出力に JSON を吐くかです。

  • exit code 0 — 何もしない (通常)
  • exit code 2 — 差し止め (PreToolUse や UserPromptSubmit で有効)
  • JSON で permissionDecision: deny — 差し止め + 理由付き
  • JSON で permissionDecision: allow — 明示的に通す
  • JSON で additionalContext — Claude に追加情報を渡す

ブランド QA では JSON 側を主に使います。理由文を Claude と書き手の両方に渡せる。なぜ止めたかが分かる形で組めます。exit code だけで止めると「なぜ止まったか分からない」状態になり、書き手が困ります。

PostToolUse は差し止めできない代わりに systemMessage を返せます。「表記揺れが三件見つかりました」といった短い通知を Claude 側に渡す用途に向きます。

複数 hook の合成と段階運用

Hooks は同じイベントに複数登録できます。matcher と if を組み合わせると、対象を絞りながら段階的に厳しくできます。

運用の順序はこの形が扱いやすい設計です。

  1. まず PostToolUse だけで走らせる — 指摘は出すが止めない
  2. 一週間ほど回して誤検出を潰す
  3. PreToolUse に格上げする — 検出したら止める
  4. UserPromptSubmit に予防を足す — そもそも依頼段階で外す

いきなり全部を差し止めに寄せると、書き手が hook に振り回されます。「発見」から入り、慣れてから「差し止め」に移す。段階で移すのが安定した設計です。

HTTP hook と外部サービス連携

Hooks は type: "http" を指定すると外部サーバーに POST できます。共通の校正 API があるなら、複数プロジェクトから同じ API を叩く形が組めます。

{
  "type": "http",
  "url": "http://localhost:8080/hooks/brand-qa",
  "timeout": 30,
  "headers": {
    "Authorization": "Bearer $BRAND_QA_TOKEN"
  },
  "allowedEnvVars": ["BRAND_QA_TOKEN"]
}

allowedEnvVars に列挙した環境変数だけがサーバーに渡ります。トークン管理を hook 設定の外に切り出せる仕組みです。リポジトリを共有する時の秘密情報の扱いが穏やかになります。

外部 API 側で意味判定モデルを持たせる形もあります。grep では拾えないニュアンスの指摘まで返せます。ただし応答が遅いと書き手のリズムを削る。timeout を短めに握るのが基本です。

安全側に寄せる三つのコツ

Hooks は強力な代わりに、設計を誤ると書き手を止めすぎます。安全側に寄せる三つのコツを最後に置きます。

一つ目は、対象ファイルを狭く握ることです。if: "Edit(*.md)" のように拡張子で対象を絞る。コード用のブランチで hook が発火して開発を止める事故を避けられます。

二つ目は、権限を狭く握ることです。プロジェクト直下の .claude/settings.json に置いた hook は「ワークスペースの信頼」を要求します。信頼していないリポジトリを clone した時、他人の hook は勝手に走りません。

三つ目は、判断の全自動化を避けることです。Hooks は「発見」に閉じる。差し止めは「絶対に外せない禁止語彙」だけに絞る。この二層構造にすると、書き手の判断が残った上で機械の目が働きます。ブランドの息づかいは書き手が持つ。Hooks はその判断を支える型として使う位置づけです。

FAQ

Q1. Claude Code Hooks の使い方は何から始めればよいですか

A. PostToolUse で「指摘だけ返す」形から始めます。禁止語彙一覧を .claude/brand/banned.txt に置く。Write / Edit の後に grep する短いスクリプトを一本書きます。一週間ほど回して誤検出を潰した上で、PreToolUse に格上げするのが安定した順序です。

Q2. PreToolUse と PostToolUse はどう使い分けますか

A. PreToolUse は「絶対に保存させたくない」時に使います。禁止語彙や表記の誤りを差し止める用途です。PostToolUse は「保存されたけれど直したい」時に使います。表記揺れや canonical 形からのずれを指摘する用途です。差し止めはできない代わりに、腰を据えた検査を回せます。

Q3. SessionStart で読み込ませる基準はどこまで書けますか

A. SessionStart は文字数の制限がありません。CLAUDE.md に収まらないブランド辞書やチェックリストを渡す場所として使えます。ただし全部渡すと文脈を食う。標準出力に「今回のセッションで最低限守るべき五項目」だけを抽出して渡す形が現実的です。

Q4. Hooks の設定を Git で共有する時に気を付ける点は何ですか

A. プロジェクト直下の .claude/settings.json は公式の「ワークスペースの信頼」を要求します。clone した相手が信頼を承認するまで hook は走りません。秘密情報は .claude/settings.local.json に切り出す。共有する設定側には環境変数の名前だけを書きます。

Q5. Hooks の差し止めが厳しすぎて書き手が困る時はどう調整しますか

A. if の条件を狭めます。対象を md ファイルだけに絞る。特定のディレクトリだけに絞る。この形で発火範囲を狭めるのが最初の一手です。それでも厳しければ、PreToolUse を PostToolUse に格下げします。「止める」を「拾う」に戻す運用です。基準そのものは残しつつ、書き手の呼吸を優先する調整です。

情報源