CLAUDE CODE/確認プロンプトを外して走らせる型
「毎回の確認が面倒だから外したい。でも事故らせたくない」人向けの、そのまま真似できる運用ルール一式。
第1部のhook(危険コマンドを止めるスクリプト)に検出漏れが10件見つかったので、判定のやり方ごと入れ替えました。止まると思っていたのに素通りしていた例です(すべて実測)。
| 書き方 | 以前 | 現在 |
|---|---|---|
rm -rfv /tmp/x | 素通り | ブロック |
rm -r -f /tmp/x | 素通り | ブロック |
rm --recursive --force /tmp/x | 素通り | ブロック |
git push origin main --force | 素通り | ブロック |
git reset HEAD~1 --hard | 素通り | ブロック |
git branch feature -D | 素通り | ブロック |
原因は、判定が「rm -rf という並びが出てくるか」という書き順ありきの探し方だったこと。フラグを分けたり後ろに書いたりするだけで外れていました。コマンドを単語に分解して危険なオプションの組み合わせで判定する方式に変え、書き順・フラグの分離・ロングオプション・引数の後置・絶対パス指定に強くしています。あわせて、判断できないときは素通りさせず止める作りにしました。
やること(5分)
~/.claude/hooks/block-dangerous-bash.sh を丸ごと上書きhooks ブロックを差し替え(hookが起動できなかったときの受け皿を足しました)jq は不要になりました。Windowsの方も Git for Windows(Git Bash)があれば動く作りになっています(実機での確認は未了です)。
~/.claude/CLAUDE.md(毎回自動で読まれる指示書)に貼るURLをうまく読めないときは「📋 Markdownをコピー」で全文を渡してください(クリップボードに入るだけで、ダウンロードは不要)。手作業で入れたい人は、第1部にファイルの中身をそのまま載せているのでコピペでOK。
Claude Code は通常、コマンドを実行するたび「これ実行していいですか?」と聞いてきます。バイパスモード(--dangerously-skip-permissions)はこの確認を全部スキップするモード。止まらないので作業速度は跳ね上がりますが、裏返すと「止める人がいない」状態です。以下は朝倉が自分の運用で立てている柵をそのまま配布用にしたものです。
バイパスモードは「AIを信用して確認をやめる」運用ではありません。確認をやめる代わりに、機械が止める柵を先に立てる運用です。柵を立てずにフラグだけ外すのが、いちばん事故ります。
良いこと:止まらない。10ステップの作業が一気に最後まで進む。
怖いこと:止める人がいない。rm -rf も本番DBの書き換えも、聞かれずに実行される。
| 層 | 誰が止めるか | 何を止めるか | 破られるか |
|---|---|---|---|
| システム層 | 機械(設定とスクリプト) | rm -rf sudo git push --force などの破壊系コマンド | 破られない。AIの判断は一切介在しない |
| 行動層 | Claude 自身 | 「本番を触る」「大量削除」「外部への発信」など、文字面では判定できない領域 | 忘れることがある |
なぜ2層いるか。hook(システム層)はコマンドの文字列しか見ません。「会員サイトの本番DBを書き換える」は文字列としてはただの curl で、危険と判定できない。逆に行動層だけだと、Claudeが忘れた瞬間に柵がなくなる。機械で止められるものは機械で、無理なものだけルールで。
~/.claude/settings.json に追記するpermissions と hooks の2ブロック。すでに settings.json がある人は、既存の内容を消さずにこの2つを足します。
{
"permissions": {
"defaultMode": "bypassPermissions",
"deny": [
"Bash(rm -rf:*)",
"Bash(rm -fr:*)",
"Bash(sudo:*)",
"Bash(sudo)",
"Bash(dd:*)",
"Bash(mkfs:*)",
"Bash(mkfs.*:*)",
"Bash(shutdown:*)",
"Bash(reboot:*)",
"Bash(halt:*)",
"Bash(poweroff:*)",
"Bash(git push --force:*)",
"Bash(git push -f:*)",
"Bash(git reset --hard:*)",
"Bash(git clean -f:*)",
"Bash(git clean -fd:*)",
"Bash(git clean -fdx:*)",
"Bash(git branch -D:*)",
"Bash(npm publish:*)",
"Bash(pnpm publish:*)",
"Bash(yarn publish:*)",
"Bash(chmod -R 777:*)",
"Bash(docker system prune:*)",
"Bash(docker volume rm:*)",
"Bash(docker volume prune:*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash \"$HOME/.claude/hooks/block-dangerous-bash.sh\" || printf '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"安全柵の起動に失敗したためブロックしました。~/.claude/hooks/block-dangerous-bash.sh があるか、改行コードが壊れていないか確認してください\"}}'"
}
]
}
]
}
}defaultMode: "bypassPermissions" を書いておくと、起動フラグを忘れてもバイパスモードで立ち上がります。「普段は確認あり、必要なときだけ」で使いたい人はこの行だけ外して claude --dangerously-skip-permissions で起動。
command の後半にある || printf ... は、hookそのものが起動できなかったときの受け皿です。スクリプトが見つからない・改行コードが壊れている・途中で落ちた、のいずれでも「判断できなかった」として拒否側に倒します。以前はこの受け皿が無く、hookが動かないときは黙って全部素通りしていました。
timeout は指定しません(既定の60秒に任せる)。短く設定すると、重い処理の最中に安全柵のほうが先に力尽きて無効になることがあります。
~/.claude/hooks/block-dangerous-bash.sh を作り、中身をそのまま貼ります。
#!/usr/bin/env bash
# 破壊的コマンドの安全柵(Claude Code の PreToolUse フック)
#
# 設計方針:
# 1. 外部依存を最小化 — jq を使わない。Git for Windows の Git Bash に jq が同梱されないため。
# 使うのは bash 組み込み / awk / grep / sed / tr のみ。
# 2. フェイルクローズ — 必要な道具が無い、解析できない、想定外の入力、のいずれでも
# 「黙って許可」にしない。判断できないときは拒否する。
# 3. 位置に依存しない — コマンドを単語に分解し、危険なオプションの「組み合わせ」で判定する。
# フラグの順序・分離・ロングオプション・引数の後置に強い。
# 4. set -e を使わない — 途中の非ゼロ終了で拒否JSONを出す前に落ちる事故を防ぐ。
#
# 入力: stdin に PreToolUse の JSON
# 出力: 拒否時のみ stdout に decision JSON(常に exit 0)
#
# テスト: bash tools/test_hook.sh (変更したら必ず流すこと)
set -uo pipefail
# --- 拒否JSONの出力(外部コマンド不要。printf は bash 組み込み) ---
json_escape() {
local s="$1"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
printf '%s' "$s"
}
deny() {
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"%s"}}\n' \
"$(json_escape "$1")"
exit 0
}
# --- 標準入力を読む(cat に依存しない) ---
input=""
while IFS= read -r _line || [ -n "$_line" ]; do
input="${input}${_line}"$'\n'
done
[ -n "${input//[$'\n\t ']/}" ] || exit 0
# --- 必要な道具の存在確認(無ければ判定不能なので拒否する=フェイルクローズ) ---
for _t in awk grep sed tr; do
if ! command -v "$_t" >/dev/null 2>&1; then
deny "安全柵が動作に必要な「${_t}」を見つけられないため、念のためブロックしました。Windowsの方は Git for Windows が入っているかご確認ください。解決しない場合は配布元にご連絡ください"
fi
done
# --- JSONエスケープを戻す ---
# 順序が重要。先に \\ を退避しないと、Windowsパス C:\\tools\\rm.exe の \t が
# タブとして誤変換され、コマンド名が壊れて検出漏れになる。
# 改行・復帰はコマンド区切り( ; )に変換する。空白にすると前後の行が1つの
# コマンドとして繋がり、誤検知と検出漏れの両方が起きる。
unescape() {
sed -e 's/\\\\/__BSLASH__/g' \
-e 's/\\n/ ; /g' -e 's/\\r/ ; /g' \
-e 's/\\u000[aAdD]/ ; /g' \
-e 's/\\t/ /g' \
-e 's/\\"/"/g' \
-e 's|\\/|/|g' \
-e 's/__BSLASH__/\\/g'
}
# --- stdin JSON から .tool_input.command を取り出す ---
extract_command() {
awk '
found { next }
{
idx = index($0, "\"command\"")
if (idx == 0) next
rest = substr($0, idx + 9)
sub(/^[ \t]*:[ \t]*/, "", rest)
if (substr(rest, 1, 1) != "\"") next
rest = substr(rest, 2)
out = ""
i = 1; n = length(rest)
while (i <= n) {
c = substr(rest, i, 1)
if (c == "\\") { out = out c substr(rest, i + 1, 1); i += 2; continue }
if (c == "\"") break
out = out c
i++
}
print out
found = 1
}
END { if (!found) exit 3 }
' 2>/dev/null
}
cmd="$(printf '%s' "$input" | extract_command | unescape)"
parse_rc=$?
if [ $parse_rc -ne 0 ] || [ -z "${cmd//[$'\n\t ']/}" ]; then
# 解析できなかった場合は素通りさせず、生の入力を走査対象にする。
# JSONの記号を空白に潰して単語境界を復元しないと、"command":"sudo ...
# のように引用符が直前に来る形で各ルールの境界判定が外れる。
cmd="$(printf '%s' "$input" | unescape | tr '":,{}[]' ' ')"
[ -n "${cmd//[$'\n\t ']/}" ] || exit 0
fi
lc="$(printf '%s' "$cmd" | tr '[:upper:]' '[:lower:]')"
has() { printf '%s' "$lc" | grep -Eq "$1"; }
hasC() { printf '%s' "$cmd" | grep -Eq "$1"; }
# ============================================================
# 単語分解による判定(位置に依存しない中核ロジック)
# ============================================================
# 原文(大文字小文字を保持)を渡す。git branch -D は大文字のDが安全か危険かを分けるため。
tokenscan() {
printf '%s' "$cmd" | awk '
function is_long(t) { return substr(t, 1, 2) == "--" }
function is_short(t) { return substr(t, 1, 1) == "-" && !is_long(t) }
function short_has(t, ch) { return is_short(t) && index(tolower(substr(t, 2)), ch) > 0 }
function base(t) {
gsub(/\\/, "/", t)
sub(/^.*\//, "", t)
sub(/\.exe$/, "", t)
return tolower(t)
}
{
line = $0
gsub(/[;&|()<>`]/, " __SEP__ ", line)
n = split(line, t, /[ \t]+/)
i = 1
while (i <= n) {
tk = t[i]
if (tk == "" || tk == "__SEP__") { i++; continue }
# 環境変数代入・単なる修飾子は読み飛ばして、次を本体コマンドとして扱う
if (tk ~ /^[A-Za-z_][A-Za-z0-9_]*=/) { i++; continue }
b = base(tk)
if (b == "command" || b == "builtin" || b == "nohup" || b == "time" || b == "env") { i++; continue }
# ---- rm: 再帰 + 強制 が揃ったら危険 ----
if (b == "rm") {
rec = 0; frc = 0; i++
while (i <= n && t[i] != "__SEP__") {
a = t[i]
if (substr(a, 1, 1) == "-") {
if (a == "--recursive" || a == "--dir" || short_has(a, "r")) rec = 1
if (a == "--force" || short_has(a, "f")) frc = 1
if (rec && frc) { print "RM"; exit }
}
i++
}
continue
}
# ---- Windows: rd / rmdir /s, del /s ----
if (b == "rd" || b == "rmdir" || b == "del" || b == "erase") {
sflag = 0; i++
while (i <= n && t[i] != "__SEP__") {
if (tolower(t[i]) == "/s") sflag = 1
if (sflag) { print "WINDEL"; exit }
i++
}
continue
}
# ---- git: サブコマンドごとに、区切りまでの全引数を見る ----
if (b == "git") {
i++
while (i <= n && t[i] != "__SEP__" && substr(t[i], 1, 1) == "-") {
if (t[i] == "-C" || t[i] == "-c") i += 2; else i++
}
sc = (i <= n && t[i] != "__SEP__") ? tolower(t[i]) : ""
args = ""; j = i + 1
while (j <= n && t[j] != "__SEP__") { args = args " " t[j]; j++ }
args = args " "
largs = tolower(args)
if (sc == "push") {
if (largs ~ / --force / && largs !~ /--force-with-lease/) { print "GITPUSH"; exit }
if (largs ~ / -[a-z]*f[a-z]* /) { print "GITPUSH"; exit }
if (args ~ / \+[^ ]+:/ ) { print "GITPUSH"; exit }
}
if (sc == "reset" && largs ~ / --hard /) { print "GITRESET"; exit }
if (sc == "clean") {
if (largs ~ / --force / || largs ~ / -[a-z]*f[a-z]* /) { print "GITCLEAN"; exit }
}
if (sc == "branch") {
if (args ~ / -[a-zA-Z]*D[a-zA-Z]* /) { print "GITBRANCH"; exit }
if (largs ~ / --delete / && largs ~ / --force /) { print "GITBRANCH"; exit }
}
if (sc == "commit" && (largs ~ /--no-verify/ || largs ~ /--no-gpg-sign/)) { print "GITCOMMIT"; exit }
continue
}
i++
}
}
' 2>/dev/null
}
TOK="$(tokenscan)"
# 環境に trash があれば、復元可能な代替として案内に含める
if command -v trash >/dev/null 2>&1; then
RM_ALT="代替: 新しい作業フォルダは mktemp -d / 既存物の削除は trash <パス>(復元可能) / zipのキャッシュ除去は -x の除外指定"
else
RM_ALT="代替: 新しい作業フォルダは mktemp -d、既存ファイルは1つずつ確認しながら削除。Windowsではエクスプローラーからゴミ箱へ移動してください"
fi
case "$TOK" in
RM)
deny "フォルダの再帰的な強制削除はブロックしました(安全柵)。同じコマンドの再送はしないでください。${RM_ALT}" ;;
WINDEL)
deny "フォルダの再帰削除(rd /s、del /s)はブロックしました(安全柵)。エクスプローラーからゴミ箱へ移動してください" ;;
GITPUSH)
deny "git push --force はブロックしました(安全柵)。共有先の作業履歴が失われます。--force-with-lease を使ってください" ;;
GITRESET)
deny "git reset --hard はブロックしました(安全柵)。未コミットの変更が消えます" ;;
GITCLEAN)
deny "git clean -f 系はブロックしました(安全柵)。追跡外のファイルが消えます" ;;
GITBRANCH)
deny "git branch -D(強制削除)はブロックしました(安全柵)。統合済みのみ削除する -d を使ってください" ;;
GITCOMMIT)
deny "git commit --no-verify / --no-gpg-sign はブロックしました(安全柵)。チェック機構はスキップしないでください" ;;
esac
# ============================================================
# 文字列パターンによる判定(単語分解に馴染まないもの)
# ============================================================
# PowerShell の再帰強制削除
if has 'remove-item[^;|&]*-recurse' && has 'remove-item[^;|&]*-force'; then
deny "Remove-Item -Recurse -Force はブロックしました(安全柵)"
fi
# 管理者権限昇格
if has '(^|[[:space:]]|;|&|\||\()(sudo|doas|pkexec)([[:space:]]|$)'; then
deny "管理者権限での実行(sudo)はブロックしました(安全柵)。必要な場合は内容を確認したうえでご自身で実行してください"
fi
# ディスク破壊
if has '(^|[[:space:]]|;|&|\|)(dd|mkfs(\.[a-z0-9]+)?)[[:space:]]'; then
deny "dd / mkfs はブロックしました(安全柵)。ディスクを破壊する可能性があります"
fi
if has '(^|[[:space:]]|;|&|\|)(format|diskpart|format-volume|clear-disk)([[:space:]]|$)' \
|| has 'cipher[[:space:]]+/w'; then
deny "ディスクの初期化・消去コマンドはブロックしました(安全柵)"
fi
# システム停止
if has '(^|[[:space:]]|;|&|\|)(shutdown|reboot|halt|poweroff|stop-computer|restart-computer)([[:space:]]|$)'; then
deny "システムの停止・再起動はブロックしました(安全柵)"
fi
# ネット経由スクリプトの即時実行
if has '(curl|wget)[[:space:]][^;|&]*\|[[:space:]]*(sudo[[:space:]]+)?(sh|bash|zsh|ksh)([[:space:]]|$)'; then
deny "ダウンロードしたスクリプトをそのままシェルに流す実行はブロックしました(安全柵)。中身を確認してから実行してください"
fi
if has '(irm|invoke-restmethod|iwr|invoke-webrequest)[[:space:]][^;|&]*\|[[:space:]]*(iex|invoke-expression)'; then
deny "ネットから取得したスクリプトの即時実行(irm | iex)はブロックしました(安全柵)"
fi
# 公開操作(取り消せない)
if has '(^|[[:space:]]|;|&|\|)(npm|pnpm|yarn)[[:space:]]+publish([[:space:]]|$)'; then
deny "npm/pnpm/yarn publish はブロックしました(安全柵)。公開は取り消せません"
fi
# 権限の全開放
if has 'chmod[[:space:]]+-[a-z]*r[a-z]*[[:space:]]+777'; then
deny "chmod -R 777 はブロックしました(安全柵)"
fi
# docker 破壊系
if has 'docker[[:space:]]+(system[[:space:]]+prune|volume[[:space:]]+rm|volume[[:space:]]+prune)'; then
deny "docker の破壊的な prune / volume rm はブロックしました(安全柵)"
fi
# fork bomb
if hasC ':\(\)[[:space:]]*\{[^}]*\|[[:space:]]*:[[:space:]]*&'; then
deny "fork bomb を検出したためブロックしました(安全柵)"
fi
# 生ディスク書き込み
if has '>[[:space:]]*/dev/(disk|sd|nvme|hd)'; then
deny "生ディスクへの書き込みはブロックしました(安全柵)"
fi
exit 0長いので枠の中でスクロールします。全文は右上の「コピー」で丸ごとクリップボードに入ります。
作ったら実行権限をつけます。
chmod +x ~/.claude/hooks/block-dangerous-bash.sh追加のインストールは不要です。以前のひな形は jq というツールに依存していましたが、なくても動くように作り替えました。Windowsの Git Bash には jq が同梱されておらず、そこで安全柵が丸ごと無効になっていたためです。
保存するときは改行コードをLFにしてください。Windowsのエディタで開いて保存し直すとCRLFに変わることがあり、そうなるとスクリプトが構文エラーで動きません(①の受け皿が拒否側に倒すので、素通りにはなりません)。
Claude Code を再起動してから、次の2つを頼みます。どちらも存在しないパスなので、万一実行されても何も消えません。
①「~/存在しないフォルダ_test を rm -rf で消して」
②「rm -rfv ~/存在しないフォルダ_test を実行して」
②が今回ふさいだ穴です。2つとも「ブロックしました(安全柵)」と出れば成功。①だけ止まって②が実行された場合は、hookの貼り替えができていません(古いままになっています)。
実行されてしまう場合は、hookのパス・実行権限(chmod +x)・Claude Code を再起動したか・settings.json のJSONが壊れていないか、を順に確認します。
cd foo && rm -rf bar のように途中に混ざった形は素通りしうる。それをコマンド文字列全体の検査で拾うのが hook の役目。片方だけでは穴が残るので両方入れるgrep の検索語や echo の文字列にたまたま該当パターンが入っただけでも止まる。止まったら書き方を変えるCLAUDE.md に貼る)ここを ~/.claude/CLAUDE.md にそのまま貼ります。「本番リスト」だけ自分の環境に置き換えてください。
## バイパスモード運用ルール
確認プロンプトが出ないモードで動いている。破壊系コマンドはシステム層(hook + permissions.deny)で
強制ブロック済み。以下はhookで拾えない領域なので必ず守る。
大原則: 取り消せる操作は自律で進める。取り消せない操作は必ず一度止まって確認する。
1. `.env` / APIキー / 認証情報をコミット・外部送信しない
2. `--no-verify` `--force` `--no-gpg-sign` など安全機構のスキップは禁止(明示指示でも再確認する)
3. 本番環境への変更は事前確認(下の「本番リスト」参照)
4. 顧客データ・記録の削除やアーカイブは事前確認(Notion DB / スプレッドシート / 顧客管理ツール)
5. 10ファイルを超える一括削除は事前確認
6. 未コミット変更を上書きしない(`git stash drop` / `git checkout --` で消さない)
7. 外部への発信(公式LINE配信 / SNS投稿 / メール送信 / 決済)は、送信直前に
「宛先・件数・本文」を提示して承認を取る。「進めて」だけを送信承認とみなさない
8. 10分を超えるタスクは、開始時と完了時に報告する
### 本番リスト(これを触るときは必ず止まる)
- (記入)例: 会員サイト https://〜 のコンテンツ・会員データ
- (記入)例: 販売中LPのページ
- (記入)例: 顧客管理のNotion DB「〇〇」
- (記入)例: 公式LINEの配信(テスト送信を除く)| ルール | なぜ必要か |
|---|---|
| 1. 認証情報 | 一度公開リポジトリに載ると、消してもコミット履歴に残る。取り消せない |
| 2. 安全機構スキップ | --force 系は「事故を防ぐ仕組みを外す」操作。外す判断は人間が握る |
| 3. 本番 | 壊れると売上と信用に直結する。ローカルのやり直しとは損害の桁が違う |
| 4. 顧客データ | 削除は取り消せない。アーカイブは戻せてもリンクや自動連携は戻らない |
| 5. 大量削除 | 1〜2ファイルの消し間違いは復旧できる。50ファイルは復旧できない |
| 6. 未コミット変更 | まだ保存されていない作業=この世に1つしかないデータ。消えたら終わり |
| 7. 外部発信 | 送ったものは取り消せない。バイパスモードで唯一「常に人が承認する」領域 |
| 8. 長時間タスク | 黙って10分動かれると、暴走か進行か判断できない |
いきなり本番業務で使わない。危険度の低いものから順に上げて、誤ブロックとAIの脱線を観察します。
| 段階 | 試すこと | 見るポイント |
|---|---|---|
| 1. 読み取り | ファイルを読む/一覧を出す/Notion検索 | 普通の作業が誤ブロックされないか |
| 2. ローカル編集 | ファイル作成・編集、スクリプト実行 | 摩擦ゼロを体感できるか。余計な編集をしないか |
| 3. git | add / commit / 通常の push | コミットは通り、force が要る場面で止まるか |
| 4. 外部ツール(本番以外) | テスト用Notionページの更新、下書き作成 | 自分から動けているか |
| 5. 本番系 | 会員サイト・LP・本番DBに関わる更新 | 事前確認ルールを守るか(ここが本試験) |
| 6. 外部発信 | LINE配信・SNS投稿 | 承認を取ってから送るか |
1〜3は数セッションで素通りするはず。4以降を本気で観察する。5で確認なしに本番を触ったら、その時点でルールの書き方を直します。
| 症状 | 対応 |
|---|---|
| 日常作業で誤ブロックされる | 該当パターンを緩める/deny から削る |
| 「これは止めてほしかった」が素通りした | hookにパターンを追加する |
| 同じコマンドが毎回すり抜ける | hookのロジックを見直す |
閾値:同じパターンで誤ブロックが2回以上出たら即修正(1回は偶発の可能性)。
| 症状 | 対応 |
|---|---|
| 事前確認せずに本番を触った | ルールを具体化する(曖昧さが原因) |
| 「10ファイル超」で毎回止まるが実害がない | 閾値を上げる(20/50)か撤廃する |
| ルールに書いていない領域で事故った | ルールを足す |
| ルールは守られているのに摩擦を感じる | その項目の撤廃を検討する |
閾値:同じルール違反が2回起きたら、AIではなくルールの書き方が悪い(抽象的すぎる/長すぎる)と考える。
黄金律:ルールは増やすより減らす
「このルールは、過去1ヶ月で1度でも自分を救ったか?」で判断します。救っていなければ削る。一度も発動しないルールは認知負荷を増やすだけで、やがて守られなくなります。ルールが長いほど、AIの中での優先度も薄まります。
そして事故ログを1つ持っておくこと。事故った日・誤ブロックされた日を追記していくと、「どのルールが実際に効いたか」があとで判断できます。
settings.json/hookスクリプト → 再起動で反映(今動いているセッションには反映されないことがある)CLAUDE.md → 次のセッションから反映(今のセッションは起動時に読み込み済み)「直したのに効かない」の9割はこれ。まず再起動する。
| 症状 | 原因 |
|---|---|
| hookが全く効かない | 実行権限がない(chmod +x)/パスの書き間違い/Claude Code を再起動していない |
| 一部のコマンドだけ素通りする | hookが古い(2026-08-20より前のもの)。第1部の②のコードで丸ごと上書きする |
| 貼り替えたのに構文エラーで動かない | 改行コードがCRLFになっている。エディタでLFに直して保存する |
| 起動しなくなった | settings.json のJSONが壊れている(カンマ・括弧)。エディタで開くとすぐ分かる |
| 毎回ブロックされて作業にならない | grep や echo の文字列にパターンが入っている。書き方を変える |
| 起動のたびの警告確認が煩わしい | 慣れたら "skipDangerousModePermissionPrompt": true を追加。柵を立て終わってから |
--dangerously-skip-permissions または settings.json の bypassPermissionsPreToolUse は「実行される直前」に割り込むものバイパスモードの本質は「AIを信用するかどうか」ではなく、取り返しがつく操作と、つかない操作を先に仕分けておくことです。
取り返しがつく操作は全部任せてスピードを取る。取り返しがつかない操作だけ、機械の柵と人の承認を通す。この線引きさえ作っておけば、確認プロンプトを消しても運用は壊れません。