やりたいこと
Claude Code にはファイル削除を禁止したい ( = 設定で "Bash(rm:*)" を禁止する)。
ただし、特定のファイルだけは自分で削除させたい (以下では永続メモリの外部ファイル)。
方針
"Bash(rm:*)" の設定の制御は難しいので、削除対象を限定した tool.sh を作る。
Claude Code には tool.sh の実行だけ許可し、tool.sh 自体の編集は禁止する。
この方法の利点
後から「この操作だけ許可したい」というのが増えたら、tool.sh に関数を追加して、エージェント定義に使用方法を記述するだけで個別操作を増やせる (手順 3. 4. は再実施不要)。
方法
- 以下の方法は、エージェントのカレントディレクトリが Claude Code 起動ディレクトリからずっと移動しない想定である。もし移動させている場合、パスの記述を修正する必要がある (私はそのような運用をしないので詳しくない)。
- 1. の想定で、エージェントに実行させるコマンドは
bash -c '.claude/tool.sh'でじゅうぶんである (はず) (投稿後に気付いた)。- つまり、
bash -c '$(pwd)/.claude/tool.sh'の$(pwd)/は不要 (なはず)。
- つまり、
手順 1. 永続メモリの外部ファイルを削除できるスクリプトを作成する
どこでもいいが私は WORK_ROOT/.claude/tool.sh に作成した。
置き場に依存しないのでユーザスコープで使うなら ~/.claude/ 以下でもよい。
#!/usr/bin/env bash
rm_memory() {
local target="$1"
local agent_memory_dir="$(pwd)/.claude/agent-memory"
[[ -n "$target" ]] || { echo '第2引数が必要です'; exit 1; }
local target_path="${agent_memory_dir}/${target}"
printf '削除します: %s\n' "$target_path"
rm "$target_path"
}
action="$1"
case "$action" in
rm-memory)
rm_memory "$2" ;;
esac
手順 2. エージェントにスクリプト (1) の使用方法を伝える
スクリプト (1) を使って永続的記憶の外部ファイルを削除するように、ちゃんと伝える。
$(pwd)/ を付ければエージェントがいる場所からのパスになる。
手元では agent に使用させるので以下では agent の定義ファイルに追記しているが、agent を指定しないセッションで使用させる場合は CLAUDE.md などに書くことになると思われる。
(前略)
- `bash -c 'source $(pwd)/.claude/tool.sh' _ rm-memory zundamon/xxx.md` &mdash
これは永続的記憶の外部ファイルが不要になったとき削除するためのコマンドです。
.claude/agent-memory/ より下のパスを渡すとそこにあるファイルを削除できます。
(後略)
手順 3. 実際にエージェントに (1) の実行を許可し、編集を禁止する
適切なスコープの settings.json に許可を追記する。編集は禁止すべきである。
{
"permissions": {
"allow": [
"Bash(bash -c 'source $(pwd)/.claude/tool.sh':*)"
]
"deny": [
"Write(./.claude/tool.sh)",
"Edit(./.claude/tool.sh)"
],
}
}
手順 4. プレツールフックを設定している場合は、忘れずに (1) を許可する
お手元の運用方法によるが、私の場合はプレツールフックでよばれるシェルスクリプトに以下のように許可を追記した。
#!/usr/bin/bash
set -euo pipefail
json=$(cat)
command=$(printf '%s' "$json" | jq -r '.tool_input.command // ""')
# (中略)
cs='source \$\(pwd\)/\.claude/tool\.sh'
pattern="^bash -c '${cs}'"
[[ "$command" =~ $pattern ]] && exit 0
exit 2
補足: $() を含むコマンドをプレツールフックで許可する場合
(手元の Windows Git Bash 環境では) コマンドに $() を含む場合は展開前の文字列をマッチさせる必要があった (プレツールフックにとんでくるのが展開前の文字列であった)。
# コマンド文字列はシングルクオートで定義し展開させずに固定する
# コマンド文字列内の $ ( ) . は正規表現特殊文字でなくそのままの文字なのでエスケープしておく
cs='source \$\(pwd\)/\.claude/tool\.sh'
# 実行コマンド全体は bach -c '...' とシングルクオートを含むのでダブルクオート文字列で定義
# (私が Windows 環境なのでこの形でないと上手く Claude Code に実行させられないだけ)
pattern="^bash -c '${cs}'"
実際、展開されていたらもう bash が使われていることになるかもしれないが、他環境でどうかは私はわからない。