@walwal-harness/cli 3.7.2 → 3.7.4
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/package.json +1 -1
- package/scripts/harness-control.sh +43 -12
- package/scripts/harness-tmux.sh +51 -55
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.4",
|
|
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"
|
|
@@ -38,8 +38,23 @@ RED="\033[31m"
|
|
|
38
38
|
CYAN="\033[36m"
|
|
39
39
|
RESET="\033[0m"
|
|
40
40
|
|
|
41
|
-
# ── Agent Session pane target ──
|
|
42
|
-
|
|
41
|
+
# ── Agent Session pane target (find by title) ──
|
|
42
|
+
find_agent_pane() {
|
|
43
|
+
tmux list-panes -t harness-studio -F '#{pane_id} #{pane_title}' 2>/dev/null \
|
|
44
|
+
| grep "Agent Session" | awk '{print $1}' | head -1
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
CONFIG="$PROJECT_ROOT/.harness/config.json"
|
|
48
|
+
|
|
49
|
+
# Build claude command from config
|
|
50
|
+
build_claude_cmd() {
|
|
51
|
+
local agent="$1"
|
|
52
|
+
local model="opus"
|
|
53
|
+
if [ -f "$CONFIG" ]; then
|
|
54
|
+
model=$(jq -r ".agents[\"${agent}\"].model // \"opus\"" "$CONFIG" 2>/dev/null)
|
|
55
|
+
fi
|
|
56
|
+
echo "claude --dangerously-skip-permissions --model ${model}"
|
|
57
|
+
}
|
|
43
58
|
|
|
44
59
|
cmd_log() {
|
|
45
60
|
local msg="$1"
|
|
@@ -63,8 +78,10 @@ cmd_clear_fail() {
|
|
|
63
78
|
}
|
|
64
79
|
|
|
65
80
|
cmd_next() {
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
local pane
|
|
82
|
+
pane=$(find_agent_pane)
|
|
83
|
+
if [ -z "$pane" ]; then
|
|
84
|
+
echo -e " ${RED}Agent Session pane not found.${RESET}"
|
|
68
85
|
return
|
|
69
86
|
fi
|
|
70
87
|
|
|
@@ -75,16 +92,25 @@ cmd_next() {
|
|
|
75
92
|
return
|
|
76
93
|
fi
|
|
77
94
|
|
|
78
|
-
|
|
95
|
+
local claude_cmd
|
|
96
|
+
claude_cmd=$(build_claude_cmd "$next_agent")
|
|
97
|
+
|
|
98
|
+
echo -e " ${CYAN}Starting ${next_agent}...${RESET}"
|
|
99
|
+
echo -e " ${DIM}${claude_cmd}${RESET}"
|
|
100
|
+
|
|
79
101
|
local ts
|
|
80
102
|
ts=$(date +"%Y-%m-%d")
|
|
81
103
|
echo "${ts} | manual | start-agent | ${next_agent}" >> "$PROGRESS_LOG"
|
|
82
|
-
|
|
104
|
+
|
|
105
|
+
# Launch claude in Agent Session pane with the agent's prompt
|
|
106
|
+
tmux send-keys -t "$pane" "${claude_cmd} -p '하네스 엔지니어링 시작'" Enter
|
|
83
107
|
}
|
|
84
108
|
|
|
85
109
|
cmd_retry() {
|
|
86
|
-
|
|
87
|
-
|
|
110
|
+
local pane
|
|
111
|
+
pane=$(find_agent_pane)
|
|
112
|
+
if [ -z "$pane" ]; then
|
|
113
|
+
echo -e " ${RED}Agent Session pane not found.${RESET}"
|
|
88
114
|
return
|
|
89
115
|
fi
|
|
90
116
|
|
|
@@ -95,21 +121,26 @@ cmd_retry() {
|
|
|
95
121
|
return
|
|
96
122
|
fi
|
|
97
123
|
|
|
124
|
+
local claude_cmd
|
|
125
|
+
claude_cmd=$(build_claude_cmd "$current_agent")
|
|
126
|
+
|
|
98
127
|
echo -e " ${CYAN}Retrying ${current_agent}...${RESET}"
|
|
99
128
|
local ts
|
|
100
129
|
ts=$(date +"%Y-%m-%d")
|
|
101
130
|
echo "${ts} | manual | retry | ${current_agent}" >> "$PROGRESS_LOG"
|
|
102
|
-
tmux send-keys -t "$
|
|
131
|
+
tmux send-keys -t "$pane" "${claude_cmd} -p '하네스 엔지니어링 시작'" Enter
|
|
103
132
|
}
|
|
104
133
|
|
|
105
134
|
cmd_stop() {
|
|
106
|
-
|
|
107
|
-
|
|
135
|
+
local pane
|
|
136
|
+
pane=$(find_agent_pane)
|
|
137
|
+
if [ -z "$pane" ]; then
|
|
138
|
+
echo -e " ${RED}Agent Session pane not found.${RESET}"
|
|
108
139
|
return
|
|
109
140
|
fi
|
|
110
141
|
|
|
111
142
|
echo -e " ${YELLOW}Sending Ctrl+C to Agent Session...${RESET}"
|
|
112
|
-
tmux send-keys -t "$
|
|
143
|
+
tmux send-keys -t "$pane" C-c
|
|
113
144
|
local ts
|
|
114
145
|
ts=$(date +"%Y-%m-%d")
|
|
115
146
|
echo "${ts} | manual | stop | user-initiated" >> "$PROGRESS_LOG"
|
package/scripts/harness-tmux.sh
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# harness-tmux.sh — Harness Studio tmux
|
|
2
|
+
# harness-tmux.sh — Harness Studio tmux 5-pane 레이아웃
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
# │
|
|
6
|
-
# │
|
|
7
|
-
# │
|
|
8
|
-
# │
|
|
9
|
-
# │
|
|
10
|
-
# │
|
|
11
|
-
#
|
|
12
|
-
# └───────────────────────────┴─────────────────────────┘
|
|
4
|
+
# ┌─────────────────────────┬────────────────────────┐
|
|
5
|
+
# │ Dashboard (auto-refresh)│ Monitor (compact) │
|
|
6
|
+
# │ ├────────────────────────┤
|
|
7
|
+
# │ │ Agent Session (claude) │
|
|
8
|
+
# ├─────────────────────────┤ │
|
|
9
|
+
# │ Control (harness> _) ├────────────────────────┤
|
|
10
|
+
# │ │ Eval Review │
|
|
11
|
+
# └─────────────────────────┴────────────────────────┘
|
|
13
12
|
#
|
|
14
13
|
# Usage:
|
|
15
14
|
# bash scripts/harness-tmux.sh [project-root]
|
|
16
15
|
# bash scripts/harness-tmux.sh /path/to/project --ai
|
|
17
16
|
#
|
|
18
17
|
# Options:
|
|
19
|
-
# --ai Eval Review에서 claude -p
|
|
18
|
+
# --ai Eval Review에서 claude -p AI 요약
|
|
20
19
|
# --detach 세션 생성 후 attach 하지 않음
|
|
21
20
|
# --kill 기존 harness-studio 세션 종료
|
|
22
21
|
|
|
@@ -34,7 +33,7 @@ for arg in "$@"; do
|
|
|
34
33
|
--ai) USE_AI="--ai" ;;
|
|
35
34
|
--detach) DETACH=true ;;
|
|
36
35
|
--kill)
|
|
37
|
-
tmux kill-session -t "$SESSION_NAME" 2>/dev/null && echo "
|
|
36
|
+
tmux kill-session -t "$SESSION_NAME" 2>/dev/null && echo "Killed." || echo "No session."
|
|
38
37
|
exit 0
|
|
39
38
|
;;
|
|
40
39
|
*)
|
|
@@ -43,7 +42,6 @@ for arg in "$@"; do
|
|
|
43
42
|
esac
|
|
44
43
|
done
|
|
45
44
|
|
|
46
|
-
# Auto-detect project root
|
|
47
45
|
if [ -z "$PROJECT_ROOT" ]; then
|
|
48
46
|
dir="$(pwd)"
|
|
49
47
|
while [ "$dir" != "/" ]; do
|
|
@@ -53,18 +51,16 @@ if [ -z "$PROJECT_ROOT" ]; then
|
|
|
53
51
|
fi
|
|
54
52
|
|
|
55
53
|
if [ -z "$PROJECT_ROOT" ] || [ ! -d "$PROJECT_ROOT/.harness" ]; then
|
|
56
|
-
echo "Error: .harness/
|
|
57
|
-
echo "Usage: bash scripts/harness-tmux.sh [project-root]"
|
|
54
|
+
echo "Error: .harness/ not found."
|
|
58
55
|
exit 1
|
|
59
56
|
fi
|
|
60
57
|
|
|
61
58
|
echo "Project: $PROJECT_ROOT"
|
|
62
59
|
echo "Session: $SESSION_NAME"
|
|
63
60
|
|
|
64
|
-
# Kill existing session if any
|
|
65
61
|
tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
|
|
66
62
|
|
|
67
|
-
# ── Resolve Claude command
|
|
63
|
+
# ── Resolve Claude command ──
|
|
68
64
|
HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
|
|
69
65
|
CLAUDE_CMD="claude --dangerously-skip-permissions"
|
|
70
66
|
if [ -f "$HANDOFF" ]; then
|
|
@@ -75,66 +71,66 @@ if [ -f "$HANDOFF" ]; then
|
|
|
75
71
|
fi
|
|
76
72
|
|
|
77
73
|
# ══════════════════════════════════════════
|
|
78
|
-
#
|
|
79
|
-
# ══════════════════════════════════════════
|
|
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)
|
|
74
|
+
# Build layout using explicit pane IDs
|
|
87
75
|
# ══════════════════════════════════════════
|
|
88
76
|
|
|
89
|
-
#
|
|
90
|
-
tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 200 -y 50 \
|
|
91
|
-
|
|
77
|
+
# 1. Create session → capture Dashboard pane ID
|
|
78
|
+
PANE_DASHBOARD=$(tmux new-session -d -s "$SESSION_NAME" -c "$PROJECT_ROOT" -x 200 -y 50 \
|
|
79
|
+
-P -F '#{pane_id}' \
|
|
80
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-dashboard.sh\" \"${PROJECT_ROOT}\"'")
|
|
92
81
|
|
|
93
|
-
#
|
|
94
|
-
tmux split-window -h -p 45 -t "$
|
|
95
|
-
|
|
82
|
+
# 2. Split Dashboard horizontally (55% left / 45% right) → Monitor pane
|
|
83
|
+
PANE_MONITOR=$(tmux split-window -h -p 45 -t "$PANE_DASHBOARD" -c "$PROJECT_ROOT" \
|
|
84
|
+
-P -F '#{pane_id}' \
|
|
85
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-monitor.sh\" \"${PROJECT_ROOT}\"'")
|
|
96
86
|
|
|
97
|
-
#
|
|
98
|
-
tmux split-window -v -p
|
|
99
|
-
|
|
87
|
+
# 3. Split Dashboard vertically (75% top / 25% bottom) → Control pane
|
|
88
|
+
PANE_CONTROL=$(tmux split-window -v -p 25 -t "$PANE_DASHBOARD" -c "$PROJECT_ROOT" \
|
|
89
|
+
-P -F '#{pane_id}' \
|
|
90
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-control.sh\" \"${PROJECT_ROOT}\"'")
|
|
100
91
|
|
|
101
|
-
#
|
|
102
|
-
tmux split-window -v -p
|
|
103
|
-
|
|
92
|
+
# 4. Split Monitor vertically (15% top / 85% bottom) → Agent Session pane
|
|
93
|
+
PANE_AGENT=$(tmux split-window -v -p 85 -t "$PANE_MONITOR" -c "$PROJECT_ROOT" \
|
|
94
|
+
-P -F '#{pane_id}')
|
|
104
95
|
|
|
105
|
-
#
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
# 5. Split Agent Session vertically (55% top / 45% bottom) → Eval Review pane
|
|
97
|
+
PANE_EVAL=$(tmux split-window -v -p 45 -t "$PANE_AGENT" -c "$PROJECT_ROOT" \
|
|
98
|
+
-P -F '#{pane_id}' \
|
|
99
|
+
"bash --norc --noprofile -c 'exec bash \"${SCRIPT_DIR}/harness-eval-watcher.sh\" \"${PROJECT_ROOT}\" ${USE_AI}'")
|
|
109
100
|
|
|
110
|
-
#
|
|
111
|
-
|
|
101
|
+
# 6. Agent Session — leave as empty shell, ready for Control to launch claude
|
|
102
|
+
# Pre-unset nvm conflict variable
|
|
103
|
+
tmux send-keys -t "$PANE_AGENT" "unset npm_config_prefix 2>/dev/null" Enter
|
|
104
|
+
tmux send-keys -t "$PANE_AGENT" "clear" Enter
|
|
112
105
|
|
|
113
106
|
# ── Pane titles ──
|
|
114
|
-
tmux select-pane -t "$
|
|
115
|
-
tmux select-pane -t "$
|
|
116
|
-
tmux select-pane -t "$
|
|
117
|
-
tmux select-pane -t "$
|
|
118
|
-
tmux select-pane -t "$
|
|
107
|
+
tmux select-pane -t "$PANE_DASHBOARD" -T "Dashboard"
|
|
108
|
+
tmux select-pane -t "$PANE_MONITOR" -T "Monitor"
|
|
109
|
+
tmux select-pane -t "$PANE_CONTROL" -T "Control"
|
|
110
|
+
tmux select-pane -t "$PANE_AGENT" -T "Agent Session"
|
|
111
|
+
tmux select-pane -t "$PANE_EVAL" -T "Eval Review"
|
|
119
112
|
|
|
120
113
|
tmux set-option -t "$SESSION_NAME" pane-border-status top 2>/dev/null || true
|
|
121
114
|
tmux set-option -t "$SESSION_NAME" pane-border-format " #{pane_title} " 2>/dev/null || true
|
|
122
115
|
|
|
116
|
+
# ── Focus on Control pane (where user types) ──
|
|
117
|
+
tmux select-pane -t "$PANE_CONTROL"
|
|
118
|
+
|
|
123
119
|
# ── Attach ──
|
|
124
120
|
if [ "$DETACH" = true ]; then
|
|
125
121
|
echo ""
|
|
126
|
-
echo "Session created. Attach
|
|
127
|
-
echo " tmux attach -t $SESSION_NAME"
|
|
122
|
+
echo "Session created. Attach: tmux attach -t $SESSION_NAME"
|
|
128
123
|
else
|
|
129
124
|
if [ -n "${TMUX:-}" ]; then
|
|
130
125
|
tmux switch-client -t "$SESSION_NAME"
|
|
131
126
|
else
|
|
132
127
|
echo ""
|
|
133
128
|
echo "Launching harness-studio..."
|
|
134
|
-
echo "
|
|
135
|
-
echo "
|
|
136
|
-
echo "
|
|
137
|
-
echo "
|
|
129
|
+
echo " Dashboard (left↑) : Auto-refresh progress"
|
|
130
|
+
echo " Control (left↓) : Manual commands (next/retry/stop/log)"
|
|
131
|
+
echo " Monitor (right↑) : Agent lifecycle (compact)"
|
|
132
|
+
echo " Agent Session (right↔): claude --dangerously-skip-permissions"
|
|
133
|
+
echo " Eval Review (right↓): Evaluation summaries"
|
|
138
134
|
echo ""
|
|
139
135
|
tmux attach -t "$SESSION_NAME"
|
|
140
136
|
fi
|