@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: 团队工作空间
|
name: 团队工作空间
|
||||||
description: 启动 tmux 4 Pane 团队工作空间,命名所有层级并自动加载角色提示词
|
description: 启动 tmux 5 Pane 团队工作空间,命名所有层级并自动加载角色提示词
|
||||||
---
|
---
|
||||||
|
|
||||||
## 用户输入
|
## 用户输入
|
||||||
@@ -34,18 +34,19 @@ $ARGUMENTS
|
|||||||
| Pane 2 | `后端` | 后端开发 |
|
| Pane 2 | `后端` | 后端开发 |
|
||||||
| Pane 3 | `前端` | 前端开发 |
|
| Pane 3 | `前端` | 前端开发 |
|
||||||
| Pane 4 | `测试` | 测试工程师 |
|
| Pane 4 | `测试` | 测试工程师 |
|
||||||
|
| Pane 5 | `对话` | cc 对话监控 |
|
||||||
|
|
||||||
## 布局说明
|
## 布局说明
|
||||||
|
|
||||||
使用 `/workspace/scripts/tmux-team.sh` 创建以下 4 Pane 布局:
|
使用 `/workspace/scripts/tmux-team.sh` 创建以下 5 Pane 布局:
|
||||||
|
|
||||||
```
|
```
|
||||||
+----------+------------------------------------------+
|
+----------+--------------------+--------------------+
|
||||||
| | Pane 2 (后端) | Pane 3 (前端) |
|
| | Pane 2 (后端) | Pane 3 (前端) |
|
||||||
| Pane 1 +------------------------------------------+
|
| Pane 1 +--------------------+--------------------+
|
||||||
| (PM) | Pane 4 (测试) |
|
| (PM) | Pane 4 (测试) | Pane 5 (对话) |
|
||||||
| | |
|
| | | |
|
||||||
+----------+------------------------------------------+
|
+----------+--------------------+--------------------+
|
||||||
```
|
```
|
||||||
|
|
||||||
| Pane | 窗格标题 | 角色 | 提示词文件 | 用途 |
|
| Pane | 窗格标题 | 角色 | 提示词文件 | 用途 |
|
||||||
@@ -54,6 +55,7 @@ $ARGUMENTS
|
|||||||
| 2 | 后端 | 后端开发 | `.claude/agents/isos-backend-agent.md` | 服务端开发 |
|
| 2 | 后端 | 后端开发 | `.claude/agents/isos-backend-agent.md` | 服务端开发 |
|
||||||
| 3 | 前端 | 前端开发 | `.claude/agents/isos-frontend-agent.md` | 桌面端开发 |
|
| 3 | 前端 | 前端开发 | `.claude/agents/isos-frontend-agent.md` | 桌面端开发 |
|
||||||
| 4 | 测试 | 测试工程师 | `.claude/agents/isos-test-agent.md` | 全级别测试和覆盖率 |
|
| 4 | 测试 | 测试工程师 | `.claude/agents/isos-test-agent.md` | 全级别测试和覆盖率 |
|
||||||
|
| 5 | 对话 | cc 监控 | — | 实时监控 项目/后端/前端/测试 的 cc 对话 |
|
||||||
|
|
||||||
## 执行步骤
|
## 执行步骤
|
||||||
|
|
||||||
@@ -115,6 +117,7 @@ tmux list-windows -t [会话名称] -F "Window #{window_index}: #{window_name}"
|
|||||||
| 2 | 后端 | 后端开发 | {size} | (x,y) | {cmd} |
|
| 2 | 后端 | 后端开发 | {size} | (x,y) | {cmd} |
|
||||||
| 3 | 前端 | 前端开发 | {size} | (x,y) | {cmd} |
|
| 3 | 前端 | 前端开发 | {size} | (x,y) | {cmd} |
|
||||||
| 4 | 测试 | 测试工程师 | {size} | (x,y) | {cmd} |
|
| 4 | 测试 | 测试工程师 | {size} | (x,y) | {cmd} |
|
||||||
|
| 5 | 对话 | cc 监控 | {size} | (x,y) | bash |
|
||||||
|
|
||||||
### 快速操作
|
### 快速操作
|
||||||
|
|
||||||
|
|||||||
Executable
+213
@@ -0,0 +1,213 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# tmux-cc-monitor.sh — 实时监控 tmux pane 中 cc (Claude Code) 的消息流
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# tmux-cc-monitor.sh watch %1 [%2 ...] # 监控指定 pane
|
||||||
|
# tmux-cc-monitor.sh watch-all # 监控当前 session 所有 pane
|
||||||
|
# tmux-cc-monitor.sh help
|
||||||
|
#
|
||||||
|
# 工作原理:
|
||||||
|
# 1. 每 200ms 轮询 tmux capture-pane 获取 pane 内容
|
||||||
|
# 2. 用内容 hash 跟踪已显示行,仅显示新增内容
|
||||||
|
# 3. 根据 ❯ 标记区分用户输入和模型输出
|
||||||
|
# 4. 彩色流式显示到当前终端
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
POLL_INTERVAL=0.2
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
C_INPUT='\033[1;32m'
|
||||||
|
C_OUTPUT='\033[0;36m'
|
||||||
|
C_SYSTEM='\033[0;33m'
|
||||||
|
C_RESET='\033[0m'
|
||||||
|
|
||||||
|
ts() { date +%H:%M:%S; }
|
||||||
|
|
||||||
|
strip_ansi() {
|
||||||
|
sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\x1b\[[0-9;]*m//g; s/\r$//g'
|
||||||
|
}
|
||||||
|
|
||||||
|
is_blank() {
|
||||||
|
local s="$1"
|
||||||
|
[[ -z "${s//[[:space:]]/}" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── 噪声过滤 ───
|
||||||
|
is_noise() {
|
||||||
|
local line="$1"
|
||||||
|
local trimmed
|
||||||
|
trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||||
|
|
||||||
|
[[ -z "$trimmed" ]] && return 0
|
||||||
|
|
||||||
|
# 状态栏
|
||||||
|
[[ "$trimmed" =~ ^[─═]+$ ]] && return 0
|
||||||
|
[[ "$trimmed" =~ 版本.*模型 ]] && return 0
|
||||||
|
[[ "$trimmed" =~ bypass ]] && return 0
|
||||||
|
[[ "$trimmed" =~ ^─+.*──[[:space:]]*$ ]] && return 0
|
||||||
|
|
||||||
|
# cc 加载动画 (含省略号 … 的行)
|
||||||
|
[[ "$trimmed" == *…* ]] && return 0
|
||||||
|
|
||||||
|
# cc 命令状态
|
||||||
|
[[ "$trimmed" =~ Running\ [0-9]+\ bash\ command ]] && return 0
|
||||||
|
[[ "$trimmed" =~ Ran\ [0-9]+\ bash\ command ]] && return 0
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── 行级去重 ───
|
||||||
|
# 用 (行内容hash) 跟踪已显示的行,避免重复输出
|
||||||
|
# 维护一个固定大小的环状缓冲区
|
||||||
|
SEEN_CAPACITY=500
|
||||||
|
declare -A SEEN_LINES=()
|
||||||
|
SEEN_ORDER=()
|
||||||
|
SEEN_POS=0
|
||||||
|
|
||||||
|
# 记录一行已显示,返回 true 如果是新行
|
||||||
|
mark_seen() {
|
||||||
|
local line="$1"
|
||||||
|
local hash
|
||||||
|
hash=$(echo "$line" | md5sum | cut -d' ' -f1)
|
||||||
|
|
||||||
|
if [[ -n "${SEEN_LINES[$hash]+x}" ]]; then
|
||||||
|
return 1 # 已存在
|
||||||
|
fi
|
||||||
|
|
||||||
|
SEEN_LINES["$hash"]=1
|
||||||
|
SEEN_ORDER+=("$hash")
|
||||||
|
|
||||||
|
# 裁剪缓冲区
|
||||||
|
if [[ ${#SEEN_ORDER[@]} -gt $SEEN_CAPACITY ]]; then
|
||||||
|
local old_hash="${SEEN_ORDER[0]}"
|
||||||
|
SEEN_ORDER=("${SEEN_ORDER[@]:1}")
|
||||||
|
unset 'SEEN_LINES[$old_hash]'
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── 单 Pane 监控 ───
|
||||||
|
|
||||||
|
watch_pane() {
|
||||||
|
local pane_id="$1"
|
||||||
|
local state="idle"
|
||||||
|
local last_input=""
|
||||||
|
|
||||||
|
# 初始化: 将当前所有行标记为已读
|
||||||
|
local init_content
|
||||||
|
init_content=$(tmux capture-pane -t "$pane_id" -p 2>/dev/null || echo "")
|
||||||
|
while IFS= read -r line; do
|
||||||
|
is_blank "$line" || mark_seen "$line" || true
|
||||||
|
done <<< "$(echo "$init_content" | strip_ansi)"
|
||||||
|
|
||||||
|
echo -e "${C_SYSTEM}[$(ts)][${pane_id}] 开始监控${C_RESET}"
|
||||||
|
|
||||||
|
local prev_content="$init_content"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
local content
|
||||||
|
content=$(tmux capture-pane -t "$pane_id" -p 2>/dev/null) || {
|
||||||
|
sleep 0.5
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# 快速检测: hash
|
||||||
|
local prev_hash curr_hash
|
||||||
|
prev_hash=$(echo "$prev_content" | md5sum)
|
||||||
|
curr_hash=$(echo "$content" | md5sum)
|
||||||
|
[[ "$curr_hash" == "$prev_hash" ]] && { sleep "$POLL_INTERVAL"; continue; }
|
||||||
|
|
||||||
|
# 清洗内容
|
||||||
|
local clean_curr
|
||||||
|
clean_curr=$(echo "$content" | strip_ansi)
|
||||||
|
|
||||||
|
# ── 找出所有新行(基于行级去重)──
|
||||||
|
while IFS= read -r line; do
|
||||||
|
is_blank "$line" && continue
|
||||||
|
is_noise "$line" && continue
|
||||||
|
|
||||||
|
# 检查是否是新行
|
||||||
|
if ! mark_seen "$line"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 分类 ──
|
||||||
|
if echo "$line" | grep -q '❯'; then
|
||||||
|
local input_text
|
||||||
|
input_text=$(echo "$line" | sed 's/.*❯//' | tr -d '\302\240' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||||
|
# 去重: 用 trim 后的文本比较,避免前导空格导致的重复
|
||||||
|
if [[ -n "$input_text" && "$input_text" != "$last_input" ]]; then
|
||||||
|
echo -e "${C_INPUT}[$(ts)][${pane_id}][输入]${C_RESET} ${input_text}"
|
||||||
|
last_input="$input_text"
|
||||||
|
state="output"
|
||||||
|
elif [[ -z "$input_text" ]]; then
|
||||||
|
state="idle"
|
||||||
|
last_input=""
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ "$state" == "output" ]]; then
|
||||||
|
echo -e "${C_OUTPUT}[$(ts)][${pane_id}][输出]${C_RESET} ${line}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "$clean_curr"
|
||||||
|
|
||||||
|
prev_content="$content"
|
||||||
|
sleep "$POLL_INTERVAL"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Main ───
|
||||||
|
|
||||||
|
cmd="${1:-help}"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
case "$cmd" in
|
||||||
|
watch)
|
||||||
|
[[ $# -eq 0 ]] && { echo "用法: tmux-cc-monitor.sh watch <pane_id> [pane_id ...]"; exit 1; }
|
||||||
|
|
||||||
|
pids=()
|
||||||
|
for pane_id in "$@"; do
|
||||||
|
(
|
||||||
|
# 每个 pane 有独立的去重缓冲区
|
||||||
|
SEEN_LINES=()
|
||||||
|
SEEN_ORDER=()
|
||||||
|
watch_pane "$pane_id"
|
||||||
|
) &
|
||||||
|
pids+=($!)
|
||||||
|
done
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
for pid in "${pids[@]}"; do
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
}
|
||||||
|
trap cleanup INT TERM
|
||||||
|
echo -e "${C_SYSTEM}[$(ts)] 监控已启动 (PIDs: ${pids[*]}),Ctrl+C 停止${C_RESET}"
|
||||||
|
wait
|
||||||
|
;;
|
||||||
|
|
||||||
|
watch-all)
|
||||||
|
panes=$(tmux list-panes -F '#{pane_id}' 2>/dev/null || true)
|
||||||
|
[[ -z "$panes" ]] && { echo "错误: 未找到 tmux panes"; exit 1; }
|
||||||
|
exec "$0" watch $panes
|
||||||
|
;;
|
||||||
|
|
||||||
|
help|--help|-h)
|
||||||
|
echo "用法: tmux-cc-monitor.sh <command>"
|
||||||
|
echo ""
|
||||||
|
echo "命令:"
|
||||||
|
echo " watch <pane...> 监控指定 pane 的 cc 消息流"
|
||||||
|
echo " watch-all 监控当前 session 所有 pane"
|
||||||
|
echo ""
|
||||||
|
echo "输出:"
|
||||||
|
echo -e " ${C_INPUT}[时间][pane][输入]${C_RESET} 用户提示词"
|
||||||
|
echo -e " ${C_OUTPUT}[时间][pane][输出]${C_RESET} 模型响应(流式)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "未知命令: $cmd" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
+20
-10
@@ -14,12 +14,12 @@
|
|||||||
# - 不在 tmux 中: 启动新 tmux 会话并附加
|
# - 不在 tmux 中: 启动新 tmux 会话并附加
|
||||||
#
|
#
|
||||||
# 视觉布局:
|
# 视觉布局:
|
||||||
# +----------+------------------------------------------+
|
# +----------+--------------------+--------------------+
|
||||||
# | | Pane 2 (后端) | Pane 3 (前端) |
|
# | | Pane 2 (后端) | Pane 3 (前端) |
|
||||||
# | Pane 1 +------------------------------------------+
|
# | Pane 1 +--------------------+--------------------+
|
||||||
# | (项目) | Pane 4 (测试) |
|
# | (项目) | Pane 4 (测试) | Pane 5 (对话) |
|
||||||
# | | |
|
# | | | |
|
||||||
# +----------+------------------------------------------+
|
# +----------+--------------------+--------------------+
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -216,11 +216,16 @@ build_layout() {
|
|||||||
local right_col2_id
|
local right_col2_id
|
||||||
right_col2_id=$(tmux split-window -h -t "$right_id" -l "$PANE3_W" -c "$WORKSPACE" -P -F '#{pane_id}')
|
right_col2_id=$(tmux split-window -h -t "$right_id" -l "$PANE3_W" -c "$WORKSPACE" -P -F '#{pane_id}')
|
||||||
|
|
||||||
|
# Step 4: 底部水平分为两列: Pane 4 (测试) | Pane 5 (对话)
|
||||||
|
local monitor_id
|
||||||
|
monitor_id=$(tmux split-window -h -t "$bottom_id" -l "$PANE3_W" -c "$WORKSPACE" -P -F '#{pane_id}')
|
||||||
|
|
||||||
# ── 命名窗格 ──
|
# ── 命名窗格 ──
|
||||||
tmux select-pane -t "$base_pane" -T "项目"
|
tmux select-pane -t "$base_pane" -T "项目"
|
||||||
tmux select-pane -t "$right_id" -T "后端"
|
tmux select-pane -t "$right_id" -T "后端"
|
||||||
tmux select-pane -t "$right_col2_id" -T "前端"
|
tmux select-pane -t "$right_col2_id" -T "前端"
|
||||||
tmux select-pane -t "$bottom_id" -T "测试"
|
tmux select-pane -t "$bottom_id" -T "测试"
|
||||||
|
tmux select-pane -t "$monitor_id" -T "对话"
|
||||||
|
|
||||||
# ── 启动 AI 编码工具(等待 shell 就绪后再发送,附带角色名称)──
|
# ── 启动 AI 编码工具(等待 shell 就绪后再发送,附带角色名称)──
|
||||||
local runner="${TOOL_RUNNER:-cc}"
|
local runner="${TOOL_RUNNER:-cc}"
|
||||||
@@ -248,6 +253,10 @@ build_layout() {
|
|||||||
bash "$SCRIPT_DIR/tmux-send-prompt.sh" "$bottom_id" "${PROMPT_DIR}/${PROMPT_FILES[3]}" &
|
bash "$SCRIPT_DIR/tmux-send-prompt.sh" "$bottom_id" "${PROMPT_DIR}/${PROMPT_FILES[3]}" &
|
||||||
wait
|
wait
|
||||||
|
|
||||||
|
# ── 启动 cc 实时监控 (对话 pane) ──
|
||||||
|
sleep 1
|
||||||
|
tmux send-keys -t "$monitor_id" "bash ${WORKSPACE}/scripts/tmux-cc-monitor.sh watch ${base_pane} ${right_id} ${right_col2_id} ${bottom_id}" Enter
|
||||||
|
|
||||||
# 选中主 Agent 窗格
|
# 选中主 Agent 窗格
|
||||||
tmux select-pane -t "$base_pane"
|
tmux select-pane -t "$base_pane"
|
||||||
}
|
}
|
||||||
@@ -257,18 +266,19 @@ build_layout() {
|
|||||||
print_info() {
|
print_info() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "窗格布局:"
|
echo "窗格布局:"
|
||||||
echo " +----------+------------------------------------------+"
|
echo " +----------+--------------------+--------------------+"
|
||||||
echo " | | Pane 2 (后端) | Pane 3 (前端) |"
|
echo " | | Pane 2 (后端) | Pane 3 (前端) |"
|
||||||
echo " | Pane 1 +------------------------------------------+"
|
echo " | Pane 1 +--------------------+--------------------+"
|
||||||
echo " | (项目) | Pane 4 (测试) |"
|
echo " | (项目) | Pane 4 (测试) | Pane 5 (对话) |"
|
||||||
echo " | | |"
|
echo " | | | |"
|
||||||
echo " +----------+------------------------------------------+"
|
echo " +----------+--------------------+--------------------+"
|
||||||
echo ""
|
echo ""
|
||||||
echo "角色分配:"
|
echo "角色分配:"
|
||||||
echo " Pane 1: 项目 — 项目管理、任务协调"
|
echo " Pane 1: 项目 — 项目管理、任务协调"
|
||||||
echo " Pane 2: 后端 — 服务端开发 (FastAPI + SQLite)"
|
echo " Pane 2: 后端 — 服务端开发 (FastAPI + SQLite)"
|
||||||
echo " Pane 3: 前端 — 桌面端开发 (Svelte 5 + PyWebView)"
|
echo " Pane 3: 前端 — 桌面端开发 (Svelte 5 + PyWebView)"
|
||||||
echo " Pane 4: 测试 — 全级别测试和覆盖率"
|
echo " Pane 4: 测试 — 全级别测试和覆盖率"
|
||||||
|
echo " Pane 5: 对话 — 实时监控各面板 cc 对话"
|
||||||
echo ""
|
echo ""
|
||||||
echo "所有窗格已启动 AI 编码工具并加载角色提示词"
|
echo "所有窗格已启动 AI 编码工具并加载角色提示词"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user