mirror of
https://github.com/tradecatlabs/vibe-coding-cn.git
synced 2026-08-20 14:28:05 +00:00
feat: skills - expand auto-tmux prompt tooling
This commit is contained in:
@@ -10,6 +10,7 @@ scripts/
|
||||
├── README.md # 面向人类的脚本入口说明
|
||||
├── auto-tmux.sh # 统一 tmux 自动化命令入口
|
||||
├── swarm-state.sh # 蜂群状态、任务、锁和报告管理
|
||||
├── render-swarm-prompt.sh # commander/worker/reviewer 提示词渲染
|
||||
└── auto-tmux-smoke-test.sh # 临时 tmux 会话端到端自测
|
||||
```
|
||||
|
||||
@@ -19,6 +20,8 @@ scripts/
|
||||
- 发送按键类功能必须先验证目标 pane 存在,并优先打印上下文。
|
||||
- 捕获输出默认脱敏;只有显式 `--no-redact` 才允许输出原文。
|
||||
- 批量救援必须通过 pattern 匹配触发,不允许无条件向所有 pane 发送输入。
|
||||
- 批量广播必须要求显式 session,并支持 `--dry-run`。
|
||||
- Prompt 渲染脚本只输出文本,不直接控制 tmux。
|
||||
- 状态脚本只能写入显式状态目录,不保存密钥、Token、密码或私密项目内容。
|
||||
- 自测脚本必须创建独立临时 tmux session,并在退出时清理。
|
||||
- 新增脚本后必须更新 `README.md`、`SKILL.md`、`references/index.md`,并运行 `bash -n` 与 skill strict 校验。
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
- [`auto-tmux.sh`](./auto-tmux.sh) - 统一命令入口,支持拓扑、读取、发送、巡检、救援、录制、等待和 AI 工作台初始化。
|
||||
- [`swarm-state.sh`](./swarm-state.sh) - 蜂群状态、任务、锁和报告管理。
|
||||
- [`render-swarm-prompt.sh`](./render-swarm-prompt.sh) - commander、worker、reviewer 提示词渲染。
|
||||
- [`auto-tmux-smoke-test.sh`](./auto-tmux-smoke-test.sh) - 创建临时 tmux 会话,端到端验证脚本能力。
|
||||
|
||||
## 常用命令
|
||||
@@ -17,6 +18,9 @@ skills/auto-tmux/scripts/auto-tmux.sh help
|
||||
# 查看 tmux 拓扑
|
||||
skills/auto-tmux/scripts/auto-tmux.sh topology
|
||||
|
||||
# 诊断环境
|
||||
skills/auto-tmux/scripts/auto-tmux.sh doctor --session ai-hub
|
||||
|
||||
target="$(tmux list-panes -t ai-hub:worker1 -F '#S:#I.#P' | head -n 1)"
|
||||
|
||||
# 读取指定 pane 最近 80 行
|
||||
@@ -28,6 +32,9 @@ skills/auto-tmux/scripts/auto-tmux.sh send -t "$target" --text "make test" --ent
|
||||
# 扫描某个 session 的错误
|
||||
skills/auto-tmux/scripts/auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback"
|
||||
|
||||
# 受控广播,先 dry-run
|
||||
skills/auto-tmux/scripts/auto-tmux.sh broadcast --session ai-hub --text "pwd" --enter --dry-run
|
||||
|
||||
# 生成证据快照
|
||||
skills/auto-tmux/scripts/auto-tmux.sh snapshot --session ai-hub --dir /tmp/auto-tmux-snapshot
|
||||
|
||||
@@ -38,6 +45,9 @@ skills/auto-tmux/scripts/swarm-state.sh report --dir /tmp/ai_swarm
|
||||
|
||||
# 端到端自测
|
||||
skills/auto-tmux/scripts/auto-tmux-smoke-test.sh
|
||||
|
||||
# 渲染 worker prompt
|
||||
skills/auto-tmux/scripts/render-swarm-prompt.sh worker --target "$target" --task "只运行 make test"
|
||||
```
|
||||
|
||||
## 安全约束
|
||||
@@ -46,4 +56,5 @@ skills/auto-tmux/scripts/auto-tmux-smoke-test.sh
|
||||
- `send` 会先打印目标 pane 最近上下文,再发送按键。
|
||||
- 危险命令默认拒绝,必须显式 `--force`。
|
||||
- 批量操作必须使用明确的 session/window/pane 目标。
|
||||
- 批量广播必须先 `--dry-run`,确认目标列表后再真实发送。
|
||||
- 多 worker 协作时用 `swarm-state.sh lock-acquire` 声明文件、目录或服务锁。
|
||||
|
||||
@@ -5,6 +5,7 @@ set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
AUTO_TMUX="$SCRIPT_DIR/auto-tmux.sh"
|
||||
SWARM_STATE="$SCRIPT_DIR/swarm-state.sh"
|
||||
RENDER_PROMPT="$SCRIPT_DIR/render-swarm-prompt.sh"
|
||||
SESSION="auto-tmux-smoke-$$"
|
||||
SWARM_DIR="/tmp/auto-tmux-smoke-swarm-$$"
|
||||
SNAPSHOT_DIR="/tmp/auto-tmux-smoke-snapshot-$$"
|
||||
@@ -23,14 +24,17 @@ command -v tmux >/dev/null 2>&1 || {
|
||||
|
||||
bash -n "$AUTO_TMUX"
|
||||
bash -n "$SWARM_STATE"
|
||||
bash -n "$RENDER_PROMPT"
|
||||
|
||||
"$AUTO_TMUX" hub --session "$SESSION" --workers 1 --cmd bash
|
||||
"$AUTO_TMUX" doctor --session "$SESSION" >/tmp/auto-tmux-smoke-doctor.txt
|
||||
"$AUTO_TMUX" topology --session "$SESSION" >/tmp/auto-tmux-smoke-topology.txt
|
||||
|
||||
commander_target="$(tmux list-panes -t "$SESSION:commander" -F '#S:#I.#P' | head -n 1)"
|
||||
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" 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
|
||||
"$AUTO_TMUX" snapshot --session "$SESSION" --dir "$SNAPSHOT_DIR" -n 20 >/tmp/auto-tmux-smoke-snapshot.txt
|
||||
@@ -47,5 +51,9 @@ test -s "$SNAPSHOT_DIR/topology.txt"
|
||||
"$SWARM_STATE" task-done --dir "$SWARM_DIR" --id smoke-task --owner "$worker_target" --result "ok"
|
||||
"$SWARM_STATE" report --dir "$SWARM_DIR" >/tmp/auto-tmux-smoke-report.txt
|
||||
grep -q 'smoke-task' /tmp/auto-tmux-smoke-report.txt
|
||||
"$RENDER_PROMPT" commander --session "$SESSION" --swarm-dir "$SWARM_DIR" --task "smoke" >/tmp/auto-tmux-smoke-commander-prompt.md
|
||||
"$RENDER_PROMPT" worker --session "$SESSION" --target "$worker_target" --swarm-dir "$SWARM_DIR" --task "smoke" >/tmp/auto-tmux-smoke-worker-prompt.md
|
||||
grep -q 'Commander Prompt' /tmp/auto-tmux-smoke-commander-prompt.md
|
||||
grep -q 'Worker Prompt' /tmp/auto-tmux-smoke-worker-prompt.md
|
||||
|
||||
printf 'auto-tmux smoke test ok: session=%s worker=%s\n' "$SESSION" "$worker_target"
|
||||
|
||||
@@ -13,9 +13,11 @@ auto-tmux: safe tmux automation wrapper
|
||||
|
||||
Usage:
|
||||
auto-tmux.sh help
|
||||
auto-tmux.sh doctor [--session NAME]
|
||||
auto-tmux.sh topology [--session NAME]
|
||||
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 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]
|
||||
auto-tmux.sh record start -t TARGET [--dir DIR]
|
||||
@@ -28,9 +30,11 @@ Target format:
|
||||
<session>:<window>.<pane>
|
||||
|
||||
Examples:
|
||||
auto-tmux.sh doctor --session ai-hub
|
||||
auto-tmux.sh topology
|
||||
auto-tmux.sh capture -t <target-from-topology> -n 80
|
||||
auto-tmux.sh send -t <target-from-topology> --text "make test" --enter
|
||||
auto-tmux.sh broadcast --session ai-hub --text "pwd" --enter --dry-run
|
||||
auto-tmux.sh rescue -t <target-from-topology> --pattern "(y/n)" --reply y
|
||||
auto-tmux.sh scan --session ai-hub --pattern "ERROR|Traceback"
|
||||
auto-tmux.sh snapshot --session ai-hub --dir /tmp/auto-tmux-snapshot
|
||||
@@ -47,6 +51,12 @@ warn() {
|
||||
printf 'warn: %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
status_line() {
|
||||
local status="$1"
|
||||
local message="$2"
|
||||
printf '[%s] %s\n' "$status" "$message"
|
||||
}
|
||||
|
||||
require_tmux() {
|
||||
command -v tmux >/dev/null 2>&1 || die "tmux is not installed or not in PATH"
|
||||
tmux list-sessions >/dev/null 2>&1 || die "no tmux server/session found"
|
||||
@@ -110,6 +120,71 @@ pane_list() {
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_doctor() {
|
||||
local session=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--session) session="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown doctor option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
local failures=0
|
||||
local script_dir
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
if command -v tmux >/dev/null 2>&1; then
|
||||
status_line "OK" "tmux found: $(tmux -V)"
|
||||
else
|
||||
status_line "FAIL" "tmux not found in PATH"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
|
||||
if tmux list-sessions >/dev/null 2>&1; then
|
||||
status_line "OK" "tmux server is reachable"
|
||||
else
|
||||
status_line "WARN" "tmux server has no reachable session yet"
|
||||
fi
|
||||
|
||||
for script in auto-tmux.sh swarm-state.sh auto-tmux-smoke-test.sh render-swarm-prompt.sh; do
|
||||
if [[ -x "$script_dir/$script" ]]; then
|
||||
status_line "OK" "script executable: $script"
|
||||
else
|
||||
status_line "FAIL" "script missing or not executable: $script"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
local root
|
||||
root="$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
if [[ -n "$root" ]]; then
|
||||
for rel in skills/auto-tmux/assets/oh-my-tmux skills/auto-tmux/assets/tmux-src; do
|
||||
if [[ -e "$root/$rel" ]]; then
|
||||
status_line "OK" "asset exists: $rel"
|
||||
else
|
||||
status_line "WARN" "asset missing: $rel (run git submodule update --init --recursive)"
|
||||
fi
|
||||
done
|
||||
else
|
||||
status_line "WARN" "not inside a git worktree; skipped asset checks"
|
||||
fi
|
||||
|
||||
if [[ -n "$session" ]]; then
|
||||
if tmux has-session -t "$session" 2>/dev/null; then
|
||||
status_line "OK" "session exists: $session"
|
||||
tmux list-panes -t "$session" -F ' pane #S:#{window_index}.#{pane_index} cmd=#{pane_current_command}'
|
||||
else
|
||||
status_line "FAIL" "session not found: $session"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( failures > 0 )); then
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_topology() {
|
||||
local session=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -134,6 +209,65 @@ cmd_topology() {
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_broadcast() {
|
||||
local session=""
|
||||
local text=""
|
||||
local key=""
|
||||
local enter="0"
|
||||
local clear="0"
|
||||
local force="0"
|
||||
local dry_run="0"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--session) session="${2:-}"; shift 2 ;;
|
||||
--text) text="${2:-}"; shift 2 ;;
|
||||
--key) key="${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 broadcast option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_tmux
|
||||
[[ -n "$session" ]] || die "broadcast requires --session NAME"
|
||||
tmux has-session -t "$session" 2>/dev/null || die "session not found: $session"
|
||||
[[ -n "$text" || -n "$key" ]] || die "broadcast requires --text TEXT or --key KEY"
|
||||
[[ -z "$text" || -z "$key" ]] || die "broadcast accepts only one of --text or --key"
|
||||
if [[ -n "$text" && "$force" != "1" ]] && is_dangerous_text "$text"; then
|
||||
die "refusing dangerous broadcast text without --force: $text"
|
||||
fi
|
||||
|
||||
local panes=()
|
||||
mapfile -t panes < <(pane_list "$session")
|
||||
[[ "${#panes[@]}" -gt 0 ]] || die "no panes found in session: $session"
|
||||
printf 'broadcast targets (%s):\n' "${#panes[@]}"
|
||||
printf ' %s\n' "${panes[@]}"
|
||||
|
||||
local pane
|
||||
for pane in "${panes[@]}"; do
|
||||
if [[ "$dry_run" == "1" ]]; then
|
||||
printf '[dry-run] would send to %s\n' "$pane"
|
||||
continue
|
||||
fi
|
||||
if [[ -n "$text" ]]; then
|
||||
local args=(-t "$pane" --text "$text")
|
||||
[[ "$enter" == "1" ]] && args+=(--enter)
|
||||
[[ "$clear" == "1" ]] && args+=(--clear)
|
||||
[[ "$force" == "1" ]] && args+=(--force)
|
||||
cmd_send "${args[@]}"
|
||||
else
|
||||
local args=(-t "$pane" --key "$key")
|
||||
[[ "$enter" == "1" ]] && args+=(--enter)
|
||||
[[ "$clear" == "1" ]] && args+=(--clear)
|
||||
[[ "$force" == "1" ]] && args+=(--force)
|
||||
cmd_send "${args[@]}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
cmd_capture() {
|
||||
local target=""
|
||||
local lines="$DEFAULT_LINES"
|
||||
@@ -448,9 +582,11 @@ main() {
|
||||
shift || true
|
||||
case "$cmd" in
|
||||
help|-h|--help) usage ;;
|
||||
doctor) cmd_doctor "$@" ;;
|
||||
topology) cmd_topology "$@" ;;
|
||||
capture) cmd_capture "$@" ;;
|
||||
send) cmd_send "$@" ;;
|
||||
broadcast) cmd_broadcast "$@" ;;
|
||||
rescue) cmd_rescue "$@" ;;
|
||||
scan) cmd_scan "$@" ;;
|
||||
record) cmd_record "$@" ;;
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
# Render reusable commander/worker/reviewer prompts for tmux AI swarm sessions.
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
render-swarm-prompt: render tmux AI swarm prompt templates
|
||||
|
||||
Usage:
|
||||
render-swarm-prompt.sh commander [--session NAME] [--swarm-dir DIR] [--task TEXT]
|
||||
render-swarm-prompt.sh worker --target TARGET [--session NAME] [--swarm-dir DIR] [--task TEXT]
|
||||
render-swarm-prompt.sh reviewer [--session NAME] [--swarm-dir DIR] [--task TEXT]
|
||||
|
||||
Examples:
|
||||
render-swarm-prompt.sh commander --session ai-hub --task "重构 README"
|
||||
render-swarm-prompt.sh worker --target ai-hub:2.1 --task "只检查链接"
|
||||
render-swarm-prompt.sh reviewer --session ai-hub
|
||||
EOF
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'error: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
role="${1:-help}"
|
||||
shift || true
|
||||
|
||||
session="ai-hub"
|
||||
swarm_dir="/tmp/ai_swarm"
|
||||
target=""
|
||||
task="未指定;请等待 commander 分配具体任务。"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--session) session="${2:-}"; shift 2 ;;
|
||||
--swarm-dir) swarm_dir="${2:-}"; shift 2 ;;
|
||||
--target) target="${2:-}"; shift 2 ;;
|
||||
--task) task="${2:-}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$role" in
|
||||
help|-h|--help)
|
||||
usage
|
||||
;;
|
||||
commander)
|
||||
cat <<EOF
|
||||
# tmux AI 蜂群 Commander Prompt
|
||||
|
||||
你是 tmux AI 蜂群的 commander。
|
||||
|
||||
## 当前上下文
|
||||
|
||||
- session: \`$session\`
|
||||
- swarm state dir: \`$swarm_dir\`
|
||||
- 总任务: $task
|
||||
|
||||
## 你的职责
|
||||
|
||||
1. 用 \`skills/auto-tmux/scripts/auto-tmux.sh topology --session $session\` 读取工作台拓扑。
|
||||
2. 用 \`skills/auto-tmux/scripts/swarm-state.sh init --dir $swarm_dir\` 初始化状态目录。
|
||||
3. 把总任务拆成互不冲突的 worker 子任务。
|
||||
4. 给每个子任务创建 task,并在涉及同一文件、目录或服务时要求 worker 先加锁。
|
||||
5. 用 scan/capture/snapshot 收集 worker 状态与证据。
|
||||
6. 对卡住任务先 dry-run rescue,再做最小干预。
|
||||
7. 最终只用测试、diff、日志、报告和人工验收作为完成依据。
|
||||
|
||||
## 禁止
|
||||
|
||||
- 不盲目广播危险命令。
|
||||
- 不让多个 worker 同时修改同一文件。
|
||||
- 不把 AI 口头保证当成验收结果。
|
||||
EOF
|
||||
;;
|
||||
worker)
|
||||
[[ -n "$target" ]] || die "worker role requires --target TARGET"
|
||||
cat <<EOF
|
||||
# tmux AI 蜂群 Worker Prompt
|
||||
|
||||
你是 tmux AI 蜂群中的 worker。
|
||||
|
||||
## 当前上下文
|
||||
|
||||
- session: \`$session\`
|
||||
- target: \`$target\`
|
||||
- swarm state dir: \`$swarm_dir\`
|
||||
- 分配任务: $task
|
||||
|
||||
## 你的职责
|
||||
|
||||
1. 只处理分配给你的单一任务。
|
||||
2. 开始前用 \`swarm-state.sh task-claim\` 认领任务。
|
||||
3. 修改文件、目录或服务前,用 \`swarm-state.sh lock-acquire\` 获取锁。
|
||||
4. 执行中保留关键命令、错误、测试结果和风险。
|
||||
5. 完成后用 \`swarm-state.sh task-done\` 写入结果。
|
||||
6. 汇报格式必须包含:状态、修改文件、验证结果、风险、下一步。
|
||||
|
||||
## 禁止
|
||||
|
||||
- 不修改无关文件。
|
||||
- 不绕过测试、lint、类型检查或用户指定门禁。
|
||||
- 不覆盖其他 worker 的工作。
|
||||
EOF
|
||||
;;
|
||||
reviewer)
|
||||
cat <<EOF
|
||||
# tmux AI 蜂群 Reviewer Prompt
|
||||
|
||||
你是 tmux AI 蜂群的独立 reviewer。
|
||||
|
||||
## 当前上下文
|
||||
|
||||
- session: \`$session\`
|
||||
- swarm state dir: \`$swarm_dir\`
|
||||
- 审查目标: $task
|
||||
|
||||
## 你的职责
|
||||
|
||||
1. 读取 commander 汇总、worker 结果、snapshot、record 日志和 git diff。
|
||||
2. 优先寻找 correctness、越界修改、缺失测试、风险遗漏和文档不同步。
|
||||
3. 对每个问题给出文件、证据、影响和最小修复建议。
|
||||
4. 如果没有阻塞问题,明确说明剩余风险和建议补充验证。
|
||||
|
||||
## 禁止
|
||||
|
||||
- 不只做风格审查。
|
||||
- 不把 worker 自评当作事实。
|
||||
- 不在缺少证据时宣布通过。
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
die "unknown role: $role"
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user