Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungI audited my own ML linter and had to withdraw its best evidence(21.09.2026 um 22:54 Uhr)
Sichere ProgrammierungQuantum Result Validation for Distributed Computing Systems(21.09.2026 um 22:54 Uhr)
Sichere ProgrammierungJWT Authentication and Role-Based Access Control in LocalHands(21.09.2026 um 22:56 Uhr)
Sichere ProgrammierungStochastic Parrot or Alien Mind?(21.09.2026 um 22:56 Uhr)
Sichere ProgrammierungBuilding AI for the Physical World Is a Different Engineering Problem(21.09.2026 um 22:58 Uhr)
Sichere ProgrammierungI audited my own ML linter and had to withdraw its best evidence(21.09.2026 um 22:54 Uhr)
Sichere ProgrammierungQuantum Result Validation for Distributed Computing Systems(21.09.2026 um 22:54 Uhr)
Sichere ProgrammierungJWT Authentication and Role-Based Access Control in LocalHands(21.09.2026 um 22:56 Uhr)
Sichere ProgrammierungStochastic Parrot or Alien Mind?(21.09.2026 um 22:56 Uhr)
Sichere ProgrammierungBuilding AI for the Physical World Is a Different Engineering Problem(21.09.2026 um 22:58 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Hunting Down Zombie Agents: Logging Subagent Usage with a Claude Code Stop Hook

Quick question: how many of the subagents you've defined in ~/.claude/agents/ have actually been called this month? I couldn't have told you — until I started measuring, and this morning's tally revealed 44 agents that hadn't been invoked e…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Quick question: how many of the subagents you've defined in ~/.claude/agents/ have actually been called this month? I couldn't have told you — until I started measuring, and this morning's tally revealed 44 agents that hadn't been invoked even once in 30 days. This post is part of my "Claude Code environment" series (a follow-up to automatic terminal window tiling), continuing the theme of keeping the setup from bloating. I'll walk through, with real code, a system that records subagent invocations to JSONL via a Stop hook and uses a daily report to flush out the zombies and cull them.






The problem: the gap between "defined" and "used"



Adding an agent to Claude Code is as simple as writing frontmatter in .claude/agents/*.md. Because it's so easy, "let's just define it for now" agents pile up. The problem is that there's no way to check whether an agent is actually being used.




  • There's no mechanism to aggregate invocation counts across sessions

  • A hand-written INDEX.md goes stale immediately

  • There's no evidence to base a "can I delete this?" decision on



An agent with zero usage still takes up space in the context injected at startup. Left alone, it becomes a zombie: defined but unused → unclear whether it even works → can't be deleted either.






The overall flow



The system consists of three components.




セッション終了
└─ 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: Record to JSONL with a Stop hook



~/.claude/hooks/stop_agent_tracker.sh runs when a session ends. The Stop hook receives session info and a transcript_path on stdin, so the script parses that transcript and picks out Agent tool invocations.




# 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"






The heart of the Python portion is a two-pass parse of the transcript.




# 第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")))







Note: In Claude Code transcripts, the "Task" tool is recorded as name="Agent". The subagent_type lives in input.subagent_type. The same comment appears in the code itself.




To prevent duplicates, the same session_id + tool_use_id combination is skipped (so even if the Stop hook fires multiple times in one session, nothing gets written twice).



A single JSONL record looks like this.




{"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"}}






So far, 546 records (about 176 KB) have accumulated.






Step 2: Produce a Top 10 and zero-call list over 7d/30d windows



~/.claude/scripts/agent-usage-summary.sh handles the aggregation. It's inline python3 with no external libraries.




# 使い方
agent-usage-summary.sh # デフォルト 7d
agent-usage-summary.sh 30d # 30日
agent-usage-summary.sh 7d 30d # 両方(launchdはこれ)






Internally it's three steps.




# ① 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()))






Here's this morning's actual output (~/.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
...






In 7 days, only 5 agent types were called and 47 weren't called at all. Even in the 30-day window, 44 remain at zero. general-purpose and Explore account for 95% of all invocations.






Step 3: Auto-regenerate INDEX.md with agents-index.sh



To decide whether something can be deleted, you need a cross-cutting view of what each agent was written for. agents-index.sh reads the frontmatter of ~/.claude/agents/*.md and builds 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()






The generated INDEX.md is a table like this.




<!-- 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... | * |
...






With the --json flag it also writes out .index.json, which can be reused programmatically by things like a cost tracker.



Files missing name or description in their frontmatter get listed under a ⚠️ Validation warnings section.






Step 4: Generate the daily report with launchd



~/Library/LaunchAgents/com.shun.agent-usage-daily.plist runs every day at 10:15.




<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>15</integer>
</dict>






The command it runs is simple.




<string>/bin/zsh -c
~/.claude/scripts/agent-usage-summary.sh 7d 30d
> ~/.claude/logs/agent-usage-latest.md 2>&1</string>






The PATH explicitly includes nvm and Homebrew because launchd's default PATH won't find python3.



With this in place, agent-usage-latest.md gets refreshed every morning at 10:15, so I always know how many agents have sat at zero for 30 days.






The operating routine: cull the zombies



Once a week, I run these commands.




# INDEX再生成(frontmatter変更・追加後に必ず走らせる)
~/.claude/scripts/agents-index.sh

# 0回リスト確認
cat ~/.claude/logs/agent-usage-latest.md | grep -A 9999 "0-call agents"






My decision criteria look like this.




























State Action
0 calls in 30d, and reading the description reveals no realistic use case Delete
0 calls in 30d, but there's a future use case Keep, and spell out the usage conditions in the description
0 calls in 7d, a few calls in 30d Possibly a seasonal job — hold off
Top 5 regular Revisit its permissions and tool definitions to sharpen it further


In this round of housekeeping I deleted 14 agents confirmed at zero calls over 30 days, including a11y-architect, django-reviewer, and go-build-resolver. They simply disappear from INDEX.md — nothing else changes.






Pitfalls I hit





  • Filtering on name="Task" matched nothing → In Claude Code transcripts, Task invocations are recorded as name="Agent". At first every record came back empty because of this


  • The Stop hook fired multiple times in one session, causing double writes → Fixed by adding a seen_ids duplicate check on (session_id, tool_use_id)


  • Records with an empty-string subagent_type crept in → Filter with if r.get("subagent_type") (the empty string being falsy is enough)


  • agents-index.sh parsed INDEX.md itself, creating a loop → Excluded with if p.name == "INDEX.md": continue


  • launchd's minimal PATH couldn't find python3 → Explicitly set the nvm and Homebrew paths in the plist's EnvironmentVariables


  • Agents at zero in the 30d window looked "nonexistent" in the 7d windowknown_agents is built from the file listing of agents_dir, so it doesn't depend on the window. This is the correct behavior






Wrap-up





  • The Stop hook appends to JSONL keyed by session_id + tool_use_id → invocation history accumulates across sessions


  • agent-usage-summary.sh outputs a Top 10 and a zero-call agent list over 7d/30d windows. Since it cross-references the filenames in ~/.claude/agents/*.md, any newly added agent is automatically tracked


  • agents-index.sh auto-regenerates INDEX.md from frontmatter. A hand-written index goes stale immediately, so this replaces it


  • launchd runs the aggregation daily at 10:15 and writes to agent-usage-latest.md, so "how many zombies do I have as of today?" is always answerable

  • Agents confirmed at zero calls over 30 days get deleted. When the case for deletion comes from numbers, there's no second-guessing



Next time, I'll use this log data to visualize which agents get combined for which kinds of work.






Written by **Lily* — I ship iOS apps and automate my content stack with Claude Code.

Follow along: Portfolio · X · GitHub*

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Hunting Down Zombie Agents: Logging Subagent Usage with a Claude Code Stop Hook

Thematisch verwandte Begriffe: Hunting, Down, Zombie, Agents · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-79918 | MaxKB is an open-source AI assistant for enterprise. Prior to version 2.…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick