@walwal-harness/cli 3.7.0 → 3.7.2

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/bin/init.js CHANGED
@@ -214,6 +214,20 @@ function installScripts() {
214
214
  const scriptsSrc = path.join(PKG_ROOT, 'scripts');
215
215
  const scriptsDest = path.join(PROJECT_ROOT, 'scripts');
216
216
 
217
+ // Core scripts are ALWAYS overwritten on update (not user-editable)
218
+ // These contain harness logic that must stay in sync with the CLI version
219
+ const coreScripts = new Set([
220
+ 'harness-next.sh',
221
+ 'harness-session-start.sh',
222
+ 'harness-statusline.sh',
223
+ 'harness-user-prompt-submit.sh',
224
+ 'harness-dashboard.sh',
225
+ 'harness-monitor.sh',
226
+ 'harness-eval-watcher.sh',
227
+ 'harness-tmux.sh',
228
+ 'harness-control.sh',
229
+ ]);
230
+
217
231
  if (fs.existsSync(scriptsSrc)) {
218
232
  ensureDir(scriptsDest);
219
233
  const entries = fs.readdirSync(scriptsSrc, { withFileTypes: true });
@@ -221,21 +235,20 @@ function installScripts() {
221
235
  const srcPath = path.join(scriptsSrc, entry.name);
222
236
  const destPath = path.join(scriptsDest, entry.name);
223
237
  if (entry.isDirectory()) {
224
- // Copy subdirectories (e.g., lib/)
225
- if (!fileExists(destPath) || isForce) {
226
- copyDir(srcPath, destPath);
227
- // Make scripts in subdirs executable
228
- try {
229
- const subFiles = fs.readdirSync(destPath);
230
- for (const f of subFiles) {
231
- if (f.endsWith('.sh')) {
232
- fs.chmodSync(path.join(destPath, f), '755');
233
- }
238
+ // lib/ and other subdirectories — always overwrite
239
+ copyDir(srcPath, destPath);
240
+ try {
241
+ const subFiles = fs.readdirSync(destPath);
242
+ for (const f of subFiles) {
243
+ if (f.endsWith('.sh')) {
244
+ fs.chmodSync(path.join(destPath, f), '755');
234
245
  }
235
- } catch (e) {}
236
- }
246
+ }
247
+ } catch (e) {}
237
248
  } else {
238
- if (!fileExists(destPath) || isForce) {
249
+ // Core scripts: always overwrite. Others: skip if exists (unless --force)
250
+ const isCore = coreScripts.has(entry.name);
251
+ if (isCore || !fileExists(destPath) || isForce) {
239
252
  copyFile(srcPath, destPath);
240
253
  try { fs.chmodSync(destPath, '755'); } catch (e) {}
241
254
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "3.7.0",
3
+ "version": "3.7.2",
4
4
  "description": "Production harness for AI agent engineering — Planner, Generator(BE/FE), Evaluator(Func/Visual), optional Brainstormer (requirements refinement). Supports React and Flutter FE stacks.",
5
5
  "bin": {
6
6
  "walwal-harness": "bin/init.js"
@@ -0,0 +1,152 @@
1
+ #!/bin/bash
2
+ # harness-control.sh — Control Center 프롬프트 (하단)
3
+ # 사용자 명령을 받아 Agent Session 관리 + progress.log 기록
4
+ #
5
+ # Commands:
6
+ # next / n 다음 에이전트 세션 시작
7
+ # retry / r 현재 에이전트 재실행
8
+ # stop 에이전트 세션 중지
9
+ # log <message> progress.log에 수동 메모 추가
10
+ # clear-fail failure 상태 클리어
11
+ # status / s 대시보드 즉시 갱신 (상단 pane)
12
+ # help / h 도움말
13
+ # quit / q 종료
14
+ #
15
+ # Usage: bash scripts/harness-control.sh [project-root]
16
+
17
+ set -uo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
20
+
21
+ PROJECT_ROOT="${1:-}"
22
+ if [ -z "$PROJECT_ROOT" ]; then
23
+ source "$SCRIPT_DIR/lib/harness-render-progress.sh"
24
+ PROJECT_ROOT="$(resolve_harness_root ".")" || {
25
+ echo "[control] .harness/ not found."
26
+ exit 1
27
+ }
28
+ fi
29
+
30
+ PROGRESS="$PROJECT_ROOT/.harness/progress.json"
31
+ PROGRESS_LOG="$PROJECT_ROOT/.harness/progress.log"
32
+
33
+ BOLD="\033[1m"
34
+ DIM="\033[2m"
35
+ GREEN="\033[32m"
36
+ YELLOW="\033[33m"
37
+ RED="\033[31m"
38
+ CYAN="\033[36m"
39
+ RESET="\033[0m"
40
+
41
+ # ── Agent Session pane target ──
42
+ AGENT_PANE="harness-studio.2"
43
+
44
+ cmd_log() {
45
+ local msg="$1"
46
+ if [ -z "$msg" ]; then
47
+ echo -e " ${RED}Usage: log <message>${RESET}"
48
+ return
49
+ fi
50
+ local ts
51
+ ts=$(date +"%Y-%m-%d")
52
+ echo "${ts} | manual | note | ${msg}" >> "$PROGRESS_LOG"
53
+ echo -e " ${GREEN}Logged:${RESET} ${msg}"
54
+ }
55
+
56
+ cmd_clear_fail() {
57
+ if [ ! -f "$PROGRESS" ]; then return; fi
58
+ jq '.failure = {agent: null, location: null, message: null, retry_target: null} |
59
+ .sprint.status = "in_progress" |
60
+ .sprint.retry_count = 0 |
61
+ .agent_status = "completed"' "$PROGRESS" > "${PROGRESS}.tmp" && mv "${PROGRESS}.tmp" "$PROGRESS"
62
+ echo -e " ${GREEN}Failure state cleared.${RESET}"
63
+ }
64
+
65
+ cmd_next() {
66
+ if ! tmux list-panes -t harness-studio 2>/dev/null | grep -q .; then
67
+ echo -e " ${RED}tmux session not found.${RESET}"
68
+ return
69
+ fi
70
+
71
+ local next_agent
72
+ next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
73
+ if [ "$next_agent" = "none" ] || [ "$next_agent" = "null" ]; then
74
+ echo -e " ${YELLOW}No next agent in queue.${RESET}"
75
+ return
76
+ fi
77
+
78
+ echo -e " ${CYAN}Starting ${next_agent} in Agent Session...${RESET}"
79
+ local ts
80
+ ts=$(date +"%Y-%m-%d")
81
+ echo "${ts} | manual | start-agent | ${next_agent}" >> "$PROGRESS_LOG"
82
+ tmux send-keys -t "$AGENT_PANE" "/harness-${next_agent}" Enter
83
+ }
84
+
85
+ cmd_retry() {
86
+ if ! tmux list-panes -t harness-studio 2>/dev/null | grep -q .; then
87
+ echo -e " ${RED}tmux session not found.${RESET}"
88
+ return
89
+ fi
90
+
91
+ local current_agent
92
+ current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
93
+ if [ "$current_agent" = "none" ] || [ "$current_agent" = "null" ]; then
94
+ echo -e " ${YELLOW}No current agent to retry.${RESET}"
95
+ return
96
+ fi
97
+
98
+ echo -e " ${CYAN}Retrying ${current_agent}...${RESET}"
99
+ local ts
100
+ ts=$(date +"%Y-%m-%d")
101
+ echo "${ts} | manual | retry | ${current_agent}" >> "$PROGRESS_LOG"
102
+ tmux send-keys -t "$AGENT_PANE" "/harness-${current_agent}" Enter
103
+ }
104
+
105
+ cmd_stop() {
106
+ if ! tmux list-panes -t harness-studio 2>/dev/null | grep -q .; then
107
+ echo -e " ${RED}tmux session not found.${RESET}"
108
+ return
109
+ fi
110
+
111
+ echo -e " ${YELLOW}Sending Ctrl+C to Agent Session...${RESET}"
112
+ tmux send-keys -t "$AGENT_PANE" C-c
113
+ local ts
114
+ ts=$(date +"%Y-%m-%d")
115
+ echo "${ts} | manual | stop | user-initiated" >> "$PROGRESS_LOG"
116
+ }
117
+
118
+ show_help() {
119
+ echo ""
120
+ echo -e " ${BOLD}Commands${RESET}"
121
+ echo -e " ${CYAN}next${RESET} / ${CYAN}n${RESET} Start next agent"
122
+ echo -e " ${CYAN}retry${RESET} / ${CYAN}r${RESET} Retry current agent"
123
+ echo -e " ${CYAN}stop${RESET} Send Ctrl+C to agent"
124
+ echo -e " ${CYAN}log${RESET} <message> Add note to progress.log"
125
+ echo -e " ${CYAN}clear-fail${RESET} Clear failure state"
126
+ echo -e " ${CYAN}help${RESET} / ${CYAN}h${RESET} Show this help"
127
+ echo -e " ${CYAN}quit${RESET} / ${CYAN}q${RESET} Exit"
128
+ echo ""
129
+ }
130
+
131
+ # ── Main ──
132
+ echo ""
133
+ echo -e " ${BOLD}Harness Control${RESET} ${DIM}(type 'help' for commands)${RESET}"
134
+ echo ""
135
+
136
+ while true; do
137
+ echo -ne " ${BOLD}harness>${RESET} "
138
+ read -r input || exit 0
139
+ input=$(echo "$input" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
140
+
141
+ case "$input" in
142
+ next|n) cmd_next ;;
143
+ retry|r) cmd_retry ;;
144
+ stop) cmd_stop ;;
145
+ log\ *) cmd_log "${input#log }" ;;
146
+ clear-fail) cmd_clear_fail ;;
147
+ help|h) show_help ;;
148
+ quit|q) echo -e " ${DIM}Goodbye.${RESET}"; exit 0 ;;
149
+ "") ;; # empty — just re-prompt
150
+ *) echo -e " ${DIM}Logging as note.${RESET}"; cmd_log "$input" ;;
151
+ esac
152
+ done
@@ -260,12 +260,9 @@ render_agent_info() {
260
260
  }
