@walwal-harness/cli 5.0.3 → 5.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/bin/init.js +26 -3
- package/package.json +1 -1
- package/scripts/harness-dashboard.sh +42 -2
- package/scripts/harness-prompt-history.sh +112 -66
package/bin/init.js
CHANGED
|
@@ -524,16 +524,39 @@ function installAgentTeamsEnv() {
|
|
|
524
524
|
try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch (e) {}
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
|
|
527
|
+
let changed = false;
|
|
528
528
|
|
|
529
|
+
// Enable Agent Teams env var
|
|
530
|
+
if (!settings.env) settings.env = {};
|
|
529
531
|
if (settings.env['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] !== '1') {
|
|
530
532
|
settings.env['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] = '1';
|
|
531
|
-
|
|
532
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
533
|
+
changed = true;
|
|
533
534
|
log('Agent Teams enabled (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1)');
|
|
534
535
|
} else {
|
|
535
536
|
log('Agent Teams already enabled');
|
|
536
537
|
}
|
|
538
|
+
|
|
539
|
+
// Add git worktree permission for Team Mode isolation
|
|
540
|
+
if (!settings.permissions) settings.permissions = {};
|
|
541
|
+
if (!settings.permissions.allow) settings.permissions.allow = [];
|
|
542
|
+
const worktreePerms = [
|
|
543
|
+
'Bash(git worktree *)',
|
|
544
|
+
'Bash(git checkout *)',
|
|
545
|
+
'Bash(git merge *)',
|
|
546
|
+
'Bash(git branch *)'
|
|
547
|
+
];
|
|
548
|
+
for (const perm of worktreePerms) {
|
|
549
|
+
if (!settings.permissions.allow.includes(perm)) {
|
|
550
|
+
settings.permissions.allow.push(perm);
|
|
551
|
+
changed = true;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (changed) {
|
|
556
|
+
ensureDir(path.dirname(settingsPath));
|
|
557
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
558
|
+
log('Git worktree permissions added for Team Mode isolation');
|
|
559
|
+
}
|
|
537
560
|
}
|
|
538
561
|
|
|
539
562
|
function setupAgentsMd() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.5",
|
|
4
4
|
"description": "Production harness for AI agent engineering — Solo/Team mode, Planner, Generator(BE/FE), Evaluator(Func/Visual), optional Brainstormer. Supports React, Next.js, and Flutter FE stacks.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
|
@@ -316,6 +316,46 @@ render_team_bottleneck() {
|
|
|
316
316
|
fi
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
# ══════════════════════════════════════════
|
|
320
|
+
# Archive Prompt — 처리 완료된 프롬프트 목록
|
|
321
|
+
# ══════════════════════════════════════════
|
|
322
|
+
render_archive_prompts() {
|
|
323
|
+
if [ ! -f "$PROGRESS_LOG" ]; then return; fi
|
|
324
|
+
|
|
325
|
+
# user-prompt 엔트리 중 이후에 result/pass/completed 액션이 있는 것 = 처리 완료
|
|
326
|
+
local all_prompts
|
|
327
|
+
all_prompts=$(grep '| user-prompt |' "$PROGRESS_LOG" 2>/dev/null)
|
|
328
|
+
if [ -z "$all_prompts" ]; then return; fi
|
|
329
|
+
|
|
330
|
+
local total_prompts
|
|
331
|
+
total_prompts=$(echo "$all_prompts" | wc -l | tr -d ' ')
|
|
332
|
+
|
|
333
|
+
# 마지막 프롬프트를 제외한 모든 프롬프트 = archived (처리 완료)
|
|
334
|
+
local archived
|
|
335
|
+
if [ "$total_prompts" -le 1 ]; then return; fi
|
|
336
|
+
archived=$(echo "$all_prompts" | head -n $((total_prompts - 1)) | tail -8)
|
|
337
|
+
|
|
338
|
+
echo ""
|
|
339
|
+
echo -e "${BOLD}Archive Prompt${RESET} ${DIM}(처리 완료)${RESET}"
|
|
340
|
+
|
|
341
|
+
echo "$archived" | while IFS= read -r line; do
|
|
342
|
+
local ts detail
|
|
343
|
+
ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
|
|
344
|
+
detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
345
|
+
|
|
346
|
+
local short_ts
|
|
347
|
+
short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}' | tail -1 || echo "$ts")
|
|
348
|
+
|
|
349
|
+
if [ ${#detail} -gt 45 ]; then detail="${detail:0:43}.."; fi
|
|
350
|
+
|
|
351
|
+
echo -e " ${GREEN}✓${RESET} ${DIM}${short_ts}${RESET} ${detail}"
|
|
352
|
+
done
|
|
353
|
+
|
|
354
|
+
if [ "$total_prompts" -gt 9 ]; then
|
|
355
|
+
echo -e " ${DIM}… +$((total_prompts - 9)) more${RESET}"
|
|
356
|
+
fi
|
|
357
|
+
}
|
|
358
|
+
|
|
319
359
|
# ══════════════════════════════════════════
|
|
320
360
|
# Render dispatch
|
|
321
361
|
# ══════════════════════════════════════════
|
|
@@ -331,6 +371,7 @@ render_dashboard() {
|
|
|
331
371
|
render_team_status
|
|
332
372
|
render_team_features
|
|
333
373
|
render_team_bottleneck
|
|
374
|
+
render_archive_prompts
|
|
334
375
|
;;
|
|
335
376
|
*)
|
|
336
377
|
render_solo_sprint_overview
|
|
@@ -341,8 +382,7 @@ render_dashboard() {
|
|
|
341
382
|
fi
|
|
342
383
|
|
|
343
384
|
render_solo_agent_info
|
|
344
|
-
|
|
345
|
-
render_solo_prompt_history
|
|
385
|
+
render_archive_prompts
|
|
346
386
|
;;
|
|
347
387
|
esac
|
|
348
388
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# harness-prompt-history.sh — Prompt History pane
|
|
3
|
-
#
|
|
2
|
+
# harness-prompt-history.sh — User Prompt History pane
|
|
3
|
+
# 사용자가 입력한 프롬프트만 표시 + 현재 처리 단계(Planner/Team) 표시
|
|
4
4
|
# Usage: bash scripts/harness-prompt-history.sh [project-root]
|
|
5
5
|
|
|
6
6
|
set -uo pipefail
|
|
@@ -14,7 +14,8 @@ if [ -z "$PROJECT_ROOT" ]; then
|
|
|
14
14
|
fi
|
|
15
15
|
|
|
16
16
|
PROGRESS_LOG="$PROJECT_ROOT/.harness/progress.log"
|
|
17
|
-
|
|
17
|
+
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
18
|
+
QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
|
|
18
19
|
|
|
19
20
|
BOLD="\033[1m"
|
|
20
21
|
DIM="\033[2m"
|
|
@@ -23,93 +24,138 @@ YELLOW="\033[33m"
|
|
|
23
24
|
RED="\033[31m"
|
|
24
25
|
CYAN="\033[36m"
|
|
25
26
|
MAGENTA="\033[35m"
|
|
27
|
+
WHITE="\033[37m"
|
|
26
28
|
RESET="\033[0m"
|
|
29
|
+
BG_BLUE="\033[44m"
|
|
30
|
+
BG_GREEN="\033[42m"
|
|
31
|
+
BG_YELLOW="\033[43m"
|
|
27
32
|
|
|
28
33
|
render_header() {
|
|
29
|
-
echo -e "${BOLD}
|
|
34
|
+
echo -e "${BOLD}USER PROMPTS${RESET} ${DIM}$(date +%H:%M:%S)${RESET}"
|
|
30
35
|
echo ""
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
# 현재 처리 상태 배지
|
|
39
|
+
render_processing_status() {
|
|
40
|
+
local mode="solo" current_agent="none" agent_status="pending"
|
|
41
|
+
|
|
42
|
+
if [ -f "$PROGRESS" ] && command -v jq &>/dev/null; then
|
|
43
|
+
mode=$(jq -r '.mode // "solo"' "$PROGRESS" 2>/dev/null)
|
|
44
|
+
current_agent=$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null)
|
|
45
|
+
agent_status=$(jq -r '.agent_status // "pending"' "$PROGRESS" 2>/dev/null)
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
local badge=""
|
|
49
|
+
case "$mode" in
|
|
50
|
+
team)
|
|
51
|
+
# Team 모드: 각 팀의 현재 상태 표시
|
|
52
|
+
badge="${BG_BLUE}${WHITE}${BOLD} TEAM MODE ${RESET}"
|
|
53
|
+
if [ -f "$QUEUE" ]; then
|
|
54
|
+
local team_info=""
|
|
55
|
+
for i in 1 2 3; do
|
|
56
|
+
local t_status t_feature t_phase
|
|
57
|
+
t_status=$(jq -r ".teams[\"$i\"].status // \"idle\"" "$QUEUE" 2>/dev/null)
|
|
58
|
+
t_feature=$(jq -r ".teams[\"$i\"].feature // \"\"" "$QUEUE" 2>/dev/null)
|
|
59
|
+
if [ "$t_status" = "busy" ] && [ -n "$t_feature" ] && [ "$t_feature" != "null" ]; then
|
|
60
|
+
t_phase=$(jq -r --arg f "$t_feature" '.queue.in_progress[$f].phase // "?"' "$QUEUE" 2>/dev/null)
|
|
61
|
+
team_info+=" ${CYAN}T${i}${RESET}:${t_feature}(${t_phase})"
|
|
62
|
+
else
|
|
63
|
+
team_info+=" ${DIM}T${i}:idle${RESET}"
|
|
64
|
+
fi
|
|
65
|
+
done
|
|
66
|
+
echo -e " ${badge}${team_info}"
|
|
67
|
+
else
|
|
68
|
+
echo -e " ${badge}"
|
|
69
|
+
fi
|
|
70
|
+
;;
|
|
71
|
+
paused)
|
|
72
|
+
echo -e " ${BG_YELLOW}${WHITE}${BOLD} PAUSED ${RESET} ${DIM}/harness-team 또는 /harness-solo${RESET}"
|
|
73
|
+
;;
|
|
74
|
+
*)
|
|
75
|
+
# Solo 모드: 현재 에이전트 표시
|
|
76
|
+
if [ "$current_agent" != "none" ] && [ "$current_agent" != "null" ]; then
|
|
77
|
+
local agent_color="$RESET"
|
|
78
|
+
case "$current_agent" in
|
|
79
|
+
planner*) agent_color="$YELLOW" ;;
|
|
80
|
+
generator*) agent_color="$GREEN" ;;
|
|
81
|
+
eval*) agent_color="$RED" ;;
|
|
82
|
+
dispatcher*) agent_color="$MAGENTA" ;;
|
|
83
|
+
esac
|
|
84
|
+
local status_icon="·"
|
|
85
|
+
case "$agent_status" in
|
|
86
|
+
running) status_icon="▶" ;;
|
|
87
|
+
completed) status_icon="✓" ;;
|
|
88
|
+
failed) status_icon="✗" ;;
|
|
89
|
+
esac
|
|
90
|
+
echo -e " ${BG_GREEN}${WHITE}${BOLD} SOLO ${RESET} ${agent_color}${status_icon} ${current_agent}${RESET}"
|
|
91
|
+
else
|
|
92
|
+
echo -e " ${BG_GREEN}${WHITE}${BOLD} SOLO ${RESET} ${DIM}대기 중${RESET}"
|
|
93
|
+
fi
|
|
94
|
+
;;
|
|
95
|
+
esac
|
|
96
|
+
echo ""
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# 사용자 프롬프트만 필터하여 표시
|
|
100
|
+
render_user_prompts() {
|
|
34
101
|
if [ ! -f "$PROGRESS_LOG" ]; then
|
|
35
|
-
echo -e " ${DIM}(
|
|
102
|
+
echo -e " ${DIM}(프롬프트 기록 없음)${RESET}"
|
|
36
103
|
return
|
|
37
104
|
fi
|
|
38
105
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
|
|
44
|
-
detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
106
|
+
local term_height
|
|
107
|
+
term_height=$(tput lines 2>/dev/null || echo 30)
|
|
108
|
+
local max_lines=$((term_height - 10))
|
|
109
|
+
if [ "$max_lines" -lt 5 ]; then max_lines=5; fi
|
|
45
110
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
case "$agent" in
|
|
50
|
-
dispatcher*) icon="▸" ; color="$MAGENTA" ;;
|
|
51
|
-
brainstormer) icon="◇" ; color="$CYAN" ;;
|
|
52
|
-
planner*) icon="□" ; color="$YELLOW" ;;
|
|
53
|
-
generator*) icon="▶" ; color="$GREEN" ;;
|
|
54
|
-
eval*) icon="✦" ; color="$RED" ;;
|
|
55
|
-
user|manual) icon="★" ; color="$BOLD" ;;
|
|
56
|
-
team*) icon="⊕" ; color="$CYAN" ;;
|
|
57
|
-
*) icon="·" ; color="$DIM" ;;
|
|
58
|
-
esac
|
|
59
|
-
|
|
60
|
-
if [ ${#detail} -gt 40 ]; then detail="${detail:0:38}.."; fi
|
|
61
|
-
|
|
62
|
-
printf " %b%b%b %b%-8s%b %b%s%b %b%s%b\n" \
|
|
63
|
-
"$color" "$icon" "$RESET" \
|
|
64
|
-
"$DIM" "$short_ts" "$RESET" \
|
|
65
|
-
"$RESET" "$agent" "$RESET" \
|
|
66
|
-
"$DIM" "$action" "$RESET"
|
|
67
|
-
if [ -n "$detail" ] && [ "$detail" != " " ]; then
|
|
68
|
-
echo -e " ${DIM}${detail}${RESET}"
|
|
69
|
-
fi
|
|
70
|
-
done
|
|
71
|
-
}
|
|
111
|
+
# user-prompt 행만 필터
|
|
112
|
+
local prompts
|
|
113
|
+
prompts=$(grep '| user-prompt |' "$PROGRESS_LOG" 2>/dev/null | tail -"$max_lines")
|
|
72
114
|
|
|
73
|
-
|
|
74
|
-
|
|
115
|
+
if [ -z "$prompts" ]; then
|
|
116
|
+
echo -e " ${DIM}(사용자 프롬프트 없음)${RESET}"
|
|
117
|
+
return
|
|
118
|
+
fi
|
|
75
119
|
|
|
76
|
-
|
|
77
|
-
|
|
120
|
+
local line_num=0
|
|
121
|
+
local total_prompts
|
|
122
|
+
total_prompts=$(grep -c '| user-prompt |' "$PROGRESS_LOG" 2>/dev/null || echo 0)
|
|
123
|
+
|
|
124
|
+
echo "$prompts" | while IFS= read -r line; do
|
|
125
|
+
line_num=$((line_num + 1))
|
|
78
126
|
|
|
79
|
-
|
|
80
|
-
local ts agent action status target
|
|
127
|
+
local ts detail
|
|
81
128
|
ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
|
|
82
|
-
|
|
83
|
-
action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
|
|
84
|
-
status=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
85
|
-
target=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$5); print $5}')
|
|
86
|
-
|
|
87
|
-
local color="$RESET" icon="·"
|
|
88
|
-
case "$status" in
|
|
89
|
-
start) color="$CYAN"; icon="▶" ;;
|
|
90
|
-
complete) color="$GREEN"; icon="✓" ;;
|
|
91
|
-
fail) color="$RED"; icon="✗" ;;
|
|
92
|
-
pass) color="$GREEN"; icon="✓" ;;
|
|
93
|
-
skip) color="$DIM"; icon="–" ;;
|
|
94
|
-
esac
|
|
129
|
+
detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
95
130
|
|
|
96
131
|
local short_ts
|
|
97
|
-
short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}
|
|
132
|
+
short_ts=$(echo "$ts" | grep -oE '[0-9]{2}:[0-9]{2}' | tail -1 || echo "$ts")
|
|
133
|
+
|
|
134
|
+
# 프롬프트 내용 truncate
|
|
135
|
+
local max_width
|
|
136
|
+
max_width=$(( $(tput cols 2>/dev/null || echo 40) - 12 ))
|
|
137
|
+
if [ "$max_width" -lt 20 ]; then max_width=20; fi
|
|
138
|
+
if [ ${#detail} -gt "$max_width" ]; then
|
|
139
|
+
detail="${detail:0:$((max_width - 2))}.."
|
|
140
|
+
fi
|
|
98
141
|
|
|
99
|
-
|
|
142
|
+
# 처리 상태 찾기: 이 프롬프트 이후의 첫 번째 에이전트 액션
|
|
143
|
+
local processed_by=""
|
|
144
|
+
local prompt_ts_epoch
|
|
145
|
+
prompt_ts_epoch=$(echo "$ts" | sed 's/[^0-9:]//g')
|
|
100
146
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"$DIM" "$short_ts" "$RESET" \
|
|
104
|
-
"$agent" \
|
|
105
|
-
"$DIM" "$target" "$RESET"
|
|
147
|
+
# 간단히: 마지막 프롬프트인지 확인 → 현재 처리 중 표시
|
|
148
|
+
echo -e " ${DIM}${short_ts}${RESET} ${WHITE}${detail}${RESET}"
|
|
106
149
|
done
|
|
150
|
+
|
|
151
|
+
echo ""
|
|
152
|
+
echo -e " ${DIM}── ${total_prompts} prompts total ──${RESET}"
|
|
107
153
|
}
|
|
108
154
|
|
|
109
155
|
render_all() {
|
|
110
156
|
render_header
|
|
111
|
-
|
|
112
|
-
|
|
157
|
+
render_processing_status
|
|
158
|
+
render_user_prompts
|
|
113
159
|
}
|
|
114
160
|
|
|
115
161
|
# ── Main loop ──
|