Skip to main content
Qoder CLI の拡張

フック

Hooks は Qoder CLI の主要な実行フローの要所に介入しつつ、CLI 本体とは疎結合を保つための仕組みです。代表的な用途には、ツール実行前の危険なコマンドの遮断、タスク完了時のデスクトップ通知、ファイル書き込み後の自動 lint などがあります。 Hooks は JSON 設定ファイルで定義します。コードを変更する必要はなく、設定ファイルを編集すればすぐに有効になります。

クイックスタート

以下は、Agent が rm -rf を実行しようとしたときに自動でブロックする Hook の例です。 手順 1: スクリプトを作成する
mkdir -p ~/.qoder/hooks
cat > ~/.qoder/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 ~/.qoder/hooks/block-rm.sh
手順 2: 設定ファイルを編集する ~/.qoder/settings.json に以下を追加します:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoder/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}
手順 3: 動作確認 Qoder CLI を起動し、Agent に rm -rf を含むコマンドの実行を依頼します。Hook が実行をブロックし、Agent に通知します。

設定

設定ファイルの場所

Hook 設定は以下の 3 ファイルから読み込まれ、3 つのソースは読み込まれてマージ実行されます (同じイベントの hook が互いに上書きすることはありません):
~/.qoder/settings.json                    # ユーザーレベル、すべてのプロジェクトに適用
${project}/.qoder/settings.json           # プロジェクトレベル、現在のプロジェクトに適用、git にコミットしてチームと共有可能
${project}/.qoder/settings.local.json     # プロジェクトレベル(ローカル)、.gitignore への追加を推奨

設定フォーマット

{
  "hooks": {
    "EventName": [
      {
        "matcher": "マッチ条件",
        "hooks": [
          {
            "type": "command",
            "command": "実行するコマンド",
            "timeout": 600
          }
        ]
      }
    ]
  }
}
1 つのイベントに対して複数の matcher グループを設定でき、各グループに複数の hook エントリを含められます。 グループ (HookDefinition) のフィールド:
フィールド必須説明
matcherいいえマッチ条件、省略時はすべてにマッチ
hooksはいこのグループの hook エントリ配列
asyncいいえtrue のとき、グループ内のすべての hook がバックグラウンドで実行され現在の操作をブロックしません。結果は次のモデルターンで追加コンテキストとして注入されます

Hook エントリの種類

各 hook エントリは type で種類を宣言します。種類ごとに専用フィールドがあります。

command (シェルコマンドを実行)

{
  "type": "command",
  "command": "~/.qoder/hooks/check.sh",
  "timeout": 600,
  "shell": "bash",
  "env": { "FOO": "bar" }
}
フィールド必須説明
commandはい実行するシェルコマンド
timeoutいいえタイムアウト秒数、デフォルト 600
shellいいえ"bash" または "powershell"、未指定時はシステムデフォルト
envいいえ追加の環境変数。システム環境にマージされる
ifいいえ条件フィルタ。"ToolName" または "ToolName(arg_pattern)" 形式。ツール名/引数が一致するときのみ発火
asyncいいえtrue のとき、この単一 hook をバックグラウンド実行。グループ単位の async を上書き
asyncRewakeいいえtrue のときバックグラウンド実行。exit 2 で終了した場合、stderr/stdout/error の内容からシステムリマインダーを生成してモデルを起こします。長時間チェックに有用
rewakeMessageいいえasyncRewake と併用。注入メッセージのプレフィックスを上書き
rewakeSummaryいいえasyncRewake と併用。1 行サマリーを上書き(最大 300 文字)
onceいいえtrue のとき、初回成功実行後にレジストリから削除。セッションスコープの hook のみ有効
statusMessageいいえスピナー/ステータス行の表示文を上書き
argsいいえ任意の argv 配列。設定するとフックは exec 形式(シェルを介さない)で実行されます。詳細は下の Exec form vs Shell form を参照。
command でのプレースホルダの参照
${QODER_PROJECT_DIR}${QODER_PLUGIN_ROOT} などのプレースホルダは、hook サブプロセスに環境変数として注入されます(環境変数参照)。bash ではコマンドテンプレートは CLI 側で事前置換されず、シェルが実行時に展開します。推奨される書き方:
  • プレースホルダをダブルクォートで囲む(推奨):"${QODER_PLUGIN_ROOT}"/scripts/hook.sh。空白や shell メタ文字('$、バッククォートなど)を含むパスでも単一トークンとして正しく解析されます。
  • シェル変数構文"$QODER_PLUGIN_ROOT/scripts/hook.sh"。ダブルクォート内であれば上と等価です。
  • クォートなし(非推奨):${QODER_PLUGIN_ROOT}/scripts/hook.sh。POSIX シェルでは未クォートのパラメータ展開でもフィールド分割とパス名展開が行われるため、空白を含むパスは分割され、* を含むパスは glob 展開されます。
プラグイン以外の hook で ${QODER_PLUGIN_ROOT}${QODER_PLUGIN_DATA} が使われたことを検知するプリフライトは ${...} 形式のみを対象とします(リテラル $VAR の文字列に対する誤検出を避けるため)。プリフライトの保護を受けたいプラグイン作者は ${...} 形式を推奨します。
powershell では ${QODER_PROJECT_DIR}${QODER_PLUGIN_ROOT}${QODER_PLUGIN_DATA} が CLI 側でコマンドテンプレートに置換されてから実行されます(PowerShell は環境変数アクセスに ${NAME} ではなく $env:NAME を用いるため)。
Exec form vs Shell form
コマンド hook は 2 つの実行形式をサポートします:
  • Shell 形式(デフォルト):command はシェルコード片。CLI が bash -c "<command>"(または PowerShell)を実行します。パイプ、リダイレクト、glob 展開、${VAR} 環境変数展開がすべて使えます。
  • Exec 形式args を指定したとき):command は単一の実行ファイルのパス/名前、args の各要素はリテラルな argv 1 要素です。CLI はシェルを介さず直接バイナリを起動します — 引用処理、単語分割、glob 展開は一切行いません。args を指定すると shell フィールドは無視されます。
{
  "type": "command",
  "command": "/usr/bin/python3",
  "args": ["${QODER_PLUGIN_ROOT}/scripts/check.py", "--strict"]
}
選び方:
  • 基本は shell 形式 — プレースホルダをダブルクォートで囲めば("${QODER_PLUGIN_ROOT}"/scripts/check.sh)、空白・シングルクォート・$・バッククォートを含むパスも env 透し出しで安全に扱えます。
  • shell 形式を使う場面: パイプ(grep | tee)、リダイレクト(>)、glob(*.json)など、シェル機能が必要なとき。
  • exec 形式を使う場面: パスや引数に複雑な引用が必要なシェルメタ文字が含まれるとき、またはシェル解釈を完全に避けたいとき。
Windows 上での exec 形式の注意点:
  • .bat / .cmd スクリプトは直接 exec できません。{"command": "cmd.exe", "args": ["/c", "script.bat"]} のように書いてください。
  • MSYS / Cygwin プログラムの argv セマンティクスは各プログラムが定義します。内部で必要となる引数の引用は対象プログラムのドキュメントを確認してください。
Shell コマンドプレフィックスが有効な場合、bash 互換 shell を使う shell 形式の Hook だけが、設定した実行可能な wrapper 経由で実行されます。exec 形式の Hook と PowerShell Hook は直接起動され、このプレフィックスは適用されません。

http (HTTP リクエスト送信)

hook の入力を JSON として指定 URL に POST し、レスポンスとして JSON HookOutput を期待します。
{
  "type": "http",
  "url": "https://example.com/hook",
  "headers": { "Authorization": "Bearer ${MY_TOKEN}" },
  "timeout": 600
}
フィールド必須説明
urlはいPOST 先 URL
headersいいえカスタムリクエストヘッダ。値は ${ENV_VAR} 補間をサポート
allowedEnvVarsいいえheaders で補間可能な環境変数のホワイトリスト。未指定時はすべて許可
timeoutいいえタイムアウト秒数、デフォルト 600
if / once / statusMessageいいえcommand と同じ

prompt (1 ターンの LLM 呼び出し)

