@walwal-harness/cli 4.0.0 → 5.0.1

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.
@@ -1,17 +1,9 @@
1
1
  #!/bin/bash
2
- # harness-dashboard.sh — Control Center: Dashboard + Manual Prompt
2
+ # harness-dashboard.sh — Unified Dashboard (Solo + Team mode)
3
3
  #
4
- # 상단: 실시간 대시보드 (Sprint Map | Prompt History + Processing Status)
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
- # Render functions
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
- echo -e "${BOLD}╔══════════════════════════════════════════════════════╗${RESET}"
64
- echo -e "${BOLD}║ HARNESS CONTROL CENTER ║${RESET}"
65
- echo -e "${BOLD}╚══════════════════════════════════════════════════════╝${RESET}"
66
- echo -e " ${DIM}${project_name} | ${now}${RESET}"
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
- render_sprint_overview() {
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
- render_sprint_map_lines() {
104
+ render_solo_failure_info() {
98
105
  if [ ! -f "$PROGRESS" ]; then return; fi
99
106
 
100
- local sprint_keys
101
- sprint_keys=$(jq -r '.sprint_progress // {} | keys[]' "$PROGRESS" 2>/dev/null | sort -n)
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
- echo -e "${BOLD}Sprint Map${RESET}"
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
- while IFS= read -r s; do
107
- local status feat_count notes icon
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
- case "$status" in
113
- completed) icon="●" ;;
114
- in_progress) icon="◐" ;;
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
- if [ ${#notes} -gt 28 ]; then notes="${notes:0:26}.."; fi
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
- printf "%s S%-2s %2df %-11s %s\n" "$icon" "$s" "$feat_count" "$status" "$notes"
122
- done <<< "$sprint_keys"
135
+ render_agent_bar "$PROJECT_ROOT" 2>/dev/null
136
+ echo ""
123
137
  }
124
138
 
125
- render_prompt_history_lines() {
126
- echo -e "${BOLD}Prompt History${RESET}"
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 -12 | while IFS= read -r line; do
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
- user|manual) icon="★" ; color="$BOLD" ;;
158
+ team-*) icon="◆" ; color="$CYAN" ;;
159
+ user*) icon="★" ; color="$BOLD" ;;
146
160
  *) icon="·" ; color="$DIM" ;;
147
161
  esac
148
162
 
