forge-pipeline 0.3.1 → 0.4.1

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.
Files changed (3) hide show
  1. package/forge +166 -11
  2. package/lib/utils.sh +1 -0
  3. package/package.json +1 -1
package/forge CHANGED
@@ -56,6 +56,7 @@ Usage:
56
56
  forge start <task description> Run the full pipeline
57
57
  forge run <task description> Alias for start
58
58
  forge status Show current phase and active agents
59
+ forge dashboard Live-updating agent dashboard
59
60
  forge logs [agent-name] Tail an agent's log
60
61
  forge abort Kill all agents and cleanup
61
62
  forge clean Remove .forge/ directory and branches
@@ -245,16 +246,16 @@ show_plan_summary() {
245
246
  }
246
247
 
247
248
  wait_for_approval() {
248
- printf "${BOLD}${YELLOW}Approve this plan and start implementation? [Y/n] ${RESET}"
249
+ printf "${BOLD}${YELLOW}Approve and continue? [Y/n] ${RESET}"
249
250
  local answer
250
251
  read -r answer </dev/tty
251
252
  case "$answer" in
252
253
  n|N|no|No|NO)
253
- log_info "Aborted. Edit .forge/spec/spec.md or .forge/plan.json, then run 'forge start' again."
254
+ log_info "Aborted. Edit .forge/spec/spec.md, then run 'forge start' again."
254
255
  exit 0
255
256
  ;;
256
257
  *)
257
- log_success "Plan approved. Starting autonomous execution..."
258
+ log_success "Approved. Continuing..."
258
259
  echo ""
259
260
  ;;
260
261
  esac
@@ -314,6 +315,145 @@ forge_status() {
314
315
  echo ""
315
316
  }
316
317
 
