CLAUDE CODE/確認プロンプトを外して走らせる型

バイパスモード運用ルール ひな形安全柵の立て方・慣らし方・育て方

「毎回の確認が面倒だから外したい。でも事故らせたくない」人向けの、そのまま真似できる運用ルール一式。

2026-08-20 更新

すでに設定した方は、hookを貼り替えてください

第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分)

  1. 第1部の②のコードをコピーして ~/.claude/hooks/block-dangerous-bash.sh丸ごと上書き
  2. 第1部の①の settings.json の hooks ブロックを差し替え(hookが起動できなかったときの受け皿を足しました)
  3. Claude Code を再起動
  4. 第1部の③の確認を2つとも試す

jq は不要になりました。Windowsの方も Git for Windows(Git Bash)があれば動く作りになっています(実機での確認は未了です)。

使い方(3ステップ)

  1. このページのURLをそのまま自分の Claude Code に貼って「これを読んで、この通りにセットアップして」と頼む(設定ファイルとhookを作るところまでやってくれる)
  2. 第2部の「本番リスト」を自分の環境に合わせて埋め、第2部全体を ~/.claude/CLAUDE.md(毎回自動で読まれる指示書)に貼る
  3. 第3部の順番で、軽いタスクから慣らす

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が忘れた瞬間に柵がなくなる。機械で止められるものは機械で、無理なものだけルールで。

第1部|システム層をつくる(そのままコピペ)
1

~/.claude/settings.json に追記する

permissionshooks の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秒に任せる)。短く設定すると、重い処理の最中に安全柵のほうが先に力尽きて無効になることがあります。

2

hook本体をつくる

~/.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に変わることがあり、そうなるとスクリプトが構文エラーで動きません(①の受け皿が拒否側に倒すので、素通りにはなりません)。

3

動くか確かめる

Claude Code を再起動してから、次の2つを頼みます。どちらも存在しないパスなので、万一実行されても何も消えません。

①「~/存在しないフォルダ_testrm -rf で消して」

②「rm -rfv ~/存在しないフォルダ_test を実行して」

②が今回ふさいだ穴です。2つとも「ブロックしました(安全柵)」と出れば成功。①だけ止まって②が実行された場合は、hookの貼り替えができていません(古いままになっています)。

実行されてしまう場合は、hookのパス・実行権限(chmod +x)・Claude Code を再起動したか・settings.json のJSONが壊れていないか、を順に確認します。

⚠ この柵の限界(先に知っておく)

第2部|行動層(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分動かれると、暴走か進行か判断できない
第3部|段階的に慣らす

いきなり本番業務で使わない。危険度の低いものから順に上げて、誤ブロックとAIの脱線を観察します。

段階試すこと見るポイント
1. 読み取りファイルを読む/一覧を出す/Notion検索普通の作業が誤ブロックされないか
2. ローカル編集ファイル作成・編集、スクリプト実行摩擦ゼロを体感できるか。余計な編集をしないか
3. gitadd / commit / 通常の pushコミットは通り、force が要る場面で止まるか
4. 外部ツール(本番以外)テスト用Notionページの更新、下書き作成自分から動けているか
5. 本番系会員サイト・LP・本番DBに関わる更新事前確認ルールを守るか(ここが本試験)
6. 外部発信LINE配信・SNS投稿承認を取ってから送るか

1〜3は数セッションで素通りするはず。4以降を本気で観察する。5で確認なしに本番を触ったら、その時点でルールの書き方を直します。

第4部|ルールの育て方

システム層(hook)を変えるとき

症状対応
日常作業で誤ブロックされる該当パターンを緩める/deny から削る
「これは止めてほしかった」が素通りしたhookにパターンを追加する
同じコマンドが毎回すり抜けるhookのロジックを見直す

閾値:同じパターンで誤ブロックが2回以上出たら即修正(1回は偶発の可能性)。

行動層(CLAUDE.md)を変えるとき

症状対応
事前確認せずに本番を触ったルールを具体化する(曖昧さが原因)
「10ファイル超」で毎回止まるが実害がない閾値を上げる(20/50)か撤廃する
ルールに書いていない領域で事故ったルールを足す
ルールは守られているのに摩擦を感じるその項目の撤廃を検討する

閾値:同じルール違反が2回起きたら、AIではなくルールの書き方が悪い(抽象的すぎる/長すぎる)と考える。

黄金律:ルールは増やすより減らす

「このルールは、過去1ヶ月で1度でも自分を救ったか?」で判断します。救っていなければ削る。一度も発動しないルールは認知負荷を増やすだけで、やがて守られなくなります。ルールが長いほど、AIの中での優先度も薄まります。

そして事故ログを1つ持っておくこと。事故った日・誤ブロックされた日を追記していくと、「どのルールが実際に効いたか」があとで判断できます。

付録

変更がいつ反映されるか

「直したのに効かない」の9割はこれ。まず再起動する。

よくある詰まり

症状原因
hookが全く効かない実行権限がない(chmod +x)/パスの書き間違い/Claude Code を再起動していない
一部のコマンドだけ素通りするhookが古い(2026-08-20より前のもの)。第1部の②のコードで丸ごと上書きする
貼り替えたのに構文エラーで動かない改行コードがCRLFになっている。エディタでLFに直して保存する
起動しなくなったsettings.json のJSONが壊れている(カンマ・括弧)。エディタで開くとすぐ分かる
毎回ブロックされて作業にならないgrepecho の文字列にパターンが入っている。書き方を変える
起動のたびの警告確認が煩わしい慣れたら "skipDangerousModePermissionPrompt": true を追加。柵を立て終わってから
バイパスモード
実行確認をスキップするモード。--dangerously-skip-permissions または settings.json の bypassPermissions
hook(フック)
ツール実行の前後に自動で走らせる自前のスクリプト。PreToolUse は「実行される直前」に割り込むもの
permissions.deny
Claude Code 本体が持つ拒否リスト。指定パターンのコマンドを実行させない
CLAUDE.md
起動時に毎回自動で読まれる指示書。ここに書いたことは「毎回言わなくてもいい指示」になる
rm -rf
フォルダを中身ごと問答無用で消すコマンド。ゴミ箱を経由しないので復元できない
git push --force
リモートの履歴を自分の履歴で上書きする操作。他人の作業を消しうる
フェイルクローズ
仕組みが正常に動かないときに「止める側」へ倒す考え方。逆に素通りさせてしまうのがフェイルオープンで、安全柵としては失格になる
さいごに

バイパスモードの本質は「AIを信用するかどうか」ではなく、取り返しがつく操作と、つかない操作を先に仕分けておくことです。

取り返しがつく操作は全部任せてスピードを取る。取り返しがつかない操作だけ、機械の柵と人の承認を通す。この線引きさえ作っておけば、確認プロンプトを消しても運用は壊れません。