@walwal-harness/cli 4.0.0-beta.6 → 4.0.0-beta.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "4.0.0-beta.6",
3
+ "version": "4.0.0-beta.7",
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"
@@ -1,6 +1,9 @@
1
1
  #!/bin/bash
2
- # harness-monitor.sh — Panel 2: Task & Agent Lifecycle 모니터링
3
- # audit.log 스트리밍 + progress.json 변경 감지
2
+ # harness-monitor.sh — Agent Lifecycle Monitor
3
+ #
4
+ # v3 모드: 단일 이벤트 스트림 (progress.json 변경 감지 + audit.log)
5
+ # v4 모드: 팀별 섹션 분리 (feature-queue.json + progress.log 기반)
6
+ #
4
7
  # Usage: bash scripts/harness-monitor.sh [project-root]
5
8
 
6
9
  set -uo pipefail
@@ -17,8 +20,9 @@ if [ -z "$PROJECT_ROOT" ]; then
17
20
  fi
18
21
 
19
22
  PROGRESS="$PROJECT_ROOT/.harness/progress.json"
23
+ PROGRESS_LOG="$PROJECT_ROOT/.harness/progress.log"
20
24
  AUDIT_LOG="$PROJECT_ROOT/.harness/actions/audit.log"
21
- HANDOFF="$PROJECT_ROOT/.harness/handoff.json"
25
+ QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
22
26
 
23
27
  # ── ANSI helpers ──
24
28
  BOLD="\033[1m"
@@ -28,28 +32,169 @@ YELLOW="\033[33m"
28
32
  RED="\033[31m"
29
33
  CYAN="\033[36m"
30
34
  MAGENTA="\033[35m"
35
+ BLUE="\033[34m"
31
36
  RESET="\033[0m"
32
37
 