318
+ ###############################################################################
319
+ # Dashboard command — live-updating agent overview
320
+ ###############################################################################
321
+ forge_dashboard() {
322
+ local forge_root
323
+ forge_root="$(get_forge_root)"
324
+ local refresh="${1:-2}"
325
+
326
+ trap 'tput cnorm 2>/dev/null; exit 0' INT TERM
327
+ tput civis 2>/dev/null # hide cursor
328
+
329
+ while true; do
330
+ clear
331
+
332
+ # ── Header ──
333
+ printf "${BOLD}${CYAN}╔═══════════════════════════════════════════════════════════════╗${RESET}\n"
334
+ printf "${BOLD}${CYAN}║ FORGE DASHBOARD $(date '+%H:%M:%S') ║${RESET}\n"
335
+ printf "${BOLD}${CYAN}╚═══════════════════════════════════════════════════════════════╝${RESET}\n"
336
+ echo ""
337
+
338
+ # ── Phase ──
339
+ local phase="unknown"
340
+ if [ -f "$forge_root/.forge/status/finalize.done" ]; then
341
+ phase="${GREEN}${BOLD}COMPLETE${RESET}"
342
+ elif [ -f "$forge_root/.forge/status/fix.done" ]; then
343
+ phase="${YELLOW}Fix cycle${RESET}"
344
+ elif [ -f "$forge_root/.forge/status/audit.done" ]; then
345
+ phase="${YELLOW}Audit${RESET}"
346
+ elif [ -f "$forge_root/.forge/status/integrate.done" ]; then
347
+ phase="${YELLOW}Integration${RESET}"
348
+ elif [ -f "$forge_root/.forge/status/implement.done" ]; then
349
+ phase="${YELLOW}Implementation done${RESET}"
350
+ elif [ -f "$forge_root/.forge/status/plan.done" ]; then
351
+ phase="${YELLOW}Planning done${RESET}"
352
+ elif [ -f "$forge_root/.forge/status/spec.done" ]; then
353
+ phase="${YELLOW}Spec done${RESET}"
354
+ else
355
+ phase="${CYAN}In progress...${RESET}"
356
+ fi
357
+ printf " ${BOLD}Phase:${RESET} %b\n" "$phase"
358
+
359
+ # ── Elapsed time ──
360
+ if [ -f "$forge_root/.forge/config.json" ]; then
361
+ local started_at
362
+ started_at="$(jq -r '.started_at // empty' "$forge_root/.forge/config.json" 2>/dev/null || true)"
363
+ if [ -n "$started_at" ]; then
364
+ local now elapsed_s elapsed_m elapsed_sec
365
+ now=$(date +%s)
366
+ elapsed_s=$((now - started_at))
367
+ elapsed_m=$((elapsed_s / 60))
368
+ elapsed_sec=$((elapsed_s % 60))
369
+ printf " ${BOLD}Elapsed:${RESET} %dm %ds\n" "$elapsed_m" "$elapsed_sec"
370
+ fi
371
+ fi
372
+ echo ""
373
+
374
+ # ── Active agents ──
375
+ printf "${BOLD} %-28s %-10s %s${RESET}\n" "AGENT" "STATUS" "LAST ACTIVITY"
376
+ printf " %-28s %-10s %s\n" "────────────────────────────" "──────────" "──────────────────────────────"
377
+
378
+ local sessions
379
+ sessions="$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep "^forge-" || true)"
380
+
381
+ if [ -n "$sessions" ]; then
382
+ while IFS= read -r session; do
383
+ local agent_name="${session#forge-}"
384
+ local status_icon="${GREEN}● running${RESET}"
385
+ local last_line=""
386
+
387
+ # Check for log file
388
+ local log_file="$forge_root/.forge/logs/${agent_name}.log"
389
+ if [ -f "$log_file" ]; then
390
+ # Get last non-empty line, strip ANSI codes, truncate
391
+ last_line="$(tail -5 "$log_file" 2>/dev/null | grep -v '^$' | tail -1 | sed 's/\x1b\[[0-9;]*m//g' | head -c 50 || true)"
392
+ fi
393
+
394
+ printf " %-28s %b %s\n" "$agent_name" "$status_icon" "$last_line"
395
+ done <<< "$sessions"
396
+ fi
397
+
398
+ # ── Completed agents ──
399
+ local done_files
400
+ done_files="$(find "$forge_root/.forge/status" -name "*.done" -not -name "spec.done" -not -name "plan.done" -not -name "implement.done" -not -name "integrate.done" -not -name "audit.done" -not -name "fix.done" -not -name "finalize.done" 2>/dev/null || true)"
401
+
402
+ if [ -n "$done_files" ]; then
403
+ while IFS= read -r done_file; do
404
+ local agent_name
405
+ agent_name="$(basename "$done_file" .done)"
406
+
407
+ # Skip if still running in tmux
408
+ if tmux has-session -t "forge-${agent_name}" 2>/dev/null; then
409
+ continue
410
+ fi
411
+
412
+ printf " %-28s ${DIM}✓ done${RESET}\n" "$agent_name"
413
+ done <<< "$done_files"
414
+ fi
415
+
416
+ if [ -z "$sessions" ] && [ -z "$done_files" ]; then
417
+ printf " ${DIM}(no agents yet)${RESET}\n"
418
+ fi
419
+
420
+ echo ""
421
+
422
+ # ── Progress bar ──
423
+ local total_done total_expected
424
+ total_done=$(find "$forge_root/.forge/status" -name "*.done" 2>/dev/null | wc -l | tr -d ' ')
425
+ total_expected=$(find "$forge_root/.forge/status" -name "*.expected" 2>/dev/null | wc -l | tr -d ' ')
426
+ local running_count=0
427
+ [ -n "$sessions" ] && running_count=$(echo "$sessions" | wc -l | tr -d ' ')
428
+
429
+ printf " ${BOLD}Running:${RESET} %d ${BOLD}Done:${RESET} %d" "$running_count" "$total_done"
430
+ if [ "$total_expected" -gt 0 ]; then
431
+ printf " / %d" "$total_expected"
432
+ fi
433
+ echo ""
434
+
435
+ # ── Progress bar visual ──
436
+ if [ "$total_expected" -gt 0 ]; then
437
+ local pct=$((total_done * 100 / total_expected))
438
+ local bar_width=40
439
+ local filled=$((pct * bar_width / 100))
440
+ local empty=$((bar_width - filled))
441
+ printf " ["
442
+ printf "${GREEN}"
443
+ for ((b=0; b<filled; b++)); do printf "█"; done
444
+ printf "${RESET}${DIM}"
445
+ for ((b=0; b<empty; b++)); do printf "░"; done
446
+ printf "${RESET}"
447
+ printf "] %d%%\n" "$pct"
448
+ fi
449
+
450
+ echo ""
451
+ printf "${DIM} Refreshing every %ds. Press Ctrl+C to exit.${RESET}\n" "$refresh"
452
+
453
+ sleep "$refresh"
454
+ done
455
+ }
456
+
317
457
  ###############################################################################
