0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コピペで使えるClaude Code hookの最小テンプレート——PreToolUse/PostToolUse/Stop

0
Posted at

hookを書きたい。でも何から始めればいいか分からない。

公式ドキュメントは読んだ。仕組みは理解した。でも「じゃあ実際にファイルを作って動かすまで」が遠い。

この記事では、コピペしてすぐ動く最小テンプレートを3種類用意した。PreToolUse(ツール実行前)、PostToolUse(ツール実行後)、Stop(セッション終了時)。全部bashスクリプト。全部10行前後。

hookの基本構造

どのhookも同じパターンで動く。

  1. Claude CodeがstdinにJSONを流し込む
  2. hookがJSONを読んで判断する
  3. 終了コードで結果を返す

終了コードの意味:

exit code 意味
0 問題なし。そのまま続行
1 警告。stderrの内容をClaudeにフィードバック
2 ブロック。ツール実行を止める(PreToolUseのみ)

stdoutにJSON({"decision":"approve"}等)を出すと、許可判定を上書きできる。stderrに書いた文字列はClaudeへのフィードバックになる。

1. PreToolUse テンプレート(ブロック用)

ツール実行のに走る。危険な操作を止めたいときに使う。

~/.claude/hooks/block-template.sh:

#!/bin/bash
# PreToolUse: 特定コマンドをブロックするテンプレート
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0

# ブロック条件(ここを書き換える)
if echo "$COMMAND" | grep -qE 'rm\s+-rf\s+(/|~|\$HOME)'; then
    echo "BLOCKED: rm -rf on sensitive path" >&2
    exit 2
fi

exit 0

exit 2がブロック。exit 0が許可。stderrに書いた理由はClaudeに伝わるので、Claudeが代替手段を考えてくれる。

応用例: git push --forceをブロック

if echo "$COMMAND" | grep -qE 'git\s+push\s+.*--force'; then
    echo "BLOCKED: force-push detected. Use --force-with-lease instead." >&2
    exit 2
fi

2. PreToolUse テンプレート(自動承認用)

安全だと分かっているコマンドの許可プロンプトを消す。Auto Modeじゃなくても、特定のコマンドだけ自動で通したいときに便利。

~/.claude/hooks/approve-template.sh:

#!/bin/bash
# PreToolUse: 安全コマンドを自動承認するテンプレート
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0

# 承認条件(ここを書き換える)
BASE=$(echo "$COMMAND" | awk '{print $1}')
case "$BASE" in
    cat|ls|head|tail|grep|find|wc|tree|pwd)
        echo '{"decision":"approve","reason":"Read-only command"}'
        exit 0
        ;;
esac

# マッチしなければ何もしない(通常の許可フローへ)
exit 0

stdoutに{"decision":"approve"}を出すのがポイント。これでClaude Codeの許可プロンプトがスキップされる。

3. PostToolUse テンプレート

ツール実行のに走る。結果を検証して問題があればClaudeに伝える。

~/.claude/hooks/post-check-template.sh:

#!/bin/bash
# PostToolUse: 実行結果を検証するテンプレート
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)

# ファイル操作以外はスキップ
[ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ] && exit 0

# 検証(ここを書き換える)
EXT="${FILE_PATH##*.}"
case "$EXT" in
    py)
        ERR=$(python3 -m py_compile "$FILE_PATH" 2>&1)
        if [ $? -ne 0 ]; then
            echo "SYNTAX ERROR: $ERR" >&2
            exit 1
        fi
        ;;
    sh)
        ERR=$(bash -n "$FILE_PATH" 2>&1)
        if [ $? -ne 0 ]; then
            echo "SYNTAX ERROR: $ERR" >&2
            exit 1
        fi
        ;;
    json)
        ERR=$(jq empty "$FILE_PATH" 2>&1)
        if [ $? -ne 0 ]; then
            echo "SYNTAX ERROR: $ERR" >&2
            exit 1
        fi
        ;;
esac

exit 0

exit 1で警告を返す。ブロックはしない(PostToolUseではexit 2は使えない。実行はもう終わっている)。stderrの内容がClaudeにフィードバックされ、自動的に修正を試みてくれる。

4. Stop テンプレート

セッション終了時に走る。ログ記録やクリーンアップに使う。

~/.claude/hooks/stop-template.sh:

#!/bin/bash
# Stop: セッション終了時の処理テンプレート
INPUT=$(cat)
REASON=$(echo "$INPUT" | jq -r '.stop_reason // "unknown"' 2>/dev/null)
TIMESTAMP=$(date -Iseconds)

# ログ記録(ここを書き換える)
LOG="$HOME/.claude/session-log.txt"
echo "[$TIMESTAMP] Session ended: reason=$REASON" >> "$LOG"

# 異常終了の通知(オプション)
if [ "$REASON" != "user" ] && [ "$REASON" != "normal" ]; then
    echo "WARNING: Unexpected stop reason: $REASON" >&2
fi

exit 0

Stopフックでは終了コードの意味が変わる。何を返してもセッション終了は止まらない。stderrに書いた内容はログとして残る。

settings.jsonへの登録

hookファイルを書いたら~/.claude/settings.jsonに登録する。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/block-template.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/post-check-template.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/stop-template.sh"
          }
        ]
      }
    ]
  }
}

matcherはツール名でフィルタする。"Bash"ならBashツールだけ、""(空文字)なら全ツールに反応する。Edit、Write、Readなど任意のツール名を指定できる。

テスト方法

hookを登録する前に、ターミナルで単体テストできる。

# PreToolUseのテスト(rm -rfをブロックするか?)
echo '{"tool_input":{"command":"rm -rf /"}}' | bash ~/.claude/hooks/block-template.sh
echo $?
# → 2(ブロック)

# 安全なコマンドは通るか?
echo '{"tool_input":{"command":"ls -la"}}' | bash ~/.claude/hooks/block-template.sh
echo $?
# → 0(許可)

# PostToolUseのテスト(壊れたJSONを検知するか?)
echo '{"broken":}' > /tmp/test.json
echo '{"tool_input":{"file_path":"/tmp/test.json"}}' | bash ~/.claude/hooks/post-check-template.sh
echo $?
# → 1(警告)

echo JSON | bash hook.sh; echo $?のパターンを覚えておけば、どんなhookでもテストできる。

hookを自動生成する

テンプレートのコピペで足りないとき。「やりたいこと」を日本語で伝えると、hookを自動生成してくれるツールがある。

npx cc-safe-setup --create "git commitする前にlintを実行"

442個のサンプルhookから似たものを探してインストールもできる:

# サンプル一覧
npx cc-safe-setup --examples

# 特定のサンプルをインストール
npx cc-safe-setup --install-example credential-exfil-guard

hookの設計思想から実戦パターンまで体系的にまとめた本: Claude Code 実戦ガイド


テンプレートは4つ。PreToolUseのブロック、PreToolUseの自動承認、PostToolUseの検証、Stopのログ記録。この4パターンを押さえておけば、大抵の要件はカバーできる。まずは1つコピペして動かしてみてほしい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?