Claude Code 定期実行の許可待ちを hook で防ぐ方法

こんにちは!アントロンのNaokoです。
自動にしたはずなのに、ときどき自分で押しに行っていませんか?
- 毎朝できているはずのまとめが、できていない
- 開いてみると、許可の確認が出たまま待っていた
私は毎朝7時に、Claude Code(AI に作業を頼めるアプリ)の定期実行で、その日の予定やメールをまとめた1枚を作っています。使っているのは Claude Code Desktop の「スケジュール設定されたタスク」(決めた時刻に自動でセッションを始める機能のこと)です。私の環境でも、許可の確認が出たまま63分・104分・71分止まった回がありました。
決めたコマンド以外は、確認を出さずに断る仕組みを入れました。 使ったのは hook(フック。Claude が操作をする直前などに、自分で用意したプログラムを呼び出す仕組みのこと)です。
よくある悩み:「常に許可」にしても止まる
公式の説明では、定期実行のタスクは、権限モードが Manual のとき、権限がないツールを使おうとすると、承認するまで止まります(出典:Schedule recurring tasks in Claude Code Desktop)。
公式は、タスクを作ったら Run now で1回動かし、確認が出たら「常に許可」を選んでおくよう案内しています。次の回から、同じ操作は確認なしで通ります。
ただ、「常に許可」が効くのは、一度許可したものだけです。Claude がそれまでと違うコマンドを実行しようとすると、また許可の確認が出ます。定期実行は誰も見ていない時間に動くので、確認が出ても押す人がいません。
解決の考え方:決めたもの以外は、確認を出さずに断る
- 使うコマンドは、先に許可しておく
- それ以外は、確認を出さずに断る。こちらを hook で行う
使うのは hook の次の項目です(出典:Hooks reference、Configure permissions)。
- PreToolUse:Claude がツール(ファイルの読み書きやコマンドの実行など)を使う直前、許可の確認より前に動く hook
- hook には、実行しようとしているコマンドが JSON(データを書くときの決まった形式のこと)で渡る。Bash(コマンドを実行するツールのこと)なら
tool_input.commandに入る permissionDecisionをdenyにした JSON を返すと、そのツールは実行されず、理由が Claude に伝わる。何も返さなければ、ふだんどおりの判定に進む
やったこと1:見本のフォルダに設定と hook を置いた
本物とは別に見本のフォルダを作り、Claude Code に頼んで、設定ファイル .claude\settings.json、hook .claude\hooks\guard.js、許可するコマンドで動かす hello.js を置きました。パスは見本用です。
{
"permissions": {
"allow": ["Bash(node hello.js)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node \"C:/work/my-project/.claude/hooks/guard.js\"",
"timeout": 10
}
]
}
]
}
}
hook のプログラムは、渡されたコマンドが許可した1本と同じなら何も返さず、違えば deny を返します。
// PreToolUse(matcher: Bash)から呼ばれる。許可した1本以外の Bash を断る。
const ALLOWED = "node hello.js";
let raw = "";
process.stdin.on("data", (d) => (raw += d));
process.stdin.on("end", () => {
const input = JSON.parse(raw);
const cmd = String((input.tool_input && input.tool_input.command) || "").trim();
if (cmd === ALLOWED) return; // 何も返さない=ふだんどおり進む
process.stdout.write(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: `このフォルダで使える Bash は「${ALLOWED}」だけです。`,
},
}));
});
やったこと2:hook だけを先に試した
まず hook だけを PowerShell(Windows でコマンドを打って動かすためのソフトのこと)で動かしました。Claude Code が渡すのと同じ形の JSON を、自分で渡します。
$ok = '{"tool_name":"Bash","tool_input":{"command":"node hello.js"}}'
$ng = '{"tool_name":"Bash","tool_input":{"command":"echo skip"}}'
$ok | node .claude\hooks\guard.js
$ng | node .claude\hooks\guard.js
やったこと3:定期実行のときだけ効くようにした
hook は、定期実行かどうかに関係なく、そのプロジェクトのすべてのセッションで動きます。そのままだと、ふだんの作業のコマンドまで断ってしまいます。
見分けには、hook に渡る transcript_path(セッションの記録ファイルの場所)を使いました。私の環境では、定期実行で始まったセッションの記録の最初の発言に、<scheduled-task name="タスクの名前"> が入っていました。guard.js を写して guard-scheduled.js を作り、次の所を足しました。断る理由の文も「定期実行 morning-summary で使える…」に変えています。
const fs = require("fs");
const TASK = "morning-summary"; // 定期実行の名前
// 会話の記録から、最初のユーザー発言の文字を取り出す
function firstUserText(path) {
for (const line of fs.readFileSync(path, "utf8").split("\n")) {
let j;
try { j = JSON.parse(line); } catch { continue; }
if (j.type !== "user") continue;
const c = j.message && j.message.content;
if (typeof c === "string") return c;
if (Array.isArray(c)) return c.map((x) => x.text || "").join("\n");
return "";
}
return "";
}
try {
// 定期実行の会話でなければ何も返さない
if (!firstUserText(input.transcript_path).includes(`<scheduled-task name="${TASK}"`)) return;
// ここから下は guard.js と同じ
} catch {
// 記録が読めないときは何も返さない(通す)
}見本では、定期実行のセッションの記録をまねたファイルと、ふだんのセッションをまねたファイルを作り、やったこと2の $ng に "transcript_path":"…" を足し、それぞれのファイルを入れて渡しました。

本物は、ほかの人と共有しない設定ファイル .claude\settings.local.json に登録しました。
つまずきやすい所
許可するコマンドは、1文字まで同じに書く。 見本の hook は、コマンドを文字列のまま比べています。引用符の種類やパスの書き方が違うだけでも、別のコマンドとして断ります。定期実行の指示に書いたコマンドを、そのまま写すのが確実です。
hook の allow では、設定ファイルの禁止を越えられない。 allow は試していません。参考として公式で調べると、hook が allow を返しても、設定ファイルで禁止しているものは通らないとされています(出典:Configure permissions)。
結果
10月6日に本物の hook を入れ、同じ日に、朝のまとめのセッションで echo skip が確認を出さずに断られるのを確かめました。
翌朝の10月7日の回は、実行の履歴では7時13分に始まり、7時24分に「succeeded」で終わっています。セッションの記録を見ると、この回に使った Bash は、決めた2本のコマンドだけでした。
今日、定期実行の「止まった回」を確かめてみてください
自動で動かしている方は、タスクの詳細ページの Review history(実行の履歴)で、次の2つを見てみてください。
- いつもより時間がかかった回や、終わっていない回がないか
- その回で、何の許可の確認が出ていたか
確認が出ていたコマンドが、いつも使うものなら先に許可しておく。使う予定のないものなら、断る仕組みを考える。
Claude Code Desktop を使い始めたばかりの方は、Claude Codeデスクトップ版の始め方|毎日使ってわかった安全に使う5つのコツもあわせてどうぞ。
AI に毎日の作業を任せる仕組みづくりのご相談は、お問い合わせフォームからどうぞ。
最後まで読んでくださりありがとうございました!




コメント