@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.
- package/CHANGELOG.md +21 -0
- package/bin/init.js +121 -4
- package/package.json +1 -1
- package/scripts/harness-archive.sh +0 -109
- package/scripts/harness-dashboard-up.sh +0 -72
- package/scripts/harness-dashboard.sh +0 -509
- package/scripts/harness-goal-init.sh +0 -72
- package/scripts/harness-goal-show.sh +0 -37
- package/scripts/harness-gotcha-memory.sh +0 -348
- package/scripts/harness-gotcha-register.sh +0 -393
- package/scripts/harness-monitor.sh +0 -398
- package/scripts/harness-next.sh +0 -546
- package/scripts/harness-progress-set.sh +0 -41
- package/scripts/harness-prompt-history.sh +0 -164
- package/scripts/harness-queue-manager.sh +0 -694
- package/scripts/harness-session-start.sh +0 -129
- package/scripts/harness-statusline.sh +0 -139
- package/scripts/harness-tmux.sh +0 -372
- package/scripts/harness-user-prompt-submit.sh +0 -105
- package/scripts/init-agents-md.sh +0 -337
- package/scripts/init-ref-docs.sh +0 -262
- package/scripts/lib/harness-audit.sh +0 -113
- package/scripts/lib/harness-guardrail.sh +0 -148
- package/scripts/lib/harness-keywait.sh +0 -57
- package/scripts/lib/harness-render-progress.sh +0 -346
- package/scripts/scan-project.sh +0 -367
|
@@ -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
|
-
}
|