メインコンテンツへスキップ
Hooks を使うと、QoderWork の実行フローにおける重要なポイントで、コードを変更することなくカスタムロジックを挿入できます。JSON 設定ファイルを編集するだけで、たとえば次のようなことを実現できます。
  • ツール実行前に危険な操作をブロック
  • ファイル書き込みのたびに自動で lint を実行し、コードスタイルを統一
  • Agent のタスク完了時にデスクトップ通知を送り、画面に張り付く必要をなくす
Prompt による指示とは異なり、Hooks は確定的に動作します。イベントが発火すればスクリプトは必ず実行され、モデルの解釈に結果が左右されることはありません。

クイックスタート

以下の例では、rm -rf のような危険なコマンドをブロックする方法を紹介します。
1

スクリプトを作成

mkdir -p ~/.qoderwork/hooks
cat > ~/.qoderwork/hooks/block-rm.sh << 'EOF'
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command')

if echo "$command" | grep -q 'rm -rf'; then
  echo "危険なコマンドをブロックしました: $command" >&2
  exit 2
fi

exit 0
EOF
chmod +x ~/.qoderwork/hooks/block-rm.sh
2

設定を追加

~/.qoderwork/settings.json に以下を追加します:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}
3

動作を確認

QoderWork を開き、Agent に rm -rf を含むコマンドを実行させてみてください。Hook が実行をブロックし、エラーメッセージを Agent にフィードバックします。

仕組み

Hook のライフサイクルは 3 ステップにまとめられます:スクリプトを書く → 設定に登録する → 自動的に有効になる Agent がライフサイクルイベント(例:ツール呼び出し前)に到達すると、QoderWork はそのイベントに Hook が登録されているか確認します:
  1. QoderWork は起動時にすべての Hook 設定を読み込む
  2. Agent 実行中にライフサイクルイベント(例:PreToolUse)に到達
  3. QoderWork はそのイベントに登録されたすべての Hook グループを走査し、matcher で現在のコンテキストと照合
  4. マッチした Hook は順番にシェルスクリプトを実行
  5. スクリプトは stdin 経由で JSON イベントコンテキストを受け取り、exit code と stdout で判断結果を返す
  6. QoderWork は結果を読み取り、次のアクション(続行またはブロック)を決定

前提条件

  • jq:サンプルスクリプトは jq を使って JSON を解析します。macOS では brew install jq、Linux では apt install jq でインストールしてください。
  • スクリプト権限:すべての Hook スクリプトに実行権限が必要です(chmod +x)。

Hooks の作成

1. 要件の決定:イベントと Matcher を選ぶ

まず、どこに介入し、何をマッチさせるかを決めます:
[どのタイミングで] [どの操作を] インターセプト/処理したいか?
       ↓                    ↓
   イベントを選ぶ        matcher を書く
要件イベントmatcher
Agent がシェルコマンドを実行する前にチェックPreToolUse"Bash"
Agent がファイルを書き込み・編集した後に処理PostToolUse"Write | Edit"
ツール呼び出し失敗時にログを記録PostToolUseFailure"Bash" または省略
すべてのユーザー Prompt をスクリーニングUserPromptSubmit省略(すべてにマッチ)
Agent 停止時に通知をトリガーStop省略(すべてにマッチ)
MCP ツールのみをインターセプトPreToolUse"mcp__.*"

2. Hook スクリプトを書く

Hook スクリプトは以下のプロトコルに従う標準的なシェルスクリプトです: 入力stdin 経由で JSON イベントコンテキストが渡されます。 出力:exit code で制御します。
exit 0   →  許可(実行を続行)
exit 2   →  ブロック(操作を停止、stderr が会話に注入される)
その他   →  エラー(実行を続行、stderr がユーザーに表示される)
スクリプトテンプレート:
#!/bin/bash

# 1. stdin から JSON 入力を読み取る
input=$(cat)

# 2. jq で必要なフィールドを抽出
tool_name=$(echo "$input" | jq -r '.tool_name')
tool_input=$(echo "$input" | jq -r '.tool_input')

