Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# team-pane-cleanup.sh — Agent Team tmux Pane 清理 Hook
|
||||
#
|
||||
# 用于 PostToolUse(TeamDelete) 事件
|
||||
# 读取 .claude/team-panes.json,关闭所有记录的 agent pane
|
||||
# 跳过 PM pane,仅关闭 agent pane
|
||||
#
|
||||
# 清理策略:
|
||||
# 1. Ctrl+C 终止前台进程
|
||||
# 2. 等待 3 秒让 Claude Code 优雅退出
|
||||
# 3. 无条件 kill-pane 强制关闭
|
||||
# 4. 验证 pane 确实已关闭
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
||||
STATE_FILE="${PROJECT_ROOT}/.claude/team-panes.json"
|
||||
LOG_FILE="${PROJECT_ROOT}/.claude/team-panes.log"
|
||||
|
||||
# 日志函数
|
||||
log() {
|
||||
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] team-pane-cleanup: $*"
|
||||
echo "${msg}" >&2
|
||||
echo "${msg}" >> "${LOG_FILE}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# 读取 stdin(必须读取,但不使用)
|
||||
timeout 3 cat >/dev/null 2>&1 || true
|
||||
|
||||
# 状态文件不存在 → 无需清理
|
||||
if [ ! -f "${STATE_FILE}" ]; then
|
||||
log "状态文件不存在,无需清理"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "开始清理流程"
|
||||
|
||||
# 读取 agent_pane_ids
|
||||
AGENT_PANES=$(jq -r '.agent_pane_ids[]?' "${STATE_FILE}" 2>/dev/null || true)
|
||||
PM_PANE=$(jq -r '.pm_pane_id // empty' "${STATE_FILE}" 2>/dev/null || true)
|
||||
TEAM_NAME=$(jq -r '.team_name // empty' "${STATE_FILE}" 2>/dev/null || true)
|
||||
|
||||
log "team=${TEAM_NAME}, PM=${PM_PANE}, agent_panes=[${AGENT_PANES//$'\n'/, }]"
|
||||
|
||||
if [ -z "${AGENT_PANES}" ]; then
|
||||
log "无 agent pane 需要清理"
|
||||
rm -f "${STATE_FILE}"
|
||||
rm -f /tmp/claude-pane-snapshot-* 2>/dev/null || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 关闭单个 pane 的函数
|
||||
# 参数: $1 = pane_id
|
||||
# 返回: 0=成功关闭, 1=关闭失败
|
||||
close_pane() {
|
||||
local pane_id="$1"
|
||||
|
||||
# 安全检查:确保不是 PM pane
|
||||
if [ "${pane_id}" == "${PM_PANE}" ]; then
|
||||
log "跳过 PM pane ${pane_id}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查 pane 是否仍存在
|
||||
if ! tmux list-panes -t "${pane_id}" 2>/dev/null | head -1 | grep -q .; then
|
||||
log "pane ${pane_id} 已不存在"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "关闭 pane ${pane_id} ..."
|
||||
|
||||
# 第 1 步: 发送 Ctrl+C 终止运行中的进程
|
||||
tmux send-keys -t "${pane_id}" C-c 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# 第 2 步: 再次发送 Ctrl+C(Claude Code 可能需要两次中断才退出)
|
||||
tmux send-keys -t "${pane_id}" C-c 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# 第 3 步: 如果进程已退出到 bash,发送 exit
|
||||
tmux send-keys -t "${pane_id}" 'exit' Enter 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# 第 4 步: 无条件 kill-pane(不管 pane 是否仍在)
|
||||
tmux kill-pane -t "${pane_id}" 2>/dev/null || true
|
||||
|
||||
# 第 5 步: 验证 pane 确实已关闭
|
||||
sleep 0.5
|
||||
if tmux list-panes -t "${pane_id}" 2>/dev/null | head -1 | grep -q .; then
|
||||
log "警告: pane ${pane_id} 仍在,尝试二次 kill-pane"
|
||||
tmux kill-pane -t "${pane_id}" 2>/dev/null || true
|
||||
sleep 0.5
|
||||
if tmux list-panes -t "${pane_id}" 2>/dev/null | head -1 | grep -q .; then
|
||||
log "错误: pane ${pane_id} 无法关闭"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log "pane ${pane_id} 已成功关闭"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 关闭每个 agent pane
|
||||
CLOSED_COUNT=0
|
||||
FAILED_COUNT=0
|
||||
while IFS= read -r pane_id; do
|
||||
[ -z "${pane_id}" ] && continue
|
||||
|
||||
if close_pane "${pane_id}"; then
|
||||
CLOSED_COUNT=$((CLOSED_COUNT + 1))
|
||||
else
|
||||
FAILED_COUNT=$((FAILED_COUNT + 1))
|
||||
fi
|
||||
done <<< "${AGENT_PANES}"
|
||||
|
||||
log "清理完成: 成功=${CLOSED_COUNT}, 失败=${FAILED_COUNT}"
|
||||
|
||||
# 清理状态文件
|
||||
rm -f "${STATE_FILE}"
|
||||
|
||||
# 清理所有关联的快照文件
|
||||
rm -f /tmp/claude-pane-snapshot-* 2>/dev/null || true
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user