149
- if [ ${#detail} -gt 30 ]; then detail="${detail:0:28}.."; fi
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
- render_two_columns() {
162
- local term_width col_width
163
- term_width=$(get_term_width)
164
- col_width=$(( (term_width - 7) / 2 ))
165
- if [ "$col_width" -lt 30 ]; then col_width=30; fi
166
- if [ "$col_width" -gt 55 ]; then col_width=55; fi
167
-
168
- local left_lines=() right_lines=()
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
- echo -e " ${DIM}${sep}${RESET}"
203
- echo ""
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
- render_build_status() {
207
- if [ ! -f "$PROGRESS" ]; then return; fi
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
- local build_status tsc_status routes_total
210
- build_status=$(jq -r '.build.build_status // "unknown"' "$PROGRESS" 2>/dev/null)
211
- tsc_status=$(jq -r '.build.tsc_status // "unknown"' "$PROGRESS" 2>/dev/null)
212
- routes_total=$(jq -r '.build.routes_total // 0' "$PROGRESS" 2>/dev/null)
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
- if [ "$build_status" = "null" ] || [ "$build_status" = "unknown" ]; then return; fi
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
- local build_color="$GREEN"
217
- if [ "$build_status" != "passing" ]; then build_color="$RED"; fi
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
- render_failure_info() {
226
- if [ ! -f "$PROGRESS" ]; then return; fi
281
+ render_team_bottleneck() {
282
+ if [ ! -f "$QUEUE" ] || [ ! -f "$FEATURES" ]; then return; fi
227
283
 
228
- local failure_agent
229
- failure_agent=$(jq -r '.failure.agent // empty' "$PROGRESS" 2>/dev/null)
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
- if [ -n "$failure_agent" ] && [ "$failure_agent" != "null" ]; then
232
- local failure_loc failure_msg
233
- failure_loc=$(jq -r '.failure.location // ""' "$PROGRESS")
234
- failure_msg=$(jq -r '.failure.message // ""' "$PROGRESS" | strip_ansi | tr '\n' ' ' | sed 's/ */ /g')
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
- echo -e " ${RED}${BOLD}FAIL${RESET} ${RED}${failure_agent} → ${failure_loc}${RESET}"
238
- if [ -n "$failure_msg" ]; then
239
- echo -e " ${DIM}${failure_msg}${RESET}"
240
- fi
241
- echo ""
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
- render_agent_info() {
246
- # Next action hint
247
- if [ ! -f "$PROGRESS" ]; then return; fi
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
- local next_agent agent_status
250
- next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null)
251
- agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
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 [ "$next_agent" != "none" ] && [ "$next_agent" != "null" ] && [ "$agent_status" != "blocked" ]; then
254
- echo -e " ${CYAN}Next → ${next_agent}${RESET} ${DIM}(type 'next' to start)${RESET}"
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
- # Full dashboard render (in-place update)
320
+ # Render dispatch
264
321
  # ══════════════════════════════════════════
265
322
  render_dashboard() {
266
- local buf
267
- buf=$(
268
- render_header
269
- render_sprint_overview
270
- render_build_status
271
- render_failure_info
272
- render_two_columns
273
-
274
- if [ -f "$FEATURES" ]; then
275
- render_progress "$PROJECT_ROOT" 2>/dev/null
276
- fi
277
-
278
- render_agent_info
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
- tput civis 2>/dev/null
282
- tput cup 0 0 2>/dev/null
283
- echo "$buf"
284
- tput ed 2>/dev/null
285
- tput cnorm 2>/dev/null
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 dashboard loop
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
- # v3 모드: 단일 이벤트 스트림 (progress.json 변경 감지 + audit.log)
5
- # v4 모드: 팀별 섹션 분리 (feature-queue.json + progress.log 기반)
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
- # v4 모드: 팀별 섹션 렌더링
65
+ # Team 모드: 팀별 섹션 렌더링
66
66
  # ══════════════════════════════════════════
67
67
 
68
68
  render_v4_header() {
@@ -224,7 +224,7 @@ render_v4() {
224
224
  }
225
225
 
226
226
  # ══════════════════════════════════════════
227
- # v3 모드: 단일 스트림 (기존 동작)
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
- # 매 루프마다 v4/v3 동적 감지 — 시작 후에도 모드 전환 가능
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 지정 시 항상 v4)
343
+ # 동적 모드 감지 (--team 지정 시 항상 team, 그 외 progress.json.mode 참조)
344
344
  if [ -n "$TEAM_FILTER" ] || [ -f "$QUEUE" ]; then
345
- NEW_MODE="v4"
345
+ NEW_MODE="team"
346
346
  else
347
- NEW_MODE="v3"
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" = "v3" ]; then
355
- # v3 초기화
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" = "v4" ]; then
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 v4 tmux layout
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
 
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash
2
- # harness-queue-manager.sh — Feature Queue Manager (v4.0)
2
+ # harness-queue-manager.sh — Feature Queue Manager (v5 unified)
3
3
  #
4
4
  # feature-list.json에서 depends_on 그래프를 읽어 topological sort 후
5
5
  # feature-queue.json을 생성/관리한다.
@@ -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
- # v4 Parallel Mode — feature-queue.json이 존재하면 v4 안내
31
+ # Team Mode — 팀이 자율 실행 중이면 오케스트레이터 안내
31
32
  # ─────────────────────────────────────────
32
- FEATURE_QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
33
- if [ -f "$FEATURE_QUEUE" ]; then
34
- passed=$(jq '.queue.passed | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
35
- total=$(jq '[.queue.ready, (.queue.blocked | keys), (.queue.in_progress | keys), .queue.passed, .queue.failed] | flatten | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
36
- in_prog=$(jq '.queue.in_progress | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
37
- failed=$(jq '.queue.failed | length' "$FEATURE_QUEUE" 2>/dev/null || echo 0)
38
-
39
- echo "# Harness v4 — Parallel Agent Teams active"
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 are running autonomously. You are the orchestrator."
42
- echo "# Role: Monitor dashboard, resolve failures, manual interventions only."
43
- echo "# Do NOT run /harness-generator-* or /harness-evaluator-* — Teams handle Gen+Eval."
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