@brandon_9527/tcode 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/python-src/.autodev/skills/teams/scripts/README.md +29 -0
- package/dist/python-src/.autodev/skills/teams/scripts/cursor_dispatch.sh +88 -0
- package/dist/python-src/.autodev/skills/teams/scripts/dispatch.sh +112 -0
- package/dist/python-src/.autodev/skills/teams/scripts/label.sh +62 -0
- package/dist/python-src/.autodev/skills/teams/scripts/layout.sh +181 -0
- package/dist/python-src/.autodev/skills/teams/scripts/old_dispatch.sh +42 -0
- package/dist/python-src/.autodev/skills/teams/scripts/pipe.sh +19 -0
- package/dist/python-src/.autodev/skills/teams/scripts/pipe_dispatch.sh +59 -0
- package/dist/python-src/.autodev/skills/teams/scripts/run.sh +18 -0
- package/dist/python-src/.autodev/skills/teams/scripts/stop.sh +26 -0
- package/dist/python-src/.autodev/skills/teams/scripts/tmux-layout.sh +43 -0
- package/dist/python-src/entry.py +28 -1
- package/dist/python-src/main.py +382 -3
- package/dist/python-src/pyproject.toml +1 -1
- package/dist/python-src/src/ai_tcode.egg-info/PKG-INFO +30 -0
- package/dist/python-src/src/ai_tcode.egg-info/SOURCES.txt +48 -0
- package/dist/python-src/src/ai_tcode.egg-info/dependency_links.txt +1 -0
- package/dist/python-src/src/ai_tcode.egg-info/requires.txt +26 -0
- package/dist/python-src/src/ai_tcode.egg-info/top_level.txt +9 -0
- package/dist/python-src/src/managers/manager_agent.py +200 -0
- package/dist/python-src/src/managers/manager_context.py +49 -0
- package/dist/python-src/src/managers/manager_instruction.py +192 -0
- package/dist/python-src/src/managers/sandbox.py +3 -3
- package/dist/python-src/src/middlewares/dynamic_content.py +63 -0
- package/dist/python-src/src/middlewares/hitl.py +3 -3
- package/dist/python-src/src/middlewares/memory.py +44 -0
- package/dist/python-src/src/middlewares/subagents.py +25 -25
- package/dist/python-src/src/middlewares/summary.py +35 -35
- package/dist/python-src/src/middlewares/utils.py +5 -0
- package/dist/python-src/src/prompts/prompts.py +1 -0
- package/dist/python-src/src/stream/formatter.py +17 -17
- package/dist/python-src/src/stream/handler.py +109 -81
- package/dist/python-src/src/stream/handler_with_tracker.py +10 -10
- package/dist/python-src/src/tools/web.py +10 -9
- package/dist/python-src/src/tui/chatui.py +67 -51
- package/dist/python-src/src/tui/components/tlist.py +6 -6
- package/dist/python-src/src/tui/components/tscroll_panel.py +8 -8
- package/dist/python-src/src/tui/utils/trender.py +27 -27
- package/dist/python-src/src/utils/prompt.py +15 -4
- package/dist/python-src/uv.lock +1980 -2094
- package/package.json +1 -1
- package/dist/python-src/src/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/managers/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/managers/__pycache__/sandbox.cpython-311.pyc +0 -0
- package/dist/python-src/src/middlewares/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/middlewares/__pycache__/hitl.cpython-311.pyc +0 -0
- package/dist/python-src/src/middlewares/dynamic_prompt.py +0 -15
- package/dist/python-src/src/stream/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/stream/__pycache__/emitter.cpython-311.pyc +0 -0
- package/dist/python-src/src/stream/__pycache__/file_write_parser.cpython-311.pyc +0 -0
- package/dist/python-src/src/stream/__pycache__/formatter.cpython-311.pyc +0 -0
- package/dist/python-src/src/stream/__pycache__/handler.cpython-311.pyc +0 -0
- package/dist/python-src/src/stream/__pycache__/tracker.cpython-311.pyc +0 -0
- package/dist/python-src/src/stream/__pycache__/utils.cpython-311.pyc +0 -0
- package/dist/python-src/src/tools/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/tools/__pycache__/skill_loader.cpython-311.pyc +0 -0
- package/dist/python-src/src/tools/__pycache__/tools.cpython-311.pyc +0 -0
- package/dist/python-src/src/tools/__pycache__/web.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/__pycache__/chatui.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/__pycache__/config.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/components/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/components/__pycache__/live_spinner.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/components/__pycache__/tdiff.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/components/__pycache__/tdisplay.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/components/__pycache__/tlist.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/components/__pycache__/tscroll_panel.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/utils/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/utils/__pycache__/render.cpython-311.pyc +0 -0
- package/dist/python-src/src/tui/utils/__pycache__/trender.cpython-311.pyc +0 -0
- package/dist/python-src/src/utils/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/python-src/src/utils/__pycache__/utils.cpython-311.pyc +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 启动(带角色)
|
|
2
|
+
./run.sh brain coder tester reviewer
|
|
3
|
+
|
|
4
|
+
# 动态扩容
|
|
5
|
+
./layout.sh scale brain coder tester reviewer ops logger
|
|
6
|
+
|
|
7
|
+
# 派发任务
|
|
8
|
+
./dispatch.sh coder "echo coding"
|
|
9
|
+
./dispatch.sh tester "echo testing"
|
|
10
|
+
|
|
11
|
+
./layout.sh up
|
|
12
|
+
./layoyt.sh down
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
👉 绑定快捷键(tmux 内)
|
|
16
|
+
```bash
|
|
17
|
+
tmux bind-key -n C-j run-shell "./layout.sh down"
|
|
18
|
+
tmux bind-key -n C-k run-shell "./layout.sh up"
|
|
19
|
+
```
|
|
20
|
+
👉 直接:
|
|
21
|
+
```bash
|
|
22
|
+
Ctrl + j ↓
|
|
23
|
+
Ctrl + k ↑
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
滚动 agent
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
SESSION="agent"
|
|
5
|
+
MAP_FILE="/tmp/tmux-agent.map"
|
|
6
|
+
CURSOR_FILE="/tmp/tmux-agent.cursor"
|
|
7
|
+
|
|
8
|
+
ROLE=$1
|
|
9
|
+
TASK=$2
|
|
10
|
+
|
|
11
|
+
if [ -z "$ROLE" ] || [ -z "$TASK" ]; then
|
|
12
|
+
echo "Usage: $0 <role> <task>"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# ----------------------------
|
|
17
|
+
# 查找 pane
|
|
18
|
+
# ----------------------------
|
|
19
|
+
PANE=$(grep "^$ROLE " "$MAP_FILE" | awk '{print $2}')
|
|
20
|
+
|
|
21
|
+
if [ -z "$PANE" ]; then
|
|
22
|
+
echo "Role not found: $ROLE"
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# ----------------------------
|
|
27
|
+
# cursor 工具
|
|
28
|
+
# ----------------------------
|
|
29
|
+
get_cursor() {
|
|
30
|
+
grep "^$1 " "$CURSOR_FILE" 2>/dev/null | awk '{print $2}'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
set_cursor() {
|
|
34
|
+
grep -v "^$1 " "$CURSOR_FILE" 2>/dev/null > "$CURSOR_FILE.tmp" || true
|
|
35
|
+
echo "$1 $2" >> "$CURSOR_FILE.tmp"
|
|
36
|
+
mv "$CURSOR_FILE.tmp" "$CURSOR_FILE"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ----------------------------
|
|
40
|
+
# 获取当前总行数
|
|
41
|
+
# ----------------------------
|
|
42
|
+
TOTAL_BEFORE=$(tmux -S $SOCKET capture-pane -t "$PANE" -p -J | wc -l)
|
|
43
|
+
|
|
44
|
+
LAST=$(get_cursor "$PANE")
|
|
45
|
+
|
|
46
|
+
# ✅ 修复点 1(关键)
|
|
47
|
+
if [ -z "$LAST" ]; then
|
|
48
|
+
LAST=$((TOTAL_BEFORE - 1))
|
|
49
|
+
if [ "$LAST" -lt 0 ]; then
|
|
50
|
+
LAST=0
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# ----------------------------
|
|
55
|
+
# 发送任务
|
|
56
|
+
# ----------------------------
|
|
57
|
+
tmux -S $SOCKET send-keys -t "$PANE" "$TASK" C-m
|
|
58
|
+
|
|
59
|
+
# ----------------------------
|
|
60
|
+
# 等待输出变化
|
|
61
|
+
# ----------------------------
|
|
62
|
+
for i in {1..20}; do
|
|
63
|
+
TOTAL_AFTER=$(tmux -S $SOCKET capture-pane -t "$PANE" -p -J | wc -l)
|
|
64
|
+
|
|
65
|
+
if [ "$TOTAL_AFTER" -gt "$TOTAL_BEFORE" ]; then
|
|
66
|
+
break
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
sleep 0.2
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
# ----------------------------
|
|
73
|
+
# 增量读取
|
|
74
|
+
# ----------------------------
|
|
75
|
+
|
|
76
|
+
# ✅ 修复点 2(-J)
|
|
77
|
+
NEW_OUTPUT=$(tmux -S $SOCKET capture-pane -t "$PANE" -p -J -S $((LAST + 1)))
|
|
78
|
+
|
|
79
|
+
# ----------------------------
|
|
80
|
+
# 更新 cursor
|
|
81
|
+
# ----------------------------
|
|
82
|
+
FINAL_TOTAL=$(tmux -S $SOCKET capture-pane -t "$PANE" -p -J | wc -l)
|
|
83
|
+
set_cursor "$PANE" "$FINAL_TOTAL"
|
|
84
|
+
|
|
85
|
+
# ----------------------------
|
|
86
|
+
# 输出
|
|
87
|
+
# ----------------------------
|
|
88
|
+
echo "$NEW_OUTPUT"
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
SESSION="agent"
|
|
5
|
+
MAP_FILE="/tmp/tmux-agent.map"
|
|
6
|
+
|
|
7
|
+
ROLE=$1
|
|
8
|
+
TASK=$2
|
|
9
|
+
|
|
10
|
+
if [ -z "$ROLE" ] || [ -z "$TASK" ]; then
|
|
11
|
+
echo "Usage: $0 <role> <task>"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# ----------------------------
|
|
16
|
+
# 找 pane
|
|
17
|
+
# ----------------------------
|
|
18
|
+
PANE=$(grep "^$ROLE " "$MAP_FILE" | awk '{print $2}')
|
|
19
|
+
|
|
20
|
+
if [ -z "$PANE" ]; then
|
|
21
|
+
echo "Role not found: $ROLE"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# ----------------------------
|
|
26
|
+
# task id
|
|
27
|
+
# ----------------------------
|
|
28
|
+
TASK_ID=$(date +%s%N)
|
|
29
|
+
START="__START__${TASK_ID}"
|
|
30
|
+
END="__END__${TASK_ID}"
|
|
31
|
+
|
|
32
|
+
# CMD="echo $START; $TASK; echo $END"
|
|
33
|
+
# CMD="echo $START; bash -c '$TASK'; echo $END"
|
|
34
|
+
CMD="echo $START; bash -c $(printf %q "$TASK"); echo $END"
|
|
35
|
+
|
|
36
|
+
tmux -S $SOCKET send-keys -t "$PANE" "$CMD" C-m
|
|
37
|
+
|
|
38
|
+
# ----------------------------
|
|
39
|
+
# streaming
|
|
40
|
+
# ----------------------------
|
|
41
|
+
FOUND=0
|
|
42
|
+
LAST_LINES=0
|
|
43
|
+
|
|
44
|
+
START_TIME=$(date +%s)
|
|
45
|
+
TIMEOUT=15 # 秒
|
|
46
|
+
|
|
47
|
+
while true; do
|
|
48
|
+
|
|
49
|
+
OUTPUT=$(tmux -S $SOCKET capture-pane -t "$PANE" -p -J)
|
|
50
|
+
|
|
51
|
+
# ----------------------------
|
|
52
|
+
# ⏱ timeout 防卡死
|
|
53
|
+
# ----------------------------
|
|
54
|
+
NOW=$(date +%s)
|
|
55
|
+
if [ $((NOW - START_TIME)) -gt $TIMEOUT ]; then
|
|
56
|
+
echo "[dispatch timeout]"
|
|
57
|
+
break
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# ----------------------------
|
|
61
|
+
# 🧠 START 检测(带 clear 修复)
|
|
62
|
+
# ----------------------------
|
|
63
|
+
if [ $FOUND -eq 0 ]; then
|
|
64
|
+
|
|
65
|
+
if echo "$OUTPUT" | grep -q "$START"; then
|
|
66
|
+
FOUND=1
|
|
67
|
+
|
|
68
|
+
elif echo "$OUTPUT" | grep -q "$END"; then
|
|
69
|
+
# ❗ START 被 clear 掉了
|
|
70
|
+
# 直接结束(无输出任务)
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
else
|
|
74
|
+
sleep 0.1
|
|
75
|
+
continue
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# ----------------------------
|
|
80
|
+
# 截 START 后内容
|
|
81
|
+
# ----------------------------
|
|
82
|
+
STREAM=$(echo "$OUTPUT" | awk "/$START/{flag=1;next} flag")
|
|
83
|
+
|
|
84
|
+
# ----------------------------
|
|
85
|
+
# 如果遇到 END
|
|
86
|
+
# ----------------------------
|
|
87
|
+
if echo "$STREAM" | grep -q "$END"; then
|
|
88
|
+
|
|
89
|
+
RESULT=$(echo "$STREAM" | awk "/$END/{exit} {print}")
|
|
90
|
+
|
|
91
|
+
TOTAL=$(echo "$RESULT" | wc -l)
|
|
92
|
+
|
|
93
|
+
if [ "$TOTAL" -gt "$LAST_LINES" ]; then
|
|
94
|
+
echo "$RESULT" | tail -n $(($TOTAL - $LAST_LINES))
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
break
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# ----------------------------
|
|
101
|
+
# ✅ 增量输出(去重核心)
|
|
102
|
+
# ----------------------------
|
|
103
|
+
TOTAL=$(echo "$STREAM" | wc -l)
|
|
104
|
+
|
|
105
|
+
if [ "$TOTAL" -gt "$LAST_LINES" ]; then
|
|
106
|
+
NEW_LINES=$(($TOTAL - $LAST_LINES))
|
|
107
|
+
echo "$STREAM" | tail -n $NEW_LINES
|
|
108
|
+
LAST_LINES=$TOTAL
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
sleep 0.2
|
|
112
|
+
done
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
SESSION="agent"
|
|
5
|
+
|
|
6
|
+
# shift
|
|
7
|
+
ROLES=("$@")
|
|
8
|
+
|
|
9
|
+
tmux -S $SOCKET set-option -g pane-border-status top
|
|
10
|
+
tmux -S $SOCKET set-option -g pane-border-format "#{pane_index} | #{pane_title}"
|
|
11
|
+
|
|
12
|
+
MAP_FILE="/tmp/tmux-agent.map"
|
|
13
|
+
> $MAP_FILE # 清空
|
|
14
|
+
|
|
15
|
+
PANES=($(tmux -S $SOCKET list-panes -t $SESSION -F "#{pane_id}"))
|
|
16
|
+
|
|
17
|
+
# ----------------------------
|
|
18
|
+
# 1️⃣ 固定主 pane(pane[0])
|
|
19
|
+
# ----------------------------
|
|
20
|
+
MAIN_PANE=${PANES[0]}
|
|
21
|
+
tmux -S $SOCKET select-pane -t "$MAIN_PANE" -T "main"
|
|
22
|
+
|
|
23
|
+
echo "main $MAIN_PANE" >> $MAP_FILE
|
|
24
|
+
|
|
25
|
+
# ----------------------------
|
|
26
|
+
# 2️⃣ 从 pane[1] 开始分配 agent
|
|
27
|
+
# ----------------------------
|
|
28
|
+
for i in "${!ROLES[@]}"; do
|
|
29
|
+
PANE_INDEX=$((i + 1)) # ⭐ 关键:跳过 main
|
|
30
|
+
|
|
31
|
+
PANE=${PANES[$PANE_INDEX]}
|
|
32
|
+
ROLE=${ROLES[$i]}
|
|
33
|
+
|
|
34
|
+
if [ -z "$PANE" ]; then
|
|
35
|
+
break
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# 设置 UI 标题
|
|
39
|
+
tmux -S $SOCKET select-pane -t "$PANE" -T "$ROLE"
|
|
40
|
+
|
|
41
|
+
# 写入映射
|
|
42
|
+
echo "$ROLE $PANE" >> $MAP_FILE
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
# ----------------------------
|
|
46
|
+
# 3️⃣ 多余 pane → worker
|
|
47
|
+
# ----------------------------
|
|
48
|
+
TOTAL_PANES=${#PANES[@]}
|
|
49
|
+
USED_PANES=$(( ${#ROLES[@]} + 1 ))
|
|
50
|
+
|
|
51
|
+
for ((i=USED_PANES; i<TOTAL_PANES; i++)); do
|
|
52
|
+
PANE=${PANES[$i]}
|
|
53
|
+
ROLE="worker-$i"
|
|
54
|
+
|
|
55
|
+
tmux -S $SOCKET select-pane -t "$PANE" -T "$ROLE"
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
# # ✅ 多余 pane 清空
|
|
59
|
+
# for ((i=USED_PANES; i<TOTAL_PANES; i++)); do
|
|
60
|
+
# PANE=${PANES[$i]}
|
|
61
|
+
# tmux -S $SOCKET select-pane -t "$PANE" -T ""
|
|
62
|
+
# done
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
SESSION="agent"
|
|
5
|
+
STATE_FILE="/tmp/tmux-agent.state"
|
|
6
|
+
|
|
7
|
+
MAX_VISIBLE=4
|
|
8
|
+
|
|
9
|
+
# ----------------------------
|
|
10
|
+
# 初始化
|
|
11
|
+
# ----------------------------
|
|
12
|
+
init() {
|
|
13
|
+
tmux -S $SOCKET kill-server 2>/dev/null || true
|
|
14
|
+
tmux -S $SOCKET new-session -d -s $SESSION -n main
|
|
15
|
+
|
|
16
|
+
tmux -S $SOCKET split-window -h -p 30 -t $SESSION
|
|
17
|
+
|
|
18
|
+
tmux -S $SOCKET select-layout -t $SESSION main-vertical
|
|
19
|
+
|
|
20
|
+
echo "OFFSET=0" > $STATE_FILE
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# ----------------------------
|
|
24
|
+
# scale(创建所有 agent)
|
|
25
|
+
# ----------------------------
|
|
26
|
+
scale() {
|
|
27
|
+
shift
|
|
28
|
+
ROLES=("$@")
|
|
29
|
+
|
|
30
|
+
# 保存 roles
|
|
31
|
+
echo "ROLES=\"${ROLES[*]}\"" >> $STATE_FILE
|
|
32
|
+
|
|
33
|
+
TOTAL=${#ROLES[@]}
|
|
34
|
+
CURRENT=$(tmux -S $SOCKET list-panes -t $SESSION | wc -l)
|
|
35
|
+
|
|
36
|
+
# TARGET=$((TOTAL + 1))
|
|
37
|
+
VISIBLE=$TOTAL
|
|
38
|
+
[ "$VISIBLE" -gt "$MAX_VISIBLE" ] && VISIBLE=$MAX_VISIBLE
|
|
39
|
+
|
|
40
|
+
TARGET=$((VISIBLE + 1))
|
|
41
|
+
|
|
42
|
+
while [ "$CURRENT" -lt "$TARGET" ]; do
|
|
43
|
+
tmux -S $SOCKET split-window -t $SESSION -d
|
|
44
|
+
((CURRENT++))
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
while [ "$CURRENT" -gt "$TARGET" ]; do
|
|
48
|
+
tmux -S $SOCKET kill-pane -t $SESSION:.-
|
|
49
|
+
((CURRENT--))
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
# while [ "$CURRENT" -gt "$TARGET" ]; do
|
|
53
|
+
# LAST_PANE=$(tmux -S $SOCKET list-panes -t $SESSION -F "#{pane_id}" | tail -n1)
|
|
54
|
+
# tmux -S $SOCKET kill-pane -t "$LAST_PANE"
|
|
55
|
+
# ((CURRENT--))
|
|
56
|
+
# done
|
|
57
|
+
|
|
58
|
+
tmux -S $SOCKET select-layout -t $SESSION main-vertical
|
|
59
|
+
|
|
60
|
+
render
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# ----------------------------
|
|
64
|
+
# 渲染当前窗口(最多显示4个)
|
|
65
|
+
# ----------------------------
|
|
66
|
+
render() {
|
|
67
|
+
source $STATE_FILE
|
|
68
|
+
|
|
69
|
+
IFS=' ' read -r -a ROLES <<< "$ROLES"
|
|
70
|
+
|
|
71
|
+
PANES=($(tmux -S $SOCKET list-panes -t $SESSION -F "#{pane_id}"))
|
|
72
|
+
|
|
73
|
+
TOTAL_PANES=${#PANES[@]}
|
|
74
|
+
if [ "$TOTAL_PANES" -lt "$MAX_VISIBLE" ]; then
|
|
75
|
+
VISIBLE=$TOTAL_PANES
|
|
76
|
+
else
|
|
77
|
+
VISIBLE=$MAX_VISIBLE
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# pane[0] 是 main
|
|
82
|
+
for ((i=1; i<=VISIBLE; i++)); do
|
|
83
|
+
IDX=$((OFFSET + i - 1))
|
|
84
|
+
ROLE=${ROLES[$IDX]}
|
|
85
|
+
|
|
86
|
+
if [ -z "$ROLE" ]; then
|
|
87
|
+
ROLE="empty"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
PANE=${PANES[$i]}
|
|
91
|
+
|
|
92
|
+
tmux -S $SOCKET select-pane -t $PANE -T "$ROLE"
|
|
93
|
+
done
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# render() {
|
|
97
|
+
# source $STATE_FILE
|
|
98
|
+
|
|
99
|
+
# IFS=' ' read -r -a ROLES <<< "$ROLES"
|
|
100
|
+
|
|
101
|
+
# PANES=($(tmux -S $SOCKET list-panes -t $SESSION -F "#{pane_id}"))
|
|
102
|
+
|
|
103
|
+
# TOTAL_ROLES=${#ROLES[@]}
|
|
104
|
+
# TOTAL_PANES=${#PANES[@]}
|
|
105
|
+
|
|
106
|
+
# # pane[0] 是 main
|
|
107
|
+
# AVAILABLE_PANES=$((TOTAL_PANES - 1))
|
|
108
|
+
|
|
109
|
+
# # 剩余 roles(考虑 OFFSET)
|
|
110
|
+
# REMAINING=$((TOTAL_ROLES - OFFSET))
|
|
111
|
+
|
|
112
|
+
# # 实际要显示的数量
|
|
113
|
+
# VISIBLE=$MAX_VISIBLE
|
|
114
|
+
|
|
115
|
+
# [ "$VISIBLE" -gt "$AVAILABLE_PANES" ] && VISIBLE=$AVAILABLE_PANES
|
|
116
|
+
# [ "$VISIBLE" -gt "$REMAINING" ] && VISIBLE=$REMAINING
|
|
117
|
+
|
|
118
|
+
# # ----------------------------
|
|
119
|
+
# # 渲染有效 pane
|
|
120
|
+
# # ----------------------------
|
|
121
|
+
# for ((i=1; i<=VISIBLE; i++)); do
|
|
122
|
+
# IDX=$((OFFSET + i - 1))
|
|
123
|
+
# ROLE=${ROLES[$IDX]}
|
|
124
|
+
|
|
125
|
+
# PANE=${PANES[$i]}
|
|
126
|
+
|
|
127
|
+
# tmux -S $SOCKET select-pane -t $PANE -T "$ROLE"
|
|
128
|
+
# done
|
|
129
|
+
|
|
130
|
+
# # ----------------------------
|
|
131
|
+
# # 多余 pane 标记为空
|
|
132
|
+
# # ----------------------------
|
|
133
|
+
# for ((i=VISIBLE+1; i<=AVAILABLE_PANES; i++)); do
|
|
134
|
+
# PANE=${PANES[$i]}
|
|
135
|
+
# tmux -S $SOCKET select-pane -t $PANE -T "empty"
|
|
136
|
+
# done
|
|
137
|
+
# }
|
|
138
|
+
|
|
139
|
+
# ----------------------------
|
|
140
|
+
# 向下滚动
|
|
141
|
+
# ----------------------------
|
|
142
|
+
down() {
|
|
143
|
+
source $STATE_FILE
|
|
144
|
+
|
|
145
|
+
IFS=' ' read -r -a ROLES <<< "$ROLES"
|
|
146
|
+
|
|
147
|
+
TOTAL=${#ROLES[@]}
|
|
148
|
+
|
|
149
|
+
if [ $((OFFSET + MAX_VISIBLE)) -lt $TOTAL ]; then
|
|
150
|
+
OFFSET=$((OFFSET + 1))
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
echo "OFFSET=$OFFSET" > $STATE_FILE
|
|
154
|
+
echo "ROLES=\"${ROLES[*]}\"" >> $STATE_FILE
|
|
155
|
+
|
|
156
|
+
render
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# ----------------------------
|
|
160
|
+
# 向上滚动
|
|
161
|
+
# ----------------------------
|
|
162
|
+
up() {
|
|
163
|
+
source $STATE_FILE
|
|
164
|
+
|
|
165
|
+
if [ "$OFFSET" -gt 0 ]; then
|
|
166
|
+
OFFSET=$((OFFSET - 1))
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
echo "OFFSET=$OFFSET" > $STATE_FILE
|
|
170
|
+
echo "ROLES=\"$ROLES\"" >> $STATE_FILE
|
|
171
|
+
|
|
172
|
+
render
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case "$1" in
|
|
176
|
+
init) init ;;
|
|
177
|
+
scale) scale "$@" ;;
|
|
178
|
+
up) up ;;
|
|
179
|
+
down) down ;;
|
|
180
|
+
render) render ;;
|
|
181
|
+
esac
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
SESSION="agent"
|
|
5
|
+
MAP_FILE="/tmp/tmux-agent.map"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
ROLE=$1
|
|
9
|
+
TASK=$2
|
|
10
|
+
|
|
11
|
+
if [ -z "$ROLE" ] || [ -z "$TASK" ]; then
|
|
12
|
+
echo "Usage: $0 <role> <task>"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# ----------------------------
|
|
17
|
+
# 查找 pane(只取第一个)
|
|
18
|
+
# ----------------------------
|
|
19
|
+
# PANE=$(tmux -S $SOCKET list-panes -t $SESSION -F "#{pane_id} #{pane_title}" \
|
|
20
|
+
# | grep " $ROLE$" \
|
|
21
|
+
# | head -n1 \
|
|
22
|
+
# | awk '{print $1}')
|
|
23
|
+
|
|
24
|
+
PANE=$(grep "^$ROLE " $MAP_FILE | awk '{print $2}')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if [ -z "$PANE" ]; then
|
|
28
|
+
echo "Role not found: $ROLE"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# ----------------------------
|
|
33
|
+
# 发送任务
|
|
34
|
+
# ----------------------------
|
|
35
|
+
tmux -S $SOCKET send-keys -t "$PANE" "$TASK" C-m
|
|
36
|
+
|
|
37
|
+
sleep 1
|
|
38
|
+
|
|
39
|
+
# ----------------------------
|
|
40
|
+
# 捕获输出
|
|
41
|
+
# ----------------------------
|
|
42
|
+
tmux -S $SOCKET capture-pane -t "$PANE" -p
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
SESSION="agent"
|
|
5
|
+
LOG_DIR="/tmp/tmux-agent-logs"
|
|
6
|
+
|
|
7
|
+
mkdir -p "$LOG_DIR"
|
|
8
|
+
|
|
9
|
+
tmux -S $SOCKET list-panes -t $SESSION -F "#{pane_id}" | while read pane; do
|
|
10
|
+
|
|
11
|
+
LOG_FILE="$LOG_DIR/${pane}.log"
|
|
12
|
+
|
|
13
|
+
# 清空旧日志
|
|
14
|
+
: > "$LOG_FILE"
|
|
15
|
+
|
|
16
|
+
# 绑定 pipe-pane(实时输出)
|
|
17
|
+
tmux -S $SOCKET pipe-pane -o -t "$pane" "cat >> $LOG_FILE"
|
|
18
|
+
|
|
19
|
+
done
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
MAP_FILE="/tmp/tmux-agent.map"
|
|
5
|
+
LOG_DIR="/tmp/tmux-agent-logs"
|
|
6
|
+
|
|
7
|
+
ROLE=$1
|
|
8
|
+
TASK=$2
|
|
9
|
+
|
|
10
|
+
if [ -z "$ROLE" ] || [ -z "$TASK" ]; then
|
|
11
|
+
echo "Usage: $0 <role> <task>"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# ----------------------------
|
|
16
|
+
# 找 pane
|
|
17
|
+
# ----------------------------
|
|
18
|
+
PANE=$(grep "^$ROLE " "$MAP_FILE" | awk '{print $2}')
|
|
19
|
+
|
|
20
|
+
if [ -z "$PANE" ]; then
|
|
21
|
+
echo "Role not found: $ROLE"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
LOG_FILE="$LOG_DIR/${PANE}.log"
|
|
26
|
+
|
|
27
|
+
# ----------------------------
|
|
28
|
+
# marker
|
|
29
|
+
# ----------------------------
|
|
30
|
+
TASK_ID=$(date +%s%N)
|
|
31
|
+
START="__START__${TASK_ID}"
|
|
32
|
+
END="__END__${TASK_ID}"
|
|
33
|
+
|
|
34
|
+
# 安全命令
|
|
35
|
+
SAFE_TASK=$(printf %q "$TASK")
|
|
36
|
+
|
|
37
|
+
CMD="echo $START; bash -c $SAFE_TASK; echo $END"
|
|
38
|
+
|
|
39
|
+
tmux -S $SOCKET send-keys -t "$PANE" "$CMD" C-m
|
|
40
|
+
|
|
41
|
+
# ----------------------------
|
|
42
|
+
# 真流式读取
|
|
43
|
+
# ----------------------------
|
|
44
|
+
STARTED=0
|
|
45
|
+
|
|
46
|
+
tail -n 0 -f "$LOG_FILE" | while read line; do
|
|
47
|
+
|
|
48
|
+
if [ $STARTED -eq 0 ]; then
|
|
49
|
+
[[ "$line" == *"$START"* ]] && STARTED=1
|
|
50
|
+
continue
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [[ "$line" == *"$END"* ]]; then
|
|
54
|
+
break
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
echo "$line"
|
|
58
|
+
|
|
59
|
+
done
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
|
|
5
|
+
ROLES=("$@")
|
|
6
|
+
|
|
7
|
+
# echo "ROLES: ${ROLES[@]}"
|
|
8
|
+
|
|
9
|
+
if [ ${#ROLES[@]} -eq 0 ]; then
|
|
10
|
+
echo "Usage: $0 brain coder tester ..."
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
./layout.sh init
|
|
15
|
+
./layout.sh scale "${ROLES[@]}"
|
|
16
|
+
./label.sh "${ROLES[@]}"
|
|
17
|
+
|
|
18
|
+
tmux -S $SOCKET attach -t agent
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
SOCKET="/tmp/tmux-agent.sock"
|
|
4
|
+
LOG_DIR="/tmp/tmux-agent-logs"
|
|
5
|
+
MAP_FILE="/tmp/tmux-agent.map"
|
|
6
|
+
|
|
7
|
+
# ----------------------------
|
|
8
|
+
# 关闭 tmux server
|
|
9
|
+
# ----------------------------
|
|
10
|
+
tmux -S $SOCKET kill-server 2>/dev/null || true
|
|
11
|
+
|
|
12
|
+
# ----------------------------
|
|
13
|
+
# 清理日志
|
|
14
|
+
# ----------------------------
|
|
15
|
+
if [ -d "$LOG_DIR" ]; then
|
|
16
|
+
rm -rf "$LOG_DIR"
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# ----------------------------
|
|
20
|
+
# 清理 pane 映射
|
|
21
|
+
# ----------------------------
|
|
22
|
+
if [ -f "$MAP_FILE" ]; then
|
|
23
|
+
rm -f "$MAP_FILE"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo "✅ tmux 已关闭 + 日志已清理"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
SOCKET="/tmp/tmux-demo.sock"
|
|
5
|
+
SESSION="demo"
|
|
6
|
+
WORKERS=4
|
|
7
|
+
|
|
8
|
+
# ========= 1. 清理 =========
|
|
9
|
+
tmux -S $SOCKET kill-server 2>/dev/null || true
|
|
10
|
+
|
|
11
|
+
# ========= 2. 创建主会话 =========
|
|
12
|
+
tmux -S $SOCKET new-session -d -s $SESSION -n main
|
|
13
|
+
|
|
14
|
+
# ========= 3. 左右分割(主结构) =========
|
|
15
|
+
RIGHT=$(tmux -S $SOCKET split-window -h -p 30 -t $SESSION -d -P -F "#{pane_id}")
|
|
16
|
+
|
|
17
|
+
# ========= 4. 在右侧创建纵向 worker =========
|
|
18
|
+
tmux -S $SOCKET select-pane -t $RIGHT
|
|
19
|
+
|
|
20
|
+
for ((i=1; i<WORKERS; i++)); do
|
|
21
|
+
tmux -S $SOCKET split-window -v -d
|
|
22
|
+
done
|
|
23
|
+
|
|
24
|
+
# ========= 5. 使用 main-vertical(核心!!!) =========
|
|
25
|
+
tmux -S $SOCKET select-layout -t $SESSION main-vertical
|
|
26
|
+
|
|
27
|
+
# ========= 6. 设置 pane label =========
|
|
28
|
+
tmux -S $SOCKET set-option -g pane-border-status top
|
|
29
|
+
tmux -S $SOCKET set-option -g pane-border-format "#{pane_index} | #{pane_title}"
|
|
30
|
+
|
|
31
|
+
# ========= 7. 命名 =========
|
|
32
|
+
INDEX=0
|
|
33
|
+
for pane in $(tmux -S $SOCKET list-panes -t $SESSION -F "#{pane_id}"); do
|
|
34
|
+
if [ "$INDEX" -eq 0 ]; then
|
|
35
|
+
tmux -S $SOCKET select-pane -t $pane -T "main"
|
|
36
|
+
else
|
|
37
|
+
tmux -S $SOCKET select-pane -t $pane -T "agent-$INDEX"
|
|
38
|
+
fi
|
|
39
|
+
((INDEX++))
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
# ========= 8. 进入 =========
|
|
43
|
+
tmux -S $SOCKET attach -t $SESSION
|