# 3. ロジックを記述
if [ "$tool_name" = "Bash" ]; then
  command=$(echo "$input" | jq -r '.tool_input.command')

  if echo "$command" | grep -qE 'rm\s+-rf|DROP\s+TABLE'; then
    echo "操作が拒否されました: $command" >&2
    exit 2
  fi
fi

# 4. 許可
exit 0
exit 0 時に JSON を stdout に出力して、より細かい制御も可能です:
#!/bin/bash
input=$(cat)

echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"この操作は許可されていません"}}'
exit 0

3. 設定ファイルに登録

~/.qoderwork/settings.json の対応するイベントにスクリプトパスを追加します:
{
  "hooks": {
    "イベント名": [
      {
        "matcher": "マッチ条件(任意)",
        "hooks": [
          {
            "type": "command",
            "command": "スクリプトパス"
          }
        ]
      }
    ]
  }
}

4. テストとデバッグ

ターミナルからパイプで直接テストできます:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"},"hook_event_name":"PreToolUse"}' \
  | ~/.qoderwork/hooks/block-rm.sh
echo "Exit code: $?"
stderr 出力(ブロックメッセージ)を確認:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
  | ~/.qoderwork/hooks/block-rm.sh 2>&1

Hooks の設定

設定ファイルの場所

QoderWork はユーザーレベルの設定ファイルから Hook 設定を読み込みます:
場所スコープ説明
~/.qoderwork/settings.jsonユーザー(グローバル)個人設定、すべての QoderWork セッションに適用
ホットリロードはまだサポートされていません — Hook 設定を変更した後は QoderWork を再起動してください。

設定フォーマット

{
  "hooks": {
    "イベント名": [
      {
        "matcher": "マッチ条件",
        "hooks": [
          {
            "type": "command",
            "command": "実行するコマンド"
          }
        ]
      }
    ]
  }
}
フィールド必須説明
typeはい"command" 固定
commandはい実行するシェルコマンドまたはスクリプトパス
timeoutいいえタイムアウト(秒)、デフォルト 30。カスタム値は将来のリリースでサポート予定
matcherいいえマッチ条件。省略するとそのイベントのすべての発火にマッチ
1 つのイベントに複数の matcher グループを定義でき、各グループに複数の Hook コマンドを含めることができます。

matcher ルール

matcher は Hook の発火範囲を決定します。何にマッチするかはイベントによって異なります(各イベントの説明を参照)。
パターン意味
省略または "*"すべてにマッチすべてのツールで Hook が発火
正確な値完全一致"Bash" は Bash ツールのみで発火
| 区切り複数の値にマッチ"Write | Edit" は Write または Edit で発火
正規表現正規表現マッチ"mcp__.*" はすべての MCP ツールにマッチ

ツール名マッピング

QoderWork はネイティブ名と互換名の 2 セットのツール名をサポートしています。matcher にはどちらでも使用でき、QoderWork が内部でマッピングします。
ネイティブ名互換名説明
run_in_terminalBashシェルコマンドの実行
read_fileReadファイル内容の読み取り
create_fileWriteファイルの作成・書き込み
search_replaceEditファイルの編集
delete_file-ファイルの削除
grep_codeGrepファイル内容の検索
search_fileGlobファイル名マッチ
list_dirLSディレクトリの一覧
taskTaskサブタスク・サブエージェントの起動
Skill-スキルの呼び出し
search_webWebSearchWeb 検索
fetch_contentWebFetchWeb ページコンテンツの取得
todo_writeTodoWriteTODO の書き込み
ask_user_question-ユーザーへの質問
search_memory-メモリの検索
update_memory-メモリの更新
mcp__<server>__<tool>同左MCP ツール

Hook スクリプトの作成

Hook スクリプトは stdin 経由で JSON 入力を受け取り、exit code と stdout で動作を制御します。このセクションではすべてのイベントに共通の入出力フォーマットを説明します。イベント固有のフィールドについては Hook イベントを参照してください。
QoderWork は現在、Hook スクリプトに環境変数を注入しません。すべてのデータは stdin JSON 経由で渡されます。セッション ID、作業ディレクトリ、ツール情報が必要な場合は、stdin JSON 入力から解析してください。

入力

