ClaudeにCLAUDE.md内でhooksへ移行した方が良さそうなものを移行してもらう

ClaudeにCLAUDE.md内でhooksへ移行した方が良さそうなものを移行してもらう

CLAUDE.mdに毎回実行を指示するよりhooksに入れた方が確実ではないかと思い、Claudeにhooks化を指示してみました。
2025.08.09

hooksを使った処理は通常出力にTokenが掛からないこともあり、とりあえずhooks化と試しているところですが、数が多いと手間もかかってきます。

Claudeがあることだし、Claudeに任せてみることにしました。

Claudeにhooks化させる

可能だろうかと疑問でしたが、出力完了まで1プロンプトでした。

CLAUDE.md内でhooksに変えたほうが効率的な項目があればピックアップ

実際に生成されたhooksの例

プロジェクト設定に紐づいた場所があるため、コピペでは動かないかもしれません。

#!/bin/bash

# CLAUDE.md更新時にDeepLで日本語訳をCLAUDE_ja.mdに自動生成するフック

# 翻訳実行関数
perform_translation() {
    # DeepLコマンドが存在するかチェック
    if command -v deepl >/dev/null 2>&1; then
        # システムのdeeplコマンドを使用
        if deepl CLAUDE.md -o CLAUDE_ja.md 2>/dev/null; then
            echo "✅ CLAUDE_ja.mdが正常に生成されました"
        else
            echo "❌ DeepL翻訳に失敗しました"
        fi
    elif [ -x "/opt/homebrew/bin/deepl" ]; then
        # Homebrewでインストールされたdeeplを使用
        if /opt/homebrew/bin/deepl CLAUDE.md -o CLAUDE_ja.md 2>/dev/null; then
            echo "✅ CLAUDE_ja.mdが正常に生成されました"
        else
            echo "❌ DeepL翻訳に失敗しました"
        fi
    else
        echo "⚠️ DeepLコマンドが見つかりません。手動でCLAUDE_ja.mdを更新してください。"
        echo "   実行コマンド: deepl CLAUDE.md -o CLAUDE_ja.md"
    fi
}

INPUT=$(cat)

# ツール入力を取得
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
if [ "$TOOL_INPUT" = "null" ] || [ -z "$TOOL_INPUT" ]; then
    exit 0
fi

# ファイルパスを取得(MUST utilize tool_input.file_path)
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path')
if [ -z "$FILE_PATH" ]; then
    exit 0
fi

# CLAUDE.mdファイルが更新された場合のみ処理(CLAUDE_ja.mdは除外)
if [[ "$FILE_PATH" == *"CLAUDE.md" ]]; then

    # プロジェクトルートディレクトリに移動
    PROJECT_DIR=$(dirname "$FILE_PATH")
    cd "$PROJECT_DIR" 2>/dev/null || exit 0

    # CLAUDE.mdとCLAUDE_ja.mdのタイムスタンプを比較(DeepL制限回避)
    if [ -f "CLAUDE_ja.md" ]; then
        # 各ファイルの最終更新時間を取得(秒単位)
        if command -v stat >/dev/null 2>&1; then
            CLAUDE_MD_TIME=$(stat -f %m "CLAUDE.md" 2>/dev/null || stat -c %Y "CLAUDE.md" 2>/dev/null)
            CLAUDE_JA_MD_TIME=$(stat -f %m "CLAUDE_ja.md" 2>/dev/null || stat -c %Y "CLAUDE_ja.md" 2>/dev/null)

            # CLAUDE.mdの方が新しい場合のみ翻訳実行
            if [ "$CLAUDE_MD_TIME" -gt "$CLAUDE_JA_MD_TIME" ]; then
                echo "🌏 CLAUDE.mdの更新を検出。DeepLで日本語訳を生成中..."
                perform_translation
            else
                echo "ℹ️ CLAUDE_ja.mdは既に最新です(翻訳をスキップ)"
            fi
        else
            # statコマンドが利用できない場合は常に翻訳実行
            echo "🌏 CLAUDE.mdの更新を検出。DeepLで日本語訳を生成中..."
            perform_translation
        fi
    else
        # CLAUDE_ja.mdが存在しない場合は翻訳実行
        echo "🌏 CLAUDE_ja.mdが存在しません。DeepLで日本語訳を生成中..."
        perform_translation
    fi
fi

exit 0

実行されると以下の通り。

スクリーンショット 2025-08-09 13.28.38

あとがき

問題としては、EventNameとMatcherに公式リファレンスには存在しないものを独自に指定してくることがあります。

/doctorを実行すると指摘してくれるので、プロンプト実行後はチェックをしておきましょう。

Stopの処理でもstop_hook_active==trueの判定が使われなかったり、特定ファイル編集を対象にするhooksでtook_input.file_pathでファイル判定していないケースがあったり、生成されたスクリプトの確認は必須です。公式リファレンスを参照してなさそうな出力結果だったため、serenaメモリに公式リファレンスを追加してCLAUDE.mdにhooks生成時のルールを参照するように書き入れておくとよいでしょう。

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.