318
458
  # Logs command
319
459
  ###############################################################################
@@ -440,6 +580,8 @@ while [[ $# -gt 0 ]]; do
440
580
  ;;
441
581
  status)
442
582
  SUBCOMMAND="status"; shift; break ;;
583
+ dashboard)
584
+ SUBCOMMAND="dashboard"; shift; break ;;
443
585
  logs)
444
586
  SUBCOMMAND="logs"; shift
445
587
  LOGS_AGENT="${1:-}"
@@ -498,6 +640,24 @@ run_pipeline() {
498
640
  run_phase_spec "$user_prompt" || { log_error "Phase 0 failed"; exit 1; }
499
641
  check_global_timeout
500
642
 
643
+ # ── Approval gate (after spec, before planning + execution) ──
644
+ if [ "$AUTO_APPROVE" = true ]; then
645
+ log_info "Auto-approve enabled, skipping review."
646
+ else
647
+ echo ""
648
+ printf "${BOLD}${CYAN}═══════════════════════════════════════${RESET}\n"
649
+ printf "${BOLD}${CYAN} SPEC REVIEW${RESET}\n"
650
+ printf "${BOLD}${CYAN}═══════════════════════════════════════${RESET}\n"
651
+ echo ""
652
+ echo " The spec has been written to:"
653
+ echo " ${BOLD}.forge/spec/spec.md${RESET}"
654
+ echo ""
655
+ echo " Review it, then approve to continue."
656
+ echo " If you edit the spec, changes will be used as-is."
657
+ echo ""
658
+ wait_for_approval
659
+ fi
660
+
501
661
  # Phase 1: Planning
502
662
  run_phase_plan || { log_error "Phase 1 failed"; exit 1; }
503
663
  check_global_timeout
@@ -508,14 +668,6 @@ run_pipeline() {
508
668
  exit 0
509
669
  fi
510
670
 
511
- # ── Approval gate ─────────────────────────────────────────────
512
- if [ "$AUTO_APPROVE" = true ]; then
513
- log_info "Auto-approve enabled, skipping review."
514
- else
515
- show_plan_summary "$FORGE_ROOT"
516
- wait_for_approval
517
- fi
518
-
519
671
  # ── From here on, fully autonomous ────────────────────────────
520
672
 
521
673
  # Phase 2: Implementation
@@ -570,6 +722,9 @@ case "${SUBCOMMAND:-}" in
570
722
  status)
571
723
  forge_status
572
724
  ;;
725
+ dashboard)
726
+ forge_dashboard
727
+ ;;
573
728
  logs)
574
729
  forge_logs "${LOGS_AGENT:-}"
575
730
  ;;
package/lib/utils.sh CHANGED
@@ -14,6 +14,7 @@ YELLOW='\033[0;33m'
14
14
  BLUE='\033[0;34m'
15
15
  CYAN='\033[0;36m'
16
16
  BOLD='\033[1m'
17
+ DIM='\033[2m'
17
18
  RESET='\033[0m'
18
19
 
19
20
  # ── Project root ─────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-pipeline",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Autonomous multi-agent coding pipeline",
5
5
  "bin": {
6
6
  "forge": "./bin/forge.js"