mac-declutter は何をしますか?
開発者の Mac を整理整頓するための実戦的なプレイブック:AIエージェントの散らかり、死んだバックグラウンドサービス、アプリの残骸、プロキシのトラップ、パフォーマンス監査をカバーします。
コア原則:
- 削除前に棚卸しを行う。 目隠しでアンインストールしない — サイズ、最終使用日、実行状態をまず測定し、ユーザーに選択させる。
- 明示的に依頼されない限り、セッションレコードやユーザーデータには決して触れない。 キャッシュは再生成されるが、セッションはされない。
- すべての変更後に必ず確認する — プロセスが消えたか、ポートが閉じたか、ログイン項目が削除されたか。
- システム整合性保護 (SIP) は絶対的な壁である。 ルート所有のアプリ、サンドボックスコンテナ、DriverKit 拡張機能にはそれぞれ認められた削除パスがある。OS と戦ってはならない。
フェーズ 0 — ネットワークトリアージ(最初にこれを行う)
「すべての接続が切れた」という症状は、ほとんどの場合、アプリの故障ではなく、ローカルプロキシの死が原因です:
scutil --proxy # システムプロキシが指している先
nc -z -w 2 127.0.0.1 <port> # 実際に何かがリッスンしているか?
lsof -nP -iTCP:<port> -sTCP:LISTEN # 誰が所有しているか
古典的な罠:Clash/V2Ray 系のツールは macOS システムプロキシを 127.0.0.1:PORT に設定し、その後コアプロセスが死ぬ — ルールモードは関係なく、すべてのトラフィックが玄関先で止まる。システムプロキシを尊重するアプリ(ほとんどの GUI アプリ、一部の CLI)はすべて失敗する。curl はシステムプロキシをバイパスするためまだ動作し、診断を誤らせる。修正方法:プロキシコアを再起動するか、プロキシを無効にする(networksetup -setwebproxystate Wi-Fi off + -setsecurewebproxystate + -setsocksfirewallproxystate + WPAD オフ)、またはアプリ自体のプロキシ設定を無効なものに向ける。覚えておいてほしい:curl はシステムプロキシを無視する — アプリスタイルの接続テストには curl -x http://127.0.0.1:PORT を使用すること。
フェーズ 1 — 棚卸し(読み取り専用)
scripts/inventory.sh を実行するか、手動で以下の範囲をカバーします:
- エージェント設定ディレクトリ:
~/.claude、~/.codex、~/.pi、~/.jcode、~/.config/opencodeなど。それぞれについて:サイズ(du -sh)、最終活動日(最新ファイルの mtime)、CLI バイナリがまだ存在するか(which)。 - ログイン/認証状態:
auth.json、openai-auth.json、資格情報ファイル — キーをリストアップするが、機密情報は決して出力しない。 - バックグラウンドサービス:
launchctl list | grep -v com.appleと~/Library/LaunchAgents/*.plist— 各 plist のProgramArgumentsを読み、実際に何を実行するかを把握する。 - アプリ:
/Applicationsと~/Applicationsにある Apple 製以外の.appについて、サイズ、kMDItemLastUsedDate(mdls経由)、_MASReceiptの有無(App Store アプリ)を収集する。「一度も開かれていない」+サイズ大=主要な削除候補。 - キャッシュ:
~/Library/Caches、~/.npm、pip/bun のキャッシュ — 大きく、常に安全にパージできる。
結果を保持/削除のテーブルで表示し、ユーザーに選択させる。有料/サブスクリプションアプリ(例:_MASReceipt、既知のサブスクリプション製品)にはフラグを立て、削除前に課金をキャンセルするようユーザーに促す。
フェーズ 2 — アンインストールプレイブック
対象ごとに、以下の順序で:
- サービスの停止: 関連するすべての LaunchAgent/Daemon に対して
launchctl remove <ラベル>を実行。 - plist の削除:
~/Library/LaunchAgents/<ラベル>.plistを削除。 - アプリ/CLI の削除:
rm -rfで.appを削除 /npm uninstall -g <パッケージ>/ バイナリを削除。Permission denied→ ルート所有(pkg インストール)。osascript -e 'do shell script "rm -rf ..." with administrator privileges'を使用する(GUI パスワードプロンプトが1回表示される)。決してsudoを盲目的に使わない。
- Library の残骸:
~/Library/{Application Support,Caches,Containers,HTTPStorages,Preferences}/<アプリ名またはバンドルID>を確認し、削除する。- サンドボックスの
Containers/*はルートでも削除できない場合がある(コンテナマネージャーの保護)。サイズが小さければそのままにしておく — OS が孤立したコンテナを回収する。SIP を無効にする価値は決してない。
- サンドボックスの
- システム拡張機能:
systemextensionsctl listで確認。SIP が直接のアンインストールをブロックする — 唯一の安全な方法は、ベンダーアプリを再インストールし、その公式アンインストーラー(/Library/Application Support/<ベンダー>/以下のuninstall.shを探す)を実行すること。DriverKit.dextドライバーは次回の再起動までアンロードされない — 再起動後には消えているとユーザーに伝える。 - 古いログイン項目: アプリを削除した後、「ログイン時に開く」エントリがゾンビとして残ることがよくある。
osascript -e 'tell application "System Events" to delete login item "<名前>"'で削除する。 - 実行中の残骸: 削除されたアプリが、アップデータの一時ディレクトリ(例:Squirrel の
ShipItステージング)からプロセスを残していることがある。ps aux | grep -i <名前>で確認し、それらを kill し、$HOME内のデータディレクトリ(~/.<アプリ名>)を確認する — アップデータが起動したデーモンは、そこに数百 MB を隠していることがよくある。
フェーズ 3 — キャッシュパージ(リスクゼロ)
npm cache clean --force
rm -rf ~/Library/Caches/pip ~/.bun/install/cache
rm -rf ~/Library/Caches/<削除したアプリ>/*
キャッシュは再生成される。セッション履歴(sessions/、projects/、*.jsonl 会話ログ)は再生成されない — ユーザーが明示的にそうしてほしいと言わない限り、それらは残しておく。
フェーズ 4 — パフォーマンス監査
memory_pressure | tail -2 # 空き % と圧力
sysctl -n vm.swapusage # 使用スワップは正常なマシンでは ~0 であるべき
sysctl -n vm.loadavg
ps -axm -o rss,comm | awk 'NR>1 {rss=$1; $1=""; sub(/^ /,""); a[$0]+=rss} END {for (k in a) printf "%d\t%s\n", a[k]/1024, k}' | sort -rn | head -15
- アクティビティモニタの「使用済みメモリ」にはファイルキャッシュが含まれている — GB 数ではなく、メモリプレッシャーの色とスワップで判断する。
- 大量削除後、Spotlight(
mds_stores、corespotlightd)は数時間にわたって再インデックスを行う — これは一時的なものであり、「修正」しようとしてはならない。 - RSS をアプリファミリー別にグループ化する(ブラウザやチャットアプリは多くのヘルパーを生成する)。プロセスごとのノイズではなく、ファミリーごとの合計を報告する。
- 問題がないものを最適化しない:スワップがゼロでメモリプレッシャーが緑のマシンで、日常的に使っているドライバー(例えばスマート IME)の UX を数百 MB のために犠牲にするのは悪いトレードオフである — そのように伝える。
フェーズ 5 — 最終確認
- 削除したラベルが
launchctl listに存在しないこと。plist が削除されていること。削除した名前に一致するプロセスが起動していないこと(再起動待ちのドライバーを除く)。 - プロキシポートのリッスン状態が
scutil --proxyと一致していること。主要な API エンドポイントに到達可能であること(401= 到達可能だが認証が必要、これは合格)。 - 残存する各エージェント CLI がエンドツーエンドの実際のテストに合格すること(1行のプロンプトを送信し、応答があることを確認)。
df -h /の前後でスコアを確認する。