38
+ # ── Team colors ──
39
+ T1_COLOR="$CYAN"
40
+ T2_COLOR="$MAGENTA"
41
+ T3_COLOR="$YELLOW"
42
+
43
+ team_color() {
44
+ case "$1" in
45
+ 1|team-1) echo "$T1_COLOR" ;;
46
+ 2|team-2) echo "$T2_COLOR" ;;
47
+ 3|team-3) echo "$T3_COLOR" ;;
48
+ *) echo "$DIM" ;;
49
+ esac
50
+ }
51
+
52
+ # ══════════════════════════════════════════
53
+ # v4 모드: 팀별 섹션 렌더링
54
+ # ══════════════════════════════════════════
55
+
56
+ render_v4_header() {
57
+ echo -e "${BOLD}TEAM MONITOR${RESET} ${DIM}$(date +%H:%M:%S)${RESET}"
58
+ echo ""
59
+ }
60
+
61
+ render_team_section() {
62
+ local team_num="$1"
63
+ local color
64
+ color=$(team_color "$team_num")
65
+
66
+ # 팀 상태 from queue
67
+ local t_status="idle" t_feature="—" t_phase="—" t_attempt=""
68
+ if [ -f "$QUEUE" ]; then
69
+ t_status=$(jq -r ".teams[\"$team_num\"].status // \"idle\"" "$QUEUE" 2>/dev/null)
70
+ t_feature=$(jq -r ".teams[\"$team_num\"].feature // \"—\"" "$QUEUE" 2>/dev/null)
71
+ if [ "$t_feature" != "—" ] && [ "$t_feature" != "null" ]; then
72
+ t_phase=$(jq -r --arg f "$t_feature" '.queue.in_progress[$f].phase // "?"' "$QUEUE" 2>/dev/null)
73
+ t_attempt=$(jq -r --arg f "$t_feature" '.queue.in_progress[$f].attempt // 1' "$QUEUE" 2>/dev/null)
74
+ fi
75
+ fi
76
+
77
+ # 상태 아이콘
78
+ local icon="○"
79
+ case "$t_status" in
80
+ busy) icon="▶" ;;
81
+ idle) icon="○" ;;
82
+ esac
83
+
84
+ # Phase 표시
85
+ local phase_str=""
86
+ case "$t_phase" in
87
+ gen) phase_str="Gen" ;;
88
+ gate) phase_str="Gate" ;;
89
+ eval) phase_str="Eval" ;;
90
+ *) phase_str="" ;;
91
+ esac
92
+
93
+ # 헤더 라인
94
+ printf "%b%b T%s%b " "$color" "$icon" "$team_num" "$RESET"
95
+ if [ "$t_feature" != "—" ] && [ "$t_feature" != "null" ]; then
96
+ printf "%s " "$t_feature"
97
+ if [ -n "$phase_str" ]; then
98
+ printf "%b%s%b" "$color" "$phase_str" "$RESET"
99
+ fi
100
+ if [ -n "$t_attempt" ] && [ "$t_attempt" != "—" ] && [ "$t_attempt" != "1" ]; then
101
+ printf " %b#%s%b" "$DIM" "$t_attempt" "$RESET"
102
+ fi
103
+ else
104
+ printf "%bidle%b" "$DIM" "$RESET"
105
+ fi
106
+ echo ""
107
+
108
+ # 팀 로그 (progress.log에서 team-N 엔트리만 추출)
109
+ if [ -f "$PROGRESS_LOG" ]; then
110
+ grep -i "team-${team_num}\|team_${team_num}" "$PROGRESS_LOG" 2>/dev/null | tail -5 | while IFS= read -r line; do
111
+ local ts action detail
112
+ ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
113
+ action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
114
+ detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
115
+
116
+ local short_ts
117
+ short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}' | tail -1 || echo "$ts")
118
+
119
+ if [ ${#detail} -gt 35 ]; then detail="${detail:0:33}.."; fi
120
+
121
+ # 액션별 아이콘
122
+ local a_icon="·" a_color="$DIM"
123
+ case "$action" in
124
+ gen) a_icon="▶"; a_color="$GREEN" ;;
125
+ eval) a_icon="✦"; a_color="$BLUE" ;;
126
+ pass) a_icon="✓"; a_color="$GREEN" ;;
127
+ fail) a_icon="✗"; a_color="$RED" ;;
128
+ dequeue) a_icon="→"; a_color="$CYAN" ;;
129
+ gate) a_icon="◆"; a_color="$YELLOW" ;;
130
+ *) a_icon="·"; a_color="$DIM" ;;
131
+ esac
132
+
133
+ printf " %b%s%b %b%s%b %b%s%b\n" \
134
+ "$DIM" "$short_ts" "$RESET" \
135
+ "$a_color" "$a_icon" "$RESET" \
136
+ "$DIM" "$detail" "$RESET"
137
+ done
138
+ fi
139
+ }
140
+
141
+ render_v4() {
142
+ render_v4_header
143
+
144
+ # 팀 수 확인
145
+ local team_count=3
146
+ if [ -f "$QUEUE" ]; then
147
+ team_count=$(jq '.teams | length' "$QUEUE" 2>/dev/null || echo 3)
148
+ fi
149
+
150
+ for i in $(seq 1 "$team_count"); do
151
+ render_team_section "$i"
152
+ echo ""
153
+ done
154
+
155
+ # Lead/시스템 이벤트 (team이 아닌 엔트리)
156
+ echo -e "${BOLD}SYSTEM${RESET}"
157
+ if [ -f "$PROGRESS_LOG" ]; then
158
+ grep -v 'team-[0-9]' "$PROGRESS_LOG" 2>/dev/null | grep -v '^#' | grep -v '^$' | tail -5 | while IFS= read -r line; do
159
+ local ts agent action detail
160
+ ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
161
+ agent=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
162
+ action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
163
+ detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
164
+
165
+ local short_ts
166
+ short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}' | tail -1 || echo "$ts")
167
+
168
+ if [ ${#detail} -gt 35 ]; then detail="${detail:0:33}.."; fi
169
+
170
+ printf " %b%s%b %s %b%s%b\n" \
171
+ "$DIM" "$short_ts" "$RESET" \
172
+ "$agent" \
173
+ "$DIM" "$detail" "$RESET"
174
+ done
175
+ fi
176
+ }
177
+
178
+ # ══════════════════════════════════════════
179
+ # v3 모드: 단일 스트림 (기존 동작)
180
+ # ══════════════════════════════════════════
181
+
33
182
  LAST_AGENT=""
34
183
  LAST_STATUS=""
35
184
  LAST_SPRINT=""
36
- EVENT_COUNT=0
37
185
 
38
186
  print_event() {
39
187
  local ts="$1" icon="$2" color="$3" msg="$4"
40
- EVENT_COUNT=$((EVENT_COUNT + 1))
41
188
  echo -e " ${DIM}${ts}${RESET} ${color}${icon}${RESET} ${msg}"
42
189
  }
43
190
 
44
- # ── Header ──
45
- print_header() {
191
+ print_v3_header() {
46
192
  echo -e "${BOLD}╔══════════════════════════════════════╗${RESET}"
47
193
  echo -e "${BOLD}║ AGENT LIFECYCLE MONITOR ║${RESET}"
48
194
  echo -e "${BOLD}╚══════════════════════════════════════╝${RESET}"
49
195
  echo ""
50
196
  }
51
197
 
52
- # ── Detect agent transitions from progress.json ──
53
198
  check_transitions() {
54
199
  if [ ! -f "$PROGRESS" ]; then return; fi
55
200
 
@@ -61,39 +206,22 @@ check_transitions() {
61
206
  local now
62
207
  now=$(date +"%H:%M:%S")
63
208
 
64
- # Sprint change
65
209
  if [ "$sprint" != "$LAST_SPRINT" ] && [ -n "$LAST_SPRINT" ]; then
66
210
  print_event "$now" ">>>" "$MAGENTA" "${BOLD}Sprint ${sprint} started${RESET}"
67
211
  echo ""
68
212
  fi
69
213
 
70
- # Agent change
71
214
  if [ "$agent" != "$LAST_AGENT" ] && [ -n "$LAST_AGENT" ]; then
72
215
  if [ "$LAST_AGENT" != "none" ] && [ "$LAST_AGENT" != "null" ]; then
73
216
  print_event "$now" " ✓ " "$GREEN" "${LAST_AGENT} → ${BOLD}done${RESET}"
74
217
  fi
75
218
  if [ "$agent" != "none" ] && [ "$agent" != "null" ]; then
76
219
  print_event "$now" " ▶ " "$CYAN" "${BOLD}${agent}${RESET} started"
77
-
78
- # Show handoff context if available
79
- if [ -f "$HANDOFF" ]; then
80
- local from focus
81
- from=$(jq -r '.from // empty' "$HANDOFF" 2>/dev/null)
82
- focus=$(jq -r '.focus_features // [] | join(", ")' "$HANDOFF" 2>/dev/null)
83
- if [ -n "$from" ] && [ "$from" != "null" ]; then
84
- echo -e " ${DIM}handoff from: ${from}${RESET}"
85
- fi
86
- if [ -n "$focus" ] && [ "$focus" != "null" ]; then
87
- echo -e " ${DIM}focus: ${focus}${RESET}"
88
- fi
89
- fi
90
220
  fi
91
221
  fi
92
222
 
93
- # Status change (same agent)
94
223
  if [ "$agent" = "$LAST_AGENT" ] && [ "$status" != "$LAST_STATUS" ] && [ -n "$LAST_STATUS" ]; then
95
- local status_color="$RESET"
96
- local status_icon="•"
224
+ local status_color="$RESET" status_icon="•"
97
225
  case "$status" in
98
226
  running) status_color="$GREEN"; status_icon=" ▶ " ;;
99
227
  completed) status_color="$CYAN"; status_icon=" ✓ " ;;
@@ -103,7 +231,6 @@ check_transitions() {
103
231
  esac
104
232
  print_event "$now" "$status_icon" "$status_color" "${agent} → ${BOLD}${status}${RESET}"
105
233
 
106
- # Show failure detail
107
234
  if [ "$status" = "failed" ]; then
108
235
  local fail_msg
109
236
  fail_msg=$(jq -r '.failure.message // empty' "$PROGRESS" 2>/dev/null)
@@ -118,76 +245,84 @@ check_transitions() {
118
245
  LAST_SPRINT="$sprint"
119
246
  }
120
247
 
121
- # ── Stream audit.log new lines ──
122
248
  stream_audit() {
123
249
  if [ ! -f "$AUDIT_LOG" ]; then return; fi
124
-
125
- # Use tail -f in background, parse each new line
126
250
  tail -n 0 -f "$AUDIT_LOG" 2>/dev/null | while IFS= read -r line; do
127
- # Skip comments/headers
128
251
  if [[ "$line" == "#"* ]] || [[ -z "$line" ]]; then continue; fi
129
-
130
- # Parse: TIMESTAMP | AGENT | ACTION | STATUS | TARGET | DETAIL
131
- local ts agent action status target detail
252
+ local ts agent action status target
132
253
  ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
133
254
  agent=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
134
255
  action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
135
256
  status=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
136
257
  target=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$5); print $5}')
137
- detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$6); print $6}')
138
258
 
