@walwal-harness/cli 4.0.0-beta.1 → 4.0.0-beta.11
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 +97 -49
- package/package.json +4 -2
- package/scripts/harness-dashboard-v4.sh +45 -0
- package/scripts/harness-monitor.sh +202 -67
- package/scripts/harness-prompt-history.sh +126 -0
- package/scripts/harness-studio-setup.sh +143 -0
- package/scripts/harness-studio.sh +66 -0
- package/scripts/harness-tmux-v4.sh +136 -0
- package/scripts/harness-user-prompt-submit.sh +13 -10
- package/skills/team-action/SKILL.md +192 -39
- package/skills/team-stop/SKILL.md +17 -6
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-prompt-history.sh — Prompt History pane for v4 tmux layout
|
|
3
|
+
# progress.log + audit.log에서 유저 프롬프트/에이전트 활동 히스토리를 표시
|
|
4
|
+
# Usage: bash scripts/harness-prompt-history.sh [project-root]
|
|
5
|
+
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
source "$SCRIPT_DIR/lib/harness-render-progress.sh"
|
|
10
|
+
|
|
11
|
+
PROJECT_ROOT="${1:-}"
|
|
12
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
|
13
|
+
PROJECT_ROOT="$(resolve_harness_root ".")" || { echo "[history] .harness/ not found."; exit 1; }
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
PROGRESS_LOG="$PROJECT_ROOT/.harness/progress.log"
|
|
17
|
+
AUDIT_LOG="$PROJECT_ROOT/.harness/actions/audit.log"
|
|
18
|
+
|
|
19
|
+
BOLD="\033[1m"
|
|
20
|
+
DIM="\033[2m"
|
|
21
|
+
GREEN="\033[32m"
|
|
22
|
+
YELLOW="\033[33m"
|
|
23
|
+
RED="\033[31m"
|
|
24
|
+
CYAN="\033[36m"
|
|
25
|
+
MAGENTA="\033[35m"
|
|
26
|
+
RESET="\033[0m"
|
|
27
|
+
|
|
28
|
+
render_header() {
|
|
29
|
+
echo -e "${BOLD}PROMPT HISTORY${RESET} ${DIM}$(date +%H:%M:%S)${RESET}"
|
|
30
|
+
echo ""
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
render_progress_log() {
|
|
34
|
+
if [ ! -f "$PROGRESS_LOG" ]; then
|
|
35
|
+
echo -e " ${DIM}(no progress.log yet)${RESET}"
|
|
36
|
+
return
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
grep -v '^#' "$PROGRESS_LOG" 2>/dev/null | grep -v '^$' | tail -20 | while IFS= read -r line; do
|
|
40
|
+
local ts agent action detail
|
|
41
|
+
ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
|
|
42
|
+
agent=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
|
|
43
|
+
action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
|
|
44
|
+
detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
45
|
+
|
|
46
|
+
local short_ts icon color
|
|
47
|
+
short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}:[0-9]{2}' || echo "$ts")
|
|
48
|
+
|
|
49
|
+
case "$agent" in
|
|
50
|
+
dispatcher*) icon="▸" ; color="$MAGENTA" ;;
|
|
51
|
+
brainstormer) icon="◇" ; color="$CYAN" ;;
|
|
52
|
+
planner*) icon="□" ; color="$YELLOW" ;;
|
|
53
|
+
generator*) icon="▶" ; color="$GREEN" ;;
|
|
54
|
+
eval*) icon="✦" ; color="$RED" ;;
|
|
55
|
+
user|manual) icon="★" ; color="$BOLD" ;;
|
|
56
|
+
team*) icon="⊕" ; color="$CYAN" ;;
|
|
57
|
+
*) icon="·" ; color="$DIM" ;;
|
|
58
|
+
esac
|
|
59
|
+
|
|
60
|
+
if [ ${#detail} -gt 40 ]; then detail="${detail:0:38}.."; fi
|
|
61
|
+
|
|
62
|
+
printf " %b%b%b %b%-8s%b %b%s%b %b%s%b\n" \
|
|
63
|
+
"$color" "$icon" "$RESET" \
|
|
64
|
+
"$DIM" "$short_ts" "$RESET" \
|
|
65
|
+
"$RESET" "$agent" "$RESET" \
|
|
66
|
+
"$DIM" "$action" "$RESET"
|
|
67
|
+
if [ -n "$detail" ] && [ "$detail" != " " ]; then
|
|
68
|
+
echo -e " ${DIM}${detail}${RESET}"
|
|
69
|
+
fi
|
|
70
|
+
done
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render_audit_tail() {
|
|
74
|
+
if [ ! -f "$AUDIT_LOG" ]; then return; fi
|
|
75
|
+
|
|
76
|
+
echo ""
|
|
77
|
+
echo -e "${BOLD}AUDIT${RESET}"
|
|
78
|
+
|
|
79
|
+
grep -v '^#' "$AUDIT_LOG" 2>/dev/null | grep -v '^$' | tail -10 | while IFS= read -r line; do
|
|
80
|
+
local ts agent action status target
|
|
81
|
+
ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
|
|
82
|
+
agent=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
|
|
83
|
+
action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
|
|
84
|
+
status=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
85
|
+
target=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$5); print $5}')
|
|
86
|
+
|
|
87
|
+
local color="$RESET" icon="·"
|
|
88
|
+
case "$status" in
|
|
89
|
+
start) color="$CYAN"; icon="▶" ;;
|
|
90
|
+
complete) color="$GREEN"; icon="✓" ;;
|
|
91
|
+
fail) color="$RED"; icon="✗" ;;
|
|
92
|
+
pass) color="$GREEN"; icon="✓" ;;
|
|
93
|
+
skip) color="$DIM"; icon="–" ;;
|
|
94
|
+
esac
|
|
95
|
+
|
|
96
|
+
local short_ts
|
|
97
|
+
short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}:[0-9]{2}' || echo "$ts")
|
|
98
|
+
|
|
99
|
+
if [ ${#target} -gt 25 ]; then target="${target:0:23}.."; fi
|
|
100
|
+
|
|
101
|
+
printf " %b%b%b %b%s%b %-10s %b%s%b\n" \
|
|
102
|
+
"$color" "$icon" "$RESET" \
|
|
103
|
+
"$DIM" "$short_ts" "$RESET" \
|
|
104
|
+
"$agent" \
|
|
105
|
+
"$DIM" "$target" "$RESET"
|
|
106
|
+
done
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
render_all() {
|
|
110
|
+
render_header
|
|
111
|
+
render_progress_log
|
|
112
|
+
render_audit_tail
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# ── Main loop ──
|
|
116
|
+
tput civis 2>/dev/null
|
|
117
|
+
trap 'tput cnorm 2>/dev/null; exit 0' EXIT INT TERM
|
|
118
|
+
clear
|
|
119
|
+
|
|
120
|
+
while true; do
|
|
121
|
+
buf=$(render_all 2>&1)
|
|
122
|
+
tput cup 0 0 2>/dev/null
|
|
123
|
+
echo "$buf"
|
|
124
|
+
tput ed 2>/dev/null
|
|
125
|
+
sleep 3
|
|
126
|
+
done
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-studio-setup.sh — Claude 세션에서 3-column 레이아웃 자동 구축
|
|
3
|
+
#
|
|
4
|
+
# ┌──────────────┬──────────────┬──────────────┐
|
|
5
|
+
# │ │ Dashboard │ │
|
|
6
|
+
# │ Claude │ (v4 queue) │ Team Monitor│
|
|
7
|
+
# │ (Lead) ├──────────────┤ (lifecycle) │
|
|
8
|
+
# │ │ Command │ │
|
|
9
|
+
# │ │ History │ │
|
|
10
|
+
# └──────────────┴──────────────┴──────────────┘
|
|
11
|
+
#
|
|
12
|
+
# 두 가지 상황을 모두 처리:
|
|
13
|
+
# A) tmux 안에서 실행 → 현재 pane을 split하여 레이아웃 구축
|
|
14
|
+
# B) tmux 밖에서 실행 → 새 tmux 세션 생성, Claude를 좌측 pane에서 재실행
|
|
15
|
+
#
|
|
16
|
+
# Usage:
|
|
17
|
+
# bash scripts/harness-studio-setup.sh [project-root]
|
|
18
|
+
#
|
|
19
|
+
# 이미 구축됐으면 skip (멱등성 보장)
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
24
|
+
SESSION_NAME="harness-studio"
|
|
25
|
+
|
|
26
|
+
PROJECT_ROOT="${1:-}"
|
|
27
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
|
28
|
+
dir="$(pwd)"
|
|
29
|
+
while [ "$dir" != "/" ]; do
|
|
30
|
+
if [ -d "$dir/.harness" ]; then PROJECT_ROOT="$dir"; break; fi
|
|
31
|
+
dir="$(dirname "$dir")"
|
|
32
|
+
done
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if [ -z "$PROJECT_ROOT" ] || [ ! -d "$PROJECT_ROOT/.harness" ]; then
|
|
36
|
+
echo "[studio] .harness/ not found."
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# ── Resolve Claude command ──
|
|
41
|
+
CLAUDE_CMD="claude --dangerously-skip-permissions"
|
|
42
|
+
if [ -f "$PROJECT_ROOT/.harness/handoff.json" ]; then
|
|
43
|
+
_model=$(jq -r '.model // empty' "$PROJECT_ROOT/.harness/handoff.json" 2>/dev/null)
|
|
44
|
+
if [ -n "$_model" ] && [ "$_model" != "null" ]; then
|
|
45
|
+
CLAUDE_CMD="$CLAUDE_CMD --model $_model"
|
|
46
|
+
fi
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# ══════════════════════════════════════════
|
|
50
|
+
# Case A: 이미 tmux 안에 있음 → pane split
|
|
51
|
+
# ══════════════════════════════════════════
|
|
52
|
+
if [ -n "${TMUX:-}" ]; then
|
|
53
|
+
# 멱등성: 이미 pane이 3개 이상이면 skip
|
|
54
|
+
PANE_COUNT=$(tmux list-panes | wc -l | tr -d ' ')
|
|
55
|
+
if [ "$PANE_COUNT" -ge 3 ]; then
|
|
56
|
+
echo "[studio] Layout already set up ($PANE_COUNT panes). Skipping."
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
PANE_CLAUDE=$(tmux display-message -p '#{pane_id}')
|
|
61
|
+
echo "[studio] Setting up 3-column layout (in-place split)..."
|
|
62
|
+
|
|
63
|
+
# Left 35% | Right 65%
|
|
64
|
+
PANE_MID=$(tmux split-window -h -p 65 -t "$PANE_CLAUDE" -c "$PROJECT_ROOT" \
|
|
65
|
+
-P -F '#{pane_id}' \
|
|
66
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-dashboard-v4.sh\" \"${PROJECT_ROOT}\"'")
|
|
67
|
+
|
|
68
|
+
# Middle 45% | Right 55%
|
|
69
|
+
PANE_RIGHT=$(tmux split-window -h -p 55 -t "$PANE_MID" -c "$PROJECT_ROOT" \
|
|
70
|
+
-P -F '#{pane_id}' \
|
|
71
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\"'")
|
|
72
|
+
|
|
73
|
+
# Dashboard top 45% | History bottom 55%
|
|
74
|
+
PANE_HISTORY=$(tmux split-window -v -p 55 -t "$PANE_MID" -c "$PROJECT_ROOT" \
|
|
75
|
+
-P -F '#{pane_id}' \
|
|
76
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-prompt-history.sh\" \"${PROJECT_ROOT}\"'")
|
|
77
|
+
|
|
78
|
+
# Pane titles
|
|
79
|
+
tmux select-pane -t "$PANE_CLAUDE" -T "Lead (Claude)"
|
|
80
|
+
tmux select-pane -t "$PANE_MID" -T "Dashboard"
|
|
81
|
+
tmux select-pane -t "$PANE_HISTORY" -T "Command History"
|
|
82
|
+
tmux select-pane -t "$PANE_RIGHT" -T "Team Monitor"
|
|
83
|
+
|
|
84
|
+
tmux set-option pane-border-status top 2>/dev/null || true
|
|
85
|
+
tmux set-option pane-border-format " #{pane_title} " 2>/dev/null || true
|
|
86
|
+
|
|
87
|
+
# 포커스를 Claude pane으로 복귀
|
|
88
|
+
tmux select-pane -t "$PANE_CLAUDE"
|
|
89
|
+
|
|
90
|
+
echo "[studio] Layout ready (in-place)."
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# ══════════════════════════════════════════
|
|
95
|
+
# Case B: tmux 밖에 있음 → 새 세션 생성
|
|
96
|
+
# ══════════════════════════════════════════
|
|
97
|
+
|
|
98
|
+
# 이미 세션이 있으면 attach만
|
|
99
|
+
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
|
100
|
+
echo "[studio] Session '$SESSION_NAME' already exists. Attaching..."
|
|
101
|
+
echo "[studio] ATTACH_TMUX=$SESSION_NAME"
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
echo "[studio] Creating new tmux session with 3-column layout..."
|
|
106
|
+
|
|
107
|
+
# 1. 새 세션 → Left pane (Claude 실행)
|
|
108
|
+
PANE_MAIN=$(tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 220 -y 55 \
|
|
109
|
+
-P -F '#{pane_id}')
|
|
110
|
+
|
|
111
|
+
# 2. Left 35% | Right 65%
|
|
112
|
+
PANE_MID=$(tmux split-window -h -p 65 -t "$PANE_MAIN" -c "$PROJECT_ROOT" \
|
|
113
|
+
-P -F '#{pane_id}' \
|
|
114
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-dashboard-v4.sh\" \"${PROJECT_ROOT}\"'")
|
|
115
|
+
|
|
116
|
+
# 3. Middle 45% | Right 55%
|
|
117
|
+
PANE_RIGHT=$(tmux split-window -h -p 55 -t "$PANE_MID" -c "$PROJECT_ROOT" \
|
|
118
|
+
-P -F '#{pane_id}' \
|
|
119
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\"'")
|
|
120
|
+
|
|
121
|
+
# 4. Dashboard top 45% | History bottom 55%
|
|
122
|
+
PANE_HISTORY=$(tmux split-window -v -p 55 -t "$PANE_MID" -c "$PROJECT_ROOT" \
|
|
123
|
+
-P -F '#{pane_id}' \
|
|
124
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-prompt-history.sh\" \"${PROJECT_ROOT}\"'")
|
|
125
|
+
|
|
126
|
+
# 5. Left pane에서 Claude 자동 실행
|
|
127
|
+
tmux send-keys -t "$PANE_MAIN" "unset npm_config_prefix 2>/dev/null" Enter
|
|
128
|
+
tmux send-keys -t "$PANE_MAIN" "clear && $CLAUDE_CMD" Enter
|
|
129
|
+
|
|
130
|
+
# Pane titles
|
|
131
|
+
tmux select-pane -t "$PANE_MAIN" -T "Lead (Claude)"
|
|
132
|
+
tmux select-pane -t "$PANE_MID" -T "Dashboard"
|
|
133
|
+
tmux select-pane -t "$PANE_HISTORY" -T "Command History"
|
|
134
|
+
tmux select-pane -t "$PANE_RIGHT" -T "Team Monitor"
|
|
135
|
+
|
|
136
|
+
tmux set-option -t "$SESSION_NAME" pane-border-status top 2>/dev/null || true
|
|
137
|
+
tmux set-option -t "$SESSION_NAME" pane-border-format " #{pane_title} " 2>/dev/null || true
|
|
138
|
+
|
|
139
|
+
# Focus on Claude pane
|
|
140
|
+
tmux select-pane -t "$PANE_MAIN"
|
|
141
|
+
|
|
142
|
+
echo "[studio] Session '$SESSION_NAME' created."
|
|
143
|
+
echo "[studio] ATTACH_TMUX=$SESSION_NAME"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-studio.sh — 하네스 통합 진입점
|
|
3
|
+
#
|
|
4
|
+
# 사용자가 기억할 명령: 이것 하나.
|
|
5
|
+
#
|
|
6
|
+
# bash scripts/harness-studio.sh # 자동 감지: v3 or v4
|
|
7
|
+
# bash scripts/harness-studio.sh --kill # 세션 종료
|
|
8
|
+
# bash scripts/harness-studio.sh --v3 # v3 강제
|
|
9
|
+
# bash scripts/harness-studio.sh --v4 # v4 강제
|
|
10
|
+
#
|
|
11
|
+
# 자동 감지 기준:
|
|
12
|
+
# feature-queue.json 존재 → v4 (Agent Teams 병렬 모드)
|
|
13
|
+
# 없으면 → v3 (순차 파이프라인 모드)
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
18
|
+
FORCE_MODE=""
|
|
19
|
+
|
|
20
|
+
# ── Parse args (pass through to sub-script) ──
|
|
21
|
+
PASSTHROUGH_ARGS=()
|
|
22
|
+
for arg in "$@"; do
|
|
23
|
+
case "$arg" in
|
|
24
|
+
--v3) FORCE_MODE="v3" ;;
|
|
25
|
+
--v4) FORCE_MODE="v4" ;;
|
|
26
|
+
--kill)
|
|
27
|
+
tmux kill-session -t "harness-studio" 2>/dev/null && echo "v3 killed." || true
|
|
28
|
+
tmux kill-session -t "harness-v4" 2>/dev/null && echo "v4 killed." || true
|
|
29
|
+
exit 0
|
|
30
|
+
;;
|
|
31
|
+
*)
|
|
32
|
+
PASSTHROUGH_ARGS+=("$arg")
|
|
33
|
+
;;
|
|
34
|
+
esac
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
# ── Auto-detect mode ──
|
|
38
|
+
detect_mode() {
|
|
39
|
+
local dir="${1:-.}"
|
|
40
|
+
while [ "$dir" != "/" ]; do
|
|
41
|
+
if [ -d "$dir/.harness" ]; then
|
|
42
|
+
if [ -f "$dir/.harness/actions/feature-queue.json" ]; then
|
|
43
|
+
echo "v4"
|
|
44
|
+
else
|
|
45
|
+
echo "v3"
|
|
46
|
+
fi
|
|
47
|
+
return
|
|
48
|
+
fi
|
|
49
|
+
dir="$(dirname "$dir")"
|
|
50
|
+
done
|
|
51
|
+
echo "v3" # default
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
MODE="${FORCE_MODE:-$(detect_mode "$(pwd)")}"
|
|
55
|
+
|
|
56
|
+
echo "Harness Studio — mode: $MODE"
|
|
57
|
+
echo ""
|
|
58
|
+
|
|
59
|
+
case "$MODE" in
|
|
60
|
+
v4)
|
|
61
|
+
exec bash "$SCRIPT_DIR/harness-tmux-v4.sh" "${PASSTHROUGH_ARGS[@]}"
|
|
62
|
+
;;
|
|
63
|
+
v3)
|
|
64
|
+
exec bash "$SCRIPT_DIR/harness-tmux.sh" "${PASSTHROUGH_ARGS[@]}"
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-tmux-v4.sh — v4 Agent Teams: 원커맨드 실행
|
|
3
|
+
#
|
|
4
|
+
# ┌──────────────┬──────────────┬──────────────┐
|
|
5
|
+
# │ │ Dashboard │ │
|
|
6
|
+
# │ Main Claude │ (v4 queue) │ Team Monitor│
|
|
7
|
+
# │ (Lead) ├──────────────┤ (lifecycle) │
|
|
8
|
+
# │ │ Prompt │ │
|
|
9
|
+
# │ │ History │ │
|
|
10
|
+
# └──────────────┴──────────────┴──────────────┘
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# bash scripts/harness-tmux-v4.sh # 레이아웃 + Claude 자동 실행 + team-action 자동 시작
|
|
14
|
+
# bash scripts/harness-tmux-v4.sh --no-auto # 레이아웃만 (Claude 수동 실행)
|
|
15
|
+
# bash scripts/harness-tmux-v4.sh --kill # 세션 종료
|
|
16
|
+
#
|
|
17
|
+
# 이것만 기억하세요:
|
|
18
|
+
# bash scripts/harness-tmux-v4.sh
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
23
|
+
SESSION_NAME="harness-v4"
|
|
24
|
+
|
|
25
|
+
PROJECT_ROOT=""
|
|
26
|
+
DETACH=false
|
|
27
|
+
AUTO_START=true
|
|
28
|
+
|
|
29
|
+
for arg in "$@"; do
|
|
30
|
+
case "$arg" in
|
|
31
|
+
--detach) DETACH=true ;;
|
|
32
|
+
--no-auto) AUTO_START=false ;;
|
|
33
|
+
--kill)
|
|
34
|
+
tmux kill-session -t "$SESSION_NAME" 2>/dev/null && echo "Killed." || echo "No session."
|
|
35
|
+
exit 0
|
|
36
|
+
;;
|
|
37
|
+
*)
|
|
38
|
+
if [ -d "$arg" ]; then PROJECT_ROOT="$arg"; fi
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
|
44
|
+
dir="$(pwd)"
|
|
45
|
+
while [ "$dir" != "/" ]; do
|
|
46
|
+
if [ -d "$dir/.harness" ]; then PROJECT_ROOT="$dir"; break; fi
|
|
47
|
+
dir="$(dirname "$dir")"
|
|
48
|
+
done
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if [ -z "$PROJECT_ROOT" ] || [ ! -d "$PROJECT_ROOT/.harness" ]; then
|
|
52
|
+
echo "Error: .harness/ not found."
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
echo "Project: $PROJECT_ROOT"
|
|
57
|
+
echo "Session: $SESSION_NAME"
|
|
58
|
+
|
|
59
|
+
tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
|
|
60
|
+
|
|
61
|
+
# ── Resolve Claude command ──
|
|
62
|
+
HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
|
|
63
|
+
CLAUDE_CMD="claude --dangerously-skip-permissions"
|
|
64
|
+
if [ -f "$HANDOFF" ]; then
|
|
65
|
+
_model=$(jq -r '.model // empty' "$HANDOFF" 2>/dev/null)
|
|
66
|
+
if [ -n "$_model" ] && [ "$_model" != "null" ]; then
|
|
67
|
+
CLAUDE_CMD="$CLAUDE_CMD --model $_model"
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# ══════════════════════════════════════════
|
|
72
|
+
# Build 3-column layout
|
|
73
|
+
# ══════════════════════════════════════════
|
|
74
|
+
|
|
75
|
+
# 1. Create session → Left pane (Main Claude Lead)
|
|
76
|
+
PANE_MAIN=$(tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 220 -y 55 \
|
|
77
|
+
-P -F '#{pane_id}')
|
|
78
|
+
|
|
79
|
+
# 2. Split horizontally: Left 35% | Right 65%
|
|
80
|
+
PANE_MID=$(tmux split-window -h -p 65 -t "$PANE_MAIN" -c "$PROJECT_ROOT" \
|
|
81
|
+
-P -F '#{pane_id}')
|
|
82
|
+
|
|
83
|
+
# 3. Split right section: Middle 45% | Right 55%
|
|
84
|
+
PANE_RIGHT=$(tmux split-window -h -p 55 -t "$PANE_MID" -c "$PROJECT_ROOT" \
|
|
85
|
+
-P -F '#{pane_id}' \
|
|
86
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\"'")
|
|
87
|
+
|
|
88
|
+
# 4. Split middle pane vertically: Dashboard (top 45%) | Prompt History (bottom 55%)
|
|
89
|
+
PANE_HISTORY=$(tmux split-window -v -p 55 -t "$PANE_MID" -c "$PROJECT_ROOT" \
|
|
90
|
+
-P -F '#{pane_id}' \
|
|
91
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-prompt-history.sh\" \"${PROJECT_ROOT}\"'")
|
|
92
|
+
|
|
93
|
+
# 5. Start dashboard in the middle-top pane
|
|
94
|
+
tmux send-keys -t "$PANE_MID" "bash \"${SCRIPT_DIR}/harness-dashboard-v4.sh\" \"${PROJECT_ROOT}\"" Enter
|
|
95
|
+
|
|
96
|
+
# 6. Main pane — Claude 자동 실행
|
|
97
|
+
tmux send-keys -t "$PANE_MAIN" "unset npm_config_prefix 2>/dev/null" Enter
|
|
98
|
+
tmux send-keys -t "$PANE_MAIN" "clear" Enter
|
|
99
|
+
|
|
100
|
+
if [ "$AUTO_START" = true ]; then
|
|
101
|
+
# Claude 실행 → 시작 후 자동으로 /harness-team-action 전송
|
|
102
|
+
tmux send-keys -t "$PANE_MAIN" "$CLAUDE_CMD" Enter
|
|
103
|
+
# Claude가 초기화될 시간을 준 뒤 team-action 명령 전송
|
|
104
|
+
sleep 3
|
|
105
|
+
tmux send-keys -t "$PANE_MAIN" "/harness-team-action" Enter
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# ── Pane titles ──
|
|
109
|
+
tmux select-pane -t "$PANE_MAIN" -T "Lead (Main Claude)"
|
|
110
|
+
tmux select-pane -t "$PANE_MID" -T "Dashboard"
|
|
111
|
+
tmux select-pane -t "$PANE_HISTORY" -T "Prompt History"
|
|
112
|
+
tmux select-pane -t "$PANE_RIGHT" -T "Team Monitor"
|
|
113
|
+
|
|
114
|
+
tmux set-option -t "$SESSION_NAME" pane-border-status top 2>/dev/null || true
|
|
115
|
+
tmux set-option -t "$SESSION_NAME" pane-border-format " #{pane_title} " 2>/dev/null || true
|
|
116
|
+
|
|
117
|
+
# ── Focus on Main pane ──
|
|
118
|
+
tmux select-pane -t "$PANE_MAIN"
|
|
119
|
+
|
|
120
|
+
# ── Attach ──
|
|
121
|
+
if [ "$DETACH" = true ]; then
|
|
122
|
+
echo ""
|
|
123
|
+
echo "Session created. Attach: tmux attach -t $SESSION_NAME"
|
|
124
|
+
else
|
|
125
|
+
if [ -n "${TMUX:-}" ]; then
|
|
126
|
+
tmux switch-client -t "$SESSION_NAME"
|
|
127
|
+
else
|
|
128
|
+
echo ""
|
|
129
|
+
echo "harness-v4 starting..."
|
|
130
|
+
echo ""
|
|
131
|
+
echo " All automatic. Just watch."
|
|
132
|
+
echo " Stop: bash scripts/harness-tmux-v4.sh --kill"
|
|
133
|
+
echo ""
|
|
134
|
+
tmux attach -t "$SESSION_NAME"
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
@@ -36,6 +36,19 @@ if [ -f "$CWD/.harness/progress.json" ] && command -v jq >/dev/null 2>&1; then
|
|
|
36
36
|
AGENT_STATUS=$(jq -r '.agent_status // "pending"' "$CWD/.harness/progress.json" 2>/dev/null || echo "pending")
|
|
37
37
|
fi
|
|
38
38
|
|
|
39
|
+
# ── 명령 히스토리 기록 (모든 모드 공통) ──
|
|
40
|
+
PROGRESS_LOG="$CWD/.harness/progress.log"
|
|
41
|
+
if [ -n "$PROMPT" ] && [ -d "$CWD/.harness" ]; then
|
|
42
|
+
# progress.log가 없으면 생성
|
|
43
|
+
if [ ! -f "$PROGRESS_LOG" ]; then
|
|
44
|
+
echo "# Harness Command History — $(date +%Y-%m-%d)" > "$PROGRESS_LOG"
|
|
45
|
+
fi
|
|
46
|
+
PROMPT_SHORT=$(echo "$PROMPT" | tr '\n' ' ' | sed 's/ */ /g' | cut -c1-80)
|
|
47
|
+
if [ ${#PROMPT_SHORT} -gt 2 ]; then
|
|
48
|
+
echo "$(date +"%Y-%m-%d %H:%M") | user-prompt | input | ${PROMPT_SHORT}" >> "$PROGRESS_LOG"
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
39
52
|
# ── 컨텍스트 분리 가드레일 ──
|
|
40
53
|
# 현재 에이전트가 활성인데 다른 에이전트 스킬을 호출하려는 경우 경고
|
|
41
54
|
CONTEXT_WARNING=""
|
|
@@ -58,16 +71,6 @@ if [ -f "$FEATURE_QUEUE" ]; then
|
|
|
58
71
|
V4_TOTAL=$(jq '[.queue.ready, (.queue.blocked | keys), (.queue.in_progress | keys), .queue.passed, .queue.failed] | flatten | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
|
|
59
72
|
V4_FAILED=$(jq '.queue.failed | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
|
|
60
73
|
|
|
61
|
-
# Log user prompt to progress.log (truncated to 80 chars)
|
|
62
|
-
PROGRESS_LOG="$CWD/.harness/progress.log"
|
|
63
|
-
if [ -n "$PROMPT" ] && [ -f "$PROGRESS_LOG" ]; then
|
|
64
|
-
PROMPT_SHORT=$(echo "$PROMPT" | tr '\n' ' ' | sed 's/ */ /g' | cut -c1-80)
|
|
65
|
-
# Skip logging for empty or very short prompts
|
|
66
|
-
if [ ${#PROMPT_SHORT} -gt 2 ]; then
|
|
67
|
-
echo "$(date +"%Y-%m-%d %H:%M") | user-prompt | input | ${PROMPT_SHORT}" >> "$PROGRESS_LOG"
|
|
68
|
-
fi
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
74
|
cat <<EOF
|
|
72
75
|
[harness-v4] ${V4_PASSED}/${V4_TOTAL} features passed | ${V4_FAILED} failed
|
|
73
76
|
|