#!/bin/bash # tmux-send-prompt.sh — 可靠地向 tmux pane 中的 cc 发送提示词 # # 解决的问题: # 1. 发送长文本后 Enter 可能未被正确接收 # 2. 前一个命令还没执行完就发送新命令 # 3. 无法确认命令是否已成功提交 # 4. 文件内容过长时 send-keys 不可靠 # # 用法: # tmux-send-prompt.sh [options] # # 选项: # --submit-delay 发送文本后等待多久再按 Enter(默认 1) # --from 发送方 pane ID(用于日志) # --no-verify 跳过提交验证(非阻塞模式) # # 行为: # - 文本字符串: 直接发送 # - 文件路径: 发送简短指令,让 cc 自己读取文件并执行 # # 示例: # tmux-send-prompt.sh %1 "短文本提示" # tmux-send-prompt.sh %1 /path/to/prompt.md # tmux-send-prompt.sh %1 "文本" --submit-delay 2 # tmux-send-prompt.sh %1 "文本" --from %2 --no-verify set -euo pipefail # ─── 参数解析 ─── PANE_ID='' TEXT_OR_FILE='' SUBMIT_DELAY=1 FROM_PANE='' NO_VERIFY=false while [[ $# -gt 0 ]]; do case "$1" in --submit-delay) SUBMIT_DELAY="${2:?--submit-delay 需要一个数值参数}" shift 2 ;; --from) FROM_PANE="${2:?--from 需要一个 pane ID}" shift 2 ;; --no-verify) NO_VERIFY=true shift ;; -*) echo "错误: 未知选项 $1" >&2 exit 1 ;; *) if [[ -z "$PANE_ID" ]]; then PANE_ID="$1" elif [[ -z "$TEXT_OR_FILE" ]]; then TEXT_OR_FILE="$1" elif [[ "$1" =~ ^[0-9]+$ ]]; then # 向后兼容: 第三个位置参数为数字时视为 submit-delay SUBMIT_DELAY="$1" else echo "错误: 多余参数 '$1'" >&2 exit 1 fi shift ;; esac done if [[ -z "$PANE_ID" ]]; then echo "用法: tmux-send-prompt.sh [options]" >&2 exit 1 fi if [[ -z "$TEXT_OR_FILE" ]]; then echo "错误: 需要提供文本或文件路径" >&2 exit 1 fi # ─── 日志函数 ─── LOG_FILE="${TMUX_SEND_LOG:-/workspace/.tmux-send-prompt.log}" log_call() { local status="$1" detail="${2:-}" local ts ts=$(date -Iseconds) local from_field="${FROM_PANE:-null}" local text_len=${#TEXT_OR_FILE} printf '{"ts":"%s","from":"%s","to":"%s","text_len":%d,"status":"%s","detail":"%s"}\n' \ "$ts" "$from_field" "$PANE_ID" "$text_len" "$status" "$detail" \ >> "$LOG_FILE" 2>/dev/null || true } # ─── 构建发送内容 ─── if [[ -f "$TEXT_OR_FILE" ]]; then FILE_PATH=$(realpath "$TEXT_OR_FILE") TEXT="请读取 ${FILE_PATH} 并执行其中的内容" echo "[tmux-send] 检测到文件: ${FILE_PATH},将指示 cc 自行读取" else TEXT="$TEXT_OR_FILE" fi # ─── 步骤1: 等待 cc 的 prompt 出现(❯ 字符)─── # cc 状态栏在 ❯ 下方占 3 行,需要 tail -6 才能捕获 echo "[tmux-send] 等待 cc 的 prompt 就绪..." for i in $(seq 1 30); do LAST_LINE=$(tmux capture-pane -t "$PANE_ID" -p 2>/dev/null | tail -6 | grep -c '❯' || true) if [[ "$LAST_LINE" -ge 1 ]]; then echo "[tmux-send] cc 已就绪 (等待 ${i}s)" break fi if [[ "$i" -eq 30 ]]; then echo "[tmux-send] 警告: 30s 内未检测到 prompt,尝试发送" log_call "timeout_wait" "prompt_not_found" fi sleep 1 done # ─── 步骤2: 发送文本(不带 Enter)─── tmux send-keys -t "$PANE_ID" "$TEXT" sleep "${SUBMIT_DELAY}" # ─── 步骤3: 单独发送 Enter ─── tmux send-keys -t "$PANE_ID" Enter sleep 2 # ─── 步骤4: 验证(可选)─── # cc 的 ❯ 始终在底部,无法通过 ❯ 消失判断提交成功。 # 改为:对比发送前后 pane 内容,若内容变化则说明已提交。 if [[ "$NO_VERIFY" == true ]]; then echo "[tmux-send] 跳过验证(--no-verify)" log_call "sent_no_verify" exit 0 fi BEFORE_HASH=$(tmux capture-pane -t "$PANE_ID" -p 2>/dev/null | md5sum | cut -d' ' -f1) for i in $(seq 1 3); do sleep 3 AFTER_HASH=$(tmux capture-pane -t "$PANE_ID" -p 2>/dev/null | md5sum | cut -d' ' -f1) if [[ "$BEFORE_HASH" != "$AFTER_HASH" ]]; then echo "[tmux-send] 命令已成功提交 (第 $i 次检查,内容已变化)" log_call "success" "content_changed_on_attempt_$i" exit 0 fi # 内容未变化,可能 Enter 未生效,重试一次 echo "[tmux-send] 内容未变化,重试 Enter 第 $i 次..." tmux send-keys -t "$PANE_ID" Enter done log_call "failed" "content_unchanged_after_3_retries" echo "[tmux-send] 警告: 3次重试后内容仍未变化,请手动检查" exit 1