261
261
 
262
262
  # ══════════════════════════════════════════
263
- # Full dashboard render
263
+ # Full dashboard render (in-place update)
264
264
  # ══════════════════════════════════════════
265
265
  render_dashboard() {
266
- tput cup 0 0 2>/dev/null
267
- tput civis 2>/dev/null
268
-
269
266
  local buf
270
267
  buf=$(
271
268
  render_header
@@ -280,190 +277,23 @@ render_dashboard() {
280
277
 
281
278
  render_agent_info
282
279
  )
280
+
281
+ tput civis 2>/dev/null
282
+ tput cup 0 0 2>/dev/null
283
283
  echo "$buf"
284
284
  tput ed 2>/dev/null
285
285
  tput cnorm 2>/dev/null
286
286
  }
287
287
 
288
288
  # ══════════════════════════════════════════
289
- # Command handlers
289
+ # Main — Auto-refresh dashboard loop
290
290
  # ══════════════════════════════════════════
291
+ tput civis 2>/dev/null
292
+ trap 'tput cnorm 2>/dev/null; exit 0' EXIT INT TERM
291
293
 
292
- cmd_log() {
293
- local msg="$1"
294
- if [ -z "$msg" ]; then
295
- echo -e " ${RED}Usage: log <message>${RESET}"
296
- return
297
- fi
298
-
299
- local ts
300
- ts=$(date +"%Y-%m-%d")
301
- echo "${ts} | manual | note | ${msg}" >> "$PROGRESS_LOG"
302
- echo -e " ${GREEN}Logged:${RESET} ${msg}"
303
- }
304
-
305
- cmd_clear_fail() {
306
- if [ ! -f "$PROGRESS" ]; then return; fi
307
-
308
- jq '.failure = {agent: null, location: null, message: null, retry_target: null} |
309
- .sprint.status = "in_progress" |
310
- .sprint.retry_count = 0 |
311
- .agent_status = "completed"' "$PROGRESS" > "${PROGRESS}.tmp" && mv "${PROGRESS}.tmp" "$PROGRESS"
312
- echo -e " ${GREEN}Failure state cleared.${RESET}"
313
- }
314
-
315
- cmd_next() {
316
- # Find the Agent Session pane and send the next agent command
317
- local tmux_target="harness-studio.2" # Agent Session pane
318
-
319
- if ! tmux list-panes -t harness-studio 2>/dev/null | grep -q .; then
320
- echo -e " ${RED}tmux session not found. Run inside harness-studio.${RESET}"
321
- return
322
- fi
323
-
324
- local next_agent
325
- next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
326
-
327
- if [ "$next_agent" = "none" ] || [ "$next_agent" = "null" ]; then
328
- echo -e " ${YELLOW}No next agent in queue.${RESET}"
329
- return
330
- fi
331
-
332
- echo -e " ${CYAN}Starting ${next_agent} in Agent Session pane...${RESET}"
333
-
334
- # Log the manual intervention
335
- local ts
336
- ts=$(date +"%Y-%m-%d")
337
- echo "${ts} | manual | start-agent | ${next_agent}" >> "$PROGRESS_LOG"
338
-
339
- # Send command to Agent Session pane
340
- tmux send-keys -t "$tmux_target" "/harness-${next_agent}" Enter
341
- }
342
-
343
- cmd_retry() {
344
- local tmux_target="harness-studio.2"
345
-
346
- if ! tmux list-panes -t harness-studio 2>/dev/null | grep -q .; then
347
- echo -e " ${RED}tmux session not found.${RESET}"
348
- return
349
- fi
350
-
351
- local current_agent
352
- current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
353
-
354
- if [ "$current_agent" = "none" ] || [ "$current_agent" = "null" ]; then
355
- echo -e " ${YELLOW}No current agent to retry.${RESET}"
356
- return
357
- fi
358
-
359
- echo -e " ${CYAN}Retrying ${current_agent} in Agent Session pane...${RESET}"
360
-
361
- local ts
362
- ts=$(date +"%Y-%m-%d")
363
- echo "${ts} | manual | retry | ${current_agent}" >> "$PROGRESS_LOG"
364
-
365
- tmux send-keys -t "$tmux_target" "/harness-${current_agent}" Enter
366
- }
367
-
368
- cmd_stop() {
369
- local tmux_target="harness-studio.2"
370
-
371
- if ! tmux list-panes -t harness-studio 2>/dev/null | grep -q .; then
372
- echo -e " ${RED}tmux session not found.${RESET}"
373
- return
374
- fi
375
-
376
- echo -e " ${YELLOW}Sending Ctrl+C to Agent Session...${RESET}"
377
- tmux send-keys -t "$tmux_target" C-c
378
-
379
- local ts
380
- ts=$(date +"%Y-%m-%d")
381
- echo "${ts} | manual | stop | user-initiated" >> "$PROGRESS_LOG"
382
- }
383
-
384
- show_help() {
385
- echo ""
386
- echo -e " ${BOLD}Commands${RESET}"
387
- echo -e " ${CYAN}status${RESET} / ${CYAN}s${RESET} Refresh dashboard"
388
- echo -e " ${CYAN}next${RESET} / ${CYAN}n${RESET} Start next agent in Agent Session"
389
- echo -e " ${CYAN}retry${RESET} / ${CYAN}r${RESET} Retry current agent"
390
- echo -e " ${CYAN}stop${RESET} Send Ctrl+C to Agent Session"
391
- echo -e " ${CYAN}log${RESET} <message> Add note to progress.log"
392
- echo -e " ${CYAN}clear-fail${RESET} Clear failure state"
393
- echo -e " ${CYAN}help${RESET} / ${CYAN}h${RESET} Show this help"
394
- echo -e " ${CYAN}quit${RESET} / ${CYAN}q${RESET} Exit dashboard"
395
- echo ""
396
- }
397
-
398
- # ══════════════════════════════════════════
399
- # Main — Interactive loop
400
- # ══════════════════════════════════════════
401
294
  clear
402
- render_dashboard
403
-
404
- echo ""
405
- echo -e " ${DIM}Type 'help' for commands. Dashboard auto-refreshes every 5s.${RESET}"
406
- echo ""
407
-
408
- # Background auto-refresh
409
- (
410
- while true; do
411
- sleep 5
412
- # Signal main process to refresh
413
- kill -USR1 $$ 2>/dev/null || exit 0
414
- done
415
- ) &
416
- REFRESH_PID=$!
417
-
418
- # Trap USR1 to refresh dashboard
419
- trap 'render_dashboard' USR1
420
- trap 'kill $REFRESH_PID 2>/dev/null; exit 0' EXIT INT TERM
421
295
 
422
296
  while true; do
423
- echo -ne " ${BOLD}harness>${RESET} "
424
- # Read with timeout so USR1 can interrupt
425
- if read -t 5 -r input; then
426
- # Trim whitespace
427
- input=$(echo "$input" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
428
-
429
- case "$input" in
430
- status|s)
431
- render_dashboard
432
- ;;
433
- next|n)
434
- cmd_next
435
- ;;
436
- retry|r)
437
- cmd_retry
438
- ;;
439
- stop)
440
- cmd_stop
441
- ;;
442
- log\ *)
443
- cmd_log "${input#log }"
444
- ;;
445
- clear-fail)
446
- cmd_clear_fail
447
- ;;
448
- help|h)
449
- show_help
450
- ;;
451
- quit|q)
452
- echo -e " ${DIM}Goodbye.${RESET}"
453
- exit 0
454
- ;;
455
- "")
456
- # Empty input — just refresh
457
- render_dashboard
458
- ;;
459
- *)
460
- # Unknown command — treat as manual note
461
- echo -e " ${DIM}Unknown command. Logging as note.${RESET}"
462
- cmd_log "$input"
463
- ;;
464
- esac
465
- else
466
- # Timeout — auto-refresh
467
- render_dashboard
468
- fi
297
+ render_dashboard
298
+ sleep 3
469
299
  done
