システム設定の「プライバシーとセキュリティ」→「フルディスクアクセス」を開いたら、claude がずらっと並んでいた。更新のたびに許可ダイアログが出て、そのたびに「許可」を押してきた結果である。同じ名前が縦に並ぶ一覧は、見ているだけで少し疲れる。
この記事は、Claude Codeを更新するたびにこの一覧が増える人に向けた、止め方のガイドだ。結論から言うと、更新のたびに実体の置き場所が変わることが原因で、実体を固定パスに置けば止まる。
前提は、macOSでClaude Codeをターミナルから使っていること。Claude Codeは無料プランでは使えず、Pro(月額$20)以上のサブスクリプションかAPIアカウントが要る(料金ページ)。筆者の環境はmiseでのインストールで、ネイティブインストーラとHomebrewについては公式ドキュメントと実際の配置を見て書いている。
なぜ更新のたびに許可が増えるのか
macOSのフルディスクアクセスなどの許可は、TCC(Transparency, Consent, and Control)という仕組みが管理している。TCCのデータベースは非公開なので、以下は筆者の観察に基づく理解で、Appleの公式な仕様説明ではない。
CLIツールの場合、TCCは2つのものを見ていると考えると挙動と合う。
- パス: 一覧の1行は、実体の置き場所ごとに作られる
- 署名の要件: その行の持ち主が同じかどうかを、コード署名で確かめる
ところが、多くのインストール方法はバージョンごとに別の場所へ実体を置く。
| インストール方法 | 実体の置き場所 |
|---|---|
| 公式のネイティブインストーラ | ~/.local/share/claude/versions/<バージョン> |
| Homebrewのcask | Caskroom/claude-code/<バージョン>/ |
| mise | installs/claude-code/<バージョン>/ |
更新のたびにパスが変わるので、macOSから見ると毎回「新しいアプリ」になる。
中身はほぼ同じclaudeなのに、毎回初対面の顔で挨拶される(名刺交換のたびに「はじめまして」と言われる営業先のようなものだ)。
裏を返せば、パスを固定し、署名の要件にバージョン番号が入っていなければ、新しい版を同じ場所に置いても同じ持ち主として扱われるはずだ。以下の手順はこの2点に乗っている。
手順0: この方法が効くか確かめる
先に、署名の要件にバージョン番号が入っていないことを確かめる。入っていれば、パスを固定しても版が変わるたびに別物になるので、この方法は効かない。
codesign -dr - "$(readlink -f "$(command -v claude)")"
miseをshim方式で使っている場合は、command -v claude がmise本体を指してしまう。"$(readlink -f "$(mise which claude)")" に置き換える。
筆者の環境(v2.1.292)では、要件は識別子 com.anthropic.claude-code とTeam IDだけだった。バージョン番号は含まれていない。
手順1: 固定パスへ差し替えるスクリプト
固定パスは ~/.local/opt/claude-code/claude とした。名前は何でもよいが、バージョンを含めないことだけは守る。次の内容を ~/.local/bin/pin-claude.sh などに保存する。
#!/bin/sh
# usage: pin-claude.sh [claude の実体パス] (省略時は PATH から探す)
set -eu
fixed_dir="$HOME/.local/opt/claude-code"
fixed="$fixed_dir/claude"
if [ $# -ge 1 ]; then
src="$1"
elif command -v mise >/dev/null 2>&1 && mise which claude >/dev/null 2>&1; then
src="$(mise which claude)" # mise の shim は mise 本体を指すので実体を聞く
else
src="$(command -v claude)"
fi
bin="$(readlink -f "$src")" # リンクをたどった先の実体
[ "$bin" -ef "$fixed" ] && exit 0 # 固定済み(同じファイル)なら何もしない
[ -f "$bin" ] || { echo "$bin がありません" >&2; exit 1; }
mkdir -p "$fixed_dir"
tmp="$fixed_dir/.claude.tmp.$$"
/bin/cp -c "$bin" "$tmp" # APFSクローン。大きなファイルでも即時
mv -f "$tmp" "$fixed" # 同じディレクトリ内なので rename(2) で差し替わる
ln -sf "$fixed" "$bin" # 元のパスは固定パスへのリンクにする
やっていることは、新しい版の実体を固定パスへコピーし、元の場所にはそこへのリンクを残すことだけだ。PATHやランチャはそのままで、たどり着く先が固定パスになる。コツが3つある。
- 固定済みかどうかは
-ef(同じファイルか)で比べる。 パスの文字列で比べると、$HOMEの途中にシンボリックリンクがある環境で判定をすり抜ける。すり抜けると固定パスの実体を自分自身へのリンクで上書きし、claudeが起動しなくなる。検証中に実際にこれを踏んだ - 上書きせず、隣にコピーしてから
mvで差し替える。 公式ドキュメントによれば、実行中のセッションはディスク上の実行ファイルを随時読む。同じファイルの中身を書き換えると、実行中のセッションが書きかけの中身を読みかねない。mvは同じファイルシステム内ならrename(2)で名前の指す先を付け替えるだけなので、実行中のセッションは開いている古いファイルを読み続けられる。一時ファイルを固定パスと同じディレクトリに作っているのは、必ずこの経路を通すためだ /bin/cpと絶対パスで呼ぶ。-c(APFSクローン)はmacOS標準のBSD版cpのオプションだ。nixやHomebrewでGNU coreutilsをPATHの前に入れていると、同名のcpがオプションを理解せず失敗する
手順2: 起動のたびに自動で呼ぶ
手で毎回叩くのでは、ダイアログを押す手間が別の手間に変わっただけではないだろうか。更新の直後に呼ばれるようにしたい。
定期ジョブ(launchdなど)で回す手もあるが、更新からジョブが走るまでの間にclaudeを起動すると、その1回でダイアログが出て一覧に行が増える。取りこぼしを無くすなら、起動のたびに先にスクリプトを通すのが確実だ。~/.zshrc(bashなら ~/.bashrc)に次の関数を足す。
claude() {
sh "$HOME/.local/bin/pin-claude.sh" || true # 失敗しても claude 自体は起動する
command claude "$@"
}
スクリプトは固定済みなら何もせず終わるので、毎回呼んでも害はない。筆者の環境(mise経由)で測ると、1回あたり約57msだった。この関数が効くのはシェルから claude と打った起動だけで、エディタの拡張機能などが直接起動する経路は通らない。そちらも使うなら、定期ジョブを併用して埋めておく。
この関数の中で、パスをスクリプトに渡していないのには理由がある。関数を定義すると、command -v claude は実体のパスではなく関数名の claude を返す(zshでもbashでも同じだった)。そのためスクリプト側でPATHから探させている。
偽のホームディレクトリを作り、ネイティブインストーラとmiseの配置を真似て、zshとbashで次の流れを確かめた。
| 状況 | 結果 |
|---|---|
| 初回の起動 | 実体を固定パスへ移し、元の場所はリンクになる |
| 2回目以降の起動 | 何もせず起動する(固定パスのファイルは書き換わらない) |
| 新しい版が入った直後の起動 | 起動前に新しい版を固定パスへ差し替え、新しい版が動く |
手順3: 許可を一度だけ与え直す
最後に一覧を掃除する。
readlink -f "$(command -v claude)"(miseならmise which claude)が固定パスを返すことを確認する- システム設定のフルディスクアクセスで、古い
claudeの行を削除する - 「+」で固定パスの
claudeを追加する。~/.localは隠しフォルダなので、ファイル選択のダイアログでCommand + Shift + Gを押してパスを直接入力する
インストール方法別の注意
| インストール方法 | 注意 |
|---|---|
| ネイティブインストーラ | スクリプトが差し替えるのは versions/ の下の実体で、~/.local/bin/claude のランチャには触れない。ランチャ自体を自前のリンクに置き換えることもできるが、その場合は古い版が自動で消えなくなる(公式ドキュメント) |
| Homebrew(cask) | 実体は Caskroom の版ごとのディレクトリに入る。brew upgrade の後も、上の関数が次の起動で差し替える |
| mise | PATH上の claude がshimなので、スクリプトは mise which claude で実体を探す |
caskに乗り換えれば済むのでは
「自分でインストール先をいじるより、Homebrewのcaskに乗り換えればいいのでは」と考えるのは自然だ。ところが実際は、caskも版ごとのディレクトリに実体を置くので、パスが変わる問題は残る。なのに、公式ドキュメントによればHomebrew経由のインストールは既定で自動更新されず、stableチャンネルの claude-code は最新から1週間ほど遅れる。乗り換えのコストに見合わない。
効いているかの確認
- 固定パスに許可を与えた状態で、Claude Codeを更新する
- 次に起動したとき、許可ダイアログが出ないことを見る
- フルディスクアクセスの一覧を開き、行が増えていないことを見る
手順0の確認が通っていれば、一覧は増えない見込みだ。増えた場合は、readlink -f の結果が固定パスを指しているか、PATH上に別のインストールが残っていないかを疑う。二重にインストールしていると、そちらが使われ続ける。
まとめ
| 症状 | 原因 | 対策 |
|---|---|---|
| 更新のたびに許可ダイアログが出る | 実体のパスが版ごとに変わる | 実体を固定パスに置く |
| フルディスクアクセスの一覧が増える | 同上 | 古い行を消し、固定パスに一度だけ許可 |
| 差し替えを忘れる | 手で呼んでいる | シェル関数で起動のたびに呼ぶ |
同じ構造はClaude Codeに限らない。バージョンごとにパスが変わるCLIツールに、フルディスクアクセスなどの許可を与えて使い続けているなら、同じ形で一覧が育つ。署名の要件を確かめる、実体を固定パスに置く、起動の手前で差し替える、の3点はそのまま使える。
こうした開発環境まわりの地味な手間は、気づいたときには積み上がっている。AIコーディングツールの導入や運用設計を外から整えたい場合は、AI開発支援で相談を受け付けている。
AI開発 完全ガイド — AI開発の入門から運用設計までを13本の記事で俯瞰できます。



