diff --git a/skills/auto-tmux/SKILL.md b/skills/auto-tmux/SKILL.md index a438f54..416196f 100644 --- a/skills/auto-tmux/SKILL.md +++ b/skills/auto-tmux/SKILL.md @@ -76,6 +76,7 @@ tmux capture-pane -t :. -p -S -120 **向指定 pane 发送命令或确认键** ```bash skills/auto-tmux/scripts/auto-tmux.sh send -t :. --text "make test" --enter +skills/auto-tmux/scripts/auto-tmux.sh paste -t :. --file /tmp/prompt.md --enter --dry-run skills/auto-tmux/scripts/auto-tmux.sh rescue -t :. --pattern "(y/n)" --reply y ``` diff --git a/skills/auto-tmux/references/automation.md b/skills/auto-tmux/references/automation.md index 1d00a8a..4c97ab7 100644 --- a/skills/auto-tmux/references/automation.md +++ b/skills/auto-tmux/references/automation.md @@ -20,6 +20,7 @@ | `inspect` | 输出单个 pane 元信息和最近输出 | 默认脱敏,只读 | | `capture` | 读取指定 pane 最近输出 | 默认脱敏,可保存到文件 | | `send` | 向指定 pane 发送文本或按键 | 先打印上下文,危险文本需 `--force` | +| `paste` | 通过 tmux buffer 粘贴文件内容 | 适合长 prompt,多行内容先 `--dry-run` | | `broadcast` | 向指定 session 全部 pane 发送文本或按键 | 必须显式 session,支持 `--dry-run` | | `rescue` | pattern 命中后发送确认 | 不命中不发送 | | `scan` | 批量巡检 pane 输出 | 可按 session 限定范围 | @@ -114,6 +115,7 @@ skills/auto-tmux/scripts/swarm-dispatch.sh --role worker --target "$target" --ta target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)" skills/auto-tmux/scripts/auto-tmux.sh inspect -t "$target" -n 40 skills/auto-tmux/scripts/auto-tmux.sh send -t "$target" --text "make test" --enter +skills/auto-tmux/scripts/auto-tmux.sh paste -t "$target" --file /tmp/prompt.md --enter --dry-run ``` 如果要清空当前输入行后发送: diff --git a/skills/auto-tmux/references/prompt-templates.md b/skills/auto-tmux/references/prompt-templates.md index 54f153b..33174a0 100644 --- a/skills/auto-tmux/references/prompt-templates.md +++ b/skills/auto-tmux/references/prompt-templates.md @@ -95,6 +95,16 @@ skills/auto-tmux/scripts/auto-tmux.sh send \ --enter ``` +长 prompt 可以用 tmux buffer 粘贴: + +```bash +skills/auto-tmux/scripts/auto-tmux.sh paste \ + -t "$target" \ + --file /tmp/worker-prompt.md \ + --enter \ + --dry-run +``` + ## 注意事项 - Prompt 只定义职责和边界,不替代测试、lint、diff、CI 等硬门禁。 diff --git a/skills/auto-tmux/scripts/README.md b/skills/auto-tmux/scripts/README.md index dc9d803..ef5df03 100644 --- a/skills/auto-tmux/scripts/README.md +++ b/skills/auto-tmux/scripts/README.md @@ -33,6 +33,7 @@ skills/auto-tmux/scripts/auto-tmux.sh capture -t "$target" -n 80 # 发送命令并回车 skills/auto-tmux/scripts/auto-tmux.sh send -t "$target" --text "make test" --enter +skills/auto-tmux/scripts/auto-tmux.sh paste -t "$target" --file /tmp/prompt.md --enter --dry-run # 扫描某个 session 的错误 skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback" diff --git a/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh b/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh index f4f9426..6ecaf08 100755 --- a/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh +++ b/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh @@ -17,10 +17,11 @@ BRIEF_DIR="/tmp/auto-tmux-smoke-brief-$$" WATCH_DIR="/tmp/auto-tmux-smoke-watch-$$" DISPATCH_PROMPT="/tmp/auto-tmux-smoke-dispatch-$$.md" TASK_IMPORT_FILE="/tmp/auto-tmux-smoke-tasks-$$.txt" +PASTE_FILE="/tmp/auto-tmux-smoke-paste-$$.txt" cleanup() { tmux kill-session -t "$SESSION" 2>/dev/null || true - rm -rf "$SWARM_DIR" "$SNAPSHOT_DIR" "$RECORD_DIR" "$BRIEF_DIR" "$WATCH_DIR" "$DISPATCH_PROMPT" "$TASK_IMPORT_FILE" + rm -rf "$SWARM_DIR" "$SNAPSHOT_DIR" "$RECORD_DIR" "$BRIEF_DIR" "$WATCH_DIR" "$DISPATCH_PROMPT" "$TASK_IMPORT_FILE" "$PASTE_FILE" } trap cleanup EXIT @@ -46,6 +47,8 @@ worker_target="$(tmux list-panes -t "$SESSION:worker1" -F '#S:#I.#P' | head -n 1 "$AUTO_TMUX" capture -t "$commander_target" -n 10 >/tmp/auto-tmux-smoke-capture.txt "$AUTO_TMUX" inspect -t "$commander_target" -n 10 >/tmp/auto-tmux-smoke-inspect.txt grep -q 'pane inspect' /tmp/auto-tmux-smoke-inspect.txt +printf '%s\n' 'echo AUTO_TMUX_PASTE_OK' > "$PASTE_FILE" +"$AUTO_TMUX" paste -t "$worker_target" --file "$PASTE_FILE" --enter --dry-run >/tmp/auto-tmux-smoke-paste-dry-run.txt "$AUTO_TMUX" broadcast --session "$SESSION" --text "pwd" --enter --dry-run >/tmp/auto-tmux-smoke-broadcast.txt "$AUTO_TMUX" send -t "$worker_target" --text "echo AUTO_TMUX_SMOKE_OK" --enter "$AUTO_TMUX" wait -t "$worker_target" --pattern "AUTO_TMUX_SMOKE_OK" --timeout 10 >/tmp/auto-tmux-smoke-wait.txt diff --git a/skills/auto-tmux/scripts/auto-tmux.sh b/skills/auto-tmux/scripts/auto-tmux.sh index 5922cd3..118f17b 100755 --- a/skills/auto-tmux/scripts/auto-tmux.sh +++ b/skills/auto-tmux/scripts/auto-tmux.sh @@ -18,6 +18,7 @@ Usage: auto-tmux.sh inspect -t TARGET [-n LINES] [--no-redact] auto-tmux.sh capture -t TARGET [-n LINES] [--save FILE] [--no-redact] auto-tmux.sh send -t TARGET (--text TEXT | --key KEY) [--enter] [--clear] [--force] [--dry-run] + auto-tmux.sh paste -t TARGET --file FILE [--enter] [--clear] [--force] [--dry-run] auto-tmux.sh broadcast --session NAME (--text TEXT | --key KEY) [--enter] [--clear] [--force] [--dry-run] auto-tmux.sh rescue -t TARGET [--pattern REGEX] [--reply TEXT] [-n LINES] [--force] [--dry-run] auto-tmux.sh scan [--session NAME] [-n LINES] [--pattern REGEX] [--rescue] [--reply TEXT] [--save-dir DIR] @@ -36,6 +37,7 @@ Examples: auto-tmux.sh inspect -t -n 40 auto-tmux.sh capture -t -n 80 auto-tmux.sh send -t --text "make test" --enter + auto-tmux.sh paste -t --file /tmp/prompt.md --enter --dry-run auto-tmux.sh broadcast --session ai-hub --text "pwd" --enter --dry-run auto-tmux.sh rescue -t --pattern "(y/n)" --reply y auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback" @@ -379,6 +381,56 @@ cmd_send() { fi } +cmd_paste() { + local target="" + local file="" + local enter="0" + local clear="0" + local force="0" + local dry_run="0" + while [[ $# -gt 0 ]]; do + case "$1" in + -t|--target) target="${2:-}"; shift 2 ;; + --file) file="${2:-}"; shift 2 ;; + --enter) enter="1"; shift ;; + --clear) clear="1"; shift ;; + --force) force="1"; shift ;; + --dry-run) dry_run="1"; shift ;; + -h|--help) usage; exit 0 ;; + *) die "unknown paste option: $1" ;; + esac + done + + require_tmux + require_target "$target" + [[ -n "$file" ]] || die "paste requires --file FILE" + [[ -f "$file" ]] || die "paste file not found: $file" + + printf -- '--- current context: %s ---\n' "$target" >&2 + capture_print "$target" 40 0 >&2 || true + + if [[ "$force" != "1" ]] && is_dangerous_text "$(cat "$file")"; then + die "refusing dangerous paste file without --force: $file" + fi + + if [[ "$dry_run" == "1" ]]; then + printf '[dry-run] target=%s clear=%s file=%s bytes=%s enter=%s\n' \ + "$target" "$clear" "$file" "$(wc -c < "$file" | tr -d ' ')" "$enter" + return 0 + fi + + cancel_copy_mode_if_needed "$target" + if [[ "$clear" == "1" ]]; then + tmux send-keys -t "$target" C-u + fi + local buffer_name="auto-tmux-paste-$$" + tmux load-buffer -b "$buffer_name" "$file" + tmux paste-buffer -t "$target" -b "$buffer_name" -d + if [[ "$enter" == "1" ]]; then + tmux send-keys -t "$target" Enter + fi +} + cmd_rescue() { local target="" local pattern="$DEFAULT_RESCUE_PATTERN" @@ -619,6 +671,7 @@ main() { inspect) cmd_inspect "$@" ;; capture) cmd_capture "$@" ;; send) cmd_send "$@" ;; + paste) cmd_paste "$@" ;; broadcast) cmd_broadcast "$@" ;; rescue) cmd_rescue "$@" ;; scan) cmd_scan "$@" ;;