#!/bin/bash # tmux-msg-notify.sh — 面板间消息完成通知 # # 由接收方 cc 通过 Bash 工具调用,通知发送方任务完成。 # # 用法: # tmux-msg-notify.sh "" # # 行为: # 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 \"\"}" 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 < "$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] 完成"