ãšãŒãžã§ã³ãåŒã³åºããå šèšé²ãã â Stop hook à transcript_path ã§äœ¿çšé »åºŠãã°ãèªåæ§ç¯
ãClaude Codeç°å¢ãã·ãªãŒãºã®åäœ ãŸã³ããšãŒãžã§ã³ããèªåã§åã話 ã§ã¯å®çŸ©ãããŠããã®ã«äžåºŠã䜿ãããªããšãŒãžã§ã³ããçºèŠããè©±ãæžããŸãããä»åã¯ãã®æ°åã®åºã©ãã ââ Stop hook ã®ãã€ããŒãã«å«ãŸãã transcript_path ã Python ã§èªãã§ããšãŒãžã§ã³ãåŒã³åºããèªåçã« JSONL ã«ç©ã¿äžããä»çµã¿ ã®è©±ã§ãã
ã㟠~/.claude/logs/agent-invocations.jsonl ã«ã¯ 553ä»¶ ã®åŒã³åºãèšé²ãæºãŸã£ãŠããŸããçŽè¿7æ¥ã®éèšã§ã¯ general-purpose ã58åã»Explore ã24åããã㊠47åã®ãšãŒãžã§ã³ããäžåºŠãåŒã°ããŠããªã ãšããäºå®ãããã®ä»çµã¿ãªãã«ã¯èŠããŸããã§ããã
å°ãããšïŒããã®ãšãŒãžã§ã³ããæ¬åœã«äœ¿ã£ãŠãïŒããåãããªã
~/.claude/agents/ 以äžã«ãšãŒãžã§ã³ãå®çŸ©ãå¢ããŠãããšãèªåã§æžããŠãããªãããããæåŸã«ãã€äœ¿ã£ãïŒããåãããªããªããŸãã䜿ãããŠããªãå®çŸ©ã¯ã³ã³ããã¹ã泚å
¥ãå¢ããã ãã§æå®³ã§ãã
Claude Code ã«ã¯ã»ãã·ã§ã³çµäºæã« Stop hook ãåŒã³åºãä»çµã¿ãããããã®ãã€ããŒãã« transcript_path ãå«ãŸããŠããŸãããããäŒè©±å
šäœã®ãã°ãã¡ã€ã«ãžã®ãã¹ã§ãããããèªãã°ãã©ã®ãšãŒãžã§ã³ããããã€ãäœç§ãããŠåŒãã ãããå
šéšåããŸãã
å šäœã®æµã
ã»ãã·ã§ã³çµäº â Stop hook çºç« â stop_hooks_combined.sh ããã€ããŒããåãåã â stop_agent_tracker.sh ãžæž¡ã â Python ã transcript ãèªãã§ JSONL ã«è¿œèšã
å®éã® ~/.local/bin/stop_hooks_combined.sh ã®é
ç·ã¯ãããªã£ãŠããŸãã
# stdin â tmpfile ã«ä¿åããŠè€æ° hook ãžé 次枡ã
cat > "$PAYLOAD"
for hook in \
"$HOME/.claude/hooks/stop_notify.sh" \
"$HOME/.claude/hooks/stop_cost_log.sh" \
"$HOME/.claude/hooks/stop_agent_tracker.sh" \
"$HOME/.claude/hooks/stop_session_summary.sh" \
"$HOME/.discord/stop_post_session.sh"
do
[ -x "$hook" ] && "$hook" < "$PAYLOAD" || true
done
Stop hook 㯠stdin ã§ãã€ããŒããåãåããŸããè€æ° hook ã«æµããã tmpfile ãçµç±ããåããã¯ãž < "$PAYLOAD" ã§ãªãã€ã¬ã¯ãããŠããŸãã
stop_agent_tracker.sh ã®å®è£
ã¹ã¯ãªããå šäœã¯ bash ã®ã©ãããŒãš Python ã€ã³ã©ã€ã³å®è¡ã®2å±€ã«ãªã£ãŠããŸãã
#!/usr/bin/env bash
set -uo pipefail
LOG_DIR="$HOME/.claude/logs"
OUT_LOG="$LOG_DIR/agent-invocations.jsonl"
INPUT=$(cat)
export STOP_INPUT="$INPUT"
export OUT_LOG_PATH="$OUT_LOG"
python3 - <<'PY'
# ...
PY
bash åŽã¯ stdin ãåãåã£ãŠç°å¢å€æ°ã«ã»ããããã ããåŠç㯠Python ã«ä»»ããŸãã
第1ãã¹ïŒtranscript ãå šèµ°æ»ããŠã€ã³ããã¯ã¹å
uses = {} # tool_use_id -> (ts, name, input, caller)
results = {} # tool_use_id -> (ts, is_error)
with open(tp, "r", encoding="utf-8", errors="replace") as f:
for line in f:
rec = json.loads(line)
content = rec.get("message", {}).get("content")
if not isinstance(content, list):
continue
for b in content:
btype = b.get("type")
if btype == "tool_use" and b.get("name") == "Agent":
inp = b.get("input") or {}
if "subagent_type" not in inp:
continue
uid = b.get("id")
uses[uid] = (rec.get("timestamp"), b.get("name"), inp, b.get("caller"))
elif btype == "tool_result":
rid = b.get("tool_use_id")
if rid:
results[rid] = (rec.get("timestamp"), bool(b.get("is_error")))
ãã€ã³ã㯠name == "Agent" ã§çµãããšãClaude Code ã® transcript ã§ã¯ Task ããŒã«ã name: "Agent" ãšããŠèšé²ãããŸããsubagent_type ã input ã«å
¥ã£ãŠãããã®ã ãã察象ã§ãçŽ ã® claude åŒã³åºããšã¯åºå¥ãããŸãã
éè€é²æ¢ïŒsession_id à tool_use_id ã§æ¢èšé²ãã¹ããã
Stop hook ã¯åäžã»ãã·ã§ã³ã§è€æ°åçºç«ããããšããããŸãïŒ/clear ãé·ãã»ãã·ã§ã³ïŒãéè€ãªãã«èšé²ãããããæžã蟌ã¿åã«æ¢åãã°ãèããŸãã
seen_ids = set()
if os.path.exists(out_path):
with open(out_path, "r", encoding="utf-8", errors="replace") as f:
for line in f:
r = json.loads(line)
# åäžã»ãã·ã§ã³å
ã®åäž tool_use_id ã ããã¹ããã
if r.get("session_id") == sid and r.get("tool_use_id"):
seen_ids.add(r["tool_use_id"])
session_id ãš tool_use_id ã®çµã¿åãããäžææ§ã®éµã§ããtool_use_id ã ãã§åŒŸããšãç°ãªãã»ãã·ã§ã³ã§å¶ç¶ ID ãè¡çªããå Žåã«èšé²æŒããèµ·ããŸãã
duration_ms ã®ç®åº
tool_use ã¬ã³ãŒãã®ã¿ã€ã ã¹ã¿ã³ããšã察å¿ãã tool_result ã®ã¿ã€ã ã¹ã¿ã³ãã®å·®åããšãŒãžã§ã³ãã®å®è¡æéã§ãã
def parse_ts(s):
if not s:
return None
return datetime.datetime.fromisoformat(s.replace("Z", "+00:00"))
t0 = parse_ts(use_ts)
t1 = parse_ts(res_ts)
if t0 and t1:
duration_ms = int((t1 - t0).total_seconds() * 1000)
tool_result ããŸã æ¥ãŠããªãïŒã»ãã·ã§ã³äžæãªã©ïŒå Žå㯠status: "pending" ãšã㊠duration_ms: null ã§èšé²ããŸãã
åºåã¬ã³ãŒãã®åœ¢åŒ
{
"ts": "2026-05-28T16:27:41.766Z",
"session_id": "sess_xxx",
"cwd": "~",
"tool_use_id": "toolu_014MMSdJubC215oLCxfcrjok",
"subagent_type": "general-purpose",
"description": "launchd + cron ç·ç£æ»",
"duration_ms": 177,
"status": "ok",
"caller": {"type": "direct"}
}
description 㯠300æåã§ã¯ãªããããŠããŸããtranscript äžã® description ãã£ãŒã«ãã¯èªç±èšè¿°ãªã®ã§ããŸã«é·å€§ã«ãªããŸãã
agent-usage-summary.sh ã§éèšãã
æºãŸã£ã JSONL ãéèšããã®ã ~/.claude/scripts/agent-usage-summary.sh ã§ãã
agent-usage-summary.sh # ããã©ã«ã 7d
agent-usage-summary.sh 30d # 30æ¥
agent-usage-summary.sh 7d 30d # äž¡ãŠã£ã³ããŠåæ
å®è¡ãããšããåºãŸãïŒä»æ¥ã®å®æž¬å€ïŒã
=== Agent usage (last 7d) ===
total invocations: 86 unique types: 4
Top 10:
agent calls errors
general-purpose 58 0
Explore 24 0
fork 2 0
reviewer 2 0
0-call agents (defined locally but not used in 7d): 47
- INDEX
- a11y-architect
- architect
- build-error-resolver
- code-architect
...
general-purpose ã58åã»Explore ã24åããã®2çš®ã§7æ¥ã®åŒã³åºãã®95%ãå ããŠããŸãããã㊠47åã®ãšãŒãžã§ã³ããäžåºŠãåŒã°ããŠããªããããããŸã³ããšãŒãžã§ã³ãåãã®å
¥åããŒã¿ã§ãã
~/.claude/scripts/dashboard.sh ã¯ãã®åºåãæ¯æ¥ dashboard.md ã«çµã¿èŸŒãã§åžžæå¯èŠåããŠããŸãã
echo "## ð€ Agent åŒã³åºã (7d)"
AGENT_OUT=$(~/.claude/scripts/agent-usage-summary.sh 7d 2>/dev/null)
TOP_BLOCK=$(echo "$AGENT_OUT" | awk '
/^Top 10:/ { in_block=1; next }
/^$/ && in_block { exit }
in_block { print }
' | head -5)
echo "$TOP_BLOCK"
~/.claude/agents/ã«*.mdãšããŠå®çŸ©ãããŠãããšãŒãžã§ã³ãããæ¢ç¥ãšãŒãžã§ã³ãããšããŠæ±ãããã°ã«åºçŸããªããã®ãã0-callããšããŠåæããŸããINDEX.mdãªã©ã®éãšãŒãžã§ã³ããã¡ã€ã«ãæ··å ¥ãããããå®éçšã§ã¯INDEXãæ¯å 0-call æ¬ã®å é ã«åºãŸããæ°ã«ãªãå Žåã¯known_agentsã®ååŸåã«.startswith("INDEX")ãªã©ã§é€å€ããŠãã ããã
dashboard.sh ã§ã®ããã·ã¥ããŒãçµ±å
dashboard.sh 㯠healthãauto-skills æ°ãlaunchd ãžã§ãäžèЧãªã©ãšåãã㊠agent éèšã ~/.claude/dashboard.md ã«æžãåºããŸããcron ã§ daily æŽæ°ããããšã§ãä»é±ã©ã®ãšãŒãžã§ã³ããéå®ãããŠãããããæ¯æç¢ºèªã§ããŸãã
èžãã èœãšã穎
- Stop hook ã® stdin ã¯äžåºŠããèªããªã â
stop_hooks_combined.shã tmpfile ãäœã£ãŠããåããã¯ãžãªãã€ã¬ã¯ãããèšèšã«ãããæåã¯åããã¯ã§catããããšããŠ2çªç®ä»¥éã空ã«ãªã£ãã mktempã TMPDIR å£ãã§å€±æãã âstop_hooks_combined.shã§|| PAYLOAD="/tmp/stop-hook.$$.$RANDOM.json"ã«ãã©ãŒã«ããã¯ããã空ãã¹ã®ãŸãŸãªãã€ã¬ã¯ããããšå š hook ãé»ã£ãŠ no-op ã«ãªãã- tool_use_id ã ãã§éè€æé€ãããšç°ã»ãã·ã§ã³è¡çªã§ã¬ã³ãŒããæ¶ãã â
session_id à tool_use_idã®ãã¢ã§å€å®ããããã«çŽããã name == "Task"ã§æ€çŽ¢ããŠäœãåããªã â Claude Code ã® transcript ã§ã¯ Task ããŒã«ãname: "Agent"ãšããŠèšé²ããããããã¥ã¡ã³ãã«èšèŒããªããå®ãã¡ã€ã«ã grep ããŠå€æã- description ãæ°åæåã«ãªãããšããã â 300æåã§ã¯ãªããããªããš JSONL ãèšããŠåŸã®éèšã§
json.loadsãé ããªã£ãã tool_resultãæ¥ãŠããªãç¶æ ã§ Stop hook ãçºç«ãã â ã»ãã·ã§ã³åŒ·å¶çµäºæãªã©ãstatus: "pending"ã§èšé²ããŠduration_ms: nullã«ããŠããã°éèšã¯ãšãªã§is not nullãã£ã«ã¿ã§é€å€ã§ããã
ãŸãšã
- Stop hook ã®ãã€ããŒãã«ã¯
transcript_pathããããäŒè©±å šäœã® tool åŒã³åºãå±¥æŽãèªãã name == "Agent"ãã€input.subagent_typeããã®è¡ããšãŒãžã§ã³ãåŒã³åºãsession_id à tool_use_idã§éè€æé€ããåäžã»ãã·ã§ã³ã®å€éçºç«ã«å¯Ÿå¿duration_msã¯tool_useãštool_resultã®ã¿ã€ã ã¹ã¿ã³ãå·®åã§ç®åºagent-usage-summary.shã® 0-call æ¬ããå®çŸ©ããã®ã«äœ¿ãããŠããªããšãŒãžã§ã³ããã®çºèŠè£ 眮ã«ãªã
次åã¯ããã®éèšã§æµ®ã圫ãã«ãªã£ãã䜿ãããŠããªããšãŒãžã§ã³ãããèªåã§éé¿ããä»çµã¿ ââ ãŸã³ããšãŒãžã§ã³ããèªåã§åã ã®èšèšãæžããŸãïŒãã§ã«å ¬éæžã¿ïŒã
LilyïŒ@bokuwalilyïŒâ å人éçºè ãClaude Code ã§èªåååºç€ãçµã¿ãªãããiOSã¢ããªãWebãµãŒãã¹ãéç£ããŠããŸã
- AIã§ãå¯ãŠãŠãåãä»çµã¿ããäœã£ãŠæ120äžã«ãã話㯠noteã®ææèšäº ã«ð°
- OSS: github.com/bokuwalily ð
- ææ°æ å ±ã»ãåãåãã㯠X @bokuwalily ãžð
çããã® â€ïž ãã·ã§ã¢ãå±ã¿ã«ãªããŸãïŒ