独立した 1 ターンのモデル呼び出しで hook イベントを評価します。モデルは { ok, reason } を返します: ok=true で許可、ok=false でブロック、ブロック時は reason が Agent に表示されます。
{
  "type": "prompt",
  "prompt": "コマンドが安全か判断し、安全でない場合は ok=false と reason を返してください",
  "model": "haiku",
  "timeout": 30
}
フィールド必須説明
promptはい評価モデルに送るプロンプトテンプレート。直列化されたイベント JSON が末尾に付与される
modelいいえモデル上書き。未指定時はセッションのデフォルトモデル
timeoutいいえタイムアウト秒数、デフォルト 30
if / once / statusMessageいいえcommand と同じ
独立した評価。 評価モデルは独立したセッションで実行され、入力はあなたの prompt と現在のイベントのみです。メイン会話の以前のツール呼び出し、モデル出力、それまでの状態は参照できません。判定条件は現在のイベント自体から決定できるものに限定してください。会話履歴に依存するルールはここでは信頼できる評価ができないため、独自に状態を保持する command hook か、ファイルシステムを調査できる agent hook を使用してください。

agent (子 Agent による検証)

子 Agent を起動して条件を検証します。子 Agent は StructuredOutput ツールを呼び出し { ok: boolean, reason?: string } を返す必要があります。ok=true で許可、ok=false でブロック。
{
  "type": "agent",
  "prompt": "次の変更をレビューしてください: $ARGUMENTS",
  "tools": ["Read", "Grep"],
  "maxTurns": 50,
  "timeout": 60
}
フィールド必須説明
promptはい検証プロンプト。$ARGUMENTS プレースホルダ(hook 入力 JSON に置換)をサポート
toolsいいえ子 Agent が使えるツールのホワイトリスト。未指定時は利用可能なツールをすべて継承しますが、hook 内で安全でないツール (再帰的な Agent 呼び出し、計画モード、対話的な質問など) は自動的に除外されます
maxTurnsいいえ最大エージェントターン数、デフォルト 50
modelいいえモデル上書き
timeoutいいえタイムアウト秒数、デフォルト 60
if / once / statusMessageいいえcommand と同じ
独立した評価。 prompt と同様に子 Agent は独立したセッションで実行され、メインの会話履歴は参照できません。違いはツールアクセスで、ファイルの読み取り・grep・各種チェックの実行が可能なため、実際の状態を調査する必要がある検証に適しています。

matcher と if のマッチング規則

matcher (グループレベル) は hook の発火範囲を絞り込みます。比較対象のフィールドはイベントごとに異なり (各イベントの説明を参照)、ツール名・トリガー・ソースなどです:
書き方意味
省略または "*"すべてにマッチすべてのツールで発火
完全一致完全一致"Bash" は Bash ツールのみ
| 区切り複数値マッチ"Write|Edit" は Write または Edit
正規表現正規表現マッチ"mcp__.*" はすべての MCP ツール
if (エントリレベル) はより細かいフィルタで、形式は "ToolName" または "ToolName(arg_pattern)" です:
  • ツール名部分は matcher と同じツール名マッチ規則を再利用します (正規表現や | も使えます)。
  • 括弧内の arg_patternglob パターン(正規表現ではありません)で、ツールの主要引数(Bash の command、ファイル系ツールの file_path など)に対して照合されます。
例:
if の値意味
"Bash"ツールが Bash のとき発火
"Bash(git *)"ツールが Bash かつコマンドが git で始まるとき発火
"Edit(*.ts)"ツールが Edit かつ file_path*.ts にマッチするとき発火

Hook スクリプトの書き方

Hook スクリプトは stdin で JSON 入力を受け取り、exit code と stdout で動作を制御します。本節ではすべてのイベント共通の入出力フォーマットを示します。イベント固有のフィールドはイベントリファレンスを参照してください。

入力

Hook スクリプトは stdin から JSON データを受け取ります。すべてのイベントには次の共通フィールドが含まれます:
フィールド説明
session_id現在のセッション ID
transcript_path現在の transcript ファイルパス
cwd現在の作業ディレクトリ
hook_event_name発火したイベント名
permission_mode現在の権限モード(イベントが提供する場合)
agent_id現在の Agent ID(イベントが提供する場合)
agent_type現在の Agent タイプ(イベントが提供する場合)
イベントごとに上記に追加フィールドが付加されます(各イベントの説明を参照)。 jq で入力を解析する例:
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')

出力

Hook は exit code と stdout で動作を制御します。

exit code

  • 0: 成功。stdout は下記ルールで解析されます。
  • 2: ブロック。stderr の内容が Agent にフィードバックされます(ブロックをサポートするイベントのみ有効)。
  • それ以外: 非ブロックエラー。stdout は無視され、stderr が診断ログに書き込まれ、メインフローは継続します。

stdout JSON 共通フィールド

exit が 0 で stdout が正しい JSON のとき、CLI は次のフィールドで解析します。JSON でなければプレーンテキストとして扱われます(SessionStart / UserPromptSubmit のみ、プレーンテキスト stdout を追加コンテキストとして対話に注入します)。
フィールド説明
continuefalse で後続実行の停止を要求
stopReasoncontinue: false と併用、Agent に停止理由を伝える
suppressOutputtrue のとき、hook 出力をユーザーに表示しない
systemMessageユーザーに表示される hook システムメッセージ。モデルコンテキストには注入されない
decision"allow" または "deny"、イベント固有の決定。"deny" は exit 2 と等価。ユーザー認可の要求 ("ask") は PreToolUsehookSpecificOutput.permissionDecision でのみ使用可
reason決定理由。ユーザー/モデルに表示
hookSpecificOutputイベント固有フィールドのコンテナ(各イベント参照)
イベント固有のきめ細かい制御フィールド(PreToolUsepermissionDecisionPostToolUseupdatedToolOutput など)は hookSpecificOutput オブジェクトに入ります。hookSpecificOutput を返す際は hookEventName を必ず含めてください。含まれていない場合は JSON 出力全体が拒否され、TUI に <hookName> hook error: hookSpecificOutput is missing required field "hookEventName" が表示されます。例:
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "ask"
  }
}

環境変数

Hook スクリプト実行時に利用できる環境変数:
変数説明
QODER_PROJECT_DIR現在のプロジェクトの作業ディレクトリ
QODER_PLUGIN_ROOThook がプラグイン由来のとき、そのプラグインのルートディレクトリ
QODER_PLUGIN_DATAhook がプラグイン由来のとき、そのプラグインのデータディレクトリ

イベントリファレンス

用途別にイベントを並べます。各イベントの matcher 対象、stdin 追加フィールド、ブロック対応の有無、利用可能な hookSpecificOutput フィールドを示します。

一覧

イベントmatcher 対象exit 2 ブロック主な入力フィールド
SessionStartsource (startup/resume/clear/compact/new)source, model
SessionEndreasonreason
UserPromptSubmitprompt
PreToolUseツール名tool_name, tool_input
PostToolUseツール名tool_name, tool_input, tool_response
PostToolUseFailureツール名tool_name, error, error_type
PermissionRequestツール名tool_name, tool_input
PermissionDeniedツール名tool_name, tool_input, reason
Stopstop_hook_active, last_assistant_message
StopFailureerror_typeerror_type, error
SubagentStartAgent タイプagent_id, agent_type
SubagentStopAgent タイプagent_id, agent_type, stop_hook_active
PreCompacttriggertrigger, custom_instructions
PostCompacttriggertrigger, compact_summary
Notificationnotification_typenotification_type, message
InstructionsLoadedload_reasonfile_path, memory_type, load_reason
ConfigChangesource✅ (policy_settings ソースを除く)source, file_path
CwdChangedold_cwd, new_cwd
FileChangedファイル basenamefile_path, event
WorktreeCreate非ゼロ exit で失敗name
WorktreeRemoveworktree_path
Elicitationmcp_server_namemcp_server_name, message, requested_schema
ElicitationResultmcp_server_namemcp_server_name, action, content

セッションライフサイクル

SessionStart

セッション開始時に発火。 matcher 対象: セッションのソース
matcher 値発火シナリオ
startup新規セッション起動
resume既存セッション再開
clear/clear でリセット
compactコンテキスト圧縮完了後
new新規セッション(その他のソース)
追加入力フィールド:
{
  "source": "startup",
  "model": "Auto"
}
hookSpecificOutput: additionalContext(対話に注入されるコンテキスト)
プレーンテキスト(JSON 以外)を返した場合も、stdout がコンテキストとして対話に注入されます。