139
- # Color by status
140
259
  local color="$RESET" icon="•"
141
260
  case "$status" in
142
- start) color="$CYAN"; icon="▶" ;;
143
- complete) color="$GREEN"; icon="✓" ;;
144
- fail) color="$RED"; icon="✗" ;;
145
- pass) color="$GREEN"; icon="✓" ;;
146
- skip) color="$DIM"; icon="–" ;;
261
+ start) color="$CYAN"; icon="▶" ;;
262
+ complete) color="$GREEN"; icon="✓" ;;
263
+ fail) color="$RED"; icon="✗" ;;
264
+ pass) color="$GREEN"; icon="✓" ;;
265
+ skip) color="$DIM"; icon="–" ;;
147
266
  esac
148
267
 
149
- # Short timestamp (HH:MM:SS from ISO)
150
268
  local short_ts
151
269
  short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}:[0-9]{2}' || echo "$ts")
152
-
153
- echo -e " ${DIM}${short_ts}${RESET} ${color}${icon}${RESET} ${BOLD}${agent}${RESET} ${action} ${target} ${DIM}${detail}${RESET}"
270
+ echo -e " ${DIM}${short_ts}${RESET} ${color}${icon}${RESET} ${BOLD}${agent}${RESET} ${action} ${target}"
154
271
  done &