Hook スクリプトは stdin 経由で JSON データを受け取ります。すべてのイベントに以下の共通フィールドが含まれます:
フィールド説明
session_id現在のセッション ID
transcript_pathセッション Transcript JSONL ファイルのパス(例:~/.qoderwork/projects/<エンコードパス>/<session-id>.jsonl
cwd現在の作業ディレクトリ
hook_event_nameこの Hook をトリガーしたイベント名
各イベントはこれらに加えて固有のフィールドを追加します(各 Hook イベントの説明を参照)。 jq で入力を解析:
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')

出力

Hook は exit code と stdout で動作を制御します。 exit 0 は成功を示します。QoderWork は stdout を解析します。一部のイベント(UserPromptSubmit、SessionStart など)はプレーンテキストによるコンテキスト注入または JSON による細かい制御をサポートしています。詳細は各イベントの説明を参照してください。 exit 2 はブロックエラーを示し、ブロック可能なイベントでのみ有効です。stdout は無視され、stderr がエラーメッセージとして Agent にフィードバックされます。具体的な効果はイベントによって異なります:PreToolUse はツール実行をブロック、UserPromptSubmit は Prompt を拒否、Stop は Agent の停止をブロック、など。 その他の exit code は非ブロックエラーとして扱われ、実行フローに影響しません。stderr はログにのみ記録されます。

Hook イベント

UserPromptSubmit

ユーザーが Prompt を送信した後、Agent が処理を開始する前に発火します。Prompt スクリーニング、コンテンツフィルタリング、コンテキストの自動注入に使用できます。 matcher: なし。このイベントはすべてのユーザー入力に対して発火します。 追加入力フィールド:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "ダウンロードフォルダをファイルタイプ別に整理して"
}
Prompt のブロック: exit code 2 で、stderr の内容がエラーとしてユーザーに表示され、Agent はその Prompt を処理しません。 stdout JSON 出力フィールド(exit 0 時):
フィールド説明
hookSpecificOutput.hookEventNamestring"UserPromptSubmit" 固定
hookSpecificOutput.additionalContextstringAgent の会話に注入される追加コンテキスト

PreToolUse

ツール実行前に発火します。ツール実行をブロックできます。 危険なコマンドのブロック、ファイルパスの検証、権限チェックに最適です。 matcher: ツール名(例:BashWriteEditRead、または mcp__server__tool のような MCP ツール名) 追加入力フィールド:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "rm -rf /tmp/build" }
}
ツール実行のブロック: exit code 2 で、stderr の内容がエラーとして Agent に返されます。 stdout JSON 出力フィールド(exit 0 時):
フィールド説明
hookSpecificOutput.hookEventNamestring"PreToolUse" 固定
hookSpecificOutput.permissionDecisionstring"allow""deny"、または "ask"
hookSpecificOutput.permissionDecisionReasonstring判断理由
hookSpecificOutput.updatedInputobject変更後のツール入力パラメータ(任意)
hookSpecificOutput.additionalContextstring追加コンテキスト(任意)

PostToolUse

ツール実行の成功後に発火します。ブロック不可。自動 lint、ログ記録、結果分析に使用します。 matcher: ツール名 追加入力フィールド:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": { "file_path": "/path/to/file.ts", "content": "..." },
  "tool_response": "File written successfully"
}
stdout JSON 出力フィールド(exit 0 時):
フィールド説明
hookSpecificOutput.hookEventNamestring"PostToolUse" 固定
hookSpecificOutput.feedbackstringユーザーに表示されるフィードバック

PostToolUseFailure

ツール呼び出しが失敗した後に発火します。ブロック不可。エラー監視、リトライ提案、ログ記録に使用します。 matcher: ツール名 追加入力フィールド:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" },
  "error": "Command exited with non-zero status code 1"
}
フィールド説明
errorstring失敗したツール実行のエラーメッセージ

Stop

Agent がレスポンスを完了した後に発火します。Agent の停止をブロックできます。 品質ゲート、デスクトップ通知、ログ記録に使用します。 matcher: なし。Agent が停止するたびにこのイベントが発火します。 追加入力フィールド:
{
  "session_id": "abc-123",
  "cwd": "/Users/you",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}
