@walwal-harness/cli 4.0.0-alpha.7 → 4.0.0-alpha.8

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-alpha.7",
3
+ "version": "4.0.0-alpha.8",
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"
@@ -149,10 +149,54 @@ render_feature_list() {
149
149
  echo ""
150
150
  }
151
151
 
152
+ render_prompt_history() {
153
+ local log_file="$PROJECT_ROOT/.harness/progress.log"
154
+ if [ ! -f "$log_file" ]; then return; fi
155
+
156
+ # Get terminal height to limit display
157
+ local term_h
158
+ term_h=$(tput lines 2>/dev/null || echo 50)
159
+ local max_lines=10 # show latest 10 entries
160
+
161
+ echo -e " ${BOLD}Prompt History${RESET} ${DIM}(newest first)${RESET}"
162
+
163
+ # Read non-comment lines, reverse (newest first), take max_lines
164
+ grep -v '^#' "$log_file" 2>/dev/null | grep -v '^$' | tail -r 2>/dev/null | head -"$max_lines" | \
165
+ while IFS= read -r line; do
166
+ # Parse: date | agent | action | detail
167
+ local ts agent action detail
168
+ ts=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$1); print $1}')
169
+ agent=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')
170
+ action=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$3); print $3}')
171
+ detail=$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$4); print $4}')
172
+
173
+ local short_ts icon color
174
+ short_ts=$(echo "$ts" | sed 's/^[0-9]*-//')
175
+
176
+ case "$agent" in
177
+ dispatcher*|dispatch) icon="▸"; color="$MAGENTA" ;;
178
+ team-*) icon="⚡"; color="$CYAN" ;;
179
+ manual|user) icon="★"; color="$BOLD" ;;
180
+ planner*) icon="□"; color="$YELLOW" ;;
181
+ generator*|gen*) icon="▶"; color="$GREEN" ;;
182
+ eval*) icon="✦"; color="$RED" ;;
183
+ system) icon="⚙"; color="$DIM" ;;
184
+ *) icon="·"; color="$DIM" ;;
185
+ esac
186
+
187
+ if [ ${#detail} -gt 45 ]; then detail="${detail:0:43}.."; fi
188
+
189
+ echo -e " ${color}${icon}${RESET} ${DIM}${short_ts}${RESET} ${agent} ${DIM}${action}${RESET} ${detail}"
190
+ done
191
+
192
+ echo ""
193
+ }
194
+
152
195
  render_all() {
153
196
  render_header
154
197
  render_queue_summary
155
198
  render_teams
199
+ render_prompt_history
156
200
  render_feature_list
157
201
  echo -e " ${DIM}Refreshing every 3s${RESET}"
158
202
  }
@@ -42,6 +42,25 @@ fi
42
42
  FEATURES="$PROJECT_ROOT/.harness/actions/feature-list.json"
43
43
  QUEUE="$PROJECT_ROOT/.harness/actions/feature-queue.json"
44
44
  CONFIG="$PROJECT_ROOT/.harness/config.json"
45
+ QUEUE_LOCK="$PROJECT_ROOT/.harness/.queue-lock"
46
+
47
+ # ── Atomic queue lock — prevent race conditions between teams ──
48
+ acquire_queue_lock() {
49
+ local max_wait=30 waited=0
50
+ while ! mkdir "$QUEUE_LOCK" 2>/dev/null; do
51
+ sleep 0.1
52
+ waited=$((waited + 1))
53
+ if [ "$waited" -ge $((max_wait * 10)) ]; then
54
+ rm -rf "$QUEUE_LOCK"
55
+ mkdir "$QUEUE_LOCK" 2>/dev/null || true
56
+ break
57
+ fi
58
+ done
59
+ }
60
+
61
+ release_queue_lock() {
62
+ rm -rf "$QUEUE_LOCK" 2>/dev/null || true
63
+ }
45
64
 
46
65
  # ── Concurrency from config ──
47
66
  CONCURRENCY=3
@@ -122,12 +141,14 @@ cmd_dequeue() {
122
141
  if [ -z "$team_id" ]; then echo "[queue] Usage: dequeue <team_id>"; exit 1; fi
123
142
  if [ ! -f "$QUEUE" ]; then echo "[queue] Run 'init' first."; exit 1; fi
124
143
 
144
+ acquire_queue_lock
145
+
125
146
  local feature
126
147
  feature=$(jq -r '.queue.ready[0] // empty' "$QUEUE")
127
148
 
128
149
  if [ -z "$feature" ]; then
150
+ release_queue_lock
129
151
  echo "[queue] No features in ready queue."
130
- # Check if all done
131
152
  local in_prog blocked
132
153
  in_prog=$(jq '.queue.in_progress | length' "$QUEUE")
133
154
  blocked=$(jq '.queue.blocked | length' "$QUEUE")
@@ -137,13 +158,13 @@ cmd_dequeue() {
137
158
  return 1
138
159
  fi
139
160
 
140
- # Move feature from ready → in_progress, assign to team
141
161
  jq --arg fid "$feature" --arg tid "$team_id" '
142
162
  .queue.ready -= [$fid] |
143
163
  .queue.in_progress[$fid] = { team: ($tid | tonumber), phase: "gen", attempt: 1 } |
144
164
  .teams[$tid] = { status: "busy", feature: $fid, branch: ("feature/" + $fid), pid: null }
145
165
  ' "$QUEUE" > "${QUEUE}.tmp" && mv "${QUEUE}.tmp" "$QUEUE"
146
166
 
167
+ release_queue_lock
147
168
  echo "$feature"
148
169
  }
149
170
 
@@ -155,11 +176,11 @@ cmd_pass() {
155
176
  if [ -z "$fid" ]; then echo "[queue] Usage: pass <feature_id>"; exit 1; fi
156
177
  if [ ! -f "$QUEUE" ]; then echo "[queue] Run 'init' first."; exit 1; fi
157
178
 
158
- # Get team that was working on this feature
179
+ acquire_queue_lock
180
+
159
181
  local team_id
160
182
  team_id=$(jq -r --arg fid "$fid" '.queue.in_progress[$fid].team // empty' "$QUEUE")
161
183
 
162
- # Move from in_progress → passed, free team, unblock dependents
163
184
  jq --arg fid "$fid" --arg tid "${team_id:-0}" '
164
185
  # Remove from in_progress
165
186
  del(.queue.in_progress[$fid]) |
@@ -188,6 +209,7 @@ cmd_pass() {
188
209
 
189
210
  local newly_ready
190
211
  newly_ready=$(jq -r '.queue.ready | join(", ")' "$QUEUE")
212
+ release_queue_lock
191
213
  echo "[queue] $fid PASSED. Ready: [$newly_ready]"
192
214
  }
193
215
 
@@ -199,6 +221,8 @@ cmd_fail() {
199
221
  if [ -z "$fid" ]; then echo "[queue] Usage: fail <feature_id>"; exit 1; fi
200
222
  if [ ! -f "$QUEUE" ]; then exit 1; fi
201
223
 
224
+ acquire_queue_lock
225
+
202
226
  local team_id
203
227
  team_id=$(jq -r --arg fid "$fid" '.queue.in_progress[$fid].team // empty' "$QUEUE")
204
228
 
@@ -211,6 +235,7 @@ cmd_fail() {
211
235
  else . end)
212
236
  ' "$QUEUE" > "${QUEUE}.tmp" && mv "${QUEUE}.tmp" "$QUEUE"
213
237
 
238
+ release_queue_lock
214
239
  echo "[queue] $fid FAILED."
215
240
  }
216
241
 
@@ -239,6 +264,8 @@ cmd_update_phase() {
239
264
  exit 1
240
265
  fi
241
266
 
267
+ acquire_queue_lock
268
+
242
269
  local jq_expr
243
270
  jq_expr=".queue.in_progress[\"$fid\"].phase = \"$phase\""
244
271
  if [ -n "$attempt" ]; then
@@ -246,6 +273,7 @@ cmd_update_phase() {
246
273
  fi
247
274
 
248
275
  jq "$jq_expr" "$QUEUE" > "${QUEUE}.tmp" && mv "${QUEUE}.tmp" "$QUEUE"
276
+ release_queue_lock
249
277
  }
250
278
 
251
279
  # ══════════════════════════════════════════