155
272
  AUDIT_TAIL_PID=$!
156
273
  }
157
274
 
158
- # ── Cleanup ──
275
+ # ══════════════════════════════════════════
276
+ # Mode detection & main loop
277
+ # ══════════════════════════════════════════
278
+
159
279
  cleanup() {
160
280
  if [ -n "${AUDIT_TAIL_PID:-}" ]; then
161
281
  kill "$AUDIT_TAIL_PID" 2>/dev/null || true
162
282
  fi
283
+ tput cnorm 2>/dev/null
163
284
  exit 0
164
285
  }
165
286
  trap cleanup EXIT INT TERM
166
287
 
167
- # ── Main ──
168
- print_header
169
-
170
- # Initialize state
171
- if [ -f "$PROGRESS" ]; then
172
- LAST_AGENT=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
173
- LAST_STATUS=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
174
- LAST_SPRINT=$(jq -r '.sprint.number // 0' "$PROGRESS" 2>/dev/null)
288
+ # v4 감지: feature-queue.json 존재 여부
289
+ IS_V4=false
290
+ if [ -f "$QUEUE" ]; then
291
+ IS_V4=true
175
292
  fi
176
293
 
177
- # Show existing history
178
- if [ -f "$PROGRESS" ]; then
179
- echo -e " ${BOLD}History${RESET}"
180
- jq -r '.history // [] | .[] | " \(.timestamp) \(.agent) — \(.action): \(.detail)"' "$PROGRESS" 2>/dev/null
181
- echo ""
182
- echo -e " ${DIM}── Live events below ──${RESET}"
183
- echo ""
184
- fi
294
+ if [ "$IS_V4" = true ]; then
295
+ # ── v4: 팀별 섹션, auto-refresh ──
296
+ tput civis 2>/dev/null
297
+ clear
298
+
299
+ while true; do
300
+ buf=$(render_v4 2>&1)
301
+ tput cup 0 0 2>/dev/null
302
+ echo "$buf"
303
+ tput ed 2>/dev/null
304
+ sleep 3
305
+ done
306
+ else
307
+ # ── v3: 단일 스트림, audit tail ──
308
+ print_v3_header
185
309
 
186
- # Start audit log streaming
187
- stream_audit
310
+ if [ -f "$PROGRESS" ]; then
311
+ LAST_AGENT=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
312
+ LAST_STATUS=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
313
+ LAST_SPRINT=$(jq -r '.sprint.number // 0' "$PROGRESS" 2>/dev/null)
188
314
 
189
- # Poll progress.json for transitions
190
- while true; do
191
- check_transitions
192
- sleep 2
193
- done
315
+ echo -e " ${BOLD}History${RESET}"
316
+ jq -r '.history // [] | .[] | " \(.timestamp) \(.agent) — \(.action): \(.detail)"' "$PROGRESS" 2>/dev/null
317
+ echo ""
318
+ echo -e " ${DIM}── Live events below ──${RESET}"
319
+ echo ""
320
+ fi
321
+
322
+ stream_audit
323
+
324
+ while true; do
325
+ check_transitions
326
+ sleep 2
327
+ done
328
+ fi