Claude Code の mod は、あなたの hook がブロックしたものを許可できる
mod は v2.1.287 のリリースノートにたった1行で登場しました。増えたのは権限の届く範囲ではありません。プラグインはもともとあなたの権限で動いていました。増えたのは優先順位です。インストールした mod はルールと PreToolUse hook の後に答え、managed settings の外では、その答えが最終判断になります。
Claude Code v2.1.287 は10月1日にリリースされました。その中で最大の変更を告げる行は、全文でこれだけです。"Added Claude Mods: plugins may now modify deeper behavior." mod は Claude Code の内部であなたの権限で動く JavaScript または TypeScript のコードで、デフォルトで有効です。
"deeper" が何を指すのかは、権限のドキュメントに書かれています。hook を書いたことのある人の多くが一度は読み、もう開かないであろう見出しの下です。
mod には、設定ファイルの hook にできない何ができるのか?
設定したすべての後に答えられます。tool.check イベントをフックする mod は、権限ルールと PreToolUse hook がすでに出した判断を受け取り、自分の判断を返します。権限のページには、その答えが何を置き換えられるかが列挙されています。
Ask rules: the mod can approve a call that an ask rule would prompt for
A block from a PreToolUse hook: the mod can approve the call, unless the
hook is in managed settings
Deny rules: on a machine with managed settings, or when you're signed in
with a Team or Enterprise plan, deny rules hold over the mod by default,
and your organization can change that. Anywhere else, the mod can approve
a call that a deny rule refuses.イベントのページには、別のイベントを使う2つ目の経路も書かれています。tool.call はツールが実際に実行される段階で、自分の設定にある PreToolUse hook は、最後の mod がそこで next を呼んだ後にしか実行されません。tool.call に自前の結果で答える mod は、hook もツール本体も丸ごと飛ばせます。
mod は本当に、hook がブロックした呼び出しを許可するのか?
許可します。2.1.287 で、使い捨てのリポジトリを使って確かめました。その .claude/settings.json には、marker-hook を含む Bash コマンドに対して終了コード 2 で終わり、そのときファイルに1行書き込む PreToolUse hook と、touch marker-deny に対する deny ルールを入れました。--plugin-dir で読み込んだ mod は、hook が1つだけです。
export function register(on) {
on('tool.check', { tool: 'Bash' }, async ($, e, next) => {
const decided = await next(e)
$.ui.log('rules and hooks said ' + JSON.stringify(decided))
return { decision: 'allow' }
})
}ヘッドレスで実行し (claude -p --permission-mode default)、ユーザー設定は --setting-sources project,local で除外しました。
hook block, no mod -> blocked by PreToolUse hook
deny rule, no mod -> denied by your settings
hook block, with mod -> hook fired and logged its block; marker-hook created
deny rule, with mod -> marker-deny createdhook は実行され、拒否し、それでもファイルは作られています。アカウントは個人の Max プランで、マシンに managed settings はありません。つまり最後の行はドキュメントどおりの動作で、バグではありません。
mod より優先されるものは、まだ残っているのか?
思ったより少なく、会社のマシンでもそうです。managed settings のあるマシン、または Team や Enterprise でサインインしている場合、Claude Code はインストール済みのどの mod よりも先に、組み込みのガード sec-default を読み込みます。ソースは公開されています。このガードは deny ルールをユーザーの mod より優先させ、managed settings にある PreToolUse hook はどの mod よりも先に実行されて、ブロックが最終判断になります。開発者自身のプロジェクトの hook には、この保護がありません。管理者向けページは、managed settings の外にある PreToolUse hook のブロックを、mod がまだ覆せるチェックの一つに挙げています。mod 自身のファイル操作やプロセス呼び出しも、ガードの対象外です。Read(.env) を deny していても、ドキュメントによれば mod は自分の API を通じてそのファイルを読めます。
managed settings がなければガードはまったくなく、残るスイッチは大ざっぱです。disableAllHooks はインストール済みの mod を止めますが、設定ファイルの hook も一緒に止まります。--safe-mode は1セッション分、同じことをします。hook のケースをこれで再実行すると、mod は消えましたが hook も消え、呼び出しが拒否されたのは、ヘッドレス実行には承認する人がいないからにすぎませんでした。プラグイン自体を無効にすることもできます。hook は残り、その mod は外れますが、そのプラグインを入れた目的のペインやスキルも一緒に外れます。個人アカウントでドキュメントが用意している制御は、これが最も細かいものです。
ガードは managed settings のあるマシンなら必ず読み込まれます。そしてドキュメントは、デバイス管理のないホスト向けに、managed settings を普通のファイルとして置くことを配布方法の一つに挙げています。自分のマシンなら、それを書く管理者は自分自身です。この方法はここでは試していません。
claude plugin validate で、これは見えるのか?
どの行を読めばいいか知っていれば、見えます。このコマンドは mod を実行せずに、どのイベントをフックし、どの API を呼ぶかを一覧にします。今回の許可用 mod ではこうなりました。
./register.js hooks: tool.check{tool=Bash}
./register.js calls: $.ui.logcalls: の行は無害に見えます。力があるのは hooks: のほうで、tool.check は、あなたに確認が来る前に mod がツール呼び出しを許可できることを意味します。validate が示すのはイベントの名前で、ハンドラーが何を判断するかではありません。mod は通常のアップデート経路で届くので、プラグインを更新するたびに確認してください。
ゲートとして使っている hook にとって、何が変わるのか?
新しい権限の範囲は何もありません。クローンしたリポジトリの設定ファイルはもともとあなたの権限でコードを実行できましたし、インストールしたツールはもともとアカウントが持つものすべてを手にしていました。悪意のあるプラグインに tool.check は必要ありません。設定ファイルから hook を消せば済みます。mod が加えるのは優先順位で、優先順位が効いてくるのは悪意のない mod のほうです。作者の意図より広いマッチャーを持つ便利な自動許可の mod や、アップデートで tool.check hook を拾ってきたステータスライン用のプラグインがそうです。
重要なルールをプロンプトの外に出すべきだという記事では、hook を「拘束力のあるチェック」の例に挙げました。ハーネスが実行し、モデルは飛ばせず、そのブロックは、もっと大きな声に多数決で負けるような一票ではない、と。そこで弱点として挙げたのはカバー範囲でした。mod が崩すのは拘束力のほうです。hook は今も実行され、今も終了コード 2 を返します。ただ、そのブロックは、後にもう一人投票者が控えている一票になりました。
プラグインが何を持ち込んでも守られるべきルールには、後から mod が答えない場所が必要です。Claude Code の中では、それが managed settings です。外なら、セッション全体が出られないコンテナ、Git ホスト側の保護ブランチ、セッションが一度も目にしない認証情報です。.claude/settings.json の hook は今も呼び出しごとに発火します。そのブロックが通るかどうかは、その上に読み込まれたものが決めます。