SessionEnd

セッション終了時に発火。 matcher 対象: 終了理由
matcher 値発火シナリオ
clear/clear で終了
resume別セッションへ切り替え
logoutログアウト
prompt_input_exitユーザーが入力を終了(Ctrl+D など)
bypass_permissions_disabled権限バイパスモードが無効化された
otherその他
追加入力フィールド:
{
  "reason": "prompt_input_exit"
}

UserPromptSubmit

ユーザーがプロンプトを送信した直後、Agent 処理前に発火。プロンプトの対話投入をブロック可能。 追加入力フィールド:
{
  "prompt": "ソート関数を書いてください"
}
ブロック: exit 2 でプロンプトを拒否。stderr がユーザーに表示されます。 hookSpecificOutput:
  • additionalContext: プロンプトと共に対話に注入
  • sessionTitle: 推奨セッションタイトル
プレーンテキスト(JSON 以外)を返した場合も、stdout がコンテキストとして対話に注入されます。

ツール呼び出し

PreToolUse

ツール実行前に発火。ツール実行のブロック、入力の改変が可能。 matcher 対象: ツール名(BashWriteEditReadGlobGrep など。MCP ツールは mcp__server__tool 形式) 追加入力フィールド:
{
  "tool_name": "Bash",
  "tool_input": {"command": "rm -rf /tmp/build"},
  "tool_use_id": "toolu_01ABC123"
}
ツールが MCP 由来の場合は mcp_context(server_nametool_name、接続情報を含む)と original_request_name も付与されます。
ブロック: exit 2、stderr がエラーとして Agent に返ります。 hookSpecificOutput:
フィールド説明
permissionDecision"allow" / "deny" / "ask"。トップレベル decision と等価で、こちらが優先
permissionDecisionReason決定理由。トップレベル reason を上書き
updatedInput改変済みのツール入力(元の tool_input を置換)
additionalContext対話に注入される追加コンテキスト

PostToolUse

ツールが正常終了した後に発火。 matcher 対象: ツール名 追加入力フィールド:
{
  "tool_name": "Write",
  "tool_input": {"file_path": "/path/to/file.ts", "content": "..."},
  "tool_response": {"success": true, "bytes_written": 1024},
  "tool_use_id": "toolu_01ABC123"
}
tool_response はオブジェクトで、構造はツールごとに異なります。MCP ツールも mcp_context / original_request_name を受け取ります。
hookSpecificOutput:
フィールド説明
updatedToolOutputツール応答を置換(任意のツールで利用可)
updatedMCPToolOutputMCP ツール応答のみ置換(updatedToolOutput より優先度が低い)
additionalContext対話に注入される追加コンテキスト

PostToolUseFailure

ツール実行が失敗した後に発火。 matcher 対象: ツール名 追加入力フィールド:
{
  "tool_name": "Bash",
  "tool_input": {"command": "npm test"},
  "tool_use_id": "toolu_01ABC123",
  "error": "Command exited with non-zero status code 1",
  "error_type": "execution_failed",
  "is_interrupt": false
}
hookSpecificOutput: additionalContext

PermissionRequest

ツール実行にユーザー認可が必要なときに発火。自動許可、拒否、入力改変が可能。 matcher 対象: ツール名 追加入力フィールド:
{
  "tool_name": "Bash",
  "tool_input": {"command": "rm -rf node_modules"},
  "permission_suggestions": []
}
hookSpecificOutput: decision オブジェクト。フィールドは behavior の値によって異なります。 behavior: "allow" (許可。同時に入力を書き換えたり権限を永続化できる):
{
  "behavior": "allow",
  "updatedInput": { "command": "..." },
  "updatedPermissions": []
}
フィールド説明
behavior"allow" 固定
updatedInput改変済みのツール入力 (元の tool_input を置換)
updatedPermissions永続化される権限ルール更新
behavior: "deny" (拒否。ユーザー向けメッセージを付加可能):
{
  "behavior": "deny",
  "message": "...",
  "interrupt": false
}
フィールド説明
behavior"deny" 固定
messageユーザーに表示するメッセージ
interrupt現在の操作を中断してユーザーに表示するか
PermissionRequest の hook は "ask" をサポートしません。ユーザーに対話的に確認したい場合は PreToolUsepermissionDecision: "ask" を使ってください。

