Claude Media
PowerShell Remove-ItemがClaude Codeでブロックされる条件

PowerShell Remove-ItemがClaude Codeでブロックされる条件

PowerShellのRemove-Itemはシステムパス・ワイルドカードを常に拒否し、作業ディレクトリの再帰削除だけが権限モードで変わります。cmd経由のrdやdelの扱いも扱います。

Claude CodeのPowerShellツールでRemove-Itemを実行すると、対象によって扱いがまったく違います。システムパスとワイルドカードは常に拒否、作業ディレクトリの再帰削除だけが通常の権限フローに乗ります。

この違いを知らないと、2つの疑問にぶつかります。「なぜここは確認すら出さずに拒否されるのか」。そして「なぜここはbypassPermissionsでも止まらずに実行されるのか」。どちらもWindows環境で起きやすいつまずきです。読み取り専用の調査でPowerShellを許可する例はGet-WinEventでのイベントログ調査にあります。

Claude CodeでRemove-Itemが止まる3パターン

Remove-ItemはrmのCritical pathsリストとは別に、専用のチェックを持っています。対象に応じて、最初にマッチした条件が適用されます。

判定

Remove-Itemの3パターン

  • システムパス

    ルート、ドライブルート、ホームディレクトリなど。どのモードでも確認なしで拒否されます。

  • ワイルドカード

    裸の*、または/*・\*で終わる指定。分類器に届く前に、確認なしで拒否されます。

  • 作業ディレクトリへの-Recurse

    作業ディレクトリまたはその親が対象。権限モードに従い、bypassPermissionsだけがチェックを飛ばします。

上の2つは、プロンプトの応答でも許可できない拒否です。3つ目だけが条件付きで、プロンプトが出ないからといって実行されたとは限りません。逆に、確認なしで即座に消えることもあります。

システムパスとワイルドカードは常に拒否される

システムパスに分類されるのは3つの区分です。ファイルシステムのルートとその直下のトップレベルディレクトリ。Windowsのドライブルートとその直下(C:\やC:\Windowsなど)。そしてホームディレクトリです。

ワイルドカードには、$dir/*のようにシェル変数配下のグロブも含まれます。auto modeの分類器に届かず拒否されるため、分類器が「安全な削除だから通す」と判断する余地はありません。

Remove-Item C:\
Remove-Item $HOME
Remove-Item *
Remove-Item .\build\*

これらはいずれも、確認プロンプトを経由せずに拒否される代表例です。.\build\*のように、プロジェクト内の限られたフォルダでも末尾が\*なら同じ扱いになります。フォルダごと消す形(Remove-Item -Recurse .\build)に書き換えれば、ワイルドカードの拒否には当たりません。

作業ディレクトリの再帰削除は権限モードで挙動が変わる

3つ目のパターンだけは、他の承認が必要な操作と同じ扱いです。作業ディレクトリそのもの、またはその親に-Recurseを付けた削除が対象になります。

モード作業ディレクトリへのRemove-Item -Recurse
default作業ディレクトリへのRemove-Item -Recurseプロンプトで確認
auto作業ディレクトリへのRemove-Item -Recurse分類器が判定
dontAsk作業ディレクトリへのRemove-Item -Recurse拒否
bypassPermissions作業ディレクトリへのRemove-Item -Recurseこのチェック自体をスキップ

v2.1.287のclaude --helpは、--permission-modeの選択肢を6つ出します。acceptEdits・auto・bypassPermissions・manual・dontAsk・planです。表のdefaultは、helpでは別名のmanualとして並びます。

manualはdefaultの別名で、どちらの値もフラグに渡せます(manualはv2.1.200以降)。フラグの使い方はClaudeのpermission-modeフラグにまとめています。

3つ目の条件に当たらないとき、つまり-Recurseが付いていない場合や、対象が作業ディレクトリの範囲外の場合は、この専用チェックの対象外です。acceptEditsでは、作業ディレクトリの中かどうかとProtected pathsの判定が通常どおり働きます。

Remove-Item -Recurse .\node_modules
Remove-Item -Recurse ..

前者は作業ディレクトリの中のサブディレクトリが対象で、作業ディレクトリそのものでも親でもありません。そのため3つ目には当たりません。後者は作業ディレクトリの親を直接指定しているので、3つ目に該当し、権限モード通りの確認フローに乗ります。同じ-Recurse付きでも、対象の位置関係だけで扱いが変わります。

auto modeの分類器が止める変数つきの削除

auto modeの分類器にも、削除に関する既定のブロックがあります。Remove-Item -Recurse -Force $dirのように、変数が指す先を会話の中から確認できない再帰削除が止まります。変数の値が、分類器の見えない過去のコマンド出力から来ているためです。

解除する方法は2つです。削除するパスを名指しするか、Claudeに解決済みの実パスを書き込んだ形で再実行させます。分類器が対象を解決できる削除は影響を受けません。同じ$dirでも、末尾が/*や\*ならこのブロックではなく、前章の無条件拒否のほうが先に働きます。

acceptEditsで自動承認される範囲

PowerShellツールが有効なとき、acceptEditsモードは次の4つを対象範囲内のパスで自動承認します。Set-Content・Add-Content・Clear-Content・Remove-Itemです。よく使われるエイリアスも同じ扱いです。対象範囲内とは、作業ディレクトリかadditionalDirectoriesの中を指します。

範囲外のパスやProtected pathsへの書き込みは、acceptEditsでもプロンプトされます。Remove-Itemには専用チェックが別にかかるため、システムパスやワイルドカードの拒否が自動承認で覆ることもありません。

acceptEditsは、変更をエディターやgit diffで後からまとめて確認したい場面向けのモードです。削除コマンドまで自動承認が及ぶ点は、使う前に押さえておく価値があります。

bypassPermissionsでも素通りするもの、しないもの

3パターンのうち、bypassPermissionsが影響するのは3つ目だけです。このモードは、作業ディレクトリの再帰削除に対する専用チェックをスキップします。

bypassPermissionsの概要表では、用途が「隔離されたコンテナとVMのみ」とされています。作業ディレクトリやその親の再帰削除が確認なしで走るのは、この前提に沿った挙動です。

もう一段強い歯止めが、拒否ルールです。deny ruleはどのモードでも効き、bypassPermissionsも例外ではありません。次の設定なら、Remove-Itemはモードにかかわらず止まります。

{
  "permissions": {
    "deny": [
      "PowerShell(Remove-Item *)"
    ]
  }
}

PowerShellの権限ルールは、エイリアスを正規化してから照合します。よく使われるエイリアスも同じルールに当たります。例えばGet-ChildItemのルールはgci・ls・dirに当たります。大文字小文字は区別されません。複合コマンドは|や;で分割され、各部分が個別に検査されます。

rmコマンドの判定との違い

同じ削除でも、rmとRemove-Itemでは安全装置の設計が異なります。

くらべる

bypassPermissionsで残る歯止め

Critical paths

rm / rmdir

作業ディレクトリやその親を対象にしても、確認プロンプトが残ります。permissions.allowのルールや、"allow"を返すPreToolUseフックでも承認できません。

専用チェック

Remove-Item

システムパスとワイルドカードは拒否のまま。作業ディレクトリへの-Recurseは、チェック自体が外れて素通りします。

rmの側は、autoとbypassPermissionsではターミナルのプロンプトに2分間のカウントダウンが付きます。時間切れになると拒否され、無人のセッションが止まらないようになっています。1つのセッションで3回時間切れになると、以降のCritical paths削除は即時拒否です。新しいメッセージを送ると数え直しになります。

autoでも、-p・Agent SDK・VS Codeのチャット・Desktopのようにターミナル以外から動く場合は、カウントダウンなしで即時拒否です。何かキーを押すと、カウントダウンは止まります。明示的なaskルールがあれば、autoでも時間切れのない確認になります。

CLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUT=1で無効にでき、設定するとautoは分類器に回り、bypassPermissionsは無期限の待ちになります。この変数もsettings.jsonのenvブロックに書いたものは無視されます。

rmのCritical pathsには、シェル変数の扱いもあります。rm -rf "$DIR"/*のように変数の直下を消す形は、変数が空だとルートの削除になるため、Critical pathsとして止まります。rm -rf "${DIR:?}"/*のように、変数が空なら失敗するガードを付ければ通ります。Remove-Itemのワイルドカード拒否は、これに近い考え方です。ただしRemove-Itemでは、変数を使うかどうかに関係なく、末尾が/*か\*なら拒否されます。

この2系統の違いは、ProtectedPathsとCritical pathsの違いで別の角度から扱っています。

cmd経由のrd・del・eraseも同じ系統で拒否される

PowerShellツールが有効だと、Remove-Itemだけでなくcmdの削除コマンド(rd・rmdir・del・erase)にも専用チェックがかかります。Claudeがcmd /c rd /s /q C:\UsersのようにPowerShellからcmdを呼ぶ場合が対象です。システムパスへの削除は、既定でどのモードでも確認なしで拒否されます。

判定にはPowerShell特有の読み方があります。リテラルの文字に続くPowerShell変数は、空として扱われます。そのためcmd /c rd /s /q "C:\$name"はC:\の削除とみなされ、拒否されます。末尾のワイルドカードは、空にするフォルダを指すものとして扱われます。

コマンド判定
cmd /c del /q C:\*判定拒否(ドライブルートを空にする)
cmd /c del /q dist\*判定拒否されない(プロジェクト内のフォルダ)

このcmdのチェックだけは、CLAUDE_CODE_DISABLE_POWERSHELL_CMD_RM_DENY=1で無効にできます。ただし値は、Claude Codeを起動する環境で設定する必要があります。settings.jsonのenvブロックに書いても無視されます。Remove-Itemのシステムパス拒否は、この変数を設定しても残ります。

判定ルールが入ったバージョン

ここまでの挙動は、バージョンを重ねて足されてきました。各変更が入ったバージョンは次のとおりです。

バージョン

Remove-Item周辺の変更が入ったバージョン

  1. v2.1.205auto mode

    変数が指す先を確認できない再帰削除を、分類器が既定でブロックします。

  2. v2.1.281rm

    autoとbypassPermissionsで、Critical paths削除に2分のカウントダウン付きプロンプトが付きます。

  3. v2.1.283cmd

    rd・rmdir・del・eraseの、システムパスへの削除を拒否するチェックが入ります。

更新前の環境では、cmd経由の削除にこのチェックが無い可能性があります。手元のバージョンはclaude --versionで確かめられます。

PowerShellツールが使える環境かを先に確認する

Remove-Itemのこうした挙動は、PowerShellツールが有効なセッションでだけ関係します。有効化の条件は環境で違います。

  • Git BashなしのWindows: 自動的に有効
  • Git BashありのWindows: claude.aiとConsoleのアカウントで既定有効
    • Amazon Bedrock・Google CloudのAgent Platform・FoundryではCLAUDE_CODE_USE_POWERSHELL_TOOL=1が必要
  • Linux・macOS・WSL: オプトイン。PowerShell 7以降のpwshをPATHに通す必要がある

プレビュー期間中は、PowerShellプロファイルが読み込まれません。Windowsではサンドボックスも未対応です。そのため、Windowsのコマンドに効く削除の歯止めは、ここまで見た権限モードのチェックと、自分で書くdenyルールが中心になります。プロファイルが読み込まれないことの影響は、Claude Codeのシェル起動設定で扱っています。

まとめ

bypassPermissionsでも通ってしまう削除は、作業ディレクトリとその親への-Recurseだけです。そこが困る環境ではdenyルールを入れておけば、残りは組み込みの拒否に任せられます。

この記事を共有:XはてブLinkedIn