Files
skills-win/claude-brain/scripts/check_agents.sh
T
2026-10-20 17:39:07 +08:00

112 lines
4.2 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# claude-brain 巡检:列出本会话每个子 agent 的最近活动时间、空闲时长与最后动作,
# 标出空闲超过阈值的 worker,供调度员每 30 分钟打印并决定是否唤醒。
#
# 原理:Claude Code 把每个 subagent 的转录写在
# ~/.claude/projects/<项目slug>/<会话ID>/subagents/agent-<id>.jsonl
# 旁边的 agent-<id>.meta.json 带 description(派出时的 description 参数)与 model。
# 转录文件的 mtime 就是该 agent 最近一次活动;最后几 KB 能看出它停在什么动作上。
#
# 用法(在 claude-brain 运行时所在的工程根目录执行):
# check_agents.sh [运行目录] [--threshold 30] [--hours 24] [--session <会话ID>] [--ids id1,id2]
# 运行目录给出时,末尾顺带打印其中的 agents.md 名册。
# --ids 只看这些 agent(前缀即可,逗号分隔);不给则列出时间窗内所有有转录的 agent。
# 输出:先一行 "巡检 HH:MM",然后 markdown 表。判定列:
# 正常 / 空闲超限 / 正常(大脑等待不计) / 已结束?(回合结束且空闲超限,需对照名册)
threshold=30
hours=24
session=""
ids=""
rundir=""
while [ $# -gt 0 ]; do
case "$1" in
--threshold) threshold="$2"; shift 2 ;;
--hours) hours="$2"; shift 2 ;;
--session) session="$2"; shift 2 ;;
--ids) ids="$2"; shift 2 ;;
*) rundir="$1"; shift ;;
esac
done
cwd="$(pwd -W 2>/dev/null || pwd)"
slug="$(printf '%s' "$cwd" | sed -e 's#[:/\\_]#-#g')"
proj="$HOME/.claude/projects/$slug"
if [ ! -d "$proj" ]; then
echo "找不到项目转录目录: $proj" >&2
echo "请在 claude-brain 运行时所在的工程根目录执行本脚本。" >&2
exit 1
fi
mins=$((hours * 60))
if [ -n "$session" ]; then
files=$(find "$proj/$session/subagents" -name 'agent-*.jsonl' 2>/dev/null)
else
files=$(find "$proj" -path '*/subagents/agent-*.jsonl' -mmin "-$mins" 2>/dev/null)
fi
now=$(date +%s)
echo "巡检 $(date +%H:%M)(阈值 ${threshold} min,时间窗 ${hours} h)"
echo
echo "| agent | 角色/任务 | 模型 | 最近活动 | 空闲 | 最后动作 | 判定 |"
echo "|---|---|---|---|---|---|---|"
over=0
# 按 mtime 新→旧排序
for f in $(ls -t $files 2>/dev/null); do
id=$(basename "$f" .jsonl | sed 's/^agent-//')
if [ -n "$ids" ]; then
hit=0
for p in $(printf '%s' "$ids" | tr ',' ' '); do
case "$id" in "$p"*) hit=1 ;; esac
done
[ $hit -eq 1 ] || continue
fi
meta="${f%.jsonl}.meta.json"
desc="?"; model="?"
if [ -f "$meta" ]; then
desc=$(grep -o '"description":"[^"]*"' "$meta" | head -n1 | sed 's/"description":"//;s/"$//')
model=$(grep -o '"model":"[^"]*"' "$meta" | head -n1 | sed 's/"model":"//;s/"$//')
fi
mt=$(stat -c %Y "$f")
idle=$(( (now - mt) / 60 ))
last=$(date -d "@$mt" +%H:%M)
tailtxt=$(tail -c 6000 "$f")
if printf '%s' "$tailtxt" | grep -q '"name":"Monitor"'; then
act="等待 Monitor"
elif printf '%s' "$tailtxt" | grep -q '"run_in_background":true'; then
act="等待后台命令"
elif printf '%s' "$tailtxt" | grep -q 'Monitor expired\|Monitor event'; then
act="收到 Monitor 事件后停住"
elif printf '%s' "$tailtxt" | tail -c 1500 | grep -q '"stop_reason":"end_turn"'; then
act="回合结束"
elif printf '%s' "$tailtxt" | grep -q '"type":"tool_use"'; then
act="工具调用中"
else
act="活动中"
fi
isbrain=0
case "$desc" in *大脑*|*brain*|*Brain*) isbrain=1 ;; esac
if [ $isbrain -eq 1 ]; then
verdict="正常(大脑等待不计)"
elif [ "$idle" -ge "$threshold" ]; then
if [ "$act" = "回合结束" ]; then verdict="已结束?(对照名册;运行中则为空闲超限)"; else verdict="**空闲超限**"; fi
over=$((over + 1))
else
verdict="正常"
fi
printf '| %s… | %s | %s | %s | %s min | %s | %s |\n' "${id:0:5}" "$desc" "$model" "$last" "$idle" "$act" "$verdict"
done
echo
if [ "$over" -gt 0 ]; then
echo "有 $over 个 agent 空闲 ≥ ${threshold} min。对照 agents.md:状态为「运行中」且报告文件不存在的,按 SKILL.md「30 分钟巡检」第 4 步介入。"
else
echo "无空闲超限的 agent。"
fi
if [ -n "$rundir" ] && [ -f "$rundir/agents.md" ]; then
echo
cat "$rundir/agents.md"
fi