PermissionDenied

権限分類器がツール呼び出しを拒否したときに発火。リトライを要求可能。 matcher 対象: ツール名 追加入力フィールド:
{
  "tool_name": "Bash",
  "tool_input": {"command": "..."},
  "tool_use_id": "toolu_01ABC123",
  "reason": "Auto mode classifier blocked this call"
}
hookSpecificOutput: retry: true でツール呼び出しのリトライを要求。

Agent フロー

Stop

メイン Agent が応答を完了し未実行ツール呼び出しがないときに発火。Agent の停止をブロックして継続させることが可能。 追加入力フィールド:
{
  "stop_hook_active": false,
  "last_assistant_message": "..."
}
フィールド説明
stop_hook_active現在のターンが Stop hook 駆動の継続中かどうか(無限ループ回避に使う)
last_assistant_message停止前の最後の Assistant メッセージ
ブロック: exit 2、stderr がメッセージとして対話に注入され、Agent が継続します。 hookSpecificOutput: clearContext: true で対話コンテキストをクリア。

StopFailure

Agent がエラーで予期せず停止したときに発火。通知のみで、出力と exit code は無視されます。 追加入力フィールド:
{
  "error_type": "rate_limit",
  "error": "...",
  "error_details": "...",
  "last_assistant_message": "..."
}
error_type の値: rate_limit / authentication_failed / billing_error / invalid_request / server_error / max_output_tokens / unknown

SubagentStart

子 Agent 起動時に発火。 matcher 対象: Agent タイプ名 追加入力フィールド:
{
  "agent_id": "a1b2c3d4",
  "agent_type": "task"
}
hookSpecificOutput: additionalContext

SubagentStop

子 Agent 完了時に発火。Stop 同様に停止をブロック可能。 matcher 対象: Agent タイプ名 追加入力フィールド:
{
  "agent_id": "a1b2c3d4",
  "agent_type": "task",
  "stop_hook_active": false,
  "agent_transcript_path": "...",
  "last_assistant_message": "..."
}
ブロック: exit 2、stderr が子 Agent の対話に注入されます。 hookSpecificOutput: clearContext: true で子 Agent のコンテキストをクリア。

コンテキスト圧縮

PreCompact

コンテキスト圧縮の前に発火。圧縮をブロック可能。 matcher 対象: トリガー方式
matcher 値発火シナリオ
manualユーザーが /compact を手動実行
autoコンテキストウィンドウが上限に近づき自動発火
追加入力フィールド:
{
  "trigger": "manual",
  "custom_instructions": "ツール呼び出し結果はすべて保持"
}
ブロック: exit 2 で今回の圧縮を阻止。

PostCompact

コンテキスト圧縮完了後に発火。 matcher 対象: トリガー方式(PreCompact と同じ) 追加入力フィールド:
{
  "trigger": "manual",
  "compact_summary": "圧縮サマリー..."
}
hookSpecificOutput: additionalContext

通知

Notification

ユーザー向けの通知(権限要求、アイドル通知、elicitation など)が発生したときに発火。 matcher 対象: 通知タイプ
matcher 値発火シナリオ
permission_promptツール権限要求
idle_promptアイドル通知
auth_success認証成功
elicitation_dialogMCP elicitation ダイアログ表示
elicitation_responseユーザーが elicitation に応答
elicitation_completeelicitation フロー完了
追加入力フィールド:
{
  "notification_type": "permission_prompt",
  "message": "Agent is requesting permission to run: rm -rf node_modules",
  "title": "Permission Required",
  "details": {}
}
hookSpecificOutput: additionalContext

コンテキストと設定の読み込み

InstructionsLoaded

