@walwal-harness/cli 4.0.0 → 5.0.0
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/README.md +118 -294
- package/assets/templates/config.json +8 -7
- package/assets/templates/progress.json.template +7 -1
- package/bin/init.js +115 -84
- package/commands/harness-solo.md +68 -0
- package/commands/harness-stop.md +53 -0
- package/{skills/team-action/SKILL.md → commands/harness-team.md} +59 -29
- package/package.json +9 -10
- package/scripts/harness-dashboard.sh +219 -154
- package/scripts/harness-monitor.sh +17 -12
- package/scripts/harness-prompt-history.sh +1 -1
- package/scripts/harness-queue-manager.sh +1 -1
- package/scripts/harness-session-start.sh +24 -13
- package/scripts/harness-statusline.sh +31 -8
- package/scripts/harness-tmux.sh +125 -72
- package/scripts/harness-user-prompt-submit.sh +22 -15
- package/skills/dispatcher/SKILL.md +6 -6
- package/skills/evaluator-functional/SKILL.md +2 -2
- package/skills/generator-frontend/SKILL.md +2 -2
- package/scripts/harness-control.sh +0 -183
- package/scripts/harness-dashboard-v4.sh +0 -200
- package/scripts/harness-eval-watcher.sh +0 -228
- package/scripts/harness-studio-setup.sh +0 -139
- package/scripts/harness-studio.sh +0 -66
- package/scripts/harness-tmux-v4.sh +0 -148
- package/skills/team-stop/SKILL.md +0 -30
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# harness-dashboard.sh —
|
|
2
|
+
# harness-dashboard.sh — Unified Dashboard (Solo + Team mode)
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# Commands:
|
|
8
|
-
# status / s 대시보드 즉시 갱신
|
|
9
|
-
# next / n 다음 에이전트 세션 시작 (Agent Session pane으로 전달)
|
|
10
|
-
# retry / r 현재 에이전트 재실행
|
|
11
|
-
# stop 에이전트 세션 중지
|
|
12
|
-
# log <message> progress.log에 수동 메모 추가
|
|
13
|
-
# clear-fail failure 상태 클리어
|
|
14
|
-
# quit / q 대시보드 종료
|
|
4
|
+
# Reads progress.json.mode to decide rendering:
|
|
5
|
+
# solo: Sequential pipeline progress (sprint map, agent bar, prompt history)
|
|
6
|
+
# team: Queue summary, team status, feature list, bottleneck alerts
|
|
15
7
|
#
|
|
16
8
|
# Usage: bash scripts/harness-dashboard.sh [project-root]
|
|
17
9
|
|
|
@@ -30,6 +22,7 @@ fi
|
|
|
30
22
|
|
|
31
23
|
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
32
24
|
FEATURES="$PROJECT_ROOT/.harness/actions/feature-list.json"
|
|
25
|
+
QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
|
|
33
26
|
PROGRESS_LOG="$PROJECT_ROOT/.harness/progress.log"
|
|
34
27
|
|
|
35
28
|
# ── ANSI helpers ──
|
|
@@ -42,7 +35,6 @@ CYAN="\033[36m"
|
|
|
42
35
|
MAGENTA="\033[35m"
|
|
43
36
|
RESET="\033[0m"
|
|
44
37
|
|
|
45
|
-
# ── Strip ANSI escape sequences ──
|
|
46
38
|
strip_ansi() {
|
|
47
39
|
sed 's/\x1b\[[0-9;]*m//g; s/\x1b\[[0-9;]*[a-zA-Z]//g'
|
|
48
40
|
}
|
|
@@ -51,23 +43,38 @@ get_term_width() {
|
|
|
51
43
|
tput cols 2>/dev/null || echo 80
|
|
52
44
|
}
|
|
53
45
|
|
|
46
|
+
get_mode() {
|
|
47
|
+
if [ -f "$PROGRESS" ] && command -v jq &>/dev/null; then
|
|
48
|
+
jq -r '.mode // "solo"' "$PROGRESS" 2>/dev/null
|
|
49
|
+
else
|
|
50
|
+
echo "solo"
|
|
51
|
+
fi
|
|
52
|
+
}
|
|
53
|
+
|
|
54
54
|
# ══════════════════════════════════════════
|
|
55
|
-
#
|
|
55
|
+
# Common header
|
|
56
56
|
# ══════════════════════════════════════════
|
|
57
|
-
|
|
58
57
|
render_header() {
|
|
59
|
-
local now project_name
|
|
58
|
+
local now project_name mode
|
|
60
59
|
now=$(date +"%H:%M:%S")
|
|
61
60
|
project_name=$(jq -r '.project_name // "Unknown"' "$PROGRESS" 2>/dev/null)
|
|
61
|
+
mode=$(get_mode)
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
local mode_label
|
|
64
|
+
case "$mode" in
|
|
65
|
+
team) mode_label="${GREEN}TEAM${RESET}" ;;
|
|
66
|
+
paused) mode_label="${YELLOW}PAUSED${RESET}" ;;
|
|
67
|
+
*) mode_label="${CYAN}SOLO${RESET}" ;;
|
|
68
|
+
esac
|
|
69
|
+
|
|
70
|
+
echo -e "${BOLD}HARNESS DASHBOARD${RESET} ${mode_label} ${DIM}${project_name} | ${now}${RESET}"
|
|
67
71
|
echo ""
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
# ══════════════════════════════════════════
|
|
75
|
+
# Solo Mode rendering
|
|
76
|
+
# ══════════════════════════════════════════
|
|
77
|
+
render_solo_sprint_overview() {
|
|
71
78
|
if [ ! -f "$PROGRESS" ]; then
|
|
72
79
|
echo -e " ${DIM}(waiting for harness init...)${RESET}"
|
|
73
80
|
return
|
|
@@ -94,39 +101,46 @@ render_sprint_overview() {
|
|
|
94
101
|
echo ""
|
|
95
102
|
}
|
|
96
103
|
|
|
97
|
-
|
|
104
|
+
render_solo_failure_info() {
|
|
98
105
|
if [ ! -f "$PROGRESS" ]; then return; fi
|
|
99
106
|
|
|
100
|
-
local
|
|
101
|
-
|
|
102
|
-
if [ -z "$sprint_keys" ]; then return; fi
|
|
107
|
+
local failure_agent
|
|
108
|
+
failure_agent=$(jq -r '.failure.agent // empty' "$PROGRESS" 2>/dev/null)
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
if [ -n "$failure_agent" ] && [ "$failure_agent" != "null" ]; then
|
|
111
|
+
local failure_loc failure_msg
|
|
112
|
+
failure_loc=$(jq -r '.failure.location // ""' "$PROGRESS")
|
|
113
|
+
failure_msg=$(jq -r '.failure.message // ""' "$PROGRESS" | strip_ansi | tr '\n' ' ' | sed 's/ */ /g')
|
|
114
|
+
if [ ${#failure_msg} -gt 80 ]; then failure_msg="${failure_msg:0:78}.."; fi
|
|
115
|
+
|
|
116
|
+
echo -e " ${RED}${BOLD}FAIL${RESET} ${RED}${failure_agent} → ${failure_loc}${RESET}"
|
|
117
|
+
if [ -n "$failure_msg" ]; then
|
|
118
|
+
echo -e " ${DIM}${failure_msg}${RESET}"
|
|
119
|
+
fi
|
|
120
|
+
echo ""
|
|
121
|
+
fi
|
|
122
|
+
}
|
|
105
123
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
status=$(jq -r ".sprint_progress[\"${s}\"].status // \"pending\"" "$PROGRESS")
|
|
109
|
-
feat_count=$(jq ".sprint_progress[\"${s}\"].features // [] | length" "$PROGRESS")
|
|
110
|
-
notes=$(jq -r ".sprint_progress[\"${s}\"].notes // \"\"" "$PROGRESS")
|
|
124
|
+
render_solo_agent_info() {
|
|
125
|
+
if [ ! -f "$PROGRESS" ]; then return; fi
|
|
111
126
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
scaffolded) icon="◑" ;;
|
|
116
|
-
*) icon="○" ;;
|
|
117
|
-
esac
|
|
127
|
+
local next_agent agent_status
|
|
128
|
+
next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
129
|
+
agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
|
|
118
130
|
|
|
119
|
-
|
|
131
|
+
if [ "$next_agent" != "none" ] && [ "$next_agent" != "null" ] && [ "$agent_status" != "blocked" ]; then
|
|
132
|
+
echo -e " ${CYAN}Next → /harness-${next_agent}${RESET}"
|
|
133
|
+
fi
|
|
120
134
|
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
render_agent_bar "$PROJECT_ROOT" 2>/dev/null
|
|
136
|
+
echo ""
|
|
123
137
|
}
|
|
124
138
|
|
|
125
|
-
|
|
126
|
-
echo -e "${BOLD}
|
|
139
|
+
render_solo_prompt_history() {
|
|
140
|
+
echo -e "${BOLD}Recent Activity${RESET}"
|
|
127
141
|
|
|
128
142
|
if [ -f "$PROGRESS_LOG" ]; then
|
|
129
|
-
grep -v '^#' "$PROGRESS_LOG" 2>/dev/null | grep -v '^$' | tail -
|
|
143
|
+
grep -v '^#' "$PROGRESS_LOG" 2>/dev/null | grep -v '^$' | tail -10 | while IFS= read -r line; do
|
|
130
144
|
local ts agent action detail
|
|
131
145
|
ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
|
|
132
146
|
agent=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
|
|
@@ -138,155 +152,203 @@ render_prompt_history_lines() {
|
|
|
138
152
|
|
|
139
153
|
case "$agent" in
|
|
140
154
|
dispatcher*) icon="▸" ; color="$MAGENTA" ;;
|
|
141
|
-
brainstormer) icon="◇" ; color="$CYAN" ;;
|
|
142
155
|
planner*) icon="□" ; color="$YELLOW" ;;
|
|
143
156
|
generator*) icon="▶" ; color="$GREEN" ;;
|
|
144
157
|
eval*) icon="✦" ; color="$RED" ;;
|
|
145
|
-
|
|
158
|
+
team-*) icon="◆" ; color="$CYAN" ;;
|
|
159
|
+
user*) icon="★" ; color="$BOLD" ;;
|
|
146
160
|
*) icon="·" ; color="$DIM" ;;
|
|
147
161
|
esac
|
|
148
162
|
|
|
149
|
-
if [ ${#detail} -gt
|
|
163
|
+
if [ ${#detail} -gt 40 ]; then detail="${detail:0:38}.."; fi
|
|
150
164
|
|
|
151
|
-
echo -e "${color}${icon}${RESET} ${DIM}${short_ts}${RESET} ${agent} ${DIM}${action}${RESET}"
|
|
152
|
-
if [ -n "$detail" ]; then
|
|
153
|
-
echo -e " ${DIM}${detail}${RESET}"
|
|
154
|
-
fi
|
|
165
|
+
echo -e "${color}${icon}${RESET} ${DIM}${short_ts}${RESET} ${agent} ${DIM}${action}${RESET} ${detail}"
|
|
155
166
|
done
|
|
156
167
|
else
|
|
157
168
|
echo -e "${DIM}(no progress.log yet)${RESET}"
|
|
158
169
|
fi
|
|
159
170
|
}
|
|
160
171
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if [ "$
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
while IFS= read -r line; do
|
|
171
|
-
left_lines+=("$line")
|
|
172
|
-
done < <(render_sprint_map_lines 2>/dev/null)
|
|
173
|
-
|
|
174
|
-
while IFS= read -r line; do
|
|
175
|
-
right_lines+=("$line")
|
|
176
|
-
done < <(render_prompt_history_lines 2>/dev/null)
|
|
177
|
-
|
|
178
|
-
local left_count=${#left_lines[@]}
|
|
179
|
-
local right_count=${#right_lines[@]}
|
|
180
|
-
local max_rows=$left_count
|
|
181
|
-
if [ "$right_count" -gt "$max_rows" ]; then max_rows=$right_count; fi
|
|
182
|
-
|
|
183
|
-
local sep=""
|
|
184
|
-
for ((i=0; i<term_width-4; i++)); do sep+="─"; done
|
|
185
|
-
echo -e " ${DIM}${sep}${RESET}"
|
|
186
|
-
|
|
187
|
-
for ((i=0; i<max_rows; i++)); do
|
|
188
|
-
local left_text="${left_lines[$i]:-}"
|
|
189
|
-
local right_text="${right_lines[$i]:-}"
|
|
190
|
-
|
|
191
|
-
local left_plain
|
|
192
|
-
left_plain=$(echo -e "$left_text" | strip_ansi)
|
|
193
|
-
local left_len=${#left_plain}
|
|
194
|
-
local pad_needed=$(( col_width - left_len ))
|
|
195
|
-
if [ "$pad_needed" -lt 0 ]; then pad_needed=0; fi
|
|
196
|
-
local padding=""
|
|
197
|
-
for ((p=0; p<pad_needed; p++)); do padding+=" "; done
|
|
198
|
-
|
|
199
|
-
echo -e " ${left_text}${padding} ${DIM}│${RESET} ${right_text}"
|
|
200
|
-
done
|
|
172
|
+
# ══════════════════════════════════════════
|
|
173
|
+
# Team Mode rendering
|
|
174
|
+
# ══════════════════════════════════════════
|
|
175
|
+
render_team_queue_summary() {
|
|
176
|
+
if [ ! -f "$QUEUE" ]; then
|
|
177
|
+
echo -e " ${DIM}(queue not initialized — run /harness-team)${RESET}"
|
|
178
|
+
return
|
|
179
|
+
fi
|
|
201
180
|
|
|
202
|
-
|
|
203
|
-
|
|
181
|
+
local ready blocked in_prog passed failed total
|
|
182
|
+
ready=$(jq '.queue.ready | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
183
|
+
blocked=$(jq '.queue.blocked | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
184
|
+
in_prog=$(jq '.queue.in_progress | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
185
|
+
passed=$(jq '.queue.passed | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
186
|
+
failed=$(jq '.queue.failed | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
187
|
+
ready=${ready:-0}; blocked=${blocked:-0}; in_prog=${in_prog:-0}; passed=${passed:-0}; failed=${failed:-0}
|
|
188
|
+
total=$((ready + blocked + in_prog + passed + failed))
|
|
189
|
+
|
|
190
|
+
local pct=0
|
|
191
|
+
if [ "$total" -gt 0 ]; then pct=$(( passed * 100 / total )); fi
|
|
192
|
+
local bar_w=16
|
|
193
|
+
local filled=$(( pct * bar_w / 100 ))
|
|
194
|
+
local empty=$(( bar_w - filled ))
|
|
195
|
+
local bar=""
|
|
196
|
+
for ((i=0; i<filled; i++)); do bar+="█"; done
|
|
197
|
+
for ((i=0; i<empty; i++)); do bar+="░"; done
|
|
198
|
+
|
|
199
|
+
echo -e " ${bar} ${passed}/${total} (${pct}%) R:${GREEN}${ready}${RESET} B:${YELLOW}${blocked}${RESET} P:${CYAN}${in_prog}${RESET} ${GREEN}✓${passed}${RESET} ${RED}✗${failed}${RESET}"
|
|
204
200
|
}
|
|
205
201
|
|
|
206
|
-
|
|
207
|
-
if [ ! -f "$
|
|
202
|
+
render_team_status() {
|
|
203
|
+
if [ ! -f "$QUEUE" ]; then return; fi
|
|
204
|
+
|
|
205
|
+
local team_count
|
|
206
|
+
team_count=$(jq '.teams | length' "$QUEUE" 2>/dev/null)
|
|
207
|
+
if [ "${team_count:-0}" -eq 0 ]; then return; fi
|
|
208
|
+
|
|
209
|
+
echo ""
|
|
210
|
+
echo -e "${BOLD}Teams${RESET}"
|
|
211
|
+
|
|
212
|
+
for i in $(seq 1 "$team_count"); do
|
|
213
|
+
local t_status t_feature t_phase t_attempt
|
|
214
|
+
t_status=$(jq -r ".teams[\"$i\"].status // \"idle\"" "$QUEUE" 2>/dev/null)
|
|
215
|
+
t_feature=$(jq -r ".teams[\"$i\"].feature // \"—\"" "$QUEUE" 2>/dev/null)
|
|
216
|
+
|
|
217
|
+
if [ "$t_feature" != "—" ] && [ "$t_feature" != "null" ]; then
|
|
218
|
+
t_phase=$(jq -r --arg f "$t_feature" '.queue.in_progress[$f].phase // "?"' "$QUEUE" 2>/dev/null)
|
|
219
|
+
t_attempt=$(jq -r --arg f "$t_feature" '.queue.in_progress[$f].attempt // 1' "$QUEUE" 2>/dev/null)
|
|
220
|
+
else
|
|
221
|
+
t_phase="—"; t_attempt=""
|
|
222
|
+
fi
|
|
208
223
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
224
|
+
local icon color
|
|
225
|
+
case "$t_status" in
|
|
226
|
+
busy) icon="▶"; color="$GREEN" ;;
|
|
227
|
+
idle) icon="○"; color="$DIM" ;;
|
|
228
|
+
*) icon="?"; color="$RESET" ;;
|
|
229
|
+
esac
|
|
230
|
+
|
|
231
|
+
local phase_short=""
|
|
232
|
+
case "$t_phase" in
|
|
233
|
+
gen) phase_short="${CYAN}Gen${RESET}" ;;
|
|
234
|
+
gate) phase_short="${YELLOW}Gate${RESET}" ;;
|
|
235
|
+
eval) phase_short="${MAGENTA}Eval${RESET}" ;;
|
|
236
|
+
*) phase_short="${DIM}-${RESET}" ;;
|
|
237
|
+
esac
|
|
213
238
|
|
|
214
|
-
|
|
239
|
+
printf " %b%b T%d %-7s %b" "$color" "$icon" "$i" "$t_feature" "$phase_short"
|
|
240
|
+
if [ -n "$t_attempt" ] && [ "$t_attempt" != "—" ]; then
|
|
241
|
+
printf " #%s/5" "$t_attempt"
|
|
242
|
+
fi
|
|
243
|
+
echo ""
|
|
244
|
+
done
|
|
245
|
+
}
|
|
215
246
|
|
|
216
|
-
|
|
217
|
-
if [ "$
|
|
218
|
-
local tsc_color="$GREEN"
|
|
219
|
-
if [ "$tsc_status" != "clean" ]; then tsc_color="$RED"; fi
|
|
247
|
+
render_team_features() {
|
|
248
|
+
if [ ! -f "$QUEUE" ] || [ ! -f "$FEATURES" ]; then return; fi
|
|
220
249
|
|
|
221
|
-
echo -e " ${BOLD}Build${RESET} ${build_color}${build_status}${RESET} | TSC: ${tsc_color}${tsc_status}${RESET} | Routes: ${routes_total}"
|
|
222
250
|
echo ""
|
|
251
|
+
echo -e "${BOLD}Features${RESET}"
|
|
252
|
+
|
|
253
|
+
jq -r --slurpfile q "$QUEUE" '
|
|
254
|
+
($q[0].queue.passed // []) as $passed |
|
|
255
|
+
($q[0].queue.failed // []) as $failed |
|
|
256
|
+
($q[0].queue.ready // []) as $ready |
|
|
257
|
+
($q[0].queue.in_progress // {}) as $prog |
|
|
258
|
+
.features[] |
|
|
259
|
+
.id as $fid |
|
|
260
|
+
(.name // .description // "?" | if length > 18 then .[0:16] + ".." else . end) as $fname |
|
|
261
|
+
(if ($fid | IN($passed[])) then "P"
|
|
262
|
+
elif $prog[$fid] then "I|\($prog[$fid].team)|\($prog[$fid].phase)"
|
|
263
|
+
elif ($fid | IN($failed[])) then "F"
|
|
264
|
+
elif ($fid | IN($ready[])) then "R"
|
|
265
|
+
else "B" end) as $st |
|
|
266
|
+
"\($st)\t\($fid)\t\($fname)"
|
|
267
|
+
' "$FEATURES" 2>/dev/null | while IFS=$'\t' read -r st fid fname; do
|
|
268
|
+
case "$st" in
|
|
269
|
+
P) printf " ${GREEN}●${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
270
|
+
F) printf " ${RED}✗${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
271
|
+
R) printf " ${YELLOW}○${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
272
|
+
B) printf " ${DIM}◌${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
273
|
+
I\|*) team=$(echo "$st" | cut -d'|' -f2)
|
|
274
|
+
phase=$(echo "$st" | cut -d'|' -f3)
|
|
275
|
+
printf " ${CYAN}◐${RESET} %-6s %-14s T%s:%s\n" "$fid" "$fname" "$team" "$phase" ;;
|
|
276
|
+
*) printf " ? %-6s %s\n" "$fid" "$fname" ;;
|
|
277
|
+
esac
|
|
278
|
+
done
|
|
223
279
|
}
|
|
224
280
|
|
|
225
|
-
|
|
226
|
-
if [ ! -f "$
|
|
281
|
+
render_team_bottleneck() {
|
|
282
|
+
if [ ! -f "$QUEUE" ] || [ ! -f "$FEATURES" ]; then return; fi
|
|
227
283
|
|
|
228
|
-
local
|
|
229
|
-
|
|
284
|
+
local failed_list
|
|
285
|
+
failed_list=$(jq -r '.queue.failed[]' "$QUEUE" 2>/dev/null)
|
|
286
|
+
if [ -z "$failed_list" ]; then return; fi
|
|
230
287
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if [ ${#failure_msg} -gt 80 ]; then failure_msg="${failure_msg:0:78}.."; fi
|
|
288
|
+
local team_count idle_teams=0 blocked_count in_prog
|
|
289
|
+
team_count=$(jq '.teams | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
290
|
+
blocked_count=$(jq '.queue.blocked | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
291
|
+
in_prog=$(jq '.queue.in_progress | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
236
292
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
fi
|
|
241
|
-
|
|
242
|
-
fi
|
|
243
|
-
}
|
|
293
|
+
for i in $(seq 1 "$team_count"); do
|
|
294
|
+
local ts
|
|
295
|
+
ts=$(jq -r ".teams[\"$i\"].status // \"idle\"" "$QUEUE" 2>/dev/null)
|
|
296
|
+
if [ "$ts" = "idle" ]; then idle_teams=$((idle_teams + 1)); fi
|
|
297
|
+
done
|
|
244
298
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
299
|
+
while IFS= read -r fid; do
|
|
300
|
+
[ -z "$fid" ] && continue
|
|
301
|
+
local deps_on_this
|
|
302
|
+
deps_on_this=$(jq --arg fid "$fid" '[.queue.blocked | to_entries[] | select(.value | index($fid))] | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
248
303
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
304
|
+
if [ "$deps_on_this" -gt 0 ]; then
|
|
305
|
+
echo ""
|
|
306
|
+
echo -e " ${RED}${BOLD}⚠ BOTTLENECK${RESET} ${RED}${fid}${RESET} failed → ${YELLOW}${deps_on_this} features blocked${RESET}"
|
|
307
|
+
if [ "$idle_teams" -gt 0 ]; then
|
|
308
|
+
echo -e " ${BOLD}→ requeue:${RESET} bash scripts/harness-queue-manager.sh requeue ${fid} ."
|
|
309
|
+
fi
|
|
310
|
+
fi
|
|
311
|
+
done <<< "$failed_list"
|
|
252
312
|
|
|
253
|
-
if [ "$
|
|
254
|
-
echo
|
|
313
|
+
if [ "$idle_teams" -eq "$team_count" ] && [ "$in_prog" -eq 0 ] && [ "$blocked_count" -gt 0 ]; then
|
|
314
|
+
echo ""
|
|
315
|
+
echo -e " ${RED}${BOLD}!! STALLED${RESET} — All teams idle, ${blocked_count} features blocked"
|
|
255
316
|
fi
|
|
256
|
-
|
|
257
|
-
# Agent bar
|
|
258
|
-
render_agent_bar "$PROJECT_ROOT" 2>/dev/null
|
|
259
|
-
echo ""
|
|
260
317
|
}
|
|
261
318
|
|
|
262
319
|
# ══════════════════════════════════════════
|
|
263
|
-
#
|
|
320
|
+
# Render dispatch
|
|
264
321
|
# ══════════════════════════════════════════
|
|
265
322
|
render_dashboard() {
|
|
266
|
-
local
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
323
|
+
local mode
|
|
324
|
+
mode=$(get_mode)
|
|
325
|
+
|
|
326
|
+
render_header
|
|
327
|
+
|
|
328
|
+
case "$mode" in
|
|
329
|
+
team|paused)
|
|
330
|
+
render_team_queue_summary
|
|
331
|
+
render_team_status
|
|
332
|
+
render_team_features
|
|
333
|
+
render_team_bottleneck
|
|
334
|
+
;;
|
|
335
|
+
*)
|
|
336
|
+
render_solo_sprint_overview
|
|
337
|
+
render_solo_failure_info
|
|
338
|
+
|
|
339
|
+
if [ -f "$FEATURES" ]; then
|
|
340
|
+
render_progress "$PROJECT_ROOT" 2>/dev/null
|
|
341
|
+
fi
|
|
280
342
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
343
|
+
render_solo_agent_info
|
|
344
|
+
echo ""
|
|
345
|
+
render_solo_prompt_history
|
|
346
|
+
;;
|
|
347
|
+
esac
|
|
286
348
|
}
|
|
287
349
|
|
|
288
350
|
# ══════════════════════════════════════════
|
|
289
|
-
# Main — Auto-refresh
|
|
351
|
+
# Main — Auto-refresh loop
|
|
290
352
|
# ══════════════════════════════════════════
|
|
291
353
|
tput civis 2>/dev/null
|
|
292
354
|
trap 'tput cnorm 2>/dev/null; exit 0' EXIT INT TERM
|
|
@@ -294,6 +356,9 @@ trap 'tput cnorm 2>/dev/null; exit 0' EXIT INT TERM
|
|
|
294
356
|
clear
|
|
295
357
|
|
|
296
358
|
while true; do
|
|
297
|
-
render_dashboard
|
|
359
|
+
buf=$(render_dashboard 2>&1)
|
|
360
|
+
tput cup 0 0 2>/dev/null
|
|
361
|
+
echo "$buf"
|
|
362
|
+
tput ed 2>/dev/null
|
|
298
363
|
sleep 3
|
|
299
364
|
done
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# harness-monitor.sh — Agent Lifecycle Monitor
|
|
2
|
+
# harness-monitor.sh — Agent Lifecycle Monitor (v5 unified)
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# Solo 모드: 단일 이벤트 스트림 (progress.json 변경 감지 + audit.log)
|
|
5
|
+
# Team 모드: 팀별 섹션 분리 (feature-queue.json + progress.log 기반)
|
|
6
6
|
#
|
|
7
7
|
# Usage: bash scripts/harness-monitor.sh [project-root]
|
|
8
8
|
|
|
@@ -62,7 +62,7 @@ team_color() {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
# ══════════════════════════════════════════
|
|
65
|
-
#
|
|
65
|
+
# Team 모드: 팀별 섹션 렌더링
|
|
66
66
|
# ══════════════════════════════════════════
|
|
67
67
|
|
|
68
68
|
render_v4_header() {
|
|
@@ -224,7 +224,7 @@ render_v4() {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
# ══════════════════════════════════════════
|
|
227
|
-
#
|
|
227
|
+
# Solo 모드: 단일 스트림 (기존 동작)
|
|
228
228
|
# ══════════════════════════════════════════
|
|
229
229
|
|
|
230
230
|
LAST_AGENT=""
|
|
@@ -333,26 +333,31 @@ cleanup() {
|
|
|
333
333
|
}
|
|
334
334
|
trap cleanup EXIT INT TERM
|
|
335
335
|
|
|
336
|
-
# 매 루프마다
|
|
336
|
+
# 매 루프마다 mode 동적 감지 — 시작 후에도 모드 전환 가능
|
|
337
337
|
tput civis 2>/dev/null
|
|
338
338
|
clear
|
|
339
339
|
|
|
340
340
|
CURRENT_MODE=""
|
|
341
341
|
|
|
342
342
|
while true; do
|
|
343
|
-
# 동적 모드 감지 (--team 지정 시 항상
|
|
343
|
+
# 동적 모드 감지 (--team 지정 시 항상 team, 그 외 progress.json.mode 참조)
|
|
344
344
|
if [ -n "$TEAM_FILTER" ] || [ -f "$QUEUE" ]; then
|
|
345
|
-
NEW_MODE="
|
|
345
|
+
NEW_MODE="team"
|
|
346
346
|
else
|
|
347
|
-
|
|
347
|
+
if [ -f "$PROGRESS" ] && command -v jq &>/dev/null; then
|
|
348
|
+
NEW_MODE=$(jq -r '.mode // "solo"' "$PROGRESS" 2>/dev/null)
|
|
349
|
+
[ "$NEW_MODE" = "team" ] || NEW_MODE="solo"
|
|
350
|
+
else
|
|
351
|
+
NEW_MODE="solo"
|
|
352
|
+
fi
|
|
348
353
|
fi
|
|
349
354
|
|
|
350
355
|
# 모드 전환 시 화면 초기화
|
|
351
356
|
if [ "$NEW_MODE" != "$CURRENT_MODE" ]; then
|
|
352
357
|
clear
|
|
353
358
|
CURRENT_MODE="$NEW_MODE"
|
|
354
|
-
if [ "$CURRENT_MODE" = "
|
|
355
|
-
#
|
|
359
|
+
if [ "$CURRENT_MODE" = "solo" ]; then
|
|
360
|
+
# solo 초기화
|
|
356
361
|
if [ -f "$PROGRESS" ]; then
|
|
357
362
|
LAST_AGENT=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
358
363
|
LAST_STATUS=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
|
|
@@ -362,7 +367,7 @@ while true; do
|
|
|
362
367
|
fi
|
|
363
368
|
fi
|
|
364
369
|
|
|
365
|
-
if [ "$CURRENT_MODE" = "
|
|
370
|
+
if [ "$CURRENT_MODE" = "team" ]; then
|
|
366
371
|
buf=$(render_v4 2>&1)
|
|
367
372
|
tput cup 0 0 2>/dev/null
|
|
368
373
|
echo "$buf"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# harness-prompt-history.sh — Prompt History pane for
|
|
2
|
+
# harness-prompt-history.sh — Prompt History pane for tmux layout
|
|
3
3
|
# progress.log + audit.log에서 유저 프롬프트/에이전트 활동 히스토리를 표시
|
|
4
4
|
# Usage: bash scripts/harness-prompt-history.sh [project-root]
|
|
5
5
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 새 세션 시작 시 자동으로:
|
|
4
4
|
# 1) 이전 에이전트가 completed이면 harness-next.sh 실행 (게이트 + handoff)
|
|
5
5
|
# 2) Planner/Dispatcher 사이클이면 audit 리셋
|
|
6
|
-
# 3)
|
|
6
|
+
# 3) 모드별 안내 출력
|
|
7
7
|
|
|
8
8
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
9
|
LIB="$SCRIPT_DIR/lib/harness-render-progress.sh"
|
|
@@ -25,22 +25,33 @@ sprint_num=$(jq -r '.sprint.number // 0' "$PROGRESS" 2>/dev/null)
|
|
|
25
25
|
current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
26
26
|
next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
27
27
|
agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
|
|
28
|
+
mode=$(jq -r '.mode // "solo"' "$PROGRESS" 2>/dev/null)
|
|
28
29
|
|
|
29
30
|
# ─────────────────────────────────────────
|
|
30
|
-
#
|
|
31
|
+
# Team Mode — 팀이 자율 실행 중이면 오케스트레이터 안내
|
|
31
32
|
# ─────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
passed
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
if [ "$mode" = "team" ]; then
|
|
34
|
+
FEATURE_QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
|
|
35
|
+
passed=0; total=0; in_prog=0; failed=0
|
|
36
|
+
if [ -f "$FEATURE_QUEUE" ]; then
|
|
37
|
+
passed=$(jq '.queue.passed | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
|
|
38
|
+
total=$(jq '[.queue.ready, (.queue.blocked | keys), (.queue.in_progress | keys), .queue.passed, .queue.failed] | flatten | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
|
|
39
|
+
in_prog=$(jq '.queue.in_progress | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
|
|
40
|
+
failed=$(jq '.queue.failed | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
echo "# Harness Team Mode active"
|
|
40
44
|
echo "# Queue: ${passed}/${total} passed, ${in_prog} in progress, ${failed} failed"
|
|
41
|
-
echo "# Teams
|
|
42
|
-
echo "#
|
|
43
|
-
|
|
45
|
+
echo "# Teams run autonomously (Gen-Eval loop, max 5 retries)."
|
|
46
|
+
echo "# Stop: /harness-stop | Switch to solo: /harness-solo"
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# ─────────────────────────────────────────
|
|
51
|
+
# Paused Mode — 중단 상태 안내
|
|
52
|
+
# ─────────────────────────────────────────
|
|
53
|
+
if [ "$mode" = "paused" ]; then
|
|
54
|
+
echo "# Harness paused — resume with /harness-team or /harness-solo"
|
|
44
55
|
exit 0
|
|
45
56
|
fi
|
|
46
57
|
|