起動を記録し、rm -rf / を含む Bash を拒否する。関数フック(early access)が必要

Zenn 本『Claude Code Mods 実践ガイド ― 関数フックで本体を改造する、動く Mod 7本』のコードです。 Claude Code 2.1.278 で claude plugin validate / claude -p の証拠ファイル / claude plugin test を通しています。各フォルダの NOTES.md に検証ログの全文があります。
| 章 | Mod | 何をするか |
|---|---|---|
| 2 | hello/(hello-guard) | 起動を記録し、rm -rf / を含む Bash を拒否する練習台 |
| 4 | usage-watch/ | 使用量とレート制限をステータス行に常駐、80/90% でトースト |
| 5 | secret-scrub/ | Read / Grep / Bash の結果から秘密情報をマスクしてからモデルに渡す |
| 6 | intent-guard/ | 危険そうな Bash を小さなモデルに判定させて拒否 |
| 7 | work-log/ | ターンごとの作業日誌と /worklog |
| 8 | model-router/ | turn.step でモデルと effort を振り分け、/route で固定 |
| 9 | notes-tool/ | モデルに note_add / note_list ツールを渡す |
| 10 | band-meter/ | プロンプト上のバンドに使用量バーと hide ボタンを描く(テストキットで検証) |
_docs/slash-commands-in-p.md — claude -p とスラッシュコマンドの実測(Git Bash の MSYS パス変換)_badshapes/ — バリデータが拒否する 4 つの書き方と、その出力使い方: CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 を設定し、claude --plugin-dir ./usage-watch のように読み込みます。関数フックは early access で、API はリリースごとに変わり得ます。
ライセンス: MIT
hooks/register.ts 22 lines1export function register(on) {
2 // 起動を証拠として残す。-p 実行では画面に何も出ないので、ファイルに書く
3 on("session.start", async ($, e, next) => {
4 const r = await next(e)
5 const line =
6 `session.start at ${new Date().toISOString()} ` +
7 `cwd=${e.cwd} surface=${e.surface} interactive=${e.isInteractive} ` +
8 `plugin=${$.plugin.name}\n`
9 await $.fs.write(`${$.plugin.root}/PROOF.txt`, line)
10 return r
11 })
12
13 // 危険なコマンドを拒否する。next を呼ばずに返すと、ツールは実行されない
14 on("tool.call", { tool: "Bash" }, async ($, e, next) => {
15 if (typeof e.command === "string" && e.command.includes("rm -rf /")) {
16 await $.fs.write(`${$.plugin.root}/DENY.txt`, `denied: ${e.command}\n`)
17 return { deny: "hello-guard: blocked a destructive command" }
18 }
19 return next(e)
20 })
21}
22