指示/メモリファイルが読み込まれたときに発火。通知のみで、出力と exit code は無視されます。 追加入力フィールド:
{
  "file_path": "/abs/path/AGENTS.md",
  "memory_type": "project",
  "load_reason": "session_start",
  "globs": ["**/AGENTS.md"],
  "trigger_file_path": "...",
  "parent_file_path": "..."
}
load_reason の値: session_start / nested_traversal / path_glob_match / include / compact

ConfigChange

セッション中に設定ファイルが変更されたときに発火。 matcher 対象: 設定ソース
matcher 値発火シナリオ
user_settingsユーザー ~/.qoder/settings.json
project_settingsプロジェクト ${project}/.qoder/settings.json
local_settingsプロジェクトローカル ${project}/.qoder/settings.local.json
policy_settingsポリシー設定
skillsSkill ディレクトリの変更
agentsカスタム Agent ディレクトリの変更
追加入力フィールド:
{
  "source": "user_settings",
  "file_path": "/abs/path/settings.json"
}
ブロック: exit 2 で今回の変更を現在のセッションに適用しません。

作業ディレクトリとファイル

CwdChanged

作業ディレクトリ変更時に発火。 追加入力フィールド:
{
  "old_cwd": "/old",
  "new_cwd": "/new"
}
hookSpecificOutput:
フィールド説明
additionalContext対話に注入されるコンテキスト
watchPathsFileChanged ウォッチャに登録する絶対パスの配列

FileChanged

監視対象ファイルが変化したときに発火。 追加入力フィールド:
{
  "file_path": "/abs/path/file.ts",
  "event": "change"
}
event の値: change / add / unlink hookSpecificOutput: additionalContextwatchPaths(CwdChanged と同じ)

Worktree 隔離

WorktreeCreate

隔離 worktree の作成が必要になったときに発火。Hook は worktree の絶対パスを返す必要があります。非ゼロ exit code は失敗扱い。 追加入力フィールド:
{
  "name": "feature-x"
}
パスの返却: stdout に絶対パスを書き出すか、hookSpecificOutput.worktreePath に設定します。

WorktreeRemove

worktree が削除されるときに発火。通知のみで、失敗時は stderr のみ表示します。 追加入力フィールド:
{
  "worktree_path": "/abs/path/worktree"
}

MCP 連携

Elicitation

MCP server がユーザー入力(elicitation)を要求したときに発火。Hook は自動的に accept / decline / cancel できます。 matcher 対象: mcp_server_name 追加入力フィールド:
{
  "mcp_server_name": "my-server",
  "message": "操作を確認してください",
  "mode": "...",
  "url": "...",
  "elicitation_id": "...",
  "requested_schema": {}
}
ブロック: exit 2 で elicitation を拒否。 hookSpecificOutput:
フィールド説明
action"accept" / "decline" / "cancel"
contentaccept 時に提供する入力内容

ElicitationResult

ユーザーが elicitation に応答した後に発火。応答を上書き可能。 matcher 対象: mcp_server_name 追加入力フィールド:
{
  "mcp_server_name": "my-server",
  "action": "accept",
  "content": {},
  "mode": "...",
  "elicitation_id": "..."
}
ブロック: exit 2 で action を decline に書き換え。 hookSpecificOutput: actioncontent(応答を上書き)

実用例

デスクトップ通知

Agent が認可を必要とした、もしくは通知を発行したときにデスクトップ通知を出します。 スクリプト ~/.qoder/hooks/notify.sh(macOS):
#!/bin/bash
input=$(cat)
ntype=$(echo "$input" | jq -r '.notification_type')

if [ "$ntype" = "permission_prompt" ]; then
  osascript -e 'display notification "認可が必要です" with title "Qoder CLI"'
else
  osascript -e 'display notification "新しい通知があります" with title "Qoder CLI"'
fi

exit 0
設定:
{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qoder/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

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

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

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

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

Agent に作業を継続させる

Agent が停止する際に未完了タスクをチェックし、ある場合はメッセージを注入して継続させます。 スクリプト ~/.qoder/hooks/check-continue.sh:
#!/bin/bash
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
  echo "未コミットの変更があります。git commit を完了させてください" >&2
  exit 2
fi

exit 0
設定: イベント Stop、command ~/.qoder/hooks/check-continue.sh
Qoder CLI を使用する
フック - Qoder