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 に書ける情報は 200 行が目安と公式が案内しています。それを超えるブランド辞書やチェックリストは、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 を書き、ifEdit(*.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 はユーザーがプロンプトを送った時に走ります。書き手が Claude Code に指示を出した瞬間に、その指示自体をブランド基準で検査できます。

たとえば「派手なキャッチコピーを作って」といった依頼を、社内基準の外側で受けたくない場合。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 を短めに (30 秒目安) 握るのが基本です。

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

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

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

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

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

FAQ

Q1. Claude Code Hooks でブランド QA を始めるには何から手をつければよいですか

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 に格下げして「止める」を「拾う」に戻します。基準そのものは残しつつ、書き手の呼吸を優先する調整です。

情報源