Initial commit
This commit is contained in:
117
hooks/README.md
Normal file
117
hooks/README.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Hooks - Claude Codeフック集
|
||||
|
||||
Claude Codeのセッションやツール実行時に自動的に実行されるフックスクリプト集です。
|
||||
|
||||
## 利用可能なフック
|
||||
|
||||
### start-session.js
|
||||
**イベント**: SessionStart
|
||||
**目的**: セッション開始時に動作モードの設定を促す
|
||||
|
||||
セッション開始時にmode_listの実行と適切なモードの有効化を促します。
|
||||
セッションのソースタイプ(startup/resume/clear/compact)に応じて適切な指示を提供します。
|
||||
これにより、コンテキストリセット後も一貫した作業スタイルを維持できます。
|
||||
|
||||
### post-tool-use.sh
|
||||
**イベント**: PostToolUse (Bashツール限定)
|
||||
**目的**: cdコマンド実行後の現在ディレクトリをコンテキストに追加
|
||||
|
||||
Bashツールで`cd`コマンドが実行された際に、移動後の現在ディレクトリを`[cwd: /path/to/dir]`形式で
|
||||
コンテキストに追加します。これにより、ディレクトリ移動の追跡が容易になります。
|
||||
|
||||
## フックの仕組み
|
||||
|
||||
### 実行フロー
|
||||
1. Claude Codeが特定のイベント(SessionStart、PostToolUse等)を検出
|
||||
2. `~/.claude/settings.json`のmatcherパターンに一致するか確認
|
||||
3. 一致した場合、指定されたコマンドを実行
|
||||
4. フックスクリプトがJSON形式で追加コンテキストを出力
|
||||
5. Claude Codeがコンテキストを会話に追加
|
||||
|
||||
### JSON出力形式
|
||||
```json
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "EventName",
|
||||
"additionalContext": "追加するテキスト"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 新規フックの作成方法
|
||||
|
||||
### 1. スクリプトの作成
|
||||
```bash
|
||||
cat > hooks/my-hook.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
echo '{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "MyEvent",
|
||||
"additionalContext": "カスタムメッセージ"
|
||||
}
|
||||
}'
|
||||
EOF
|
||||
```
|
||||
|
||||
### 2. 実行権限の付与
|
||||
```bash
|
||||
chmod +x hooks/my-hook.sh
|
||||
```
|
||||
|
||||
### 3. settings.jsonへの登録
|
||||
```json
|
||||
"EventName": [
|
||||
{
|
||||
"matcher": "パターン",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "~/Develop/otolab/ai-agent-prompts/hooks/my-hook.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## 環境変数
|
||||
|
||||
フックスクリプト内で利用可能な環境変数(イベントによって異なる):
|
||||
|
||||
### PostToolUse
|
||||
- `CLAUDE_CODE_TOOL_NAME` - 実行されたツール名(例: "Bash", "Read", "Write")
|
||||
- `CLAUDE_CODE_TOOL_PARAMS` - ツールパラメータ(JSON文字列)
|
||||
- `CLAUDE_CODE_TOOL_RESULT` - ツール実行結果(利用可能な場合)
|
||||
|
||||
## デバッグ方法
|
||||
|
||||
### ログファイルへの出力
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# デバッグ情報をログファイルに記録
|
||||
echo "$(date): Hook executed" >> /tmp/hook-debug.log
|
||||
echo "Tool: $CLAUDE_CODE_TOOL_NAME" >> /tmp/hook-debug.log
|
||||
```
|
||||
|
||||
### 環境変数の確認
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# すべての環境変数を記録
|
||||
env > /tmp/hook-env.txt
|
||||
```
|
||||
|
||||
## ベストプラクティス
|
||||
|
||||
1. **軽量に保つ**: フックの実行時間を最小限に
|
||||
2. **エラーハンドリング**: 失敗してもセッションを妨げない
|
||||
3. **条件分岐**: 必要な場合のみ出力を生成
|
||||
4. **ログ記録**: デバッグ用のログは別ファイルに
|
||||
|
||||
## 注意事項
|
||||
|
||||
- フックスクリプトの出力はJSON形式である必要があります
|
||||
- 不正なJSON出力はエラーになる可能性があります
|
||||
- matcherパターンは正確に設定してください
|
||||
- 設定変更後はClaude Codeの再起動が必要です
|
||||
|
||||
---
|
||||
**作成**: 2025年10月6日
|
||||
15
hooks/hooks.json
Normal file
15
hooks/hooks.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"matcher": "startup|compact|resume|clear",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/start-session.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
69
hooks/post-tool-use.sh
Executable file
69
hooks/post-tool-use.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PostToolUse hook for Bash tool
|
||||
# 1. Adds current working directory to context when cd command is executed
|
||||
# 2. Warns about git add -A/. without git status in the same command
|
||||
#
|
||||
# NOTE: このhookは一時的に無効化されています (2025-10-22)
|
||||
# 理由: Bashツール実行時にエラーが発生してツールが異常終了する問題が確認された
|
||||
# 問題が解決次第、hooks/hooks.jsonで再度有効化する予定
|
||||
|
||||
# Function to output JSON response
|
||||
output_json() {
|
||||
local additional_context="$1"
|
||||
if [ -n "$additional_context" ]; then
|
||||
cat <<EOF
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PostToolUse",
|
||||
"additionalContext": "$additional_context"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if this is a Bash tool call
|
||||
if [ "$CLAUDE_CODE_TOOL_NAME" == "Bash" ]; then
|
||||
# Extract the command from JSON params
|
||||
COMMAND=$(echo "$CLAUDE_CODE_TOOL_PARAMS" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"command"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
||||
|
||||
# Collect all messages to output
|
||||
MESSAGES=""
|
||||
|
||||
# Check if cd command was executed
|
||||
if echo "$COMMAND" | grep -q "cd[[:space:]]"; then
|
||||
# Get current working directory
|
||||
CWD=$(pwd)
|
||||
# Add to messages
|
||||
if [ -z "$MESSAGES" ]; then
|
||||
MESSAGES="[cwd: $CWD]"
|
||||
else
|
||||
MESSAGES="${MESSAGES}
|
||||
[cwd: $CWD]"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if git add -A or git add . was executed
|
||||
if echo "$COMMAND" | grep -q "git[[:space:]]\+add[[:space:]]\+\(-A\|\.\)" ; then
|
||||
# Check if git status is also in the same command (e.g., git add -A && git status)
|
||||
if ! echo "$COMMAND" | grep -q "git[[:space:]]\+status"; then
|
||||
# Count modified files
|
||||
if git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
MODIFIED_COUNT=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
|
||||
WARNING="[git] ⚠️ ${MODIFIED_COUNT}個のファイルが対象 (git add -A/.) - git statusで内容を確認してください"
|
||||
if [ -z "$MESSAGES" ]; then
|
||||
MESSAGES="$WARNING"
|
||||
else
|
||||
MESSAGES="${MESSAGES}
|
||||
${WARNING}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Output combined messages
|
||||
if [ -n "$MESSAGES" ]; then
|
||||
output_json "$MESSAGES"
|
||||
fi
|
||||
fi
|
||||
24
hooks/pre-compact.sh
Executable file
24
hooks/pre-compact.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
# コンテキスト圧縮前のモード状態復元
|
||||
|
||||
MESSAGE="⚠️ コンテキスト圧縮前の処理\n\n"
|
||||
MESSAGE="${MESSAGE}以下の手順を実行してください:\n"
|
||||
MESSAGE="${MESSAGE}1. コンテキストから最新のアクティブなモードリストを検索\n"
|
||||
MESSAGE="${MESSAGE}2. mode_setツールで状態を保存(MCPオンメモリに保存)\n\n"
|
||||
MESSAGE="${MESSAGE}※ この処理により、compact後もMCPサーバーのモード状態が保持されます\n\n"
|
||||
|
||||
MESSAGE="${MESSAGE}⚠️ コンテキスト圧縮完了後の処理\n\n"
|
||||
MESSAGE="${MESSAGE}以下の手順をTODOの最初に追加してください:\n"
|
||||
MESSAGE="${MESSAGE}1. **mode_show()** を実行してモード文書を再読み込み\n\n"
|
||||
MESSAGE="${MESSAGE}※ 保存されたモードの状態を復元します。"
|
||||
|
||||
# Use jq to properly encode JSON with multi-line strings
|
||||
jq -n \
|
||||
--arg message "$MESSAGE" \
|
||||
'{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PreCompact",
|
||||
additionalContext: $message
|
||||
}
|
||||
}'
|
||||
113
hooks/start-session.js
Executable file
113
hooks/start-session.js
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 標準入力からJSONを読み取る
|
||||
let inputData = '';
|
||||
|
||||
process.stdin.on('data', (chunk) => {
|
||||
inputData += chunk;
|
||||
});
|
||||
|
||||
process.stdin.on('end', () => {
|
||||
try {
|
||||
const hookData = JSON.parse(inputData);
|
||||
const source = hookData.source || 'unknown';
|
||||
|
||||
// sourceに応じたメッセージを構築
|
||||
const lines = [
|
||||
'═══════════════════════════════════════════'
|
||||
];
|
||||
|
||||
switch (source) {
|
||||
case 'startup':
|
||||
lines.push([
|
||||
'📋 **Session Setup - 新規セッション開始**',
|
||||
'',
|
||||
'作業環境をセットアップします。',
|
||||
'TodoWriteツールで以下のタスクをリストの最初に追加してください:',
|
||||
'',
|
||||
'1. mode_list()を実行して利用可能なモードを確認する',
|
||||
'2. 適切なモードを有効化する',
|
||||
'',
|
||||
'これらは作業開始前の準備ステップです。',
|
||||
].join('\n'));
|
||||
break;
|
||||
|
||||
case 'resume':
|
||||
lines.push([
|
||||
'📋 **Session Setup - セッション再開**',
|
||||
'',
|
||||
'中断していたセッションを再開します。',
|
||||
'TodoWriteツールで以下のタスクをリストの最初に追加してください:',
|
||||
'',
|
||||
'1. コンテキストから最新のアクティブモードリストを検索する',
|
||||
'2. mode_set()を実行してモードをMCPメモリに復元する',
|
||||
'',
|
||||
'これらは作業再開前の準備ステップです。',
|
||||
].join('\n'));
|
||||
break;
|
||||
|
||||
case 'clear':
|
||||
lines.push([
|
||||
'📋 **Session Setup - モードクリア後**',
|
||||
'',
|
||||
'アシスタント動作モードがクリアされました。',
|
||||
'TodoWriteツールで以下のタスクをリストの最初に追加してください:',
|
||||
'',
|
||||
'1. mode_show()を実行してモードドキュメントを再読み込みする',
|
||||
'',
|
||||
'これは作業継続前の準備ステップです。',
|
||||
].join('\n'));
|
||||
break;
|
||||
|
||||
case 'compact':
|
||||
lines.push([
|
||||
'📋 **Session Setup - コンテキスト圧縮後の再開**',
|
||||
'',
|
||||
'コンテキストが圧縮されました。モード状態を復元します。',
|
||||
'TodoWriteツールで以下のタスクをリストの最初に追加してください:',
|
||||
'',
|
||||
'1. mode_show()を実行してモード状態を復元する',
|
||||
'',
|
||||
'これは作業継続前の準備ステップです。',
|
||||
'',
|
||||
'💡 ヒント: mode_show()は「サマリが存在する場合でも原文を読み直す」指示を含んでいます。',
|
||||
].join('\n'));
|
||||
break;
|
||||
|
||||
default:
|
||||
lines.push([
|
||||
'📋 **Session Setup**',
|
||||
'',
|
||||
`セッションが開始されました (source: ${source})。`,
|
||||
'TodoWriteツールで以下のタスクをリストの最初に追加してください:',
|
||||
'',
|
||||
'1. mode_list()を実行する',
|
||||
].join('\n'));
|
||||
break;
|
||||
}
|
||||
|
||||
// .serenaディレクトリのチェック
|
||||
const serenaPath = path.join(process.cwd(), '.serena');
|
||||
if (fs.existsSync(serenaPath) && fs.statSync(serenaPath).isDirectory()) {
|
||||
lines.push('* Serenaのアクティベートを行ってください');
|
||||
}
|
||||
|
||||
lines.push('═══════════════════════════════════════════');
|
||||
|
||||
// JSON出力
|
||||
const output = {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'SessionStart',
|
||||
additionalContext: lines.join('\n')
|
||||
}
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(output, null, 2));
|
||||
} catch (error) {
|
||||
console.error('Error processing hook input:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user