Files
team/scripts/tmux-msg-notify.sh
2026-04-19 22:23:34 +08:00

161 lines
5.1 KiB
Bash
Executable File

#!/bin/bash
# tmux-msg-notify.sh — 面板间消息完成通知
#
# 由接收方 cc 通过 Bash 工具调用,通知发送方任务完成。
#
# 用法:
# tmux-msg-notify.sh <msg_id> <reply_to_pane> "<summary>"
#
# 行为:
# 1. 更新原消息状态为 completed
# 2. 创建回复信封
# 3. 通过 tmux-send-prompt.sh 向发送方发送通知
#
# 示例:
# tmux-msg-notify.sh msg-20260419-142357-a1b2 %1 "已完成用户 API 实现"
set -euo pipefail
MSG_ID="${1:?用法: tmux-msg-notify.sh <msg_id> <reply_to_pane> \"<summary>\"}"
REPLY_TO_PANE="${2:?需要 reply_to_pane 参数}"
SUMMARY="${3:?需要完成摘要}"
MSG_DIR="${TMUX_MSG_DIR:-/workspace/.tmux-messages}"
INDEX_FILE="${MSG_DIR}/index.jsonl"
SEND_SCRIPT="/workspace/scripts/tmux-send-prompt.sh"
# ─── 辅助函数 ───
append_index() {
local json_line="$1"
mkdir -p "$(dirname "$INDEX_FILE")"
(
flock -x 200
echo "$json_line" >> "$INDEX_FILE"
) 200>"${INDEX_FILE}.lock"
}
write_envelope() {
local filepath="$1" json="$2"
mkdir -p "$(dirname "$filepath")"
local tmp="${filepath}.tmp"
echo "$json" > "$tmp"
mv "$tmp" "$filepath"
}
get_pane_title() {
local pane_id="$1"
tmux display-message -t "$pane_id" -p '#{pane_title}' 2>/dev/null || echo "未知"
}
# ─── 主流程 ───
echo "[tmux-notify] 处理完成通知: ${MSG_ID}"
# 查找原消息信封
ENVELOPE_PATH=$(find "$MSG_DIR" -name "${MSG_ID}.json" -type f 2>/dev/null | head -1)
if [[ -z "$ENVELOPE_PATH" ]]; then
echo "[tmux-notify] 警告: 未找到消息 ${MSG_ID},仍发送通知"
# 即使找不到原消息,也发送通知(宽容模式)
FROM_TITLE=$(get_pane_title "$TMUX_PANE")
TO_TITLE=$(get_pane_title "$REPLY_TO_PANE")
REPLY_PROMPT="[通知] 来自 ${FROM_TITLE} 的完成通知
原始任务 ID: ${MSG_ID}
完成摘要: ${SUMMARY}
无需回复此通知。"
# 直接发送
PROMPT_DIR_FALLBACK="${MSG_DIR}/prompts"
mkdir -p "$PROMPT_DIR_FALLBACK"
TMP_PROMPT="${PROMPT_DIR_FALLBACK}/notify-${MSG_ID}.md"
echo "$REPLY_PROMPT" > "$TMP_PROMPT"
bash "$SEND_SCRIPT" "$REPLY_TO_PANE" "$TMP_PROMPT" 2>&1 || true
echo "[tmux-notify] 通知已发送(无信封模式)"
exit 0
fi
# 读取原消息
ENVELOPE=$(cat "$ENVELOPE_PATH")
# 解析原消息字段
FROM_PANE=$(echo "$ENVELOPE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['from_pane'])" 2>/dev/null)
FROM_TITLE=$(echo "$ENVELOPE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['from_title'])" 2>/dev/null)
TO_TITLE=$(echo "$ENVELOPE" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['to_title'])" 2>/dev/null)
TASK_SUMMARY=$(echo "$ENVELOPE" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('task_summary','')[:80])" 2>/dev/null)
NOW=$(date -Iseconds)
# 更新原消息状态
python3 -c "
import sys, json
with open('${ENVELOPE_PATH}') as f:
d = json.load(f)
d['status'] = 'completed'
d['replied_at'] = '${NOW}'
d['reply_summary'] = '''${SUMMARY}'''
with open('${ENVELOPE_PATH}', 'w') as f:
json.dump(d, f, ensure_ascii=False, indent=2)
" 2>/dev/null
# 创建回复信封
REPLY_MSG_ID="reply-${MSG_ID}"
DAY_DIR=$(date +%Y/%m/%d)
REPLY_ENVELOPE_PATH="${MSG_DIR}/${DAY_DIR}/${REPLY_MSG_ID}.json"
SENDER_PANE="${TMUX_PANE:-%0}"
SENDER_TITLE=$(get_pane_title "$SENDER_PANE")
REPLY_ENVELOPE=$(cat <<EOJSON
{"msg_id":"${REPLY_MSG_ID}","direction":"reply","in_reply_to":"${MSG_ID}","from_pane":"${SENDER_PANE}","from_title":"${SENDER_TITLE}","to_pane":"${REPLY_TO_PANE}","to_title":"${FROM_TITLE}","task_summary":null,"task_file":null,"status":"created","created_at":"${NOW}","delivered_at":null,"replied_at":null,"reply_msg_id":null,"reply_summary":"${SUMMARY}","error":null}
EOJSON
)
write_envelope "$REPLY_ENVELOPE_PATH" "$REPLY_ENVELOPE"
# 写入索引
INDEX_LINE=$(printf '{"msg_id":"%s","direction":"reply","in_reply_to":"%s","from":"%s","to":"%s","status":"created","summary":"%s","ts":"%s"}\n' \
"$REPLY_MSG_ID" "$MSG_ID" "$SENDER_PANE" "$REPLY_TO_PANE" "${SUMMARY}" "$NOW")
append_index "$INDEX_LINE"
# 构建通知提示词
REPLY_PROMPT="## 任务完成通知 [${REPLY_MSG_ID}]
来自: ${SENDER_TITLE} (Pane ${SENDER_PANE})
原始任务: ${TASK_SUMMARY}
消息 ID: ${MSG_ID}
完成摘要: ${SUMMARY}
无需回复此通知。"
# 写提示词文件(持久化,供发送方 cc 异步读取)
PROMPT_DIR="${MSG_DIR}/prompts"
mkdir -p "$PROMPT_DIR"
TMP_PROMPT="${PROMPT_DIR}/notify-${MSG_ID}.md"
echo "$REPLY_PROMPT" > "$TMP_PROMPT"
echo "[tmux-notify] 发送通知到 ${REPLY_TO_PANE}..."
if bash "$SEND_SCRIPT" "$REPLY_TO_PANE" "$TMP_PROMPT" 2>&1; then
# 更新回复状态
DELIVERED_TS=$(date -Iseconds)
python3 -c "
import sys, json
with open('${REPLY_ENVELOPE_PATH}') as f:
d = json.load(f)
d['status'] = 'delivered'
d['delivered_at'] = '${DELIVERED_TS}'
with open('${REPLY_ENVELOPE_PATH}', 'w') as f:
json.dump(d, f, ensure_ascii=False, indent=2)
" 2>/dev/null
echo "[tmux-notify] 通知已发送: ${REPLY_MSG_ID}"
else
echo "[tmux-notify] 警告: 通知发送失败,但完成状态已记录"
fi
# 提示词文件保留,供发送方 cc 异步读取
echo "[tmux-notify] 完成"