@walwal-harness/cli 3.7.4 → 4.0.0-alpha.10
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/assets/templates/config.json +11 -0
- package/bin/init.js +34 -2
- package/package.json +1 -1
- package/scripts/harness-control-v4.sh +97 -0
- package/scripts/harness-dashboard-v4.sh +243 -0
- package/scripts/harness-queue-manager.sh +367 -0
- package/scripts/harness-session-start.sh +18 -0
- package/scripts/harness-studio-v4.sh +122 -0
- package/scripts/harness-team-worker.sh +415 -0
- package/scripts/harness-user-prompt-submit.sh +31 -1
- package/skills/evaluator-functional/SKILL.md +26 -0
- package/skills/generator-frontend/SKILL.md +17 -0
|
@@ -209,6 +209,17 @@
|
|
|
209
209
|
"frontend_cwd": "",
|
|
210
210
|
"timeout_seconds": 120,
|
|
211
211
|
"on_fail": "reroute_to_generator"
|
|
212
|
+
},
|
|
213
|
+
"parallel": {
|
|
214
|
+
"comment": "v4 Parallel Agent Teams 설정. npx walwal-harness v4 로 실행.",
|
|
215
|
+
"enabled": false,
|
|
216
|
+
"concurrency": 3,
|
|
217
|
+
"max_attempts_per_feature": 3,
|
|
218
|
+
"gen_model": "sonnet",
|
|
219
|
+
"eval_model": "opus",
|
|
220
|
+
"branch_strategy": "feature-branch",
|
|
221
|
+
"merge_on_pass": true,
|
|
222
|
+
"rebase_on_conflict": true
|
|
212
223
|
}
|
|
213
224
|
},
|
|
214
225
|
"evaluation": {
|
package/bin/init.js
CHANGED
|
@@ -226,6 +226,11 @@ function installScripts() {
|
|
|
226
226
|
'harness-eval-watcher.sh',
|
|
227
227
|
'harness-tmux.sh',
|
|
228
228
|
'harness-control.sh',
|
|
229
|
+
'harness-studio-v4.sh',
|
|
230
|
+
'harness-dashboard-v4.sh',
|
|
231
|
+
'harness-control-v4.sh',
|
|
232
|
+
'harness-queue-manager.sh',
|
|
233
|
+
'harness-team-worker.sh',
|
|
229
234
|
]);
|
|
230
235
|
|
|
231
236
|
if (fs.existsSync(scriptsSrc)) {
|
|
@@ -568,8 +573,9 @@ function showHelp() {
|
|
|
568
573
|
Usage:
|
|
569
574
|
npx walwal-harness Initialize project for harness engineering
|
|
570
575
|
npx walwal-harness --force Re-initialize (overwrites existing files)
|
|
571
|
-
npx walwal-harness studio Launch Harness Studio (tmux
|
|
572
|
-
npx walwal-harness studio --ai Studio + AI eval summary
|
|
576
|
+
npx walwal-harness studio Launch Harness Studio v3 (tmux 5-pane)
|
|
577
|
+
npx walwal-harness studio --ai Studio v3 + AI eval summary
|
|
578
|
+
npx walwal-harness v4 Launch Studio v4 (3 Parallel Agent Teams)
|
|
573
579
|
npx walwal-harness --help Show this help
|
|
574
580
|
|
|
575
581
|
What it does:
|
|
@@ -619,6 +625,27 @@ function runStudio() {
|
|
|
619
625
|
execSync(cmd, { stdio: 'inherit' });
|
|
620
626
|
}
|
|
621
627
|
|
|
628
|
+
function runStudioV4() {
|
|
629
|
+
const scriptsDir = path.join(PKG_ROOT, 'scripts');
|
|
630
|
+
const tmuxScript = path.join(scriptsDir, 'harness-studio-v4.sh');
|
|
631
|
+
|
|
632
|
+
if (!fs.existsSync(tmuxScript)) {
|
|
633
|
+
log('ERROR: harness-studio-v4.sh not found. Update @walwal-harness/cli to >= 4.0.0');
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
try {
|
|
638
|
+
execSync('which tmux', { stdio: 'ignore' });
|
|
639
|
+
} catch {
|
|
640
|
+
log('ERROR: tmux is required. Install with: brew install tmux');
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const cmd = `bash "${tmuxScript}" "${PROJECT_ROOT}"`.trim();
|
|
645
|
+
log('Launching Harness Studio v4 (Parallel Agent Teams)...');
|
|
646
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
647
|
+
}
|
|
648
|
+
|
|
622
649
|
function main() {
|
|
623
650
|
if (isHelp) {
|
|
624
651
|
showHelp();
|
|
@@ -630,6 +657,11 @@ function main() {
|
|
|
630
657
|
return;
|
|
631
658
|
}
|
|
632
659
|
|
|
660
|
+
if (subcommand === 'studio-v4' || subcommand === 'v4') {
|
|
661
|
+
runStudioV4();
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
|
|
633
665
|
const pkg = require(path.join(PKG_ROOT, 'package.json'));
|
|
634
666
|
console.log('');
|
|
635
667
|
console.log('╔══════════════════════════════════════╗');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-alpha.10",
|
|
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"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-control-v4.sh — v4 Control Center
|
|
3
|
+
#
|
|
4
|
+
# Commands:
|
|
5
|
+
# init Initialize feature queue
|
|
6
|
+
# start Launch all idle team workers
|
|
7
|
+
# pause <team> Pause team worker
|
|
8
|
+
# resume <team> Resume team worker
|
|
9
|
+
# assign <fid> <t> Force-assign feature to team
|
|
10
|
+
# requeue <fid> Move failed feature back to ready
|
|
11
|
+
# concurrency <N> Change parallel team count
|
|
12
|
+
# status / s Show queue status
|
|
13
|
+
# log <message> Add manual note
|
|
14
|
+
# help / h Show help
|
|
15
|
+
# quit / q Exit
|
|
16
|
+
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
20
|
+
|
|
21
|
+
PROJECT_ROOT="${1:-}"
|
|
22
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
|
23
|
+
source "$SCRIPT_DIR/lib/harness-render-progress.sh"
|
|
24
|
+
PROJECT_ROOT="$(resolve_harness_root ".")" || { echo "[control] .harness/ not found."; exit 1; }
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
|
|
28
|
+
PROGRESS_LOG="$PROJECT_ROOT/.harness/progress.log"
|
|
29
|
+
QUEUE_MGR="$SCRIPT_DIR/harness-queue-manager.sh"
|
|
30
|
+
|
|
31
|
+
BOLD="\033[1m"
|
|
32
|
+
DIM="\033[2m"
|
|
33
|
+
GREEN="\033[32m"
|
|
34
|
+
YELLOW="\033[33m"
|
|
35
|
+
RED="\033[31m"
|
|
36
|
+
CYAN="\033[36m"
|
|
37
|
+
RESET="\033[0m"
|
|
38
|
+
|
|
39
|
+
cmd_init() {
|
|
40
|
+
bash "$QUEUE_MGR" init "$PROJECT_ROOT"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
cmd_status() {
|
|
44
|
+
bash "$QUEUE_MGR" status "$PROJECT_ROOT"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
cmd_requeue() {
|
|
48
|
+
local fid="${1:-}"
|
|
49
|
+
if [ -z "$fid" ]; then echo -e " ${RED}Usage: requeue <feature_id>${RESET}"; return; fi
|
|
50
|
+
bash "$QUEUE_MGR" requeue "$fid" "$PROJECT_ROOT"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
cmd_log() {
|
|
54
|
+
local msg="$1"
|
|
55
|
+
if [ -z "$msg" ]; then echo -e " ${RED}Usage: log <message>${RESET}"; return; fi
|
|
56
|
+
local ts
|
|
57
|
+
ts=$(date +"%Y-%m-%d")
|
|
58
|
+
echo "${ts} | manual | note | ${msg}" >> "$PROGRESS_LOG"
|
|
59
|
+
echo -e " ${GREEN}Logged:${RESET} ${msg}"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
show_help() {
|
|
63
|
+
echo ""
|
|
64
|
+
echo -e " ${BOLD}Harness v4 Control${RESET}"
|
|
65
|
+
echo -e " ${CYAN}init${RESET} Initialize feature queue from feature-list.json"
|
|
66
|
+
echo -e " ${CYAN}status${RESET} / ${CYAN}s${RESET} Show queue + team status"
|
|
67
|
+
echo -e " ${CYAN}requeue${RESET} <fid> Move failed feature back to ready"
|
|
68
|
+
echo -e " ${CYAN}log${RESET} <message> Add manual note to progress.log"
|
|
69
|
+
echo -e " ${CYAN}help${RESET} / ${CYAN}h${RESET} Show this help"
|
|
70
|
+
echo -e " ${CYAN}quit${RESET} / ${CYAN}q${RESET} Exit control"
|
|
71
|
+
echo ""
|
|
72
|
+
echo -e " ${DIM}Teams auto-start when studio launches.${RESET}"
|
|
73
|
+
echo -e " ${DIM}Workers auto-dequeue from the ready queue.${RESET}"
|
|
74
|
+
echo ""
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ── Main ──
|
|
78
|
+
echo ""
|
|
79
|
+
echo -e " ${BOLD}Harness v4 Control${RESET} ${DIM}(type 'help' for commands)${RESET}"
|
|
80
|
+
echo ""
|
|
81
|
+
|
|
82
|
+
while true; do
|
|
83
|
+
echo -ne " ${BOLD}v4>${RESET} "
|
|
84
|
+
read -r input || exit 0
|
|
85
|
+
input=$(echo "$input" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
86
|
+
|
|
87
|
+
case "$input" in
|
|
88
|
+
init) cmd_init ;;
|
|
89
|
+
status|s) cmd_status ;;
|
|
90
|
+
requeue\ *) cmd_requeue "${input#requeue }" ;;
|
|
91
|
+
log\ *) cmd_log "${input#log }" ;;
|
|
92
|
+
help|h) show_help ;;
|
|
93
|
+
quit|q) echo -e " ${DIM}Goodbye.${RESET}"; exit 0 ;;
|
|
94
|
+
"") ;; # empty
|
|
95
|
+
*) echo -e " ${DIM}Unknown command. Type 'help'.${RESET}" ;;
|
|
96
|
+
esac
|
|
97
|
+
done
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-dashboard-v4.sh — v4 Dashboard: Feature Queue + Team Status
|
|
3
|
+
# Auto-refresh 3초 간격. feature-queue.json + feature-list.json 시각화.
|
|
4
|
+
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
source "$SCRIPT_DIR/lib/harness-render-progress.sh"
|
|
9
|
+
|
|
10
|
+
PROJECT_ROOT="${1:-}"
|
|
11
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
|
12
|
+
PROJECT_ROOT="$(resolve_harness_root ".")" || { echo "[dash] .harness/ not found."; exit 1; }
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
|
|
16
|
+
FEATURES="$PROJECT_ROOT/.harness/actions/feature-list.json"
|
|
17
|
+
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
18
|
+
|
|
19
|
+
BOLD="\033[1m"
|
|
20
|
+
DIM="\033[2m"
|
|
21
|
+
GREEN="\033[32m"
|
|
22
|
+
YELLOW="\033[33m"
|
|
23
|
+
RED="\033[31m"
|
|
24
|
+
CYAN="\033[36m"
|
|
25
|
+
MAGENTA="\033[35m"
|
|
26
|
+
RESET="\033[0m"
|
|
27
|
+
|
|
28
|
+
render_header() {
|
|
29
|
+
local now project_name
|
|
30
|
+
now=$(date +"%H:%M:%S")
|
|
31
|
+
project_name=$(jq -r '.project_name // "Unknown"' "$PROGRESS" 2>/dev/null)
|
|
32
|
+
|
|
33
|
+
echo -e "${BOLD}╔════════════════════════════════════════════════╗${RESET}"
|
|
34
|
+
echo -e "${BOLD}║ HARNESS v4 — Parallel Agent Teams ║${RESET}"
|
|
35
|
+
echo -e "${BOLD}╚════════════════════════════════════════════════╝${RESET}"
|
|
36
|
+
echo -e " ${DIM}${project_name} | ${now}${RESET}"
|
|
37
|
+
echo ""
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
render_queue_summary() {
|
|
41
|
+
if [ ! -f "$QUEUE" ]; then
|
|
42
|
+
echo -e " ${DIM}(queue not initialized — run 'init' in Control)${RESET}"
|
|
43
|
+
return
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
local ready blocked in_prog passed failed total concurrency
|
|
47
|
+
ready=$(jq '.queue.ready | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
48
|
+
blocked=$(jq '.queue.blocked | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
49
|
+
in_prog=$(jq '.queue.in_progress | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
50
|
+
passed=$(jq '.queue.passed | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
51
|
+
failed=$(jq '.queue.failed | length' "$QUEUE" 2>/dev/null || echo 0)
|
|
52
|
+
concurrency=$(jq '.concurrency // 3' "$QUEUE" 2>/dev/null || echo 3)
|
|
53
|
+
ready=${ready:-0}; blocked=${blocked:-0}; in_prog=${in_prog:-0}; passed=${passed:-0}; failed=${failed:-0}
|
|
54
|
+
total=$((ready + blocked + in_prog + passed + failed))
|
|
55
|
+
|
|
56
|
+
# Progress bar
|
|
57
|
+
local pct=0
|
|
58
|
+
if [ "$total" -gt 0 ]; then pct=$(( passed * 100 / total )); fi
|
|
59
|
+
local bar_w=20
|
|
60
|
+
local filled=$(( pct * bar_w / 100 ))
|
|
61
|
+
local empty=$(( bar_w - filled ))
|
|
62
|
+
local bar=""
|
|
63
|
+
for ((i=0; i<filled; i++)); do bar+="█"; done
|
|
64
|
+
for ((i=0; i<empty; i++)); do bar+="░"; done
|
|
65
|
+
|
|
66
|
+
echo -e " ${BOLD}Queue${RESET} ${bar} ${passed}/${total} (${pct}%) ${DIM}concurrency=${concurrency}${RESET}"
|
|
67
|
+
echo -e " Ready:${GREEN}${ready}${RESET} Blocked:${YELLOW}${blocked}${RESET} Progress:${CYAN}${in_prog}${RESET} Pass:${GREEN}${passed}${RESET} Fail:${RED}${failed}${RESET}"
|
|
68
|
+
echo ""
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
render_teams() {
|
|
72
|
+
if [ ! -f "$QUEUE" ]; then return; fi
|
|
73
|
+
|
|
74
|
+
local team_count
|
|
75
|
+
team_count=$(jq '.teams | length' "$QUEUE" 2>/dev/null)
|
|
76
|
+
if [ "${team_count:-0}" -eq 0 ]; then return; fi
|
|
77
|
+
|
|
78
|
+
echo -e " ${BOLD}Teams${RESET}"
|
|
79
|
+
|
|
80
|
+
for i in $(seq 1 "$team_count"); do
|
|
81
|
+
local t_status t_feature t_phase t_attempt
|
|
82
|
+
t_status=$(jq -r ".teams[\"$i\"].status // \"idle\"" "$QUEUE" 2>/dev/null)
|
|
83
|
+
t_feature=$(jq -r ".teams[\"$i\"].feature // \"—\"" "$QUEUE" 2>/dev/null)
|
|
84
|
+
|
|
85
|
+
# Get phase from in_progress
|
|
86
|
+
if [ "$t_feature" != "—" ] && [ "$t_feature" != "null" ]; then
|
|
87
|
+
t_phase=$(jq -r --arg f "$t_feature" '.queue.in_progress[$f].phase // "?"' "$QUEUE" 2>/dev/null)
|
|
88
|
+
t_attempt=$(jq -r --arg f "$t_feature" '.queue.in_progress[$f].attempt // 1' "$QUEUE" 2>/dev/null)
|
|
89
|
+
else
|
|
90
|
+
t_phase="—"
|
|
91
|
+
t_attempt="—"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
local icon color
|
|
95
|
+
case "$t_status" in
|
|
96
|
+
busy) icon="▶" ; color="$GREEN" ;;
|
|
97
|
+
idle) icon="○" ; color="$DIM" ;;
|
|
98
|
+
paused) icon="⏸" ; color="$YELLOW" ;;
|
|
99
|
+
*) icon="?" ; color="$RESET" ;;
|
|
100
|
+
esac
|
|
101
|
+
|
|
102
|
+
local phase_display=""
|
|
103
|
+
case "$t_phase" in
|
|
104
|
+
gen) phase_display="${CYAN}GEN${RESET}" ;;
|
|
105
|
+
gate) phase_display="${YELLOW}GATE${RESET}" ;;
|
|
106
|
+
eval) phase_display="${MAGENTA}EVAL${RESET}" ;;
|
|
107
|
+
*) phase_display="${DIM}${t_phase}${RESET}" ;;
|
|
108
|
+
esac
|
|
109
|
+
|
|
110
|
+
printf " %b %b Team %d %-8s %b attempt %s\n" "$color" "$icon" "$i" "$t_feature" "$phase_display" "$t_attempt"
|
|
111
|
+
done
|
|
112
|
+
echo ""
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
render_feature_list() {
|
|
116
|
+
if [ ! -f "$QUEUE" ] || [ ! -f "$FEATURES" ]; then return; fi
|
|
117
|
+
|
|
118
|
+
echo -e " ${BOLD}Features${RESET}"
|
|
119
|
+
|
|
120
|
+
# Single jq call: merge feature-list + queue state → pre-formatted lines
|
|
121
|
+
jq -r --slurpfile q "$QUEUE" '
|
|
122
|
+
($q[0].queue.passed // []) as $passed |
|
|
123
|
+
($q[0].queue.failed // []) as $failed |
|
|
124
|
+
($q[0].queue.ready // []) as $ready |
|
|
125
|
+
($q[0].queue.in_progress // {}) as $prog |
|
|
126
|
+
.features[] |
|
|
127
|
+
.id as $fid |
|
|
128
|
+
(.name // .description // "?" | if length > 22 then .[0:20] + ".." else . end) as $fname |
|
|
129
|
+
(if ($fid | IN($passed[])) then "P"
|
|
130
|
+
elif $prog[$fid] then "I|\($prog[$fid].team)|\($prog[$fid].phase)"
|
|
131
|
+
elif ($fid | IN($failed[])) then "F"
|
|
132
|
+
elif ($fid | IN($ready[])) then "R"
|
|
133
|
+
else "B" end) as $st |
|
|
134
|
+
"\($st)\t\($fid)\t\($fname)"
|
|
135
|
+
' "$FEATURES" 2>/dev/null | while IFS=$'\t' read -r st fid fname; do
|
|
136
|
+
case "$st" in
|
|
137
|
+
P) printf " ${GREEN}●${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
138
|
+
F) printf " ${RED}✗${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
139
|
+
R) printf " ${YELLOW}○${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
140
|
+
B) printf " ${DIM}◌${RESET} %-6s %s\n" "$fid" "$fname" ;;
|
|
141
|
+
I\|*) # in_progress: extract team and phase
|
|
142
|
+
team=$(echo "$st" | cut -d'|' -f2)
|
|
143
|
+
phase=$(echo "$st" | cut -d'|' -f3)
|
|
144
|
+
printf " ${CYAN}◐${RESET} %-6s %-18s T%s:%s\n" "$fid" "$fname" "$team" "$phase" ;;
|
|
145
|
+
*) printf " ? %-6s %s\n" "$fid" "$fname" ;;
|
|
146
|
+
esac
|
|
147
|
+
done
|
|
148
|
+
|
|
149
|
+
echo ""
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
render_user_prompts() {
|
|
153
|
+
local log_file="$PROJECT_ROOT/.harness/progress.log"
|
|
154
|
+
if [ ! -f "$log_file" ]; then return; fi
|
|
155
|
+
|
|
156
|
+
# Extract user-prompt entries (newest first, max 5)
|
|
157
|
+
local user_lines
|
|
158
|
+
user_lines=$(grep 'user-prompt' "$log_file" 2>/dev/null | tail -r 2>/dev/null | head -5)
|
|
159
|
+
|
|
160
|
+
if [ -z "$user_lines" ]; then return; fi
|
|
161
|
+
|
|
162
|
+
echo -e " ${BOLD}Manual Prompts${RESET} ${DIM}(newest first)${RESET}"
|
|
163
|
+
|
|
164
|
+
echo "$user_lines" | while IFS= read -r line; do
|
|
165
|
+
local ts detail
|
|
166
|
+
ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
|
|
167
|
+
detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
168
|
+
|
|
169
|
+
# Short timestamp (HH:MM or MM-DD HH:MM)
|
|
170
|
+
local short_ts
|
|
171
|
+
short_ts=$(echo "$ts" | sed 's/^[0-9]*-//')
|
|
172
|
+
|
|
173
|
+
if [ ${#detail} -gt 55 ]; then detail="${detail:0:53}.."; fi
|
|
174
|
+
|
|
175
|
+
echo -e " ${BOLD}★${RESET} ${DIM}${short_ts}${RESET} ${detail}"
|
|
176
|
+
done
|
|
177
|
+
|
|
178
|
+
echo ""
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
render_team_activity() {
|
|
182
|
+
local log_file="$PROJECT_ROOT/.harness/progress.log"
|
|
183
|
+
if [ ! -f "$log_file" ]; then return; fi
|
|
184
|
+
|
|
185
|
+
local max_lines=8
|
|
186
|
+
|
|
187
|
+
echo -e " ${BOLD}Activity${RESET} ${DIM}(newest first)${RESET}"
|
|
188
|
+
|
|
189
|
+
# All non-user-prompt entries, newest first
|
|
190
|
+
grep -v '^#' "$log_file" 2>/dev/null | grep -v '^$' | grep -v 'user-prompt' | \
|
|
191
|
+
tail -r 2>/dev/null | head -"$max_lines" | \
|
|
192
|
+
while IFS= read -r line; do
|
|
193
|
+
local ts agent action detail
|
|
194
|
+
ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
|
|
195
|
+
agent=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
|
|
196
|
+
action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
|
|
197
|
+
detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
|
|
198
|
+
|
|
199
|
+
local short_ts icon color
|
|
200
|
+
short_ts=$(echo "$ts" | sed 's/^[0-9]*-//')
|
|
201
|
+
|
|
202
|
+
case "$agent" in
|
|
203
|
+
dispatcher*) icon="▸"; color="$MAGENTA" ;;
|
|
204
|
+
team-*) icon="⚡"; color="$CYAN" ;;
|
|
205
|
+
manual) icon="★"; color="$BOLD" ;;
|
|
206
|
+
planner*) icon="□"; color="$YELLOW" ;;
|
|
207
|
+
gen*) icon="▶"; color="$GREEN" ;;
|
|
208
|
+
eval*) icon="✦"; color="$RED" ;;
|
|
209
|
+
system) icon="⚙"; color="$DIM" ;;
|
|
210
|
+
*) icon="·"; color="$DIM" ;;
|
|
211
|
+
esac
|
|
212
|
+
|
|
213
|
+
if [ ${#detail} -gt 45 ]; then detail="${detail:0:43}.."; fi
|
|
214
|
+
|
|
215
|
+
echo -e " ${color}${icon}${RESET} ${DIM}${short_ts}${RESET} ${agent} ${DIM}${action}${RESET} ${detail}"
|
|
216
|
+
done
|
|
217
|
+
|
|
218
|
+
echo ""
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
render_all() {
|
|
222
|
+
render_header
|
|
223
|
+
render_queue_summary
|
|
224
|
+
render_teams
|
|
225
|
+
render_user_prompts
|
|
226
|
+
render_team_activity
|
|
227
|
+
render_feature_list
|
|
228
|
+
echo -e " ${DIM}Refreshing every 3s${RESET}"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# ── Main loop ──
|
|
232
|
+
tput civis 2>/dev/null
|
|
233
|
+
trap 'tput cnorm 2>/dev/null; exit 0' EXIT INT TERM
|
|
234
|
+
|
|
235
|
+
clear
|
|
236
|
+
|
|
237
|
+
while true; do
|
|
238
|
+
buf=$(render_all 2>&1)
|
|
239
|
+
tput cup 0 0 2>/dev/null
|
|
240
|
+
echo "$buf"
|
|
241
|
+
tput ed 2>/dev/null
|
|
242
|
+
sleep 3
|
|
243
|
+
done
|