「Claude Code環境」シリーズです。前回のターミナルウィンドウ自動タイリングから引き続き、環境の「肥大化を防ぐ」話をします。
~/.claude/agents/ に定義したサブエージェントが、実際に何回呼ばれているか把握できていますか。私は把握できていませんでした。エージェントを追加し続けた結果、今朝の集計では 30日間で一度も呼び出されていないエージェントが44本あると判明しました。この記事では、Stop hookでサブエージェント呼び出しをJSONLに記録し、日次レポートでゾンビを炙り出して刈る仕組みを実コード付きで説明します。
困りごと:「定義した」と「使われた」の乖離
Claude Codeのエージェントは .claude/agents/*.md に frontmatter を書くだけで追加できます。手軽なぶん「とりあえず定義」が溜まります。問題は、使われていないエージェントの存在を確認する手段がないことです。
- セッションをまたいで呼び出し回数を集計する仕組みがない
-
INDEX.mdを手で書くと即腐る - 「削除していいか」を判断する根拠がない
使用率ゼロのエージェントは、起動時のコンテキスト注入で場所だけ取ります。放置すれば「定義してあるのに使われない → 機能しているか不明 → 削除もできない」のゾンビ状態になります。
全体の流れ
3つのコンポーネントで構成しています。
セッション終了
└─ Stop hook (stop_agent_tracker.sh)
└─ transcript.jsonl を解析 → agent-invocations.jsonl に追記
│
毎日 10:15 launchd ──────┘
└─ agent-usage-summary.sh 7d 30d
└─ agent-usage-latest.md(Top10 + 0回リスト)
手動 or cron
└─ agents-index.sh → INDEX.md 自動再生成
Step 1:Stop hookでJSONLに記録する
~/.claude/hooks/stop_agent_tracker.sh がセッション終了時に動きます。Stop hookのstdinにはセッション情報と transcript_path が渡されるので、そのtranscriptを解析してAgentツール呼び出しを拾います。
# stop_agent_tracker.sh(抜粋)
# stdin: {"session_id":"...","transcript_path":"...","hook_event_name":"Stop",...}
# 出力: ~/.claude/logs/agent-invocations.jsonl
OUT_LOG="$LOG_DIR/agent-invocations.jsonl"
Python部分の核心はtranscriptの2パス解析です。
# 第1パス: tool_use (name="Agent") と tool_result をインデックス化
uses = {} # id -> (ts, name, input, caller)
results = {} # tool_use_id -> (ts, is_error)
for b in content:
if btype == "tool_use" and b.get("name") == "Agent":
inp = b.get("input") or {}
if "subagent_type" not in inp:
continue
uses[uid] = (ts, b.get("name"), inp, b.get("caller"))
elif btype == "tool_result":
results[rid] = (ts, bool(b.get("is_error")))
Claude Codeのtranscriptでは「Task」ツールは name="Agent" として記録されます。subagent_type は input.subagent_type にあります。コード中でも同じコメントが入っています。
重複防止のため、同一 session_id + tool_use_id の組み合わせはスキップします(同じセッションで複数回Stop hookが走っても二重書きしない)。
JSOBLの1レコードはこんな形です。
{"ts": "2026-05-28T16:27:41.766Z", "session_id": "TEST-AGENT-TRACKER-001",
"cwd": "~", "tool_use_id": "toolu_014MM...", "subagent_type": "general-purpose",
"description": "launchd + cron 総監査", "duration_ms": 177, "status": "ok",
"caller": {"type": "direct"}}
現在546レコード・約176KBが蓄積されています。
Step 2:7d/30d窓でTop10と0回エージェントを出す
~/.claude/scripts/agent-usage-summary.sh が集計を担います。外部ライブラリなしのpython3インラインです。
# 使い方
agent-usage-summary.sh # デフォルト 7d
agent-usage-summary.sh 30d # 30日
agent-usage-summary.sh 7d 30d # 両方(launchdはこれ)
内部は3ステップです。
# ① JOSNLをロードしてウィンドウでフィルタ
cutoff = now - td
recent = [r for r in records if r["_dt"] >= cutoff]
# ② subagent_type でカウント + エラー数
counts = Counter(r.get("subagent_type", "") for r in recent if r.get("subagent_type"))
errors = Counter(r.get("subagent_type", "") for r in recent if r.get("status") == "error")
# ③ ~/.claude/agents/*.md のファイル名一覧と突き合わせて未使用を出す
known_agents = set()
for fp in glob.glob(os.path.join(agents_dir, "*.md")):
known_agents.add(os.path.splitext(os.path.basename(fp))[0])
unused = sorted(known_agents - set(counts.keys()))
今朝の実出力(~/.claude/logs/agent-usage-latest.md)がこれです。
=== Agent usage (last 7d) ===
total invocations: 127 unique types: 5
Top 10:
agent calls errors
general-purpose 76 0
Explore 45 0
Content Creator 2 0
fork 2 0
reviewer 2 0
0-call agents (defined locally but not used in 7d): 47
- a11y-architect
- architect
- build-error-resolver
- code-architect
- code-explorer
- code-reviewer
...
7日で呼ばれたのは5種類、呼ばれていないのは47本。30日窓でも44本が0回のままです。general-purpose と Explore で全体の95%を占めています。
Step 3:agents-index.shでINDEX.mdを自動再生成
「削除していいか」を判断するには、各エージェントが何を想定して書かれたかを横断的に見る必要があります。agents-index.sh が ~/.claude/agents/*.md の frontmatter を読んで INDEX.md を作ります。
# 簡易frontmatterパーサ(PyYAML依存なし)
m = re.match(r"^---\n(.*?)\n---\n", text, flags=re.DOTALL)
body = m.group(1)
for line in body.split("\n"):
k, _, v = line.partition(":")
out[k.strip()] = v.strip()
出力の INDEX.md はこんな表になります。
<!-- AUTO-GENERATED by ~/.claude/scripts/agents-index.sh — DO NOT EDIT MANUALLY -->
# Agents Index (51 agents · 2026-07-14 10:15)
| Name | Model | Description | Tools |
|------|-------|-------------|-------|
| `general-purpose` (general-purpose.md) | - | General-purpose agent for... | * |
...
--json フラグを付けると .index.json も書き出されます。cost-tracker等でプログラマブルに再利用できます。
frontmatter に name か description が欠けているファイルは ⚠️ Validation warnings セクションに列挙されます。
Step 4:launchdで日次レポートを自動生成
~/Library/LaunchAgents/com.shun.agent-usage-daily.plist が毎日10:15に走ります。
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>15</integer>
</dict>
実行コマンドはシンプルです。
<string>/bin/zsh -c
~/.claude/scripts/agent-usage-summary.sh 7d 30d
> ~/.claude/logs/agent-usage-latest.md 2>&1</string>
PATHにnvmとHomebrewを明示しているのは、launchdのデフォルトPATHだと python3 が素通りするためです。
これで毎朝10:15に agent-usage-latest.md が更新され、「30日0回のエージェントは何本か」が常に分かります。
運用フロー:ゾンビを刈る
週に一度、このコマンドを叩きます。
# INDEX再生成(frontmatter変更・追加後に必ず走らせる)
~/.claude/scripts/agents-index.sh
# 0回リスト確認
cat ~/.claude/logs/agent-usage-latest.md | grep -A 9999 "0-call agents"
判断基準は次のようにしています。
| 状態 | 対応 |
|---|---|
| 30d 0回・description を読んで使う想定がない | 削除 |
| 30d 0回・将来使う想定がある | description に使用条件を明記して残す |
| 7d 0回・30d 数回 | 季節ジョブの可能性あり・保留 |
| Top5常連 | 権限・ツール定義を見直してさらに研ぎ澄ます |
今回の棚卸しで a11y-architect、django-reviewer、go-build-resolver など30日0回が確定した14本を削除しました。INDEX.mdから消えるだけで他は何も変わりません。
踏んだ落とし穴
-
name="Task"でフィルタしてもヒットしない → Claude CodeのtranscriptではTask呼び出しがname="Agent"として記録される。最初これで全レコードが空だった -
同一セッションで複数回Stop hookが走り二重書きが発生 →
seen_idsで(session_id, tool_use_id)の重複チェックを入れて解消 -
subagent_typeが空文字列のレコードが混入 →if r.get("subagent_type")でフィルタする(空文字falsy判定で十分) -
agents-index.shが
INDEX.md自身を解析してループ →if p.name == "INDEX.md": continueで除外 -
launchdの最小PATHで
python3が見つからない → plistのEnvironmentVariablesにnvmパスとHomebrewパスを明示 -
30d窓で0回のエージェントが7d窓では「存在しない」扱いになる →
known_agentsはagents_dirのファイル一覧から取るので窓に依存しない。正しい動作
まとめ
- Stop hookがsession_id + tool_use_idの組でJSONLに追記 → セッションまたぎで呼び出し履歴が蓄積される
-
agent-usage-summary.sh は7d/30d窓でTop10と0回エージェント一覧を出す。
~/.claude/agents/*.mdのファイル名と突き合わせるので、新しいエージェントを追加すれば自動的に追跡対象に入る - agents-index.sh がfrontmatterからINDEX.mdを自動再生成。手書きINDEXは即腐るのでこれで代替する
-
launchd が毎日10:15に集計を走らせて
agent-usage-latest.mdに書き出す。「今日の時点で何本ゾンビか」が常に分かる - 30日0回が確定したエージェントは削除する。削除の根拠が数値で取れると迷わない
次回は、このログデータをもとに「どの作業でどのエージェントを組み合わせているか」のパターンを可視化した話を書きます。
Lily(@bokuwalily)― 個人開発者。Claude Code で自動化基盤を組みながら、iOSアプリやWebサービスを量産しています
- 制作物・記事は bokuwalily.com にまとめています🖥️
- AIで「寝てても回る仕組み」を作って月120万にした話は noteの有料記事 に💰
- OSS: github.com/bokuwalily 🐙
- 最新情報・お問い合わせは X @bokuwalily へ🌍
皆さんの ❤️ やシェアが励みになります!