@@ -97,12 +97,38 @@ run_pre_eval_gate() {
97
97
  timeout=$(jq -r '.flow.pre_eval_gate.timeout_seconds // 120' "$CONFIG" 2>/dev/null)
98
98
 
99
99
  # 실패 위치 결정 (backend or frontend)
100
+ # current_agent가 실패로 변경된 경우, completed_agents에서 직전 Generator를 찾는다
100
101
  local location="backend"
101
102
  local checks_key="backend_checks"
102
- if [ "$current_agent" = "generator-frontend" ] || [ "$current_agent" = "generator-frontend-flutter" ]; then
103
- location="frontend"
104
- checks_key="frontend_checks"
105
- fi
103
+ local source_agent="$current_agent"
104
+
105
+ # current_agent가 generator가 아닌 경우 (예: dispatcher로 리라우팅된 상태),
106
+ # completed_agents에서 마지막 generator를 찾는다
107
+ case "$source_agent" in
108
+ generator-frontend|generator-frontend-flutter)
109
+ location="frontend"
110
+ checks_key="frontend_checks"
111
+ ;;
112
+ generator-backend)
113
+ location="backend"
114
+ checks_key="backend_checks"
115
+ ;;
116
+ *)
117
+ # completed_agents에서 마지막 generator를 역순으로 찾기
118
+ local last_gen
119
+ last_gen=$(jq -r '.completed_agents // [] | map(select(startswith("generator-"))) | last // empty' "$PROGRESS" 2>/dev/null)
120
+ case "$last_gen" in
121
+ generator-frontend|generator-frontend-flutter)
122
+ location="frontend"
123
+ checks_key="frontend_checks"
124
+ ;;
125
+ generator-backend)
126
+ location="backend"
127
+ checks_key="backend_checks"
128
+ ;;
129
+ esac
130
+ ;;
131
+ esac
106
132
 