フィールド説明
stop_hook_activeboolean前回の Stop Hook ブロック後に Agent がリトライしている場合に trueスクリプトはこのフィールドを確認し、true の場合は exit 0 して無限ループを防止する必要があります。
Agent の停止をブロック: exit code 2 で、ブロック理由がユーザーメッセージとして会話に注入され、Agent は作業を続行します。 stdout JSON 出力フィールド(exit 0 時):
{
  "decision": "block",
  "reason": "Tests failing. Fix them before completing."
}
フィールド説明
decisionstring"block"(停止をブロックし、Agent に作業を続行させる)
reasonstringブロックの理由。会話にメッセージとして注入される
無限ループの防止: Stop Hook が Agent をブロック(exit 2)すると、Agent はリトライし、Stop イベントが stop_hook_active: true で再度発火します。スクリプトは必ずこのフィールドを確認し、true の場合は exit 0 してください。そうしないと Hook が無限にブロックし続けます。

SessionStart

セッション開始時に発火します。 matcher: セッションソース
matcher 値発火シナリオ
startup新しいセッションの開始
resume既存セッションの再開
compactコンテキスト圧縮完了後
追加入力フィールド:
{
  "source": "startup",
  "agent_type": "coder",
  "model": ""
}

SessionEnd

セッション終了時に発火します。 matcher: 終了理由
matcher 値発火シナリオ
prompt_input_exitユーザーが入力を終了(Ctrl+D 等)
otherその他の理由
追加入力フィールド:
{
  "reason": "prompt_input_exit"
}

SubagentStart / SubagentStop

サブ Agent の起動・完了時に発火します。SubagentStop は Stop と同様、サブ Agent の停止をブロックできます。 matcher: Agent タイプ名 SubagentStart 追加入力フィールド:
{
  "agent_id": "83f488ef",
  "agent_type": "general-purpose"
}
SubagentStop 追加入力フィールド:
{
  "agent_id": "83f488ef",
  "agent_type": "general-purpose",
  "agent_transcript_path": "",
  "stop_hook_active": false
}
フィールド説明
agent_idstringサブ Agent ID
agent_typestringサブ Agent タイプ(例:"general-purpose"
agent_transcript_pathstringサブ Agent の Transcript ファイルのパス(SubagentStop のみ)
stop_hook_activeboolean前回の SubagentStop ブロック後のリトライかどうか(SubagentStop のみ)

PreCompact

コンテキスト圧縮前に発火します。 matcher: トリガー方法
matcher 値発火シナリオ
manualユーザーが手動で /compact を実行
autoコンテキストウィンドウが満杯になった時に自動トリガー
追加入力フィールド:
{
  "trigger": "manual",
  "custom_instructions": "すべてのツール呼び出し結果を保持"
}

Notification

通知イベント発火時(権限リクエスト、タスク完了等)。 matcher: 通知タイプ
matcher 値発火シナリオ
permission権限リクエスト通知
resultAgent の結果通知
追加入力フィールド:
{
  "message": "Agent is requesting permission to run: rm -rf node_modules",
  "title": "Permission Required",
  "notification_type": "permission"
}

PermissionRequest

ツール実行にユーザー認可が必要な時に発火します。 matcher: ツール名 追加入力フィールド:
{
  "tool_name": "Bash",
  "tool_input": {"command": "rm -rf node_modules"}
}

シナリオ例

危険なコマンドのブロック

スクリプト ~/.qoderwork/hooks/block-dangerous.sh
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command')

if echo "$command" | grep -qE 'rm\s+-rf|DROP\s+TABLE|mkfs|dd\s+if='; then
  echo "危険なコマンドをブロックしました: $command" >&2
  exit 2
fi

exit 0
設定:イベント PreToolUse、matcher Bash、command ~/.qoderwork/hooks/block-dangerous.sh

ファイル書き込み後の自動 Lint

Agent がファイルを書き込み・編集するたびに、自動で lint を実行します。 スクリプト ~/.qoderwork/hooks/auto-lint.sh
#!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')

# Only lint JS/TS files
case "$file_path" in
  *.js|*.ts|*.jsx|*.tsx)
    npx eslint "$file_path" --fix 2>/dev/null
    ;;
esac

