#!/bin/bash # Foxhunt statusline — git, build, argo, GPU nodes, ruflo session # K8s data cached to /tmp to avoid slow kubectl on every refresh set -euo pipefail INPUT=$(cat) CWD=$(echo "$INPUT" | jq -r '.workspace.current_dir // .cwd') cd "$CWD" 2>/dev/null || exit 0 # --- Colors --- R='\033[0m' B='\033[1m' DIM='\033[2m' RED='\033[31m' GRN='\033[32m' YEL='\033[33m' BLU='\033[34m' MAG='\033[35m' CYN='\033[36m' # --- K8s cache (refresh every 30s) --- K8S_CACHE="/tmp/.foxhunt-statusline-k8s" K8S_AGE=999 [ -f "$K8S_CACHE" ] && K8S_AGE=$(( $(date +%s) - $(stat -c %Y "$K8S_CACHE") )) if [ "$K8S_AGE" -gt 60 ]; then { # Argo: latest workflow WF=$(argo list -n foxhunt --no-headers 2>/dev/null | head -1) WF_NAME=$(echo "$WF" | awk '{print $1}') WF_STATUS=$(echo "$WF" | awk '{print $2}') WF_AGE=$(echo "$WF" | awk '{print $3}') # GPU nodes by pool name H100=$(kubectl get nodes -l k8s.scaleway.com/pool-name=ci-training-h100 --no-headers 2>/dev/null | wc -l | tr -d ' ') L40S=$(kubectl get nodes -l k8s.scaleway.com/pool-name=ci-training --no-headers 2>/dev/null | wc -l | tr -d ' ') PLATFORM=$(kubectl get nodes -l k8s.scaleway.com/pool-name=platform --no-headers 2>/dev/null | wc -l | tr -d ' ') # Ruflo daemon: parse status table for daemon state + active workers RUFLO_RAW=$(timeout 5 npx ruflo@latest daemon status 2>/dev/null || echo "") RUFLO_DAEMON="off" RUFLO_WORKERS_ON=0 RUFLO_WORKERS_TOTAL=0 RUFLO_WORKERS_RUNNING="" if echo "$RUFLO_RAW" | grep -q "RUNNING"; then RUFLO_DAEMON="on" fi # Count enabled workers and collect running ones while IFS='|' read -r _ name on status _ _ _ _; do name=$(echo "$name" | tr -d ' ') on=$(echo "$on" | tr -d ' ') status=$(echo "$status" | tr -d ' ') [ -z "$name" ] || [ "$name" = "Worker" ] && continue RUFLO_WORKERS_TOTAL=$((RUFLO_WORKERS_TOTAL + 1)) if [ "$on" = "✓" ]; then RUFLO_WORKERS_ON=$((RUFLO_WORKERS_ON + 1)) if [ "$status" = "running" ]; then RUFLO_WORKERS_RUNNING="${RUFLO_WORKERS_RUNNING}${name}," fi fi done <<< "$(echo "$RUFLO_RAW" | grep '|' | grep -v '^+' | grep -v 'Worker')" echo "WF_NAME=$WF_NAME" echo "WF_STATUS=$WF_STATUS" echo "WF_AGE=$WF_AGE" echo "H100=$H100" echo "L40S=$L40S" echo "PLATFORM=$PLATFORM" echo "RUFLO_DAEMON=$RUFLO_DAEMON" echo "RUFLO_WORKERS_ON=$RUFLO_WORKERS_ON" echo "RUFLO_WORKERS_TOTAL=$RUFLO_WORKERS_TOTAL" echo "RUFLO_WORKERS_RUNNING=$RUFLO_WORKERS_RUNNING" } > "$K8S_CACHE" 2>/dev/null || true fi source "$K8S_CACHE" 2>/dev/null || true # --- Git --- BRANCH=$(git branch --show-current 2>/dev/null || echo "") DIRTY=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ') # --- Build freshness --- BUILD_AGE="" for profile in dev-release release; do STAMP=$(find "target/$profile" -name '*.d' -newer Cargo.lock -print -quit 2>/dev/null) if [ -n "$STAMP" ]; then MTIME=$(stat -c %Y "$STAMP" 2>/dev/null || echo 0) MINS=$(( ($(date +%s) - MTIME) / 60 )) if [ "$MINS" -lt 60 ]; then BUILD_AGE="${MINS}m" elif [ "$MINS" -lt 1440 ]; then BUILD_AGE="$(( MINS / 60 ))h" else BUILD_AGE="$(( MINS / 1440 ))d"; fi break fi done # --- Service binaries --- BINS=0 for svc in api trading_service ml_training_service backtesting_service broker_gateway_service data_acquisition_service trading_agent_service fxt; do { [ -f "target/release/$svc" ] || [ -f "target/dev-release/$svc" ]; } && BINS=$((BINS + 1)) done # --- Ruflo session --- SESSION_FILE="$CWD/.claude-flow/sessions/current.json" S_EDITS=0 S_CMDS=0 S_TASKS=0 S_ERRS=0 if [ -f "$SESSION_FILE" ]; then S_EDITS=$(jq -r '.metrics.edits // 0' "$SESSION_FILE" 2>/dev/null) S_CMDS=$(jq -r '.metrics.commands // 0' "$SESSION_FILE" 2>/dev/null) S_TASKS=$(jq -r '.metrics.tasks // 0' "$SESSION_FILE" 2>/dev/null) S_ERRS=$(jq -r '.metrics.errors // 0' "$SESSION_FILE" 2>/dev/null) fi # --- Ruflo agents --- AGENTS_FILE="$CWD/.claude-flow/metrics/swarm-activity.json" AGENT_COUNT=0 SWARM_ACTIVE="" if [ -f "$AGENTS_FILE" ]; then AGENT_COUNT=$(jq -r '.processes.estimated_agents // 0' "$AGENTS_FILE" 2>/dev/null) SA=$(jq -r '.swarm.active // false' "$AGENTS_FILE" 2>/dev/null) [ "$SA" = "true" ] && SWARM_ACTIVE="1" fi # --- Ruflo learning --- LEARN_FILE="$CWD/.claude-flow/metrics/learning.json" PATTERNS=0 if [ -f "$LEARN_FILE" ]; then ST=$(jq -r '.patterns.shortTerm // 0' "$LEARN_FILE" 2>/dev/null) LT=$(jq -r '.patterns.longTerm // 0' "$LEARN_FILE" 2>/dev/null) PATTERNS=$((ST + LT)) fi # ============= OUTPUT ============= # Git printf "${B}foxhunt${R}" [ -n "$BRANCH" ] && printf " ${YEL}$BRANCH${R}" [ "$DIRTY" -gt 0 ] && printf " ${RED}${DIRTY}d${R}" # Build printf " ${DIM}|${R}" if [ -n "$BUILD_AGE" ]; then printf " ${GRN}build:${BUILD_AGE}${R}" else printf " ${DIM}no build${R}" fi printf " ${CYN}${BINS}/8svc${R}" # Argo workflow printf " ${DIM}|${R}" if [ -n "${WF_STATUS:-}" ] && [ "$WF_STATUS" != "" ]; then case "$WF_STATUS" in Succeeded) printf " ${GRN}argo:ok${R}" ;; Running) printf " ${YEL}argo:run${R}" ;; Failed) printf " ${RED}argo:FAIL${R}" ;; Pending) printf " ${YEL}argo:wait${R}" ;; *) printf " ${DIM}argo:${WF_STATUS}${R}" ;; esac [ -n "${WF_AGE:-}" ] && printf "${DIM}(${WF_AGE})${R}" else printf " ${DIM}argo:--${R}" fi # GPU nodes printf " ${DIM}|${R}" if [ "${H100:-0}" -gt 0 ]; then printf " ${GRN}H100:${H100}${R}" else printf " ${DIM}H100:0${R}" fi if [ "${L40S:-0}" -gt 0 ]; then printf " ${GRN}L40S:${L40S}${R}" else printf " ${DIM}L40S:0${R}" fi printf " ${CYN}cpu:${PLATFORM:-?}${R}" # Ruflo daemon + workers printf " ${DIM}|${R}" if [ "${RUFLO_DAEMON:-off}" = "on" ]; then printf " ${GRN}ruflo${R}" # Show running workers or idle count if [ -n "${RUFLO_WORKERS_RUNNING:-}" ]; then RUNNING_LIST=$(echo "$RUFLO_WORKERS_RUNNING" | sed 's/,$//' | tr ',' ' ') printf "${DIM}(${R}${YEL}${RUNNING_LIST}${R}${DIM})${R}" else printf "${DIM}(${RUFLO_WORKERS_ON:-0}w idle)${R}" fi else printf " ${RED}ruflo:off${R}" fi # Ruflo session activity + swarm if [ "$((S_EDITS + S_CMDS + S_TASKS))" -gt 0 ] || [ -n "$SWARM_ACTIVE" ] || [ "$AGENT_COUNT" -gt 0 ] || [ "$PATTERNS" -gt 0 ]; then [ "$S_EDITS" -gt 0 ] && printf " ${MAG}${S_EDITS}ed${R}" [ "$S_CMDS" -gt 0 ] && printf " ${CYN}${S_CMDS}cmd${R}" [ "$S_TASKS" -gt 0 ] && printf " ${GRN}${S_TASKS}task${R}" [ "$S_ERRS" -gt 0 ] && printf " ${RED}${S_ERRS}err${R}" if [ -n "$SWARM_ACTIVE" ]; then printf " ${MAG}swarm:${AGENT_COUNT}ag${R}" elif [ "$AGENT_COUNT" -gt 0 ]; then printf " ${DIM}${AGENT_COUNT}ag${R}" fi [ "$PATTERNS" -gt 0 ] && printf " ${MAG}${PATTERNS}pat${R}" fi echo