107
133
  local -a checks
108
134
  mapfile -t checks < <(jq -r ".flow.pre_eval_gate.${checks_key}[]" "$CONFIG" 2>/dev/null)
@@ -77,36 +77,45 @@ fi
77
77
  # ══════════════════════════════════════════
78
78
  # Layout Construction
79
79
  # ══════════════════════════════════════════
80
- # Step 1: Create session → Pane 0 = Main (Control Center)
81
- tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 200 -y 50
80
+ # ══════════════════════════════════════════
81
+ # Pane layout:
82
+ # 0 = Dashboard (top-left, auto-refresh)
83
+ # 1 = Monitor (top-right, compact)
84
+ # 2 = Agent Session (mid-right, claude)
85
+ # 3 = Eval Review (bottom-right)
86
+ # 4 = Control Prompt (bottom-left, interactive)
87
+ # ══════════════════════════════════════════
82
88
 
83
- # Step 2: Split right column (45% width) → Pane 1 = Monitor (top-right, compact)
89
+ # Step 1: Create session → Pane 0 = Dashboard (top-left)
90
+ tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 200 -y 50 \
91
+ "exec bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-dashboard.sh\" \"${PROJECT_ROOT}\"'"
92
+
93
+ # Step 2: Split right column (45%) → Pane 1 = Monitor (top-right, compact)
84
94
  tmux split-window -h -p 45 -t "$SESSION_NAME" -c "$PROJECT_ROOT" \
