@walwal-harness/cli 6.0.4 → 6.0.5

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,346 +0,0 @@
1
- #!/bin/bash
2
- # harness-render-progress.sh — Feature-level 프로그래스 렌더링 (공통 함수)
3
- # source로 포함하여 사용: source "$(dirname "$0")/lib/harness-render-progress.sh"
4
-
5
- # ─────────────────────────────────────────
6
- # Dependencies: jq
7
- # ─────────────────────────────────────────
8
- check_jq() {
9
- if ! command -v jq &>/dev/null; then
10
- echo "[harness] ERROR: jq is required. Install with: brew install jq" >&2
11
- return 1
12
- fi
13
- }
14
-
15
- # ─────────────────────────────────────────
16
- # resolve_harness_root — .harness/ 디렉토리 찾기
17
- # ─────────────────────────────────────────
18
- resolve_harness_root() {
19
- local dir="${1:-.}"
20
- dir="$(cd "$dir" && pwd)"
21
- while [ "$dir" != "/" ]; do
22
- if [ -d "$dir/.harness" ]; then
23
- echo "$dir"
24
- return 0
25
- fi
26
- dir="$(dirname "$dir")"
27
- done
28
- return 1
29
- }
30
-
31
- # ─────────────────────────────────────────
32
- # render_progress — Feature-level 프로그래스 출력
33
- #
34
- # Args:
35
- # $1 — project root (optional, defaults to auto-detect)
36
- #
37
- # Reads:
38
- # .harness/progress.json
39
- # .harness/config.json
40
- # .harness/actions/feature-list.json (if exists)
41
- # ─────────────────────────────────────────
42
- render_progress() {
43
- check_jq || return 1
44
-
45
- local PROJECT_ROOT
46
- PROJECT_ROOT="$(resolve_harness_root "${1:-.}")" || {
47
- echo "[harness] .harness/ directory not found" >&2
48
- return 1
49
- }
50
-
51
- local PROGRESS="$PROJECT_ROOT/.harness/progress.json"
52
- local CONFIG="$PROJECT_ROOT/.harness/config.json"
53
- local FEATURES="$PROJECT_ROOT/.harness/actions/feature-list.json"
54
-
55
- if [ ! -f "$PROGRESS" ]; then
56
- return 1
57
- fi
58
-
59
- # Read progress.json
60
- local pipeline sprint_num sprint_status retry_count
61
- local current_agent agent_status next_agent
62
- local failure_agent failure_location failure_message
63
-
64
- pipeline=$(jq -r '.pipeline // "unknown"' "$PROGRESS")
65
- sprint_num=$(jq -r '.sprint.number // 0' "$PROGRESS")
66
- sprint_status=$(jq -r '.sprint.status // "init"' "$PROGRESS")
67
- retry_count=$(jq -r '.sprint.retry_count // 0' "$PROGRESS")
68
- current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS")
69
- agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS")
70
- next_agent=$(jq -r '.next_agent // "none"' "$PROGRESS")
71
- failure_agent=$(jq -r '.failure.agent // empty' "$PROGRESS")
72
- failure_location=$(jq -r '.failure.location // empty' "$PROGRESS")
73
- failure_message=$(jq -r '.failure.message // empty' "$PROGRESS")
74
-
75
- # Read max retries from config
76
- local max_retries=10
77
- if [ -f "$CONFIG" ]; then
78
- max_retries=$(jq -r '.flow.max_retries_per_sprint // 10' "$CONFIG")
79
- fi
80
-
81
- # Pipeline agents for progress bar
82
- local -a pipeline_agents
83
- if [ -f "$CONFIG" ] && [ "$pipeline" != "null" ] && [ "$pipeline" != "unknown" ]; then
84
- mapfile -t pipeline_agents < <(jq -r ".flow.pipeline_selection.pipelines[\"${pipeline}\"][]" "$CONFIG" 2>/dev/null | sed 's/:.*//')
85
- fi
86
-
87
- # ── Header ──
88
- local header="Sprint ${sprint_num} / ${pipeline}"
89
- local pad_len=$(( 40 - ${#header} ))
90
- local padding=""
91
- for ((i=0; i<pad_len; i++)); do padding+="═"; done
92
- echo ""
93
- echo " ═══ ${header} ${padding}"
94
- echo ""
95
-
96
- # ── Feature Progress ──
97
- if [ -f "$FEATURES" ]; then
98
- local total_features completed_features=0 in_progress_features=0
99
- total_features=$(jq '.features | length' "$FEATURES" 2>/dev/null || echo 0)
100
-
101
- if [ "$total_features" -gt 0 ]; then
102
- # Determine columns based on pipeline
103
- local show_be=true show_fe=true
104
- case "$pipeline" in
105
- BE-ONLY) show_fe=false ;;
106
- FE-ONLY) show_be=false ;;
107
- esac
108
-
109
- # Build feature lines
110
- local i=0
111
- while [ $i -lt "$total_features" ]; do
112
- local fid fname passes be_pass fe_pass eval_func_pass eval_visual_pass
113
- fid=$(jq -r ".features[$i].id // \"F-$((i+1))\"" "$FEATURES")
114
- fname=$(jq -r ".features[$i].name // .features[$i].title // .features[$i].description // \"Feature $((i+1))\"" "$FEATURES")
115
- passes=$(jq -r ".features[$i].passes // [] | .[]" "$FEATURES" 2>/dev/null)
116
-
117
- # Truncate name to 20 chars
118
- if [ ${#fname} -gt 20 ]; then
119
- fname="${fname:0:18}.."
120
- fi
121
-
122
- # Check passes
123
- be_pass="○"; fe_pass="○"; eval_cq_pass="○"; eval_func_pass="○"; eval_visual_pass="○"
124
- local eval_done=false
125
-
126
- while IFS= read -r p; do
127
- case "$p" in
128
- generator-backend) be_pass="✓" ;;
129
- generator-frontend) fe_pass="✓" ;;
130
- evaluator-code-quality) eval_cq_pass="✓" ;;
131
- evaluator-functional) eval_func_pass="✓" ;;
132
- evaluator-visual) eval_visual_pass="✓" ;;
133
- esac
134
- done <<< "$passes"
135
-
136
- # Eval column: chain 내 존재하는 평가자 모두 PASS 여야 ✓
137
- local eval_pass="○"
138
- local eval_passed_count=0
139
- local eval_total=3
140
- [ "$eval_cq_pass" = "✓" ] && eval_passed_count=$((eval_passed_count + 1))
141
- [ "$eval_func_pass" = "✓" ] && eval_passed_count=$((eval_passed_count + 1))
142
- [ "$eval_visual_pass" = "✓" ] && eval_passed_count=$((eval_passed_count + 1))
143
- if [ "$eval_passed_count" -eq "$eval_total" ]; then
144
- eval_pass="✓"
145
- elif [ "$eval_passed_count" -gt 0 ]; then
146
- eval_pass="◐"
147
- fi
148
-
149
- # Feature status icon
150
- local icon="○"
151
- local is_complete=false
152
- if [ "$show_be" = true ] && [ "$show_fe" = true ]; then
153
- # FULLSTACK: all must pass
154
- if [ "$be_pass" = "✓" ] && [ "$fe_pass" = "✓" ] && [ "$eval_pass" = "✓" ]; then
155
- icon="✓"; is_complete=true
156
- elif [ "$be_pass" = "✓" ] || [ "$fe_pass" = "✓" ]; then
157
- icon="◐"
158
- fi
159
- elif [ "$show_be" = true ]; then
160
- # BE-ONLY
161
- if [ "$be_pass" = "✓" ] && [ "$eval_pass" = "✓" ]; then
162
- icon="✓"; is_complete=true
163
- elif [ "$be_pass" = "✓" ]; then
164
- icon="◐"
165
- fi
166
- else
167
- # FE-ONLY
168
- if [ "$fe_pass" = "✓" ] && [ "$eval_pass" = "✓" ]; then
169
- icon="✓"; is_complete=true
170
- elif [ "$fe_pass" = "✓" ]; then
171
- icon="◐"
172
- fi
173
- fi
174
-
175
- if [ "$is_complete" = true ]; then
176
- completed_features=$((completed_features + 1))
177
- fi
178
-
179
- # Build columns
180
- local cols=""
181
- if [ "$show_be" = true ]; then cols+="BE${be_pass} "; fi
182
- if [ "$show_fe" = true ]; then cols+="FE${fe_pass} "; fi
183
- cols+="Eval${eval_pass}"
184
-
185
- printf " %s %-5s %-22s %s\n" "$icon" "$fid" "$fname" "$cols"
186
-
187
- i=$((i + 1))
188
- done
189
-
190
- echo ""
191
-
192
- # ── Progress Bar ──
193
- local steps_per_feature=0
194
- if [ "$show_be" = true ]; then steps_per_feature=$((steps_per_feature + 1)); fi
195
- if [ "$show_fe" = true ]; then steps_per_feature=$((steps_per_feature + 1)); fi
196
- steps_per_feature=$((steps_per_feature + 1)) # eval
197
-
198
- local total_steps=$((total_features * steps_per_feature))
199
- local done_steps=0
200
-
201
- # Count individual passes across all features
202
- i=0
203
- while [ $i -lt "$total_features" ]; do
204
- passes=$(jq -r ".features[$i].passes // [] | .[]" "$FEATURES" 2>/dev/null)
205
- while IFS= read -r p; do
206
- case "$p" in
207
- generator-backend)
208
- if [ "$show_be" = true ]; then done_steps=$((done_steps + 1)); fi
209
- ;;
210
- generator-frontend)
211
- if [ "$show_fe" = true ]; then done_steps=$((done_steps + 1)); fi
212
- ;;
213
- evaluator-code-quality|evaluator-functional|evaluator-visual)
214
- # Chain 내 평가자. 아래에서 통합 집계.
215
- ;;
216
- esac
217
- done <<< "$passes"
218
-
219
- # Check eval completion — 체인상의 3개 평가자 모두 통과 시에만 1 step 인정
220
- local ecq ef ev
221
- ecq=$(jq -r ".features[$i].passes // [] | map(select(. == \"evaluator-code-quality\")) | length" "$FEATURES" 2>/dev/null)
222
- ef=$(jq -r ".features[$i].passes // [] | map(select(. == \"evaluator-functional\")) | length" "$FEATURES" 2>/dev/null)
223
- ev=$(jq -r ".features[$i].passes // [] | map(select(. == \"evaluator-visual\")) | length" "$FEATURES" 2>/dev/null)
224
- if [ "${ecq:-0}" -gt 0 ] && [ "${ef:-0}" -gt 0 ] && [ "${ev:-0}" -gt 0 ]; then
225
- done_steps=$((done_steps + 1))
226
- fi
227
-
228
- i=$((i + 1))
229
- done
230
-
231
- local pct=0
232
- if [ "$total_steps" -gt 0 ]; then
233
- pct=$(( done_steps * 100 / total_steps ))
234
- fi
235
-
236
- local bar_width=16
237
- local filled=$(( pct * bar_width / 100 ))
238
- local empty=$(( bar_width - filled ))
239
- local bar=""
240
- for ((j=0; j<filled; j++)); do bar+="█"; done
241
- for ((j=0; j<empty; j++)); do bar+="░"; done
242
-
243
- echo " Features: ${completed_features}/${total_features} completed"
244
- echo " Pipeline: ${bar} ${pct}%"
245
- fi
246
- else
247
- echo " (feature-list.json not yet created — run Planner first)"
248
- fi
249
-
250
- echo ""
251
-
252
- # ── Current Agent Status ──
253
- if [ "$current_agent" != "none" ] && [ "$current_agent" != "null" ]; then
254
- echo " Current: ${current_agent} [${agent_status}] (retry ${retry_count}/${max_retries})"
255
- fi
256
-
257
- # ── Failure Info ──
258
- if [ -n "$failure_agent" ] && [ "$failure_agent" != "null" ]; then
259
- echo ""
260
- echo " ⚠ FAIL: ${failure_agent} → ${failure_location}"
261
- if [ -n "$failure_message" ] && [ "$failure_message" != "null" ]; then
262
- echo " ${failure_message}"
263
- fi
264
- fi
265
-
266
- # ── Blocked ──
267
- if [ "$agent_status" = "blocked" ]; then
268
- echo ""
269
- echo " ✖ BLOCKED: retry limit (${max_retries}) reached — user intervention required"
270
- fi
271
-
272
- # ── Next Action ──
273
- if [ "$next_agent" != "none" ] && [ "$next_agent" != "null" ] && [ "$agent_status" != "blocked" ]; then
274
- # Read model & thinking mode from config
275
- local next_model="opus"
276
- local next_thinking="null"
277
- if [ -f "$CONFIG" ]; then
278
- next_model=$(jq -r ".agents[\"${next_agent}\"].model // \"opus\"" "$CONFIG" 2>/dev/null || echo "opus")
279
- next_thinking=$(jq -r ".agents[\"${next_agent}\"].thinking_mode // \"null\"" "$CONFIG" 2>/dev/null || echo "null")
280
- fi
281
-
282
- local mode_str=""
283
- if [ "$next_thinking" != "null" ]; then
284
- mode_str=" [/${next_thinking}]"
285
- fi
286
-
287
- echo ""
288
- echo " Next → /harness-${next_agent} (${next_model}${mode_str})"
289
- echo " Tip → 새 세션을 시작하면 자동으로 안내됩니다"
290
- fi
291
-
292
- echo ""
293
- }
294
-
295
- # ─────────────────────────────────────────
296
- # render_agent_bar — 에이전트 시퀀스 바 (compact)
297
- # ─────────────────────────────────────────
298
- render_agent_bar() {
299
- local PROGRESS="${1:-.}/.harness/progress.json"
300
- local CONFIG="${1:-.}/.harness/config.json"
301
- local PIPELINE_JSON="${1:-.}/.harness/actions/pipeline.json"
302
-
303
- if [ ! -f "$PROGRESS" ] || [ ! -f "$CONFIG" ]; then return 1; fi
304
-
305
- local pipeline current_agent fe_stack fe_target
306
- pipeline=$(jq -r '.pipeline // "unknown"' "$PROGRESS")
307
- current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS")
308
- fe_stack="react"
309
- fe_target="web"
310
- if [ -f "$PIPELINE_JSON" ]; then
311
- fe_stack=$(jq -r '.fe_stack // "react"' "$PIPELINE_JSON" 2>/dev/null || echo "react")
312
- fe_target=$(jq -r '.fe_target // empty' "$PIPELINE_JSON" 2>/dev/null || true)
313
- if [ -z "$fe_target" ]; then
314
- fe_target="web" # v5.6.5+: 치환 로직 제거. 기본 web.
315
- fi
316
- fi
317
-
318
- local completed_agents
319
- completed_agents=$(jq -r '.completed_agents // [] | .[]' "$PROGRESS")
320
-
321
- if [ "$pipeline" = "null" ] || [ "$pipeline" = "unknown" ]; then return 1; fi
322
-
323
- local agents_line=" Agents: "
324
- local first=true
325
-
326
- while IFS= read -r agent; do
327
- agent=$(echo "$agent" | sed 's/:.*//') # strip mode suffix like :light, :api-only
328
- # v5.6.5+: fe_stack 에이전트 치환 제거. 스택별 동작은 ref-docs 로 조절.
329
-
330
- if [ "$first" = true ]; then
331
- first=false
332
- else
333
- agents_line+=" → "
334
- fi
335
-
336
- if echo "$completed_agents" | grep -q "^${agent}$"; then
337
- agents_line+="${agent}✓"
338
- elif [ "$agent" = "$current_agent" ]; then
339
- agents_line+="[${agent}]"
340
- else
341
- agents_line+="${agent}"
342
- fi
343
- done < <(jq -r ".flow.pipeline_selection.pipelines[\"${pipeline}\"][]" "$CONFIG" 2>/dev/null)
344
-
345
- echo "$agents_line"
346
- }