exit 0
設定:イベント PostToolUse、matcher Write|Edit、command ~/.qoderwork/hooks/auto-lint.sh

デスクトップ通知

Agent の結果や権限リクエスト時にデスクトップ通知を表示します。 スクリプト ~/.qoderwork/hooks/notify.sh(macOS):
#!/bin/bash
input=$(cat)
message=$(echo "$input" | jq -r '.message // "タスク完了"' | head -c 100)
title=$(echo "$input" | jq -r '.title // "QoderWork"')

osascript -e "display notification \"$message\" with title \"$title\""

exit 0
設定:イベント Notification、matcher なし、command ~/.qoderwork/hooks/notify.sh

Prompt コンテンツスクリーニング

スクリプト ~/.qoderwork/hooks/check-prompt.sh
#!/bin/bash
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt')

if echo "$prompt" | grep -qiE '(password|secret|api_key|token)\s*[:=]\s*\S+'; then
  echo "Prompt に機密情報が含まれている可能性があります。確認して再送信してください。" >&2
  exit 2
fi

exit 0
設定:イベント UserPromptSubmit、matcher なし、command ~/.qoderwork/hooks/check-prompt.sh

ツール失敗時のログ記録

スクリプト ~/.qoderwork/hooks/log-failure.sh
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')
error=$(echo "$input" | jq -r '.error')
timestamp=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$timestamp] $tool_name 失敗: $error" >> ~/.qoderwork/hooks/failure.log

exit 0
設定:イベント PostToolUseFailure、matcher なし、command ~/.qoderwork/hooks/log-failure.sh

Agent を継続させる

Agent が停止する際に未完了タスクがないかチェックし、コミットされていない git 変更がある場合は Agent の停止をブロックします。 スクリプト ~/.qoderwork/hooks/check-continue.sh
#!/bin/bash
# 未コミットの git 変更をチェック
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
  echo "未コミットの変更を検出しました。git commit を完了してください" >&2
  exit 2
fi

exit 0
設定:イベント Stop、command ~/.qoderwork/hooks/check-continue.sh

Prompt 送信時にコンテキストを注入

ユーザーの各プロンプト送信前に、現在の git ブランチ情報を Agent コンテキストとして自動注入します。 スクリプト ~/.qoderwork/hooks/inject-branch.sh
#!/bin/bash
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [ -n "$branch" ]; then
  echo "現在の git ブランチ: $branch"
fi
exit 0
設定:イベント UserPromptSubmit、command ~/.qoderwork/hooks/inject-branch.sh

機密情報を含む Prompt のブロック

パスワードやシークレットキーなどの機密キーワードを含む Prompt を検出してブロックします。 スクリプト ~/.qoderwork/hooks/block-sensitive.sh
#!/bin/bash
input=$(cat)
prompt=$(echo "$input" | jq -r '.prompt')

if echo "$prompt" | grep -qi 'password\|secret\|credential'; then
  echo "Prompt に機密情報が含まれているため、ブロックされました" >&2
  exit 2
fi

exit 0
設定:イベント UserPromptSubmit、command ~/.qoderwork/hooks/block-sensitive.sh

完全な設定例

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/check-prompt.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/block-dangerous.sh"
          }
        ]
      }
    ],
    "PostToolUseFailure": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/log-failure.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoderwork/hooks/notify-done.sh"
          }
        ]
      }
    ]
  }
}

注意事項

  • タイムアウト処理: Hook スクリプトのデフォルトタイムアウトは 30 秒です。タイムアウト後、スクリプトは終了され、許可(続行)として扱われます。カスタムタイムアウトは将来のリリースでサポート予定です。
  • エラー処理: スクリプトが予期しないコード(0 と 2 以外)で終了した場合、エラーメッセージはユーザーに表示されますが、Agent は中断されずに続行します。
  • スクリプト権限: スクリプトに実行権限があることを確認してください(chmod +x)。
  • jq 依存: サンプルスクリプトは JSON 解析に jq を使用しています。システムにインストールされていることを確認してください(macOS: brew install jq、Linux: apt install jq)。
  • 再起動が必要: ~/.qoderwork/settings.json を変更した後、QoderWork を再起動して変更を適用してください。