85
95
  "exec bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\"'"
86
96
 
87
- # Step 3: Split Pane 1 vertically → Pane 2 = Agent Session (mid-right)
88
- # Monitor gets ~20% of right column height, remaining 80% split below
97
+ # Step 3: Split Pane 1 vertically (80% below) → Pane 2 = Agent Session
89
98
  tmux split-window -v -p 80 -t "${SESSION_NAME}.1" -c "$PROJECT_ROOT"
90
-
91
- # Pane 2 = Agent Session (interactive shell, will run claude)
92
99
  tmux send-keys -t "${SESSION_NAME}.2" "$CLAUDE_CMD" Enter
93
100
 
94
- # Step 4: Split Pane 2 vertically → Pane 3 = Eval Review (bottom-right)
95
- # Agent Session and Eval Review split 1:1
101
+ # Step 4: Split Pane 2 vertically (50%) → Pane 3 = Eval Review
96
102
  tmux split-window -v -p 50 -t "${SESSION_NAME}.2" -c "$PROJECT_ROOT" \
97
103
  "exec bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-eval-watcher.sh\" \"${PROJECT_ROOT}\" ${USE_AI}'"
98
104
 
99
- # ── Launch Dashboard in Main pane ──
100
- tmux send-keys -t "${SESSION_NAME}.0" "bash ${SCRIPT_DIR}/harness-dashboard.sh '${PROJECT_ROOT}'" Enter
105
+ # Step 5: Split Dashboard (Pane 0) vertically → Pane 4 = Control Prompt (bottom-left)
106
+ # Dashboard gets ~75% height, Control Prompt gets ~25%
107
+ tmux split-window -v -p 25 -t "${SESSION_NAME}.0" -c "$PROJECT_ROOT"
108
+ tmux send-keys -t "${SESSION_NAME}.4" "bash ${SCRIPT_DIR}/harness-control.sh '${PROJECT_ROOT}'" Enter
101
109
 
102
- # ── Select Main pane as active ──
103
- tmux select-pane -t "${SESSION_NAME}.0"
110
+ # ── Select Control Prompt as active (where user types) ──
111
+ tmux select-pane -t "${SESSION_NAME}.4"
104
112
 
105
113
  # ── Pane titles ──
106
- tmux select-pane -t "${SESSION_NAME}.0" -T "Control Center"
114
+ tmux select-pane -t "${SESSION_NAME}.0" -T "Dashboard"
107
115
  tmux select-pane -t "${SESSION_NAME}.1" -T "Monitor"
108
116
  tmux select-pane -t "${SESSION_NAME}.2" -T "Agent Session"
109
117
  tmux select-pane -t "${SESSION_NAME}.3" -T "Eval Review"
118
+ tmux select-pane -t "${SESSION_NAME}.4" -T "Control"
110
119
 
111
120
  tmux set-option -t "$SESSION_NAME" pane-border-status top 2>/dev/null || true
112
121
  tmux set-option -t "$SESSION_NAME" pane-border-format " #{pane_title} " 2>/dev/null || true